使用.NET在同一台机器上进行通信的两个进程有什么最好的(或者可能不是最好的 - 只是好的)?
实际上,我正在开发的应用程序中的两个进程甚至不是两个不同的程序;它们只是同一个EXE的两个实例。我想做一些像单身人士的应用程序,但每个用户拥有它(意味着具有多个用户的终端服务器或Citrix或App-V服务器应该能够启动他们自己的应用程序的单个副本)。如果另一个实例由同一个用户运行,它应该只将任务委托给已经运行的实例,然后退出。每个程序用户只应运行一个实例。到目前为止,我已经完成了(感谢StackOverflow)使用Mutex检测应用程序实例是否已在运行的部分。但我需要第二个应用实例才能将数据发送到第一个应用实例。
我倾向于使用命名管道和WCF的NetNamedPipeBinding,但如果你有更好的想法,我会非常感激。谢谢:))
答案 0 :(得分:3)
命名管道通常是最好的,但也考虑MSMQ用于火灾和遗忘方案,以确保消息传递随着时间的推移。这实际上取决于您的邮件大小。 TCP会变得棘手,因为每个用户都需要一个唯一的端口。
答案 1 :(得分:3)
IPC就是我过去用过的。这非常简单。 .Net远程 是一个不错的选择,但遗憾的是它是一个受限制的选项,因为你不能在CF上使用它。
下面是我用于执行进程间通信的类的副本,如果您愿意,可以将它与MutEx结合使用,但这不是必需的。只要“pMappedMemoryName”和“pNamedEventName”在两个进程中都相同,它就可以正常工作。我试图尽可能地将它作为事件驱动。
只需使用Poke方法写入数据,然后使用Peek方法读取数据,尽管我将其设计为在新数据可用时自动触发事件。通过这种方式,您可以简单地订阅IpcEvent事件,而不必担心昂贵的民意调查。
public class IpcService {
private IServiceContext mContext;
const int maxLength = 1024;
private Thread listenerThread;
private readonly string mMappedMemoryName;
private readonly string mNamedEventName;
public event EventHandler<TextualEventArgs> IpcEvent;
private readonly bool mPersistantListener;
public IpcService(bool pPersistantListener)
: this(pPersistantListener, "IpcData", "IpcSystemEvent") {
;
}
public IpcService(bool pPersistantListener, string pMappedMemoryName, string pNamedEventName) {
mPersistantListener = pPersistantListener;
mMappedMemoryName = pMappedMemoryName;
mNamedEventName = pNamedEventName;
}
public void Init(IServiceContext pContext) {
mContext = pContext;
listenerThread = new Thread(new ThreadStart(listenUsingNamedEventsAndMemoryMappedFiles));
listenerThread.IsBackground = !mPersistantListener;
listenerThread.Start();
}
private void listenUsingNamedEventsAndMemoryMappedFiles() {
IntPtr hWnd = EventsManagement.CreateEvent(true, false, mNamedEventName);
while (listenerThread != null) {
if (Event.WAITOBJECT == EventsManagement.WaitForSingleObject(hWnd, 1000)) {
string data = Peek();
EventsManagement.ResetEvent(hWnd);
EventHandler<TextualEventArgs> handler = IpcEvent;
if (handler != null) handler(this, new TextualEventArgs(data));
}
}
EventsManagement.SetEvent(hWnd);
Thread.Sleep(500);
HandleManagement.CloseHandle(hWnd);
}
public void Poke(string format, params object[] args) {
Poke(string.Format(format, args));
}
public void Poke(string somedata) {
using (MemoryMappedFileStream fs = new MemoryMappedFileStream(mMappedMemoryName, maxLength, MemoryProtection.PageReadWrite)) {
fs.MapViewToProcessMemory(0, maxLength);
fs.Write(Encoding.ASCII.GetBytes(somedata + "\0"), 0, somedata.Length + 1);
}
IntPtr hWnd = EventsManagement.CreateEvent(true, false, mNamedEventName);
EventsManagement.SetEvent(hWnd);
Thread.Sleep(500);
HandleManagement.CloseHandle(hWnd);
}
public string Peek() {
byte[] buffer;
using (MemoryMappedFileStream fs = new MemoryMappedFileStream(mMappedMemoryName, maxLength, MemoryProtection.PageReadWrite)) {
fs.MapViewToProcessMemory(0, maxLength);
buffer = new byte[maxLength];
fs.Read(buffer, 0, buffer.Length);
}
string readdata = Encoding.ASCII.GetString(buffer, 0, buffer.Length);
return readdata.Substring(0, readdata.IndexOf('\0'));
}
private bool mDisposed = false;
public void Dispose() {
if (!mDisposed) {
mDisposed = true;
if (listenerThread != null) {
listenerThread.Abort();
listenerThread = null;
}
}
}
~IpcService() {
Dispose();
}
}
我希望这会有所帮助。
答案 2 :(得分:2)
我会选择命名管道。
基本上,每个用户都需要一个唯一的端点。与您的Mutex一样,您可能会使用用户名来查找要使用的命名管道的名称。这甚至可能取代您对Mutex的使用:尝试找出此特定的命名管道是否已存在,如果存在,则表示您是第二个要运行的实例。
将TCP端口映射到用户上更加困难。
哦,通过使用命名管道,我实际上是在说“远程命名管道”。
答案 3 :(得分:0)
.Net Remoting也可能很方便。它实施起来快速而简单。
答案 4 :(得分:0)
您可以通过在其名称前添加“Local”来将Mutex的范围设置为“session local”;阅读MSDN了解更多信息。通过这种方式,您可以限制每个终端会话的流程实例(好吧,这不完全是您所要求的)。
答案 5 :(得分:0)
Sendmessage / Postmessage和Getmessage是我喜欢的API。
的SendMessage:
http://pinvoke.net/default.aspx/user32.SendMessage
PostMessage的:
http://pinvoke.net/default.aspx/user32.PostMessage
的GetMessage:
http://pinvoke.net/default.aspx/user32.GetMessage
答案 6 :(得分:-3)
另一种方法是使用好的旧DDE(动态数据交换): http://www.codeplex.com/ndde
DDE建立在Windows消息之上,但它是一个抽象层。
(是的,我喜欢我的朋友WIN32-api);)