【问题标题】:Calling C DLL function from C# - parameter struct too large or complex to marshal从 C# 调用 C DLL 函数 - 参数结构太大或太复杂而无法编组
【发布时间】:2015-06-23 12:06:22
【问题描述】:

我在使用 C# 从 C DLL 调用函数时遇到问题。我试图编组结构,但一个异常告诉我我的结构太大或太复杂而无法编组......我计算了 15256 字节的完整大小。

原来的C函数是这样的:

int32_t CN_API SetConfig(int32_t handle, int32_t bId, ModeEnum Mode, ParamStruct params);

我在 C# 中定义的结构如下所示:

struct ParamStruct
{
    int param1;
    ...
    //Here are some primitve arrays and variables...
    ...
    structArray1[16]
    //    |__...some integers
    //    |__structA
    //            |__...some variables

    int param8

    structB
    //    |__...some integer arrays and a float array
    //    |__structArray2[16]
    //            |__...some variables

    enum param9
    ...
    //here are some more structs and struct arrays
    ...
}

我知道“语法”不正确。只是你知道我的意思;)。

现在我想调用这个函数并将 ParamStruct 传递给它。

我的问题是,我该怎么做?你们中有人有解决我问题的好主意吗? 可以用 IntPtr 来做吗? 或者是用 C# 重写我的结构的最佳方法,使其不那么复杂?

如果您需要更详细的信息,请告诉我。

【问题讨论】:

  • 最简单的方法是找到避免如此大的结构的方法
  • 我只有 dll,不能改变里面的任何东西。我的意思是,我必须忍受这么大的结构。
  • 我认为 C++/CLI 可能是最好的前进方式。从 C# pinvoke 手动编组该结构将是可怕的。
  • 我发现围绕你希望使用的 dll 编写一个简单的 C++ 包装器,它只接受原始类型,这使得 pinvoke 的东西变得更加简单。此外,如果您创建结构的实例,则可以将 IntPtr 传递给您的包装器,然后在包装器中“编组”它。
  • @David Watts:感谢您的回答。我也想过这种可能性。但我希望有一种“简单”的方式或解决方法可以直接从 C# 中完成。 DLL 中还有更多运行良好的函数。这只是我遇到问题的一个功能。因此,我不想编写 C++ 包装器,因为如果我想这样做,我必须更改代码中的许多内容。

标签: c# c struct marshalling dllimport


【解决方案1】:

我通过重新定义本机方法以获取字节 * 参数并手动将结构复制到字节数组来解决了类似的问题。

public override void ToByteArray(out byte[] bEeprom)
{
  int arrOffs, arrOffs1, arrOffs2;
  int length = Size();
  int ptrOffset = 0;
  byte[] convString;
  int maxLen;

  IntPtr ptr = Marshal.AllocHGlobal(length);

  Marshal.WriteByte(ptr, ptrOffset, renumerationMode);
  ptrOffset += sizeof(byte);
  Marshal.WriteInt16(ptr, ptrOffset, (short)vendorID);
  ptrOffset += sizeof(short);
  Marshal.WriteInt16(ptr, ptrOffset, (short)productID);
  ptrOffset += sizeof(short);
  Marshal.WriteInt16(ptr, ptrOffset, (short)deviceID);
  ptrOffset += sizeof(short);
  Marshal.WriteByte(ptr, ptrOffset, config);
  ptrOffset += sizeof(byte);
  Marshal.WriteByte(ptr, ptrOffset, eepromVersion);
  ptrOffset += sizeof(byte);
  Marshal.WriteInt16(ptr, ptrOffset, hwVersion);
  ptrOffset += sizeof(short);
  for (arrOffs = 0; arrOffs < configFlags.Length; arrOffs++)
  {
    Marshal.WriteByte(ptr, ptrOffset, configFlags[arrOffs]);
    ptrOffset += sizeof(byte);
  }
......
  Debug.Assert(ptrOffset == length);

  bEeprom = new byte[length];
  Marshal.Copy(ptr, bEeprom, 0, length);
  Marshal.FreeHGlobal(ptr);
}

【讨论】:

  • 我后来发现有更有效的复制数组的方法(请参阅 Marshal Copy 方法),但没有时间重构代码。
  • 感谢 PaulF。我有同样的想法,使用字节数组而不是结构。但是你能告诉我我是如何准确定义 dll 导入然后用那个数组调用函数的吗?
  • 导入为 int SetConfig(int handle, int bId, int Mode, byte[] params); & 使用编组数组调用。
  • 我在之后检查了我的代码 & 它实际上将指针传递给了结构 & 使用了以下定义 "public unsafe static extern WriteEEprom(short handle, byte *eeprom)" & in包装代码类似于 " byte[] bE; E.ToByteArray(out bE); unsafe { fixed (byte *pE = bE) { NativeMethods.WriteEEprom(handle, pE); } }" i> 您可能需要按照类似的方式修改您的代码。
  • 我试图用你的方法传递我的字节数组。有时它可以工作,但大多数情况下我得到一个 AccesViolationException (“尝试读取或写入受保护的内存。这通常表明其他内存已损坏。”)我的字节大小以及我的参数值都是正确的。你知道为什么会这样吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多