Marshalling structs from WM_COPYDATA messages
I am trying to get a C# WPF application to communicate with another application written in C using WM_COPYDATA. The C app is trying to send a struct as follows:
typedef struct
{
int x;
int y;
char str[40];
double d;
char c;
} DATASTRUCT;
In my C# app I have defined a struct as follows:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct DATASTRUCT
{
public int x;
public int y;
[MarshalAs(UnmanagedType.LPStr, SizeConst=40)]
public string s;
public double d;
public char c;
};
And the code to receive the WM_COPYDATA message is as follows:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
hwndSource = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
hwndSource.AddHook(new HwndSourceHook(WndProc));
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == 0x4A)
{
DATASTRUCT data = (DATASTRUCT)Marshal.PtrToStructure(lParam, typeof(DATASTRUCT));
this.updateText(data);
handled = true;
}
return (IntPtr)0;
}
I am receiving messages from the C application, but all the data in the struct is gibberish. Previous to this I was able to manually extract a开发者_开发百科n array of bytes from the lParam pointer and then use System.BitConverter and System.Text.Encoding.ACII to interpret the byte array, and that worked pretty well. But now I am trying to do it in a cleaner way and it's just not working.
After a long time searching for the answer to this question, I realized that I was missing a VERY important step. I feel like an idiot, but here's the answer to my own question.
The C# struct should look like this:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public unsafe struct DataStruct
{
public int x;
public int y;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=40)]
public string s;
public double d;
public char c;
};
And another struct must be defined to receive the WM_COPYDATA info. It looks like this:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public unsafe struct CopyDataStruct
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
And the WndProc method should be changed to look like this:
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == 0x4A)
{
CopyDataStruct cps = (CopyDataStruct)Marshal.PtrToStructure(lParam, typeof(CopyDataStruct));
DataStruct data = (DataStruct)Marshal.PtrToStructure(cps.lpData, typeof(DataStruct));
updateText(data);
handled = true;
}
return (IntPtr)0;
}
I was using the CopyDataStruct in my previous working solution, and I just forgot to use it in the newer version.
Part of the problem is that the str member needs to be a ByValTStr not a LPSTR since it's an inlined string array. Try the following definition
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct DATASTRUCT
{
public int x;
public int y;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=40)]
public string s;
public double d;
public char c;
};
精彩评论