将指向class的指针传递给C#中的非托管c ++代码

时间:2012-10-19 16:04:07

标签: c# c++ marshalling unmanaged

我在dll中有一个c ++导出函数:

int MyMethod(ulong pid, MyStruct* struct);

MyStruct被描述为类:

class MyStruct
{
public:
uchar   nVersion;
uchar   nModuleType;
uchar   nMachine64;
uchar   nReserved;
ulong  data1;
ulong  data2;
ulong  data3;
};

我试图将此功能导入我的C#代码,如下所示:

[DllImport("mydll.dll", EntryPoint = "#24")]
private static extern int _MyMethod(long pid, ref MyStruct struct);

C#中的类:

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
stuct MyStruct
{
    public byte nVersion;
    public byte nModuleType;
    public byte nMachine64;
    public byte nReserved;
    public ulong data1;
    public ulong data2;
    public ulong data3;
}

我得到System.AccessViolationException

MyStruct struct = new MyStruct();
_MyMethod(4728, ref struct);

出了什么问题?

更新: System.Runtime.InteropServices.Marshal.SizeOf(struct)返回32.为什么?我认为它应该是4 * 1 + 8 * 3 = 28

2 个答案:

答案 0 :(得分:1)

在C#中,我们有class个es和struct个。所有class类型都是引用,但struct类型是值类型。这意味着当你有类似class MyStruct的东西而你写MyStruct s时,它实际上就像是指向基类的指针,当你通过引用传递它时你实际上传递了该指针的地址,所以它什么都没有期待指向主struct的指针的C ++。根据此解决方案,您的问题是将class转换为struct

C#中的{p> longulong是64位类型,而它们在C ++中是32位(至少是MSVC),所以当你声明你的函数时它的第一个参数是long您发送额外的32位值,可能会覆盖下一个参数并导致它无效:

Stack:
    32 bit: [first 32 bit of the first parameter]
    32 bit: [second 32 bit of the first parameter]
    32 bit: [address of your structure]

因此,当函数调用它时,将使用无效参数作为struct的地址。所以只需将您的函数定义更改为:

[DllImport("mydll.dll", EntryPoint = "#24")]
private static extern int _MyMethod(int pid, ref MyStruct struct);

你的结构:

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
stuct MyStruct
{
    public byte nVersion;
    public byte nModuleType;
    public byte nMachine64;
    public byte nReserved;
    public uint data1;
    public uint data2;
    public uint data3;
}

可能是你的错误的来源是函数的第一个参数,因为函数需要一个32位的值而你提供一个64位的实际上你提供2,3位的值来导致函数的函数

答案 1 :(得分:1)

由于ulong的对齐要求,SizeOf()返回32,这是一个8字节的值类型。 StructLayoutAttribute.Pack的默认值为8,与本机代码中使用的默认包装相同。因此data1与偏移量8对齐,nReserved和data1之间有4个字节的间隙。所以4 x 1 + 4 + 3 x 8 = 32。

您可能会遇到这种差距,因为在使用MSVC编译器编译的本机C ++代码中,ulong是4个字节。与C#中的uint相同。所以修复结构声明,用uint替换ulong。

下一个问题,即AV的原因,就是你在C#中将结构声明为一个类。哪个是始终通过引用传递的引用类型。您当前的声明等同于C ++代码中的MyStruct **。删除声明中的 ref 或将其声明为结构而不是类。