PowerShell中的异步命名管道使用回调

时间:2015-07-10 10:24:24

标签: .net powershell asynchronous named-pipes

我尝试使用.net NamedPipeServerStream在PowerShell中使用回调来异步使用命名管道。

我目前正在使用以下代码:

服务器端:

$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")

客户方:

$pipe = new-object System.IO.Pipes.NamedPipeClientStream("alert");
$pipe.Connect(3000); 

$sw = new-object System.IO.StreamWriter($pipe);
$sw.WriteLine("Test"); 

我首先调用服务器端代码,报告回调已成功注册

AsyncState                                        IsCompleted AsyncWaitHandle                       CompletedSynchronously
----------                                        ----------- ---------------                       ----------------------
alertCallback                                           False System.Threading.ManualRese...                         False

一旦客户端代码被调用,powershell服务器脚本崩溃 - 不会抛出异常,只是我得到了一个" Powershell已停止工作" Windows风格错误框。我不知道为什么会发生这种情况,而且我似乎无法从脚本中获取任何异常或其他调试信息以了解出现了什么问题。如果我尝试同步执行相同操作,一切都按预期工作。非常感谢任何帮助。

2 个答案:

答案 0 :(得分:1)

  1. 希望客户端代码在作业内或在不同的脚本中。
  2. pipeDirection添加到客户端管道。
  3. 在写入客户端流之前添加sw.AutoFlush = $true
  4. 如果仍然无效,请尝试将CRLF添加到邮件:$sw.WriteLine("Test\r\n");
  5. 希望这有帮助。

答案 1 :(得分:0)

我遇到了同样的问题,并且我在Windows事件日志中发现了一个异常,该异常类似于所描述的错误here

我实现了相同的解决方案,并且能够像这样完成这项工作:

服务器代码:

$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
    }
}

客户代码

$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
    }
}