C# 跨进程连接匿名管道会产生无效句柄错误I';我使用System.Io.Pipes

C# 跨进程连接匿名管道会产生无效句柄错误I';我使用System.Io.Pipes,c#,ipc,C#,Ipc,我正试图使用System.Io.pipes提供的匿名管道来组合一个类来处理进程之间的Ipc 我遇到的问题是,当我使用单个进程测试类时,管道设置正确,我可以在客户机和服务器之间毫无问题地发送数据。但是,当我将客户机和服务器拆分为单独的进程(在同一台机器上)时,客户机无法连接到服务器管道的末端 调用时引发错误System.Io.Exception无效管道句柄 _outboundPipeServerStream = new AnonymousPipeClientStream(PipeDirection

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

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

调用时引发错误System.Io.Exception无效管道句柄

_outboundPipeServerStream = new AnonymousPipeClientStream(PipeDirection.Out, serverHandle);
该类的完整代码粘贴在下面

基本上它的工作是这样的

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();
    }
  • 服务器进程。为入站数据创建匿名管道集-将此管道称为
  • 服务器进程。启动客户端进程并通过命令参数传递PipeHandle
  • 客户端进程。连接到管道A的末端
  • 客户端进程。为入站数据创建匿名管道集(管道B) 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();
        }
    

    提前感谢您的建议。

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

    • 您需要指定客户端的管道句柄在创建时是可继承的
    • 启动客户机时,需要指定可继承句柄将被继承
    如果您错过了这两个步骤中的任何一个,那么管道句柄将在客户端进程中无效

    此外,您的第4步也不起作用。如果在客户机中创建管道句柄,当您将其传递回服务器时,它对服务器没有任何意义。您可以使用该函数实现这一点,但是在服务器中创建所有句柄并在客户端继承它们要容易得多


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

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

    感谢您花时间回答。为清楚起见,此类位于服务器和客户端都引用的共享程序集中。它的操作模式由使用的构造函数决定-无参数表示服务器,有参数表示服务器的管道句柄,表示作为客户端连接。你说的很有道理,我会让你知道它是如何进行的。我决定使用命名管道解决方案,它非常有效。如果你还想用UAC提升子进程,那怎么办?这要求
    UseShellExecute
    true
    @Alejandro尝试使用将升级的桥接进程,然后启动子进程,并将
    UseShellExecute
    设置为
    false
    。所以我建议以下方案:主流程(用户级)->桥接流程(升级,拥有句柄)->子流程(将在父流程升级后升级)。