【问题标题】:Access Violation for Marshalling Ptr To Struct将 Ptr 编组到 Struct 的访问冲突
【发布时间】:2014-03-31 01:08:03
【问题描述】:

我在我的 c dll 中定义了以下结构:

typedef struct {
    char Name[10];
    char Flag[10];
} CountryData;

typedef struct {
    int NumElements;
    TrackData Elements[1000];
} CountryArray;

这样暴露的:

__declspec( dllexport ) CountryArray* _cdecl GetAllCountries()
{
    CountryArray fakedata;
    CountryData fakecountry = CountryData();
    strcpy_s(fakecountry.Name, "TEST");
    strcpy_s(fakecountry.Flag, "TEST");
    fakedata.Elements[0] = faketrack;
    fakedata.NumElements = 1;
    return new CountryArray(fakedata);
}

现在在 c# 中我定义了这些结构:

    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
    public struct COUNTRY
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
        public string Name;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
        public string Flag;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct COUNTRY_ARRAY
    {
        public int NumElements;
        public IntPtr Elements;
    }

我通过这个导入访问它:

    [DllImport("Countries.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?GetAllCountries@@YAPAUCountryArray@@XZ")]
    public static extern IntPtr GetAllCountries();

最后我尝试像这样整理数据:

IntPtr countryPtr = Natives.GetAllCountries();
Natives.COUNTRY_ARRAY countries = (Natives.COUNTRY_ARRAY)Marshal.PtrToStructure(countryPtr, typeof(Natives.COUNTRY_ARRAY));
for (int i = 0; i < countries.NumElements; i++)
{
    IntPtr iptr = (IntPtr)(countries.Elements.ToInt32() + (i * Marshal.SizeOf(typeof(Natives.COUNTRY))));
    try
    {
        //fails here
        Natives.COUNTRY country = (Natives.COUNTRY)Marshal.PtrToStructure(iptr, typeof(Natives.COUNTRY));                    
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }                
}

国家的编组是我收到此错误的地方:

System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
   at System.Runtime.InteropServices.Marshal.PtrToStructureHelper(IntPtr ptr, Object structure, Boolean allowValueClasses)

我尝试修改 COUNTRY 结构的大小并更改字符集,但仍然出现此错误。我完全被卡住了,这可能是什么问题?

【问题讨论】:

    标签: c# c++ c struct marshalling


    【解决方案1】:

    所以您的问题是您将Elements 的值视为指针,而实际上它是数组第一个元素的数据。在您的测试用例中,这在 32 位系统上为 Elements 提供了 0x54455354 的值,在 64 位系统上为 0x5445535400000000 提供了值(十六进制的“TEST”)。您正在偏移该指针。相反,您希望获得指向 Elements 字段的指针,可以这样做:

    IntPtr elementsOffset = Marshal.OffsetOf(typeof(COUNTRY_ARRAY), "Elements");
    

    然后,您将在计算每个元素的位置时获取从函数返回的指针并添加偏移量。

    另外,您应该always use IntPtr's ToInt64() method。它在 32 位系统上稍微慢了一点,但避免了在 64 位系统中截断内存地址的可怕边缘情况。

    此外,您的入口点上的名称修饰使我相信这是使用 C++ 编译器编译的。如果您使用不同的编译器甚至同一编译器的不同版本进行编译,您的绑定将会失败。您应该做的是将这些方法导出为 C 函数,以免它们被破坏。为此,请将要导出的方法包装在 extern "C" { ... } 块中。

    此外,您可以通过让结构为您完成工作来完全避免此问题:

    [StructLayout(LayoutKind.Sequential)]
    public struct COUNTRY_ARRAY
    {
        public int NumElements;
    
        [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1000)]
        public COUNTRY[] Elements;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-16
      • 2013-06-23
      相关资源
      最近更新 更多