【问题标题】:Passing a pointer to class into unmanaged c++ code from C#将指向类的指针从 C# 传递到非托管 C++ 代码中
【发布时间】:2012-10-10 07:10:24
【问题描述】:

我在 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

【问题讨论】:

  • 它与“unsafe”关键字无关,它只是一个标准的marshal问题......
  • @user287107 你是对的,我把这个问题从答案的上下文中拿出来了。
  • 顺便说一句,我曾经为 Delphi 编写过相同的代码来使用 c++ dll - 它确实有效。所以dll不会有问题。
  • 大小不同,因为有padding。编译器会将结构大小增加到一个方便的数字。
  • c++ 做同样的事情吗?我想也许这就是问题

标签: c# c++ marshalling unmanaged


【解决方案1】:

在 C# 中,我们有 classes 和 structs。所有class 类型都是引用类型,但struct 类型是值类型。这意味着当你有类似class MyStruct 的东西并且你写MyStruct s 它实际上是一个指向基类的指针,当你通过引用传递它时,你实际上传递了该指针的地址,所以它与 C++ 无关期望指向主要struct 的指针。根据此解决您的问题的方法是将class 转换为struct。

long 和 ulong 在 C# 中是 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]

所以当函数被调用时,它将把一个无效的参数作为结构的地址。所以只需将您的函数定义更改为:

[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、32 位的值

【讨论】:

  • @DenisKildishev C# 中的一个额外注释 ulong 在 CLR 中是 UInt64,但 C++ 中的 ulong 在 CLR 中是 UInt32 或在 C# 中是 uint,当然这不是 @987654340 的来源@ 在你的代码中
  • 谢谢!不知道。 long in _MyMethod 的论点当然很关键
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-21
    • 1970-01-01
    • 2011-07-12
    • 2021-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多