2008
09.23

Getting data across process boundaries can be tricky sometimes, especially when one of the processes is running managed code (.NET) and one is running a native executable.  It gets even more complicated if you want to do it on Windows CE.

In Win32, if I want to call from COM to .NET, it is pretty straightforward because the CLR will create a managed object and a COM Callable Wrapper (or CCW) that acts as a proxy for the COM object to talk to the managed object.  It’s a little clunky, but it works and is well documented on MSDN.

On Windows CE or Windows Mobile, however, (as best as I can determine), the CCW option does not exist.  The CE runtime won’t allow an unmanaged process to “host” the CLR so the CCW proxy can be created.  So how do you get data from unmanaged to managed code on Windows CE, then?

Good question.  I found the answer in the WM_COPYDATA message.  This cool little message lets you send data between processes, and even seems to work between managed and unmanaged code.

How to use it?

Let’s say that on the unmanaged side I want to send a string of data, “Hello”, to a managed .exe written in C#.  The unmanaged code is not COM, just regular old C++.
To set up the unmanaged code, declare a COPYDATASTRUCT, and fill out the members:

COPYDATASTRUCT cds;
cds.dwData = 0;
//Unicode chars. Don't need room for NULL on end
cds.cbData = 5 * sizeof(WCHAR);
cds.lpData = L"Hello";

Then just do a SendMessage with the address of cds:

::SendMessage(hManagedWnd, WM_COPYDATA, (WPARAM)myHwnd, (LPARAM)&cds);

Whoops, what is hManagedWnd? It is the HWND of the managed window where you are sending the data. How did I get that handle? With a call to FindWindow, of course. Just fill in the name of the window you are looking for and you get the handle back.

HWND hManagedWnd= FindWindow(NULL, L"NameOfManagedWindow");

Awesome.
So how do I catch this on the managed side? Good question.
Just to make this a little more interesting, I create a little managed window that just sits and waits for a WM_COPYDATA message. .NET has a handy class called MessageWindow that “Provides the ability to send and receive Windows-based messages.” Just what I am looking for.
Declare the COPYDATASTRUCT type:

[StructLayout(LayoutKind.Sequential)]
struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}

Derive a class from MessageWindow:

public class MsgWindow : MessageWindow

Define WM_COPYDATA:

public const int WM_COPYDATA = 0x004A;

Set the text of the window in the constructor:

public MsgWindow(Form1 msgform)
{
this.Text = "NameOfManagedWindow";
}

Override the WndProc (Yes, you can get to the WndProc in .NET):

protected override void WndProc(ref Message msg)
{
  switch (msg.Msg)
  {
    case WM_COPYDATA:
    {
      COPYDATASTRUCT cds = new COPYDATASTRUCT();
      cds = (COPYDATASTRUCT)Marshal.PtrToStructure
              (msg.LParam, typeof(COPYDATASTRUCT));
      if (cds.cbData > 0)
      {
         byte[] data = new byte[cds.cbData];
         Marshal.Copy(cds.lpData, data, 0, cds.cbData);
         UnicodeEncoding ue = new UnicodeEncoding();
         String str = ue.GetString(data, 0, data.Length);
         //At this point, str will contain "Hello"
      }
    }
  }
  base.WndProc(ref msg);
}

Create an instance of MsgWindow in your form’s constructor:

public partial class Form1 : Form
{
  MsgWindow MsgWin;
  public Form1()
  {
    MsgWin = new MsgWindow(this);
  }
}

In this way, even though it is a bit awkward, you can get data across from unmanaged to managed code. Working with binary data would be a bit trickier, but will still work. Underneath it all, Windows creates a memory-mapped file to handle the actual data transfer, which saves you the trouble of doing it yourself.
midniteblogger

Comments are closed.