【问题标题】:Asynchronous named pipes in powershell using callbacksPowershell中使用回调的异步命名管道
【发布时间】:2015-07-10 10:24:23
【问题描述】:

我正在尝试使用 .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 样式错误框。我不知道为什么会发生这种情况,而且我似乎无法从脚本中获得任何异常或其他调试信息来了解出了什么问题。如果我尝试同步做同样的事情,一切都会按预期工作。非常感谢任何帮助。

【问题讨论】:

  • 客户端上的 Windows 日志中的任何内容?
  • 很遗憾没有。我一直在查看事件日志的 Powershell 部分,我可以看到我知道的异常条目,并且已经从之前尝试编写上述代码的失败中报告给控制台,所以我认为这是正确的地方。
  • 您可以使用 sysinternals 的 procdump 让它在发生异常时创建转储文件。像procdump -ma -e 100 <PID> 这样的东西。有了故障转储后,您可以开始使用 WinDbg 进行调查。

标签: .net powershell asynchronous named-pipes


【解决方案1】:
  1. 希望客户端代码在作业内部或不同的脚本中。
  2. pipeDirection 添加到客户端管道。
  3. 在写入客户端流之前添加sw.AutoFlush = $true
  4. 如果还是不行,请尝试在消息中添加 CRLF:$sw.WriteLine("Test\r\n");

希望这会有所帮助。

【讨论】:

    【解决方案2】:

    我遇到了同样的问题,我在 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
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-09
      • 2021-05-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多