views:

541

answers:

7

What's the best (or maybe not the best -- just good) way for two processes in the same machine to communicate, using .NET?

Actually the two processes in the app I'm working on aren't even two different programs; they're just two instances of the same EXE. I wanted to do something like a singleton app, but have it per user (meaning a Terminal Server or Citrix or App-V server with multiple users should be able to launch their own single copy of the app). If another instance is run by the same user, it should just delegate the task to the already running instance, then exit. Only one instance per user of the program should be running. So far I've done (thanks to StackOverflow) the part that detects whether an instance of the app is already running, using Mutex. But I need the second app instance to be able to send data to the first app instance.

I'm leaning towards using named pipes and WCF's NetNamedPipeBinding for this, but if you have better ideas I'll really appreciate it. Thanks :)

+3  A: 

Named pipes are usually best but also consider MSMQ for a fire and forget scenario that ensures message delivery over time. It really depends on your message sizes as well. TCP would become tricky because you'd need a unique port per user.

DarkwingDuck
MSMQ sounds too heavy for what I want to do...thanks anyway
cruizer
Yeah I agree I don't want to listen to some TCP port...
cruizer
Named pipes will be fast, and WCF will make it very easy.
DarkwingDuck
A: 

.Net Remoting might also be handy. It's fast and easy to implement.

read more on MSDN

pirho
A: 

You can set the scope of a Mutex to "session local" by prefixing its name with "Local\"; read MSDN for more. This way you can limit the process instances per terminal session (okay, this is not exactly what you asked).

thanks but i think that's the default behaviour already. and i'm able to make the mutual exclusion work properly; what I wanted opinions on was the interprocess communication part.
cruizer
A: 

Sendmessage/Postmessage and Getmessage are APIs I like for this.

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

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

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

Stefan
I would seriously consider NOT to use this. This particular method of communication is definately not the .NET way.
Dave Van den Eynde
Everything else suggested above also have some or other performance baggage to it. Windows messages by themselves are pretty lightweight...only the pInvoke part might be harmful/slow.
Sesh
Its easy to use. And I think its a valid answer. Maybe not fitting all cases but If you are familiar with using API then its has a place. It may also exist a .Net way to listen to messages, I havent looked into that yet.
Stefan
Ill take every -1 I can get on this, I think its a perfect option and important information when talking about messages between applications on same computer.
Stefan
+1  A: 

I would go for Named Pipes.

Basically, you'll need a unique endpoint per user. As with your Mutex, you'll probably use the username to find out the name of the named pipe to use. This might even replace your use of the Mutex: try to find out if a named pipe for this particular already exists, and if it does, it means you're the second instance to run.

It's harder to map a TCP port onto a user.

Oh, and by using Named Pipes, I'm actually saying "Remoting over Named Pipes".

Dave Van den Eynde
+1  A: 

IPC is what I've used in the past for this. And it is supprisingly easy. .Net remoting is a good option but unfortunately it is a restricted option becasue you can't for example use it on the CF.

Below is a copy of the class I use to perform Inter-process Communication, you can use it in conjuction with a MutEx if you wish, but it isnt necessary. As long as the "pMappedMemoryName" and "pNamedEventName" are the same in both processes, it should work just fine. I tried to make it as event driven as possible.

Simply use the Poke method to write data, and the Peek method to read it, although I designed it to automatically fire an event when new data is available. In this way you can simply subscribe to the IpcEvent event and not have to worry about expensive polls.

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

  }

I hope this helps.

Ash
@Ash - GREAT IDEA! This isn't going to meet my needs for this project, but I will use it in the future. Thanks for sharing.
photo_tom
A: 

Another way is to use the good old DDE (Dynamic Data Exchange): http://www.codeplex.com/ndde

DDE builds on windows messages but is an abstraction-layer above it.

(Yeah, I like my friends WIN32-api) ;)

Stefan
DDE is nothing but a fancy layer on top of Windows Messages. The link you specify also mentions that the library there is for use with legacy applications.
Dave Van den Eynde
Just as I wrote. I still like the simplicity with it. Just a couple of APIs and you are going.
Stefan