【问题标题】:Sending pointer to C# struct into C++ DLL将指向 C# 结构的指针发送到 C++ DLL
【发布时间】:2012-03-01 14:10:38
【问题描述】:

我在 DLL 中有一个 C++ 函数,它接受一个指向结构 JPInfo 的指针,该结构在函数中填充了从服务器接收的数据,C++ 结构的布局如下所示:

typedef struct JP
{
    unsigned char type;
    DWORD value;
} JP;

typedef struct JPInfo
{
    JP jps[3];
    _int16 ConT;
    _int16 CallT;
    unsigned char ret;
    unsigned char count;
    unsigned char JPOffset;
    unsigned char JPPeriod;
} JPInfo;

函数在 DLL 中导出如下:

__declspec(dllexport) DWORD __stdcall GetJPInfo(JPInfo* jpi, DWORD time);

该函数需要一个指向 JPInfo 结构的指针,我试图在 C# 中模拟这个结构

[StructLayout(LayoutKind.Sequential, Size = 5), Serializable]
public struct JP
{
    byte type;
    int value;
}

[StructLayout(LayoutKind.Sequential,Size=23),Serializable]
public struct JPInfo
{
    JP[] jps;
    Int16 ConT;
    Int16 CallT;
    byte ret;
    byte count;
    byte JPOffset;
    byte JPPeriod;
}

我尝试像这样从 C# 调用函数:

[DllImport("DLLImp.dll")]
    unsafe public static extern int GetJP(ref JPInfo jpi, int time);
// then in main...
JPInfo jpi = new JPInfo;
GetJackpotValues(ref jpi, 4000);

我收到“System.ExecutionEngineException”类型的未处理异常。我的 JPInfo 结构中没有固定大小的 JP 结构数组,所以我不知道如何处理。

谢谢。

【问题讨论】:

  • 尽量不要明确指定 sie 并且默认 Marshaller 会执行此操作。 (大小=23,5 等)

标签: c# c++ dll pinvoke


【解决方案1】:

您是否尝试过删除结构上的 Size 属性?做类似的事情时,我不必指定大小。对于您的数组属性,请尝试将它们归因于:

[StructLayout(LayoutKind.Sequential)]
public struct JPInfo
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    JP[] jps;
    Int16 ConT;
    Int16 CallT;
    byte ret;
    byte count;
    byte JPOffset;
    byte JPPeriod;
}

【讨论】:

  • 嗯,我现在没有收到错误,但是结构没有被填充,在 DLL 中执行了一个字节的直接字节复制,因此结构中项目的顺序至关重要,确实我声明结构的方式保证结构中的成员在内存中的顺序相同?
  • 是的,这是一条路。我还将在创建时初始化数组 JP[] jps = new JP[3];.
  • 通过指定 LayoutKind.Sequential,内存将按照您在 .NET 应用程序中定义结构属性的顺序进行布局。拥有动态数组大小对我来说没有意义,因为您必须通过管道发送精确的内存分配。
  • @Krizz 为什么需要这样做? pinvoke marshaller 将使用默认值进行初始化,因此根本不需要您概述的步骤。
【解决方案2】:

假设 C++ 结构已打包,您的 C# 结构应如下所示:

[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct JP
{
    byte type;
    uint value;
}

[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct JPInfo
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=3)]
    JP[] jps;
    Int16 ConT;
    Int16 CallT;
    byte ret;
    byte count;
    byte JPOffset;
    byte JPPeriod;
}

另一方面,如果它们未打包,则将Pack 参数删除到StructLayout 属性。您应该在 C++ 头文件中查找 #pragma pack 语句以了解 C++ 结构是否已打包。

我猜 C++ 结构是打包的,因为你说它们被映射到从服务器接收的数据上。

你的导入应该是这样的:

[DllImport("DLLImp.dll")]
public static extern uint GetJP(ref JPInfo jpi, uint time);

DWORD 转换为 uint 而不是 int,这里不需要不安全的代码。

【讨论】:

  • 绝对完美!谢谢大卫。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-07-14
  • 1970-01-01
  • 2021-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-30
相关资源
最近更新 更多