问题
I'm attempting to use a named pipe using a .net NamedPipeServerStream asynchronously using callbacks in powershell.
I'm currently using the following code:
Server side:
$myCallback = [AsyncCallback]{
"Connected"
}
$pipe = New-Object System.IO.Pipes.NamedPipeServerStream("alert", [System.IO.Pipes.PipeDirection]::InOut, 1, [System.IO.Pipes.PipeTransmissionMode]::Message, [System.IO.Pipes.PipeOptions]::Asynchronous)
$pipe.BeginWaitForConnection($myCallback, "alertCallback")
Client side:
$pipe = new-object System.IO.Pipes.NamedPipeClientStream("alert");
$pipe.Connect(3000);
$sw = new-object System.IO.StreamWriter($pipe);
$sw.WriteLine("Test");
I call the server side code first, which reports that the callback has been registered successfully
AsyncState IsCompleted AsyncWaitHandle CompletedSynchronously
---------- ----------- --------------- ----------------------
alertCallback False System.Threading.ManualRese... False
As soon as the client side code is called the powershell server script crashes - an exception is not thrown, simply I get a "Powershell has stopped working" windows style error box. I'm at a loss as to why this occurs, and I can't seem to get any exception or other debugging information out of the script to understand what is going wrong. If I attempt to do the same synchronously everything works as expected. Any help is much appreciated.
回答1:
- Hope Client code is inside job or in different script.
- Add
pipeDirection
to Client pipe. - Add
sw.AutoFlush = $true
before writing to client stream. - If it still does not work, try adding CRLF to message:
$sw.WriteLine("Test\r\n");
Hope this helps.
回答2:
I had the same problem and I found an exception in the Windows Event Log that is similar to the error described here
I implemented the same solution and I was able to make this work like this:
Server code:
$PipeName = 'testPipe'
$PipeDir = [System.IO.Pipes.PipeDirection]::In
$PipeOpt = [System.IO.Pipes.PipeOptions]::Asynchronous
$PipeMode = [System.IO.Pipes.PipeTransmissionMode]::Message
$helper = @'
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Management.Automation.Runspaces;
public class RunspacedDelegateFactory
{
public static Delegate NewRunspacedDelegate(Delegate _delegate, Runspace runspace)
{
Action setRunspace = () => Runspace.DefaultRunspace = runspace;
return ConcatActionToDelegate(setRunspace, _delegate);
}
private static Expression ExpressionInvoke(Delegate _delegate, params Expression[] arguments)
{
var invokeMethod = _delegate.GetType().GetMethod("Invoke");
return Expression.Call(Expression.Constant(_delegate), invokeMethod, arguments);
}
public static Delegate ConcatActionToDelegate(Action a, Delegate d)
{
var parameters =
d.GetType().GetMethod("Invoke").GetParameters()
.Select(p => Expression.Parameter(p.ParameterType, p.Name))
.ToArray();
Expression body = Expression.Block(ExpressionInvoke(a), ExpressionInvoke(d, parameters));
var lambda = Expression.Lambda(d.GetType(), body, parameters);
var compiled = lambda.Compile();
return compiled;
}
}
'@
add-type -TypeDefinition $helper
$Global:messageReceived = $null
$callback = [System.AsyncCallback] {
param([IAsyncResult] $iar)
$pipeServer.EndWaitForConnection($iar)
$Global:messageReceived = $sr.Readline()
$pipeServer.Close()
}
$runspacedDelegate = [RunspacedDelegateFactory]::NewRunspacedDelegate($callback, [Runspace]::DefaultRunspace)
try
{
$pipeServer = New-Object system.IO.Pipes.NamedPipeServerStream($PipeName, $PipeDir, 1, $PipeMode, $PipeOpt)
$sr = new-object System.IO.StreamReader($pipeServer)
$task = $pipeServer.BeginWaitForConnection($runspacedDelegate ,$null)
while(! $Global:messageReceived)
{
Write-Host 'waiting'
Start-Sleep 1
}
Write-Host "Message Received: $messageReceived"
}
catch
{
Write-Host "Error receiving pipe message: $_" -ForegroundColor Red
}
finally
{
if ($sr)
{
$sr.Dispose()
$sr = $null
}
if ($pipeServer)
{
$pipeServer.Dispose()
$pipeServer = $null
}
}
Client Code
$PipeName = 'testPipe'
$PipeDir = [System.IO.Pipes.PipeDirection]::Out
$PipeOpt = [System.IO.Pipes.PipeOptions]::Asynchronous
$Message = Read-Host 'Enter the message to send'
try
{
$pipeClient = new-object System.IO.Pipes.NamedPipeClientStream(".", $PipeName, $PipeDir, $PipeOpt)
$sw = new-object System.IO.StreamWriter($pipeClient)
$pipeClient.Connect(1000)
if (!$pipeClient.IsConnected)
{
throw "Failed to connect client to pipe $pipeName"
}
$sw.AutoFlush = $true
$sw.WriteLine($Message)
}
catch
{
Write-Host "Error sending pipe message: $_" -ForegroundColor Red
}
finally
{
if ($sw)
{
$sw.Dispose()
$sw = $null
}
if ($pipeClient)
{
$pipeClient.Dispose()
$pipeClient = $null
}
}
来源:https://stackoverflow.com/questions/31338421/asynchronous-named-pipes-in-powershell-using-callbacks