Asynchronous named pipes in powershell using callbacks

女生的网名这么多〃 提交于 2021-02-19 09:04:31

问题


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:


  1. Hope Client code is inside job or in different script.
  2. Add pipeDirection to Client pipe.
  3. Add sw.AutoFlush = $true before writing to client stream.
  4. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!