Send/Receive message To/From two running application

For a process running on the same machine, probably the lightest weight solution is to use PostThreadMessage(). I'm really surprised no one gave this answer, it's old school Windows programming. The OP was very close. Observe:

  • Every process has a main thread (native thread).
  • The main thread has a message queue.
  • The main thread has a thread ID which is global to the system.

All of the ingredients are there, it's a matter of putting them together. Conceptually it's straightforward, the tricky part is communicating the RECEIVER's main thread ID to the SENDER. You have a few options:

  1. From the SENDER, in Win32 you could dig out the thread ID from the RECEIVER's Thread Information Block. https://stackoverflow.com/a/8058710/420400
  2. When RECEIVER starts, you could save off the thread ID in its Process.StartInfo.Environment. It's really there, and it will be visible in SysInternals' Process Explorer - but you'll have difficulty getting at it. Again there is a Win32 solution for this. https://www.codeproject.com/Articles/25647/Read-Environment-Strings-of-Remote-Process
  3. When RECEIVER starts, you could save off the thread ID in shared memory.
  4. (Or something better...)

Options 1 & 2 seem like security exploits, so for this example I went with option 3 and shared the thread ID in a tiny memory mapped file.

The RECEIVER looks something like this

enum WM { USER = 0x400 }

class MyMessageFilter : IMessageFilter
{
    public bool PreFilterMessage(ref Message m)
    {
        if ((WM)m.Msg == WM.USER)
        {
            Console.WriteLine("WM_USER received.");
            return true;
        }

        return false;
    }
}

class RECEIVER : IDisposable
{
    MemoryMappedFile mmf;
    bool disposed = false;

    public void MyMessageLoop()
    {
        uint mainThreadId = GetCurrentThreadId();
        Console.WriteLine(mainThreadId);
        mmf = MemoryMappedFile.CreateNew(Constants.ThreadIDFileName, IntPtr.Size, MemoryMappedFileAccess.ReadWrite);
        using (var accessor = mmf.CreateViewAccessor(0, IntPtr.Size, MemoryMappedFileAccess.ReadWrite))
        {
            accessor.Write(0, mainThreadId);
        }
        Application.AddMessageFilter(new MyMessageFilter());
        Application.Run();
    }

    [DllImport("kernel32.dll")]
    static extern uint GetCurrentThreadId();

    // Implement IDisposable and ~RECEIVER() to delete the semaphore, omitted for brevity
    // https://docs.microsoft.com/en-us/dotnet/api/system.idisposable?view=netframework-4.7.2
    #region
    ...
    #endregion
}

And the SENDER looks something like this

enum WM { USER = 0x400 }

class Program
{
    static void Main(string[] args)
    {
        string procName = "RECEIVER";
        Process[] processes = Process.GetProcesses();

        Process process = (from p in processes
                           where p.ProcessName.ToUpper().Contains(procName)
                           select p
                          ).First();

        uint threadId;
        using (var mmf = MemoryMappedFile.OpenExisting(Constants.ThreadIDFileName, MemoryMappedFileRights.Read))
        using (var accessor = mmf.CreateViewAccessor(0, IntPtr.Size, MemoryMappedFileAccess.Read))
        {
            accessor.Read(0, out serviceThreadId);
        }

        PostThreadMessage(threadId, (uint)WM.USER, UIntPtr.Zero, IntPtr.Zero);
    }

    [return: MarshalAs(UnmanagedType.Bool)]
    [DllImport("user32.dll", SetLastError = true)]
    public static extern bool PostThreadMessage(uint threadId, uint msg, IntPtr wParam, IntPtr lParam);
}

There are different ways to share information between 2 processes.

First at all you have to think if both processes are going to be always in the same machine or not when your application scales up.

Different Machines

  • Use TCP/UDP socket connection (Can be the quickest solution)
  • Use MSMQ
  • Use WebServices, WCF or Restful Web Service.
  • Reading from a common entry in a db. (Not recommended)
  • Named Pipes (Check this) (Named pipes can be in same machine or fly over a network)

Always in same machine.

  • Shared memory (You can use memory mapped files)
  • Reading from a common file (You can use FileWatcher)

Preferred choice: MSMQ

If I were you I would preserve the ability of having processes in different machines so I would use, as Maarten suggested, two windows services that uses MSMQ to communicate. Why?

  1. MSMQ allows you not to lose messages (in case RECEIVER is down)
  2. MSMQ allows you to have processes in same machine or in different machines
  3. Windows service give you the ability to start/stop the processes easily
  4. Windows service can me monitored my SNMP and in general they integrate easily with windows admin tools.

Second preferred choice: Restful Web Service

If you don't want to use MSMQ I would use two Restful Web Service hosted in IIS to communicate both processes. It can be useful if you have an scenario where RECEIVER is not interested in messages from SENDER if they arrive late.

Tags:

C#

Message