PowerShellクラスの.NETタイプ

Oct 23 2020

PowerShellクラスのベストプラクティスに頭を悩ませようとしていますが、バルーンのヒントを処理するための単純なクラスとの混乱に直面しています。

Add-Type -assemblyName:System.Drawing
Add-Type -assemblyName:System.Windows.Forms

class PxMessage {
    static [PxMessage] $instance static $balloon
    static $defaultIcon static [PxMessage] GetInstance($processIcon) {
        if ([PxMessage]::instance -eq $null) { [PxMessage]::instance = [PxMessage]::new() #[PxMessage]::balloon = [Windows.Forms.NotifyIcon]::new() [PxMessage]::balloon = New-Object Windows.Forms.NotifyIcon [PxMessage]::defaultIcon = $processIcon
        }

        return [PxMessage]::instance
    }

    [Void] SendMessage ([String]$title, [String]$message, [String]$messageIcon) { [PxMessage]::balloon.icon = [PxMessage]::defaultIcon [PxMessage]::balloon.balloonTipTitle = $title
        [PxMessage]::balloon.balloonTipText = $message [PxMessage]::balloon.balloonTipIcon = $messageIcon
        [PxMessage]::balloon.visible = $true [PxMessage]::balloon.ShowBalloonTip(0) [PxMessage]::balloon.Dispose } } $processIcon = [System.Drawing.Icon]::ExtractAssociatedIcon($(Get-Process -id:$PID | Select-Object -expandProperty:path))
$message = [PxMessage]::GetInstance($processIcon)

$message.SendMessage('Title', "$(Get-Date)", 'Info')

2つの質問があります:

1:なぜ機能する[PxMessage]::balloon = New-Object Windows.Forms.NotifyIconのに、[PxMessage]::balloon = [Windows.Forms.NotifyIcon]::new()機能しない(Unable to find typeエラー)のですか?そして、これは、使用[Type]::new()がまだ完全にサポートされていないことを示唆していますか?一貫性を保つために、どこでもNew-Objectを使用する方が良いですか?または、少なくとも私自身のクラスのどこでも?

2:プロパティとパラメータを入力したいのですがUnable to find type、$balloon&$defaultIconプロパティを入力したとき、またはメソッドに$processIconパラメータを入力したときにもエラーが発生しますGetInstance。

明らかに、タイプが定義されていても、プロパティを入力できます。では、2つの[System.Drawing]&の違いは何[System.Windows.Forms]ですか?これはバグですか、それとも機能ですか?そして、同様に動作する他のタイプはありますか?

回答

4 MathiasR.Jessen Oct 22 2020 at 22:44

これは本質的に競合状態です!

PowerShellがスクリプトファイルの実行を開始すると、次の3つのフェーズが実行されます。

  • 構文解析
  • コンパイル
  • 実行

非常にまず最初に処理され、コンパイルフェーズは、(その前に実行も始まる)は、次のとおりです。

  • usingスクリプトの先頭にあるすべてのステートメント
  • 任意の型定義(任意classまたはenumキーワード)は個別にコンパイルされます

したがって、[Windows.Forms.NotifyIcon]クラス定義内の型リテラルの解決に関連するエラーは、実行される前 Add-Type -AssemblyName:System.Windows.Formsに実際にスローされます。

オプションのカップル:

ネストされたスクリプト

依存関係をロードする別のローダースクリプトを記述します。

# loader.ps1
Add-Type -AssemblyName System.Windows.Forms,System.Drawing
. .\scriptWithTypes.ps1
# scriptWithTypes.ps1
class ClassDependentOnForms
{
  [Windows.Forms.NotifyIcon]$BalloonTipIcon
}

モジュール付き

モジュールを使用すると、カスタムタイプ定義をコンパイルする前に依存関係を管理する方が少し簡単RequiredAssembliesです。モジュールマニフェストにアセンブリ名を追加するだけです。

New-ModuleManifest ... -RootModule moduleWithTypes.psm1 -RequiredAssemblies System.Windows.Forms,System.Drawing

ディスクからロード using assembly ...

必要なアセンブリのパスがわかっている場合は、次のusing assemblyステートメントを使用して解析時にロードできます。

using assembly '.\path\to\System.Drawing.dll'
using assembly '.\path\to\System.Windows.Forms.dll'

class FormsDependentClass
{
  [Windows.Forms.NotifyIcon]$BallonTipIcon
}

ユースケースの場合、GACから名前でアセンブリをロードするのではなく、アセンブリをハードコーディングする必要があるため、この最後の1つはあまり魅力的ではありません。


そもそもなぜこれが起こるのですか?

PowerShellの他のすべては、「一度に1つのステートメント」、つまり解釈であるため、この動作は少し混乱する可能性があります。

この例外の理由は、スクリプトと関数がカスタムパラメータタイプをカプセル化できるようにするためです。

param(
  [CustomEnumType]$Option ) begin { enum CustomEnumType { None Option1 Option2 } } end { # do something based on $Option
}

CustomEnumType解析時にこのプリエンプティブなコンパイルがないと、PowerShellは-Optionパラメーター引数のオートコンプリートと入力検証を提供できません-その型は存在しないためです