我在我的 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 结构的大小并更改字符集,但仍然出现此错误。我完全卡住了,这里可能是什么问题?
最佳答案
所以您的问题是您将 Elements
的值视为指针,而实际上它是数组第一个元素的数据。在您的测试用例中,这为 Elements
提供了 32 位系统上的 0x54455354
值和 64 位系统上的 0x5445535400000000
值(“TEST “十六进制)。您正在抵消该指针。相反,您想要获取指向 Elements
字段的指针,这可以像这样完成:
IntPtr elementsOffset = Marshal.OffsetOf(typeof(COUNTRY_ARRAY), "Elements");
然后,在计算每个元素的位置时,您将获取从函数返回的指针并向其添加偏移量。
另外,你应该 always use IntPtr's ToInt64()
method .它在 32 位系统上有点慢,但避免了在 64 位系统中截断内存地址的可怕边缘情况。
此外,入口点上的名称修改让我相信这是用 C++ 编译器编译的。如果您使用不同的编译器甚至同一编译器的不同版本进行编译,您的绑定(bind)将失败。您应该做的是将这些方法导出为 C 函数,这样它们就不会被破坏。为此,将要导出的方法包装在 extern "C"{ ... }
block 中。
此外,您可以通过让结构为您完成工作来完全避免此问题:
[StructLayout(LayoutKind.Sequential)]
public struct COUNTRY_ARRAY
{
public int NumElements;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 1000)]
public COUNTRY[] Elements;
}
关于c# - 将 Ptr 编码到结构的访问冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22752118/