【问题标题】:Connecting Anonymous Pipe across processes gives Invalid Handle Error, I'm using System.Io.Pipes跨进程连接匿名管道会产生无效句柄错误,我正在使用 System.Io.Pipes
【发布时间】:2012-03-09 03:10:36
【问题描述】:

我正在尝试使用 System.Io.Pipes 提供的匿名管道组合一个类来处理进程之间的 Ipc。

我遇到的问题是,当我使用单个进程测试类时,管道设置正确,我可以毫无问题地在客户端和服务器之间发送数据。但是,当我将客户端和服务器拆分为单独的进程(在同一台机器上)时,客户端无法连接到服务器管道的末端。

调用时报错System.Io.Exception Invalid pipe handle

_outboundPipeServerStream = new AnonymousPipeClientStream(PipeDirection.Out, serverHandle);

类的完整代码粘贴在下面。

基本上它的工作是这样的;

  1. 服务器进程。为入站数据创建匿名管道集 - 调用此管道 A
  2. 服务器进程。启动客户端进程并通过命令参数传递 PipeHandle
  3. 客户端进程。连接到管道 A 的末端
  4. 客户端进程。为入站数据创建匿名管道集(管道 B) 5 客户流程。使用管道 A 将管道句柄传回服务器
  5. 服务器进程。连接到管道 B 的末端

所以现在我们有两个匿名管道,在服务器和客户端之间指向相反的方向。

这是我的 IPC 类的完整代码

    public class MessageReceivedEventArgs : EventArgs
{
    public string Message { get; set; }
}

public class IpcChannel : IDisposable
{
    private AnonymousPipeServerStream _inboundPipeServerStream;
    private StreamReader _inboundMessageReader;
    private string _inboundPipeHandle;

    private AnonymousPipeClientStream _outboundPipeServerStream;
    private StreamWriter _outboundMessageWriter;

    public delegate void MessageReceivedHandler(object sender, MessageReceivedEventArgs e);
    public event MessageReceivedHandler MessageReceived;

    private Thread _clientListenerThread;
    private bool _disposing = false;

    public IpcChannel()
    {
        SetupServerChannel();
    }

    public IpcChannel(string serverHandle)
    {
        SetupServerChannel();
        // this is the client end of the connection

        // create an outbound connection to the server
        System.Diagnostics.Trace.TraceInformation("Connecting client stream to server : {0}", serverHandle);
        SetupClientChannel(serverHandle);

        IntroduceToServer();
    }

    private void SetupClientChannel(string serverHandle)
    {
        _outboundPipeServerStream = new AnonymousPipeClientStream(PipeDirection.Out, serverHandle);
        _outboundMessageWriter = new StreamWriter(_outboundPipeServerStream)
        {
            AutoFlush = true
        };
    }

    private void SetupServerChannel()
    {
        _inboundPipeServerStream = new AnonymousPipeServerStream(PipeDirection.In);
        _inboundMessageReader = new StreamReader(_inboundPipeServerStream);
        _inboundPipeHandle = _inboundPipeServerStream.GetClientHandleAsString();
        _inboundPipeServerStream.DisposeLocalCopyOfClientHandle();

        System.Diagnostics.Trace.TraceInformation("Created server stream " + _inboundPipeServerStream.GetClientHandleAsString());

        _clientListenerThread = new Thread(ClientListener)
        {
            IsBackground = true
        };

        _clientListenerThread.Start();

    }

    public void SendMessage(string message)
    {
        System.Diagnostics.Trace.TraceInformation("Sending message {0} chars", message.Length);

        _outboundMessageWriter.WriteLine("M" + message);
    }

    private void IntroduceToServer()
    {
        System.Diagnostics.Trace.TraceInformation("Telling server callback channel is : " + _inboundPipeServerStream.GetClientHandleAsString());

        _outboundMessageWriter.WriteLine("CI" + _inboundPipeServerStream.GetClientHandleAsString());
    }

    public string ServerHandle
    {
        get
        {
            return _inboundPipeHandle;
        }
    }

    private void ProcessControlMessage(string message)
    {
        if (message.StartsWith("CI"))
        {
            ConnectResponseChannel(message.Substring(2));
        }
    }

