Powershellで名前付きパイプを扱う
.netで名前付きパイプを扱う方法ですね。
同期バージョン
とりあえずEchoサーバー
$pipe = New-Object System.IO.Pipes.NamedPipeServerStream "PshPipeName", InOut $pipe.WaitForConnection() $buf = New-Object byte[] 1024 $loop = $true while($loop) { $len = $pipe.Read($buf, 0, $buf.Length) $cmd = [System.Text.Encoding]::Unicode.GetString($buf, 0, $len) if($cmd -match '^exit|^quit') { $loop = $false } else { $wb = [System.Text.Encoding]::Unicode.GetBytes($cmd) $pipe.Write($wb, 0, $wb.Length) } } $pipe.Close()
クライアント側
$pipe = New-Object System.IO.Pipes.NamedPipeClientStream ".", "PshPipeName", InOut $pipe.Connect() $buf = New-Object byte[] 1024 $wb = [System.Text.Encoding]::Unicode.GetBytes("Hello, world.") $pipe.Write($wb, 0, $wb.Length) $len = $pipe.Read($buf, 0, $buf.Length) [System.Text.Encoding]::Unicode.GetString($buf, 0, $len) $wb = [System.Text.Encoding]::Unicode.GetBytes("exit") $pipe.Write($wb, 0, $wb.Length) $pipe.Close()
サーバー側で作成するパイプは、接続数を定義したり
$pipe = New-Object System.IO.Pipes.NamedPipeServerStream "PshPipeName", InOut, 1
他オプションを定義したりできます。
$pipe = New-Object System.IO.Pipes.NamedPipeServerStream "PshPipeName", InOut, 1, Byte, None, 1024, 1024
が、バッファがなくなったり、読み込むものがないのにReadしたりすると、ブロックします。
ということで、非同期を検討する。
非同期
AsyncCallbackは、スクリプトブロックをキャストすればOK的な文章を見たのだけど、、、上手くいかない。
やむなくブリッジクラスを作ってしのぐ。
非同期版Echoサーバー。
if (-not ("CallbackEventBridge" -as [type])) { Add-Type @" using System; public sealed class CallbackEventBridge { public event AsyncCallback CallbackComplete = delegate {}; private void CallbackInternal(IAsyncResult result) { CallbackComplete(result); } public AsyncCallback Callback { get { return new AsyncCallback(CallbackInternal); } } } "@} $po = New-Object PSObject $po | Add-Member NoteProperty "buf" (New-Object byte[] 1024) $po | Add-Member NoteProperty "pipe" (New-Object System.IO.Pipes.NamedPipeServerStream "PshPipeName", InOut, 1, Byte, Asynchronous, 1024, 1024) $po | Add-Member NoteProperty "wb" (New-Object CallbackEventBridge) $po | Add-Member NoteProperty "rb" (New-Object CallbackEventBridge) $callback = { param($asyncResult) # 本当は書き込んだサイズをチェックすべきかも $asyncResult.AsyncState.EndWrite($asyncResult) } Register-ObjectEvent -InputObject $po.wb -EventName CallbackComplete -action $callback > $null $callback = { param($asyncResult) $asyncResult.AsyncState.pipe.EndWaitForConnection($asyncResult) } Register-ObjectEvent -InputObject $po.rb -EventName CallbackComplete -action $callback > $null $ar = $po.pipe.BeginWaitForConnection($po.rb.Callback, $po) while(!$ar.IsCompleted) { Start-Sleep -Milliseconds 100 } $callback = { param($asyncResult) $po = $asyncResult.AsyncState $len = $po.pipe.EndRead($asyncResult) $cmd = [System.Text.Encoding]::Unicode.GetString($po.buf, 0, $len) if($cmd -match '^exit|^quit') { $po.pipe.Close() } else { $wb = [System.Text.Encoding]::Unicode.GetBytes($cmd) $ar = $po.pipe.BeginWrite($wb, 0, $wb.Length, $po.wb.Callback, $po) while(!$ar.IsCompleted -and $po.pipe.IsConnected) { Start-Sleep -Milliseconds 100 } } } Register-ObjectEvent -InputObject $po.rb -EventName CallbackComplete -action $callback > $null while($po.pipe.IsConnected) { $ar = $po.pipe.BeginRead($po.buf, 0, $po.buf.Length, $po.rb.Callback, $po) while(!$ar.IsCompleted) { Start-Sleep -Milliseconds 100 } } $po.pipe.Close()
なんというスクリプト量の増加、、、。
一応、クライアント側も非同期版で書いてみる(whileループで同期するけど)
if (-not ("CallbackEventBridge" -as [type])) { Add-Type @" using System; public sealed class CallbackEventBridge { public event AsyncCallback CallbackComplete = delegate {}; private void CallbackInternal(IAsyncResult result) { CallbackComplete(result); } public AsyncCallback Callback { get { return new AsyncCallback(CallbackInternal); } } } "@} $po = New-Object PSObject $po | Add-Member NoteProperty "buf" (New-Object byte[] 1024) $po | Add-Member NoteProperty "pipe" (New-Object System.IO.Pipes.NamedPipeClientStream ".", "PshPipeName", InOut, Asynchronous) $po | Add-Member NoteProperty "wb" (New-Object CallbackEventBridge) $po | Add-Member NoteProperty "rb" (New-Object CallbackEventBridge) $po | Add-Member NoteProperty "res" "" $callback = { param($asyncResult) # 本当は書き込んだサイズをチェックすべきかも $asyncResult.AsyncState.EndWrite($asyncResult) } Register-ObjectEvent -InputObject $po.wb -EventName CallbackComplete -action $callback > $null $callback = { param($asyncResult) $len = $asyncResult.AsyncState.pipe.EndRead($asyncResult) $po.res = [System.Text.Encoding]::Unicode.GetString($asyncResult.AsyncState.buf, 0, $len) } Register-ObjectEvent -InputObject $po.rb -EventName CallbackComplete -action $callback > $null while(!$po.pipe.IsConnected) { # 無限リトライでなく、あきらめるということも知る必要があるかもしれない $po.pipe.Connect(100) > $null } $wb = [System.Text.Encoding]::Unicode.GetBytes("Hello, world.") $ar = $po.pipe.BeginWrite($wb, 0, $wb.Length, $po.wb.Callback, $po) while(!$ar.IsCompleted) { Start-Sleep -Milliseconds 100 } $ar = $po.pipe.BeginRead($po.buf, 0, $po.buf.Length, $po.rb.Callback, $po) while(!$ar.IsCompleted) { Start-Sleep -Milliseconds 100 } $po.res $wb = [System.Text.Encoding]::Unicode.GetBytes("exit") $ar = $po.pipe.BeginWrite($wb, 0, $wb.Length, $po.wb.Callback, $po) while(!$ar.IsCompleted) { Start-Sleep -Milliseconds 100 } $po.pipe.Close()