質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.62%

New-Module と Export-ModuleMembet がどういうものか教えてください。

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 728

kamikazelight

score 172

--追記--

imihito様より 「自分の環境でもISEからはエラーは出ないですね」との情報を頂いたので別PCで確認を取ったところそちらのPCではエラーは出ませんでした。
1/18 時間があれば再度 該当PCにて検証したいと思います。
検証した結果エラーは出なくなっていました。

前提・実現したいこと

前回の質問でimihito様に頂いたコードの中に
New-Module と Export-ModuleMembet が使われているのですが
どういうものだかよくわかりません。
公式のヘルプを見たのですがそれでもわかりません。

触ってみてカプセル化のようなものだと検討をつけて
試に 頂いたコードを書き換えて タイマーをスクリプトブロック内に入れて
みたのですが
動作的には正しく動くのですがエラーが頻発します。

New-Module と Export-ModuleMembetの正しい使い方、及び挙動と
何故 該当のソースコードの内容でエラーが出てしまうのか教えて頂きたいです。
お願い致します。

発生している問題・エラーメッセージ

"Opacity" の設定中に例外が発生しました: "破棄されたオブジェクトにアクセスできません。
オブジェクト名 'Form' です。"
発生場所 D:\Users\Desktop\Formサンプル.ps1:60 文字:13
+             $mainForm.Opacity = $newOpacity
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], SetValueInvocationException
    + FullyQualifiedErrorId : ExceptionWhenSetting

該当のソースコード

using namespace System.Windows.Forms
using namespace System.Drawing

Set-StrictMode -Version Latest

[scriptblock]$_Get_Form = {
    #フォームの作成
    [Form]$Form = 
        New-Object -TypeName Form -Property @{
            Width   = 600
            Height  = 300
            Opacity = 0.1
            Text    = 'Test'
            TopMost = $true
        }

    #データグリッドビューの作成
    [string[]]$ListName = @('test1', 'test2')
    [DataGridView[]]$List = 
        $ListName | 
            ForEach-Object -Process {
                # 共通の設定は生成時に`-Property`で設定
                New-Object -TypeName DataGridView -Property @{
                    AutoSizeColumnsMode = [DataGridViewAutoSizeColumnsMode]::AllCells
                    AutoSizeRowsMode    = [DataGridViewAutoSizeColumnsMode]::AllCells
                    Name = 'List_{0}' -f $_
                    Dock = [DockStyle]::Fill
                }
            }
    #$List[0].Add_SelectionChanged($_ListNowUp)
    $List[0].MultiSelect = $false
    #$List[1].Columns[0].DefaultCellStyle.WrapMode = [DataGridViewTriState]::True

    [SplitContainer]$SplitContainer = [SplitContainer]::new()
    $SplitContainer.Panel1.Controls.Add($List[0])
    $SplitContainer.Panel2.Controls.Add($List[1])
    $SplitContainer.Name = "SplitContainer_1"
    $Form.Controls.Add($SplitContainer)

    [Timer]$mouseMonitor = 
        New-Object -TypeName Timer -Property @{
            Interval = 50
            Enabled  = $true
            Tag      = $Form
        }
    $X = 1 # こちらの$XをAとする
    $mouseMonitor.add_Tick({
        Write-host $X # Aの$Xの値が出力される
        [Form]$mainForm = $Form # [Form]::ActiveForm

        # タイトルバー含めたフォームの大きさは`Bounds`から、
        # マウスの位置は`[Cursor]::Position`から取得できるため
        # フォームの中に、マウスが入っているかどうかで確認
        [Double]$newOpacity =
            if ($mainForm.Bounds.Contains([Cursor]::Position)) {
                1
            } else {
                0.1
            }

        if ($mainForm.Opacity -ne $newOpacity) { # 負荷軽減のため(意味があるか未確認)
            $mainForm.Opacity = $newOpacity
        }
        $Script:X += 10 # Aの$Xの値が加算される 2回目以降の実行結果から値が保持されていることが確認できる
    })

    Export-ModuleMember -Variable Form, mouseMonitor, List
}