    private void ConnectResponseChannel(string channelHandle)
    {
        System.Diagnostics.Trace.TraceInformation("Connecting response (OUT) channel to : {0}", channelHandle);

        _outboundPipeServerStream = new AnonymousPipeClientStream(PipeDirection.Out, channelHandle);
        _outboundMessageWriter = new StreamWriter(_outboundPipeServerStream);
        _outboundMessageWriter.AutoFlush = true;
    }

    private void ClientListener()
    {
        System.Diagnostics.Trace.TraceInformation("ClientListener started on thread {0}", Thread.CurrentThread.ManagedThreadId);

        try
        {
            while (!_disposing)
            {
                var message = _inboundMessageReader.ReadLine();
                if (message != null)
                {
                    if (message.StartsWith("C"))
                    {
                        ProcessControlMessage(message);
                    }
                    else if (MessageReceived != null)
                        MessageReceived(this, new MessageReceivedEventArgs()
                        {
                            Message = message.Substring(1)
                        });
                }
            }
        }
        catch (ThreadAbortException)
        {
        }
        finally
        {

        }
    }

    public void Dispose()
    {
        _disposing = true;

        _clientListenerThread.Abort();

        _outboundMessageWriter.Flush();
        _outboundMessageWriter.Close();
        _outboundPipeServerStream.Close();
        _outboundPipeServerStream.Dispose();

        _inboundMessageReader.Close();
        _inboundMessageReader.Dispose();

        _inboundPipeServerStream.DisposeLocalCopyOfClientHandle();
        _inboundPipeServerStream.Close();
        _inboundPipeServerStream.Dispose();
    }
}

在单个进程中,可以这样使用;

class Program
{
    private static IpcChannel _server;
    private static IpcChannel _client;

    static void Main(string[] args)
    {
        _server = new IpcChannel();
        _server.MessageReceived += (s, e) => Console.WriteLine("Server Received : " + e.Message);

        _client = new IpcChannel(_server.ServerHandle);
        _client.MessageReceived += (s, e) => Console.WriteLine("Client Received : " + e.Message);


        Console.ReadLine();

        _server.SendMessage("This is the server sending to the client");

        Console.ReadLine();

        _client.SendMessage("This is the client sending to the server");

        Console.ReadLine();

        _client.Dispose();
        _server.Dispose();
    }

提前感谢您的任何建议。

【问题讨论】:

    标签: c# ipc


    【解决方案1】:

    您没有发布服务器代码,但无论如何。在服务器中:

    • 创建时需要指定客户端的管道句柄是可继承的。
    • 启动客户端时,您需要指定继承可继承句柄。

    如果您错过了这些步骤中的任何一个,则管道句柄在客户端进程中将无效。

    另外,您的第 4 步也行不通。如果您在客户端创建一个管道句柄,那么当您将其传回时,它对服务器没有任何意义。您可以使用DuplicateHandle 函数来完成这项工作,但是在服务器中创建所有句柄并在客户端中继承它们要容易得多

    关键是句柄是每个进程的,而不是系统范围的。

    【讨论】:

    • 感谢您花时间回答。为清楚起见,此类位于服务器和客户端都引用的共享程序集中。它的操作模式由使用的构造函数决定——无参数意味着服务器,有参数,服务器的管道句柄,意味着作为客户端连接。你说的有道理,我会告诉你进展如何。
    • 我决定使用命名管道解决方案,它很管用。
    【解决方案2】:

    启动子进程不要忘记设置UseShellExecute = false,否则句柄不会被继承。

    【讨论】:

    • 如果您想使用 UAC 提升子进程怎么办?这要求UseShellExecutetrue
    • @Alejandro 尝试使用将被升级的桥接进程,然后将UseShellExecute 设置为false 启动子进程。所以我提出以下方案:主进程(用户级别)-> 桥接进程(升级,拥有句柄)-> 子进程(将在父进程升​​级后升级)。
    猜你喜欢
    • 2023-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-08
    • 1970-01-01
    • 2016-05-16
    相关资源
    最近更新 更多