6 Stimmen

Wie können in .NET zwei Prozesse auf demselben Rechner am besten miteinander kommunizieren?

Was ist der beste (oder vielleicht nicht der beste - nur ein guter) Weg für zwei Prozesse auf demselben Rechner, um mit .NET zu kommunizieren?

Eigentlich sind die beiden Prozesse in der Anwendung, an der ich arbeite, nicht einmal zwei verschiedene Programme; sie sind nur zwei Instanzen derselben EXE. Ich wollte so etwas wie eine Singleton-App machen, aber pro Benutzer (d. h. ein Terminalserver oder Citrix- oder App-V-Server mit mehreren Benutzern sollte in der Lage sein, ihre eigene einzelne Kopie der App zu starten). Wenn eine andere Instanz vom selben Benutzer ausgeführt wird, sollte sie die Aufgabe einfach an die bereits laufende Instanz delegieren und sich dann beenden. Pro Benutzer sollte nur eine Instanz des Programms ausgeführt werden. Bisher habe ich (dank StackOverflow) den Teil, der erkennt, ob eine Instanz der App bereits ausgeführt wird, mit Mutex erledigt. Aber ich brauche die zweite App-Instanz, um Daten an die erste App-Instanz senden zu können.

Ich bin in Richtung der Verwendung von benannten Pipes und WCF NetNamedPipeBinding für diese lehnen, aber wenn Sie bessere Ideen haben, ich werde wirklich zu schätzen wissen. Danke :)

3voto

DarkwingDuck Punkte 2636

Benannte Leitungen sind in der Regel am besten geeignet, aber auch MSMQ kann für ein "Fire and Forget"-Szenario verwendet werden, das die Zustellung von Nachrichten über einen längeren Zeitraum gewährleistet. Das hängt auch von der Größe Ihrer Nachrichten ab. TCP würde schwierig werden, da Sie für jeden Benutzer einen eigenen Port benötigen würden.

0 Stimmen

Named Pipes werden schnell sein, und WCF wird es sehr einfach machen.

3voto

Ash Punkte 22398

IPC habe ich in der Vergangenheit für diese Zwecke verwendet. Und es ist verblüffend einfach. .Net-Remoting es eine gute Option, aber leider eine eingeschränkte Option, weil man sie zum Beispiel nicht auf dem CF verwenden kann.

Nachfolgend finden Sie eine Kopie der Klasse, die ich für die Inter-Prozess-Kommunikation verwende. Sie können sie in Verbindung mit einem MutEx verwenden, wenn Sie möchten, aber es ist nicht notwendig. Solange "pMappedMemoryName" und "pNamedEventName" in beiden Prozessen gleich sind, sollte es gut funktionieren. Ich habe versucht, es so ereignisgesteuert wie möglich zu machen.

Verwenden Sie einfach die Poke-Methode, um Daten zu schreiben, und die Peek-Methode, um sie zu lesen, obwohl ich sie so konzipiert habe, dass automatisch ein Ereignis ausgelöst wird, wenn neue Daten verfügbar sind. Auf diese Weise können Sie einfach das IpcEvent-Ereignis abonnieren und müssen sich nicht um teure Polls kümmern.

  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();
    }

  }

Ich hoffe, das hilft.

0 Stimmen

GROSSE IDEE! Sie entspricht zwar nicht meinen Bedürfnissen für dieses Projekt, aber ich werde sie in Zukunft verwenden. Danke fürs Teilen.

0 Stimmen

Es fehlt die Klasse EventsManagement.

0 Stimmen

Sie müssen den entsprechenden Verweis hinzufügen, um Zugriff auf die Klasse EventsManagement zu erhalten.

2voto

Dave Van den Eynde Punkte 16711

Ich würde mich für Named Pipes entscheiden.

Grundsätzlich benötigen Sie einen eindeutigen Endpunkt pro Benutzer. Wie bei Ihrer Mutex werden Sie wahrscheinlich den Benutzernamen verwenden, um den Namen der zu verwendenden Named Pipe herauszufinden. Dies könnte sogar Ihre Verwendung der Mutex ersetzen: Versuchen Sie herauszufinden, ob eine Named Pipe für diesen Benutzer bereits existiert, und wenn ja, bedeutet dies, dass Sie die zweite Instanz sind, die ausgeführt wird.

Es ist schwieriger, einen TCP-Port einem Benutzer zuzuordnen.

Oh, und wenn ich Named Pipes verwende, meine ich eigentlich "Remoting über Named Pipes".

1voto

Stefan Punkte 11254

Sendmessage/Postmessage und Getmessage sind APIs, die ich für diesen Zweck mag.

Sendemitteilung:
http://pinvoke.net/default.aspx/user32.SendMessage

Postmessage:
http://pinvoke.net/default.aspx/user32.PostMessage

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

0 Stimmen

Ich würde ernsthaft in Erwägung ziehen, dies NICHT zu verwenden. Diese spezielle Methode der Kommunikation ist definitiv nicht der .NET Weg.

0 Stimmen

Alles andere, was oben vorgeschlagen wurde, ist auch mit der einen oder anderen Leistungseinschränkung verbunden. Windows-Nachrichten selbst sind ziemlich leicht... nur der pInvoke Teil könnte schädlich/langsam sein.

0 Stimmen

Es ist einfach zu benutzen. Und ich denke, es ist eine gültige Antwort. Vielleicht nicht passend für alle Fälle, aber wenn Sie mit der Verwendung von API vertraut sind, dann hat es einen Platz. Es kann auch ein .Net Weg, um Nachrichten zu hören existieren, ich habe in das noch nicht untersucht.

0voto

pirho Punkte 2902

.Net Remoting könnte auch praktisch sein. Es ist schnell und einfach zu implementieren.

Lesen Sie mehr auf MSDN

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X