6

.NET を使用して、同じマシン内の 2 つのプロセスが通信するための最良の (または最良ではないかもしれませんが、単に良い) 方法は何ですか?

実際、私が取り組んでいるアプリの 2 つのプロセスは、2 つの異なるプログラムではありません。これらは、同じ EXE の 2 つのインスタンスにすぎません。シングルトン アプリのようなことをしたかったのですが、それをユーザーごとに行いました (つまり、複数のユーザーがいるターミナル サーバーまたは Citrix または App-V サーバーは、アプリの独自の単一コピーを起動できる必要があります)。別のインスタンスが同じユーザーによって実行されている場合は、既に実行中のインスタンスにタスクを委任してから終了する必要があります。プログラムのユーザーごとに 1 つのインスタンスのみを実行する必要があります。これまでのところ、Mutex を使用して、アプリのインスタンスが既に実行されているかどうかを検出する部分を (StackOverflow のおかげで) 実行しました。しかし、最初のアプリ インスタンスにデータを送信するには、2 番目のアプリ インスタンスが必要です。

これには名前付きパイプと WCF の NetNamedPipeBinding を使用することに傾いていますが、より良いアイデアがあれば、本当に感謝しています。ありがとう :)

4

7 に答える 7

3

IPCは私が過去にこれに使用したものです。そして、それは驚くほど簡単です。.Net Remoting良いオプションですが、たとえば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 に答える
3

通常は名前付きパイプが最適ですが、時間の経過に伴うメッセージ配信を保証するファイア アンド フォーゲット シナリオの MSMQ も検討してください。メッセージのサイズにも大きく依存します。ユーザーごとに一意のポートが必要になるため、TCP は扱いにくくなります。

于 2009-01-22T08:06:30.447 に答える
2

私は名前付きパイプに行きます。

基本的に、ユーザーごとに一意のエンドポイントが必要です。Mutex と同様に、おそらくユーザー名を使用して、使用する名前付きパイプの名前を見つけます。これは、Mutex の使用を置き換えることさえあります。この特定の名前付きパイプが既に存在するかどうかを調べてみてください。存在する場合は、実行する 2 番目のインスタンスであることを意味します。

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

GetMessage:
http://pinvoke.net/default.aspx/user32.GetMessage

于 2009-01-22T08:55:18.070 に答える
-3

もう1つの方法は、古き良きDDE(Dynamic Data Exchange)を使用することです:http: //www.codeplex.com/ndde

DDEはWindowsメッセージに基づいて構築されていますが、その上の抽象化レイヤーです。

(ええ、私は友達のWIN32-apiが好きです);)

于 2009-01-22T09:56:01.583 に答える