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?
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.