匿名管道 c# WPF

Posted

技术标签:

【中文标题】匿名管道 c# WPF【英文标题】:Anonymous Pipes c# WPF 【发布时间】:2020-08-05 12:28:06 【问题描述】:

我正在尝试通过在同一台机器上运行两个 WPF 应用程序来实现 IPC。我需要实现类似于 Example using Winform 的东西。有人可以将下面的代码转换为 WPF/C#,因为您可以看到委托调用直接依赖于 Winform 控件,

if (theArgs.Length == 0)
        
            this.Text = "Pipe Server";
            pipe = new AnonymousPipes("Server end of the pipe.", Application.ExecutablePath, "additionalArgs=your_own_command_line_args_here", delegate(String msg)
            
                this.Invoke((MethodInvoker)delegate()
                
                    this.lbTextIn.Items.Add(msg);
                );
            , delegate()
            
                // We're disconnected!
                try
                
                    if (!this.IsDisposed)
                    
                        this.Invoke((MethodInvoker)delegate()
                        
                            this.lbTextIn.Items.Add("Client disconnected!");
                        );
                    
                
                catch (Exception)  
            );
        
        else
        
            this.Text = "Pipe Client";
            pipe = new AnonymousPipes("Client end of the pipe.");
            pipe.ConnectToPipe(theArgs[0], delegate(String msg)
            
                this.Invoke((MethodInvoker)delegate()
                
                    lbTextIn.Items.Add(msg);
                );
            , delegate()
            
                // We're disconnected!
                this.Close();
            );
        

Wrapper类的实现如下,

public class AnonymousPipes

    private String clientPath;
    private AnonymousPipeServerStream outGoingServerPipe;
    private AnonymousPipeServerStream inComingServerPipe;
    private PipeStream clientIn;
    private PipeStream clientOut;
    private Process pipeClient;
    private String incomingHandle;
    private String outgoingHandle;
    private StreamWriter ssw;
    private StreamWriter csw;
    private bool serverMode;
    private bool running;
    private CallBack callback;
    private DisconnectEvent disconnectEvent;
    private String msgError;
    private String name;

    public delegate void CallBack(String msg);
    public delegate void DisconnectEvent();
    public String ermsg;

    public bool isConnected()
    
        return running;
    

    public String GetPipeName()
    
        return name;
    

    public AnonymousPipes(String pipeName)
    
        this.name = pipeName;
    

    private String StartPipeServer()
    
        serverMode = true;
        outGoingServerPipe = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable);
        inComingServerPipe = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable);

        return outGoingServerPipe.GetClientHandleAsString() + ":::" + inComingServerPipe.GetClientHandleAsString();
    

    public AnonymousPipes(String pipeName, String clientPath, String cmdLineArgs, CallBack callback,
        DisconnectEvent disconnectEvent)
    
        String args;
        this.clientPath = clientPath;
        this.callback = callback;
        this.disconnectEvent = disconnectEvent;
        this.name = pipeName;
        this.running = true;

        serverMode = true;

        args = StartPipeServer() + " " + cmdLineArgs;

        try
        
            pipeClient = new Process();
            pipeClient.StartInfo.FileName = clientPath;
            pipeClient.StartInfo.Arguments = args;
            pipeClient.StartInfo.UseShellExecute = false;
            pipeClient.Start();
        
        catch (Exception ex)
        
            ermsg = ex.Message;
            running = false;
            return;
        

        outGoingServerPipe.DisposeLocalCopyOfClientHandle();
        inComingServerPipe.DisposeLocalCopyOfClientHandle();

        ssw = new StreamWriter(outGoingServerPipe);
        ssw.AutoFlush = true;
        ssw.WriteLine("SYNC");

        outGoingServerPipe.WaitForPipeDrain();

        new Thread(delegate ()
        

            using (StreamReader isr = new StreamReader(inComingServerPipe))
            
                String tmp;
                while (running && inComingServerPipe.IsConnected)
                
                    tmp = isr.ReadLine();
                    if (tmp != null)
                    
                        callback(tmp);
                    
                
            

            running = false;
            disconnectEvent();

        ).Start();
    

    public bool SendText(String msg)
    
        return SendText(msg, ref msgError);
    

    public bool SendText(String msg, ref String errMsg)
    
        if (serverMode)
        
            try
            
                ssw.WriteLine(msg);
                outGoingServerPipe.WaitForPipeDrain();
                return true;
            
            catch (Exception ex)
            
                errMsg = ex.Message;
                return false;
            
        
        else
        
            try
            
                csw.WriteLine(msg);
                clientOut.WaitForPipeDrain();
            
            catch (Exception)  
            return true;
        
    

    public void ConnectToPipe(String clientHandles, CallBack callback, DisconnectEvent disconnectEvent)
    
        String[] handles = System.Text.RegularExpressions.Regex.Split(clientHandles, ":::");
        this.incomingHandle = handles[0];
        this.outgoingHandle = handles[1];
        this.callback = callback;
        this.disconnectEvent = disconnectEvent;
        running = true;
        serverMode = false;

        new Thread(delegate ()
        
            clientIn = new AnonymousPipeClientStream(PipeDirection.In, this.incomingHandle);
            clientOut = new AnonymousPipeClientStream(PipeDirection.Out, this.outgoingHandle);

            csw = new StreamWriter(clientOut);
            csw.AutoFlush = true;

            using (StreamReader sr = new StreamReader(clientIn))
            
                string temp;

                do
                
                    temp = sr.ReadLine();
                
                while (!temp.StartsWith("SYNC") && running);

                while (running && clientIn.IsConnected)
                
                    temp = sr.ReadLine();
                    if (temp != null)  callback(temp); 
                

                running = false;
                disconnectEvent();
            
        ).Start();
    

    public void Close()
    
        running = false;

        try
        
            pipeClient.Close();
        
        catch (Exception)  

        try
        
            outGoingServerPipe.Close();
        
        catch (Exception)  

        try
        
            inComingServerPipe.Close();
        
        catch (Exception)  

        try
        
            clientOut.Close();
        
        catch (Exception)  

        try
        
            clientIn.Close();
        
        catch (Exception)  

        try
        
            ssw.Close();
        
        catch (Exception)  

        try
        
            csw.Close();
        
        catch (Exception)  
    

