6

同一台机器上的两个进程使用 .NET 进行通信的最佳(或者可能不是最好的——只是好的)方式是什么?

实际上,我正在开发的应用程序中的两个进程甚至不是两个不同的程序。它们只是同一个 EXE 的两个实例。我想做类似单例应用程序的事情,但每个用户都有它(意味着具有多个用户的终端服务器或 Citrix 或 App-V 服务器应该能够启动他们自己的应用程序的单个副本)。如果另一个实例由同一个用户运行,它应该将任务委托给已经运行的实例,然后退出。该程序的每个用户只能运行一个实例。到目前为止,我已经完成了(感谢 StackOverflow)使用 Mutex 检测应用程序实例是否已在运行的部分。但我需要第二个应用程序实例才能将数据发送到第一个应用程序实例。

我倾向于为此使用命名管道和 WCF 的 NetNamedPipeBinding,但如果您有更好的想法,我将不胜感激。谢谢 :)

4

7 回答 7

3

命名管道通常是最好的,但也考虑使用 MSMQ 来确保消息随时间传递。这实际上也取决于您的消息大小。TCP 会变得很棘手,因为您需要每个用户都有一个唯一的端口。

于 2009-01-22T08:06:30.447 回答
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();
    }

  }

我希望这有帮助。

于 2009-01-22T09:55:48.260 回答
2

我会选择命名管道。

基本上,每个用户都需要一个唯一的端点。与您的 Mutex 一样,您可能会使用用户名来找出要使用的命名管道的名称。这甚至可能取代您对 Mutex 的使用:尝试找出此特定的命名管道是否已经存在,如果存在,则意味着您是第二个运行的实例。

将 TCP 端口映射到用户更难。

哦,通过使用命名管道,我实际上是在说“通过命名管道进行远程处理”。

于 2009-01-22T08:55:49.910 回答
1

Sendmessage/Postmessage 和 Getmessage 是我喜欢的 API。

发送消息: http ://pinvoke.net/default.aspx/user32.SendMessage

留言: http ://pinvoke.net/default.aspx/user32.PostMessage

获取消息:http :
//pinvoke.net/default.aspx/user32.GetMessage

于 2009-01-22T08:55:18.070 回答
0

.Net Remoting 也可能很方便。它快速且易于实施。

在 MSDN 上阅读更多内容

于 2009-01-22T08:31:11.847 回答
0

您可以将 Mutex 的范围设置为“会话本地”,方法是在其名称前加上“Local\”;阅读MSDN了解更多信息。通过这种方式,您可以限制每个终端会话的流程实例(好吧,这不是您所要求的)。

于 2009-01-22T08:50:01.013 回答
-3

另一种方法是使用旧的 DDE(动态数据交换): http: //www.codeplex.com/ndde

DDE 建立在 Windows 消息之上,但在它之上是一个抽象层。

(是的,我喜欢我的朋友 WIN32-api);)

于 2009-01-22T09:56:01.583 回答