# `Export-ModuleMember`で設定した要素(Form, List)をメンバーとして持つPSCustomObjectを生成($Scirpt:~を避けたかったため)
[psobject]$formContainer = New-Module -AsCustomObject -ScriptBlock $_Get_Form

# タイマーの生成
$X = 2 # こちらの$XをBとする
$formContainer.mouseMonitor.Start()
$formContainer.Form.ShowDialog() > $null
$formContainer.Form.Dispose()
$formContainer.mouseMonitor.Dispose() # Formとの紐付けが無いため、忘れないように

補足情報(FW/ツールのバージョンなど)

Win10

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

まずはじめに。トリッキーなコードを書いてしまい、混乱させてしまったようですみません。

New-Module -AsCustomObject ~の使い方は一般的な使い方では無いため、基本のモジュールの話から簡単に書いておきます。

about_Modules | Microsoft Docs
about_Modules 非公式?日本語版

PowerShell のモジュール詳解とモジュールへのコマンドレット配置手法を考える - tech.guitarrapc.cóm

VBAを触っていらっしゃっているのでVBAの概念で例えると、標準モジュールがイメージにもっとも近いです。
任意の変数・関数を格納でき、必要に応じてそれらを外部に公開できるものです。

PowerShellのモジュールの場合、動的なロード・アンロード、引数を渡しての初期化なども行えますが、通常使う分には上記のようなイメージです。

中の変数・関数のうち、「何を公開するか」の指定に使用するのが、質問の一つであるExport-ModuleMemberです。


PowerShellのモジュールは、通常は$env:PSModulePath以下にフォルダとして配置します。
フォルダ名がモジュール名となり、フォルダの中に「モジュール名.psm1」や「モジュール名.psd1」といったファイルを配置することでモジュールとして読み込めるようになります。

質問のもう一つであるNew-Moduleは、ファイルとしてのスクリプトの代わりにスクリプトブロックを読み込んで、動的にモジュールを生成するものです。


普通に読み込んだモジュールはExport-ModuleMemberで指定された変数・関数を公開するだけです。
しかし-AsCustomObjectスイッチを設定することで、指定された変数・関数をメンバーとして持つPSCustomObjectを生成します。
これもVBAに例えると、標準モジュールのコードをクラスモジュールに書いてインスタンスできるようにした、というイメージです。

前回の質問では、
変数のスコープがややわかりにくい、などの理由から、スコープを切り分けたい・カプセル化したいと考えました。

その方法として、クラスなどいくつか候補がありましたが、処理がスクリプトブロックの形でまとまっていたため、比較的変更量が少ないNew-Module -AsCustomObjectを使用した、ということになります。

New-Module自体はモジュールを生成するためのものであるため、そのあたりでどうしてもトリッキーな書き方となってしまいました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/18 12:01

    変数の中に変数や関数などを格納できるところに魅力を感じていたのですが
    その場合は Classを使った方がいいでしょうか?

    今までは ClassA.ps1 に ClassA を記載して Main.ps1 でClassAのインスタンスを作成しようとしたときに
    事前にドットソースで読み込んでもインテリセンスが効かず
    苦肉の策でClassA.ps1にClassAのインスタンスを作成する関数を記載して Main.ps1でその関数を呼び出して使ったりとかだったのでClassを極力避けていたのですが
    今回の件で考えなおしてみたらClassA.ps1をClassAのインスタンスを返すようにしてしまえば
    Static とかは意味がなくなってしまいますが
    変数の中に変数や関数などを格納したいという目的は達成できそうです。

    ですがこれも一般的な使い方から外れていると思うのですが

    こういった場合は
    Class と New-Module どちらを使った方が見やすい(扱いやすい)と思いますか?

    キャンセル

  • 2019/01/18 20:59

    New-ModuleはPowerShell v2からありましたが、その上でPowerShell v5でクラス構文が追加されました。という事でクラスの方が良いと思われます。

    PowerShell v5はあまり使ったことがありませんが、`using module モジュール名`でモジュール内のクラスのインテリセンスは効きそうですね

    キャンセル

  • 2019/01/19 09:42 編集

    インテリセンスが効かないと思っていたのですが
    そういうことだったのですね
    試してみます。
    しばらくはクラスを使ってみることにします。
    今回も ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.62%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る