C#と諸々

C#がメインで他もまぁ諸々なブログです
おかしなこと書いてたら指摘してくれると嬉しいです(´・∀・`)
つーかコメント欲しい(´・ω・`)

2006/12/15 00:41
PowerShell関数は、通常、引数が二つで戻り値の型がvoid型であるデリゲートにしか、キャストすることができません。
ちょっと不便だと感じたので、PowerShell関数を任意のデリゲートにキャストするコマンドレットを作成しました。


【 0. 目次 】
1. Download
2. 解説
3. 使用例


【 1. Download 】
このコマンドレットは、YokoKen.PowerShell.Utilityというスナップインに含まれています。このスナップインは以下の記事にて公開しています。

YokoKen.PowerShell.Utility


【 2. 解説 】
[ 2.1 コマンド名 ]
このコマンドレットには、ConvertTo-Delegateという名前が付けられています。

[ 2.2 概要 ]
ConvertTo-Delegateコマンドレットは、指定したPowerShell関数 ( = ScriptBlockオブジェクト ) を、指定したデリゲートにキャストします。指定できるデリゲートに制限はありません。

[ 2.3 構文 ]
ConvertTo-Delegateコマンドレットの構文は以下のようになっています。

ConvertTo-Delegate [-Target] <System.Management.Automation.ScriptBlock> [-DelegateType] <System.Type> [[-ErrorNotifiedByDialog] <System.Boolean>]


[  パラメータ  ]
・Target <System.Management.Automation.ScriptBlock>
デリゲートにキャストするPowerShell関数を指定します。Targetパラメータに指定できるPowerShell関数に制限はありません。PowerShell関数の戻り値が、DelegateTypeパラメータに指定したデリゲートに定義されている戻り値の型と異なる場合でも、問題なく実行できます。
必須
:  true
位置 : 1
既定値 : -
パイプライン入力を許可する
: true (ByValue)
ワイルドカード文字を許可する : false
Nullを許可する : false

・DelegateType <System.Type>
PowerShell関数をキャストするデリゲートを指定します。DelegateTypeパラメータには、デリゲート ( = MulticastDelegate派生型 ) を指定してください。それ以外では例外が発生します。
必須
: true
位置 : 2
既定値 :  -
パイプライン入力を許可する
: false
ワイルドカード文字を許可する :  false
Nullを許可する :  false

・ErrorNotifiedByDialog <System.Boolean>
デリゲートオブジェクトとして実行されたPowerShell関数が、実行中に例外をスローした時、例外の情報をダイアログで通知するかどうかを指定します。falseを指定した場合は、PowerShellのホストアプリケーションから、通常通りに通知されます。規定値はfalseです。
必須
: false
位置 : 3
既定値 :  false
パイプライン入力を許可する
: false
ワイルドカード文字を許可する :  false
Nullを許可する :  false

[ 2.4 詳細 ]

・デリゲートオブジェクトとして呼び出されたPowerShell関数の引数
デリゲートオブジェクトとして呼び出されたPowerShell関数で、引数を受け取ることができます。ただし、$Args変数から取得する以外の手段で引数を取得することはできません。例えば、以下の例では引数を取得できず、例外が発生します。

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms");
$form1 = New-Object "System.Windows.Forms.Form";
function form1_Click([object] $sender, [EventArgs] $e)
{
    # デリゲートオブジェクトとして呼び出されると、$senderにはNullが入っています。
    ([System.Windows.Forms.Form]$sender).Text = "Hoge";
}

$clickHandler = ConvertTo-Delegate $Function:form1_Click System.EventHandler;
$form1.Add_Click($clickHandler);
[System.Windows.Forms.Application]::Run($form1);


正しく動作させるには、以下のように記述します。

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms");
$form1 = New-Object "System.Windows.Forms.Form";
function form1_Click
{
    # デリゲートオブジェクトとして呼び出されても、$Argsの各要素には適切な値が入っています。
    ([System.Windows.Forms.Form]$Args[0]).Text = "Hoge";
}

$clickHandler = ConvertTo-Delegate $Function:form1_Click System.EventHandler;
$form1.Add_Click($clickHandler);
[System.Windows.Forms.Application]::Run($form1);


・デリゲートオブジェクトとして呼び出されたPowerShell関数の戻り値
デリゲートオブジェクトとして呼び出されたPowerShell関数は、以下の条件を満たすことで、戻り値を返すことができます。

条件 : PowerShell関数の戻り値が、キャストしたデリゲートに定義されている戻り値の型にキャストできること。

条件を満たさない場合でも問題なく実行できますが、戻り値はNullとなります。 ( キャストしたデリゲートに定義されている戻り値の型がVoid型の場合は、何も返しません。 )

例えば、以下の例では戻り値はNullとなります。 ( ResolveEventHandlerデリゲートの戻り値の型は、System.Reflection.Assemblyクラスです。 )

# ResolveEventHandlerにキャストして呼び出されたとき、戻り値はNullとなります。
$func1 = ConvertTo-Delegate {"test";} ResolveEventHandler;
$func1.Invoke($(), $());


以下の例のように、PowerShell関数の戻り値が、デリゲートに定義されている戻り値の型にキャストできる場合は、戻り値が返されます。

# ResolveEventHandlerにキャストして呼び出されたとき、戻り値は適切な値となります。
$func1 = ConvertTo-Delegate {[System.Reflection.Assembly]::GetExecutingAssembly();} ResolveEventHandler;
$func1.Invoke($(), $());



【 3. 使用例 】
・イベントハンドラ
以下のコードは、フォームが閉じられる際に、終了確認ダイアログを表示し、「いいえ」が選択された場合に終了をキャンセルさせる例です。 ( コード中で使用しているRun-Formコマンドレットについては、こちらの記事を参照してください。 )

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms");
$form = New-Object "System.Windows.Forms.Form";
$formClosingHandlScript =
  {
    $sender = [System.Windows.Forms.Form]$Args[0];
    $e = [System.Windows.Forms.FormClosingEventArgs]$Args[1];
    $messageBoxButton = [System.Windows.Forms.MessageBoxButtons]::YesNo;
    $messageBoxIcon = [System.Windows.Forms.MessageBoxIcon]::Question;
    $dialogResult = [System.Windows.Forms.MessageBox]::Show($sender, "終了しますか?", "確認", $messageBoxButton, $messageBoxIcon);
    if ($dialogResult -ne [System.Windows.Forms.DialogResult]::Yes)
    {
      $e.Cancel = $True;
    }
  };

$formClosingHandler = ConvertTo-Delegate $formClosingHandlScript System.Windows.Forms.FormClosingEventHandler;
$form.Add_FormClosing($formClosingHandler);
Run-Form $form;


・Threadクラスによるマルチスレッド
以下の例のように、Threadクラスを用いることで、ThreadStartデリゲートにキャストしたPowerShell関数を別スレッド上で実行させることができます。
ただし、別スレッドでの実行を目的にPowerShell関数をデリゲートにキャストする場合、ErrorNotifiedByDialogパラメータには必ず Trueを指定してください。Trueを指定しないと、別スレッド上でのPowerShell関数の実行中に例外が発生した場合に、例外が適切に処理されないため PowerShellが強制終了してしまいます。

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms");
$messageBoxClass = [System.Windows.Forms.MessageBox];
$process = ConvertTo-Delegate {[void] $messageBoxClass::Show("test");} System.Threading.ThreadStart $True;
$newThread = New-Object System.Threading.Thread $process;
$newThread.Start();












トラックバックURL↓
http://csharper.blog57.fc2.com/tb.php/66-38746b90