【问题讨论】:

WPF 不会影响 IPC 的工作方式。您真正要问的是如何从另一个线程修改 UI,而答案是 - 根本不是那样的。自 2010 年引入 Tasks 以来,也没有更早。你关注的文章不好 我建议您改为查看文档,尤其是 How to: Use Anonymous Pipes for Local Interprocess Communication。 PipeStream 仍然是一个流,这意味着您可以使用 async/await 从中读取数据并更新 UI,而无需启动原始线程、使用包装器或必须使用 Invoke Progress reporting 可通过Process<T>、cancellation 至CancellationToken 获得 感谢您的建议,我正在尝试在 IPC 上使用 wpf 到 wpf 通信之间的任意管道找到一个很好的示例。 再一次,WPF 与 IPC 没有任何关系。您可以只使用 doc 示例来设置客户端和服务器流。您的 实际 问题是如何从另一个线程修改 UI。对于 Winforms 和 WPF,自 2012 年以来的答案都是 async/await,而不是 Invoke。如果您查看管道的文档示例,您会发现它比您的代码简单很多 【参考方案1】:

Invoke 替换为Dispatcher.Invoke,例如:

this.Dispatcher.Invoke(delegate ()

    this.lbTextIn.Items.Add(msg);
);

【讨论】:

以上是关于匿名管道 c# WPF的主要内容,如果未能解决你的问题,请参考以下文章

匿名管道:当子进程被杀死时,父进程中的 ReadFile 继续等待

c++里面执行一个exe文件,匿名管道

Linux_Centos进程间通信_管道(匿名管道_命名管道)

匿名管道

Win32:匿名管道上的事务

Linux匿名管道