Window Message Based Communication Between Different Applications

|

There are a number of ways to make inter-process communications on Windows platform, some of which are memory mapped files, mailslots, named pipes etc. But there is another way of doing inter-process communications. the WM_COPYDATA window message enables you to send data to other applications, the only requirement on the receiving applications is that the receiving application should have a thread running message pump, running message pump is the only way to process window messages. I've created a sample application on how to use this technique to send string messages between two Windows Forms applications.

I have two Windows Forms applications, one is called SenderApp which is responsible to send messages, it typically uses the Win32 SendMessage to send WM_COPYDATA messages to the receiving application called ReceiverApp, in the SendMessage method, you should specify lParam arugment a pointer to a COPYDATASTRUCT structure, COPYDATASTRUCT carries on the data you want to send to other applications.

[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT{
    /// <summary>
    /// Specifies data to be passed to the receiving application.
    /// </summary>
    public Int32 dwData;
    /// <summary>
    /// Specifies the size, in bytes, of the data pointed to by the lpData member.
    /// </summary>
    public Int32 cbData;
    /// <summary>
    /// Pointer to data to be passed to the receiving application. This member can be IntPtr.Zero.
    /// </summary>
    public IntPtr lpData;
}

[DllImport("user32.dll")]
public static extern Boolean SendMessage(IntPtr hwnd, UInt32 Msg, IntPtr wParam, ref COPYDATASTRUCT lParam);

Here is the code for the SenderApp:

public partial class SenderForm : Form
{
    public const UInt32 WM_COPYDATA = 0x004a;

    public SenderForm()
    {
        InitializeComponent();
    }

    private void sendButton_Click(Object sender, EventArgs e)
    {
        IntPtr receiverHwnd = FindWindow(null, "Receiver Form");
        if (receiverHwnd == IntPtr.Zero)
        {
            MessageBox.Show("Receiver App hasn't start yet!");
            return;
        }

        String msg = msgTextBox.Text;

        // Allocate unmanaged memory.
        IntPtr msgPtr = Marshal.AllocHGlobal(msg.Length);

        // Convert string to a sequence of bytes.
        Byte[] msgData = Encoding.Default.GetBytes(msg);
       
        // Copy the bytes from managed heap to unmanaged memory.
        Marshal.Copy(msgData, 0, msgPtr, msgData.Length);

        COPYDATASTRUCT copyData = new COPYDATASTRUCT();
        copyData.dwData = Encoding.Default.CodePage;
        copyData.cbData = msgData.Length + 1; //We should take the null-terminator into consideration.
        copyData.lpData = msgPtr;
        SendMessage(receiverHwnd, WM_COPYDATA, this.Handle, ref copyData);
        Marshal.FreeHGlobal(msgPtr);
    }

    /// <summary>
    /// The main entry point for the SenderApp.
    /// </summary>
    [STAThread]
    public static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new SenderForm());
    }
}

And this is code for ReceiverApp:

public partial class ReceiverForm : Form
{
    public const UInt32 WM_COPYDATA = 0x004a;

    public ReceiverForm()
    {
        InitializeComponent();
    }

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_COPYDATA)
        {
            COPYDATASTRUCT copyData = (COPYDATASTRUCT)m.GetLParam(typeof(COPYDATASTRUCT));
            Byte[] data = new Byte[copyData.cbData - 1]; //CLR doesn't recognize the null-terminator, so we should strip it.
            Marshal.Copy(copyData.lpData, data, 0, data.Length);
            String msg = Encoding.GetEncoding(copyData.dwData).GetString(data, 0, data.Length);
            msgTextBox.Text = msg;
        }
        base.WndProc(ref m);
    }

    /// <summary>
    /// The main entry point for the ReceiverApp.
    /// </summary>
    [STAThread]
    public static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new ReceiverForm());
    }
}

Put the above code up and running, you get the following screenshot:

 

You can download the full vs project from here.

2 comments:

Term Papers said...

This article was extremely interesting, especially since I was searching for thoughts on this subject last Thursday

hotel granduca houston said...

Hello there! Incidentally, I like the way you have structured your site, it is super and very easy to follow. I have bookmarked you and will be back regularly. thanks for sharing my friend, it can make a great difference !