COPYDATASTRUCT和Marshal.PtrToStructure抛出AccessViolationException

时间:2015-01-06 05:07:28

标签: c# com marshalling

我的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调试器窗口中工作。

有人可以帮忙吗?

1 个答案:

答案 0 :(得分:2)

根据UnmanagedType Enumeration的文档,如果字符串是一个固定长度的字符数组,它完全存储在struct(我猜它是)中,而不是指向存储在别处的字符串的指针,您应该在Falconview属性的MarshalAs属性中使用UnmanagedType.ByValTStr而不是UnmanagedType.LPStr。虽然我担心编组器可能会因为TStr位而将字符串编组为Unicode而不是ANSI,但是文档说marshaller将引用您已经包含在StructLayout属性中的CharSet以进行该确定,因此不应该不成问题。不幸的是,我不确定如何测试这个解决方案,因为我不确定你使用的是什么库,所以请告诉我这是否适合你。