我的64位C#4.5.1 WinForms应用程序,使用以下代码,正确地从外部32位C ++ DLL(我无法控制)获取COPYDATASTRUCT消息,如使用该地址的内存窗口中所示存储在lpData:
中protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case (int)WM.COPYDATA:
var msg = Marshal.PtrToStructure<COPYDATASTRUCT>(m.LParam);
Console.WriteLine(string.Format(
"msg = [" + Environment.NewLine +
" dwData = {0}" + Environment.NewLine +
" cbData = {1}" + Environment.NewLine +
" lpData = {2}" + Environment.NewLine +
" ]", msg.dwData, msg.cbData, msg.lpData.ToString("x16")));
var pData = Marshal.PtrToStructure<GEORECT>(msg.lpData);
// Code to fetch data
break;
}
}
输出窗口显示传入的内存地址包含正确的数据,如以下结构所述:
[StructLayout(LayoutKind.Sequential, Pack = 1)] public struct COPYDATASTRUCT { public IntPtr dwData { get; private set; } public int cbData { get; private set; } public IntPtr lpData { get; private set; } }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct GEORECT
{
[MarshalAs(UnmanagedType.LPStr, SizeConst = 11)]
public string Falconview; // "FALCONVIEW\0"
public MessageType Type { get; set; } // MessageType.FV_RUBBERBAND_GEORECT_MSG (3)
public int MessageId { get; set; }
public double NW_Latitude { get; set; }
public double NW_Longitude { get; set; }
public double SE_Latitude { get; set; }
public double SE_Longitude { get; set; }
}
然而Marshal.PtrToStructure总是抛出AccessViolationException,虽然我可以看到数据是正确的:结构大小的字节数返回49,并且有49个字节的格式正确。
我已经检查过本机和托管结构的大小是相同的,并且对于包含的C字符串有适当的属性,并且已经查找了Marshal.PtrToStructure throws exception这样的示例,但它们并没有解释我的问题。
我猜测可能问题是32位地址没有正确地转换为IntPtr并用于64位地址空间,但我无法验证这一点,因为64位地址(高位为零) )在VS调试器窗口中工作。
有人可以帮忙吗?
答案 0 :(得分:2)
根据UnmanagedType Enumeration的文档,如果字符串是一个固定长度的字符数组,它完全存储在struct(我猜它是)中,而不是指向存储在别处的字符串的指针,您应该在Falconview属性的MarshalAs属性中使用UnmanagedType.ByValTStr而不是UnmanagedType.LPStr。虽然我担心编组器可能会因为TStr位而将字符串编组为Unicode而不是ANSI,但是文档说marshaller将引用您已经包含在StructLayout属性中的CharSet以进行该确定,因此不应该不成问题。不幸的是,我不确定如何测试这个解决方案,因为我不确定你使用的是什么库,所以请告诉我这是否适合你。