【发布时间】: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。如果您查看管道的文档示例,您会发现它比您的代码简单很多。