COM時代のWindows Script Host(WSH)に対し、 .NET Framework時代にはPowerShell(PSH)がCLI環境として提供されており、 WSH同様PSHもVisual Studio .NETからの制御(Automation)が可能になっています。
ここでいうAutomationとは、VBやC#などEXE形式のプログラム内でWSHやPSHをCOM/.NETのオブジェクトとして扱い使用することを指し、 EXEは処理の一部をWSH/PSHに肩代わりさせることができます。 これの利点としては、
PSHはWSH同様スクリプトによる処理に加えパイプラインという処理が用意されていますが、 複雑な処理を1行のパイプラインで記述するというのはそれなりの習熟が必要で、 関数(Function)も併用してAutomationしたいこともあるでしょう。 ここではPSH Automationで関数を定義・呼び出す方法を書いておきます。 なおVB.NETで説明してますが、C#でも同じはずです。読み替えてくださいませ。
[void] (New-Item -path $PSHome -itemType "directory" -name "SDK"); [System.AppDomain]::CurrentDomain.GetAssemblies() | Where {$_.FullName -like "*Version=1.0.0.0*";} | Copy-Item -path {$_.Location;} -destination ($PSHome + "\SDK");
二つの方法がありますが、一長一短ですので好きなほうを使用すればいいでしょう。 パイプラインの方が機能的には制限が無い(オブジェクトを渡せる)ので慣れればこちら一本でこなせますが、 どうも私は全引数がしかも名無しで一個の$input変数に入っているというのは、気持ち悪くてねぇ(だからPerlの関数も好きではない^^;)
引数が数字とか文字列とかコマンドラインに記述し易い場合は、こちらの方が値の渡しが楽です。
関数を呼ぶ際はRunspaceInvokeインスタンスを作成後、Invokeメソッドに"関数名 引数1 引数2 ..."と引数込みの文字列を付けて呼び出します。 よってこの方法を使う場合、引数を文字列内に記述できないといけません。 Visual Studio側で作成したオブジェクトを渡したい場合は、次のパイプラインを使用することになります。
Dim runspaceConfig As RunspaceConfiguration = RunspaceConfiguration.Create() Dim scripts As RunspaceConfigurationEntryCollection(Of ScriptConfigurationEntry) = runspaceConfig.Scripts scripts.Append(New ScriptConfigurationEntry("add", "param([double]$a,[double]$b);$a+$b")) Dim runspaceInvoke As New RunspaceInvoke(runspaceConfig) Dim results As ObjectModel.Collection(Of PSObject) = runspaceInvoke.Invoke("add 1 8") For Each result As PSObject In results Console.WriteLine(result.ImmediateBaseObject.ToString) Next
引数の引渡しのために、呼び出し側・関数側双方で手数が増えますが、文字列では表現できないオブジェクトを渡せるという代え難い機能が使えます。
引数は特殊変数$inputの中に収められるので関数はここから値を得ます。中身はEnumlatorなのでforeachなどで取り出します。 引数を全部足すとかいう関数ならいいのですが、そうでなければforeachでどういう順番に値が取り出せるのか気になるところ。 C.Input.Write()でオブジェクトを入れた順番だと思います・・・多分^^; 万全を期すなら、
ここではCommands.Addでひとつの関数を呼び出してますが、もちろん|で複数のCmdletなどを繋いでもかまいません。これはRunspaceInvokeを使う場合でも同じです。
最後の結果を取り出すところは、RunspaceInvokeを使う場合と同じです。
Dim runspaceConfig As RunspaceConfiguration = RunspaceConfiguration.Create() Dim scripts As RunspaceConfigurationEntryCollection(Of ScriptConfigurationEntry) = runspaceConfig.Scripts scripts.Append(New ScriptConfigurationEntry("add", "$a=0;foreach($b in $input){$a=$a+$b};$a")) Dim runspace As Runspace = RunspaceFactory.CreateRunspace(runspaceConfig) runspace.Open() Dim rsPipeline As Pipeline = runspace.CreatePipeline() rsPipeline.Commands.Add("add") rsPipeline.Input.Write(1.0) rsPipeline.Input.Write(8.0) Dim results As ObjectModel.Collection(Of PSObject) = rsPipeline.Invoke For Each result As PSObject In results Console.WriteLine(result.ImmediateBaseObject.ToString) Next
PS> function add([double]$a, [double]$b){$a+$b} PS> type function:add param([double]$a, [double]$b) $a+$bここで出てくる内容がPowerShellの正規の関数表現だということなのでしょう。 普段最初の方法で関数を定義している場合、一度PowerShell上で定義したりdir function:で出てくる関数を適当に選んで、 type function:関数名で確認するといいでしょう。
引数はRunspaceInvoke、パイプラインどちらの方法でも指定可能です。 パイプラインの場合には、パイプラインから入れる全オブジェクトに共通の指定を引数で渡すと良いでしょう。 ただし文字列で表現できないような指定ならば、パイプラインからオブジェクトを入れる必要があります。