1

I have a pretty well-known setup for the inter-process data exchange using the (ugly) WM_COPYDATA message. It's not my decision, I have to support it in a legacy app.

const uint WM_COPYDATA = 0x004A;

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

[DllImport("user32.dll")]
static extern int SendMessage(IntPtr hwnd, uint msg, IntPtr wparam, ref COPYDATASTRUCT lparam);

If I'm sending just a plain struct, without attaching the additional data, it works just fine:

COPYDATASTRUCT container;
container.dwData = 42;
container.cbData = 0;
container.lpData = IntPtr.Zero;
SendMessage(myHwnd, WM_COPYDATA, IntPtr.Zero, ref container);

On the receiver side (an external WinForms app), I get this message and can correctly read the dwData field:

protected override void WndProc(ref Message m)
{
    if (m.Msg == WM_COPYDATA)
    {
        var container = (COPYDATASTRUCT)m.GetLParam(typeof(COPYDATASTRUCT));
        MessageBox.Show(container.dwData.ToString()); // 42
    }
    base.WndProc(ref m);
}

But as soon as I attach some additional payload, the external application stops receiving this message. The m.Msg == WM_COPYDATA condition is always false in the receiver window proc, and the sender gets a 0 result from the SendMessage call.

COPYDATASTRUCT container;
container.dwData = 42;
container.cbData = 4;
container.lpData = Marshal.AllocHGlobal(4);
int result = SendMessage(hwnd, WM_COPYDATA, IntPtr.Zero, ref container); // 0

(Actually, this is a dummy payload, but with the real one, it's the same.)

string payload = "test";
container.cbData = (payload.Length + 1) * 2;
container.lpData = Marshal.StringToHGlobalUni(payload);

I have also tried to marshal the COPYDATASTRUCT manually (using Marshal.StructToPtr) by changing the last parameter type of the SendMessage to IntPtr, unfortunately with no success (the same behavior).

I have tried to rely on the CLR marshalling by changing the COPYDATASTRUCT definition:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct COPYDATASTRUCT
{
  public uint dwData;
  public int cbData;

  [MarshalAs(UnmanagedType.LPWStr)]
  public string lpData;
}

Guess what? No effect.

What's wrong with my setup? Why the message cannot be received when a payload is attached to the data struct?

dymanoid
  • 14,771
  • 4
  • 36
  • 64
  • 3
    dwData is an IntPtr (variable size 32 or 64), not a 32bit value. It may work on 32bit but will fail on 64bit. lpData must be an IntPtr, not a string. https://stackoverflow.com/questions/6779731/c-sharp-using-sendmessage-problem-with-wm-copydata – Simon Mourier Jun 14 '17 at 15:41

1 Answers1

4

The structure definition is incorrect, it should be

[StructLayout(LayoutKind.Sequential)]
struct COPYDATASTRUCT
{
  public IntPtr dwData; // in C/C++ this is an UINT_PTR, not an UINT
  public int cbData;
  public IntPtr lpData;
}

Your definition of dwData was matching ok in a 32-bit process (4-bytes), but not in a 64-bit process (8 bytes). Since this is the first field in the structure, all bets are off when its definition is incorrect.

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298