COPYDATASTRUCT 和 Marshal.PtrToStructure 抛出 AccessViolationException

COPYDATASTRUCT and Marshal.PtrToStructure throws AccessViolationException

我的 64 位 C# 4.5.1 WinForms 应用程序使用以下代码从外部 32 位 C++ DLL(我无法控制)正确获取 COPYDATASTRUCT 消息,如内存 Window 使用存储在 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;
    }
}

输出window表明传入的内存地址包含正确的数据,如下结构所述:

[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[=13=]"

    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,尽管我可以看到数据是正确的:结构大小的字节数 returns 49,并且有 49 个字节的格式正确。

我已检查本机结构和托管结构是否具有相同的大小,并且对所包含的 C 字符串具有适当的属性,并查找了 Marshal.PtrToStructure throws exception 等示例,但它们没有阐明我的问题。

我推测问题可能是 32 位地址没有被正确地转换为 IntPtr 并在 64 位地址空间中使用,但我无法验证这一点,因为 64 位地址(具有高零位)在 VS 调试器中工作 window.

有人能帮忙吗?

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