【问题标题】:Marshaling nested structures from C# to C++将嵌套结构从 C# 编组到 C++
【发布时间】:2015-03-18 15:59:27
【问题描述】:

我在 C++ 中有以下类型:

typedef void* keychain_handle;

typedef struct {
  const char* keyHolderName;
  unsigned int numKeys;
  key* keys;
} key_holder;

typedef struct {
  const char* keyName;
  unsigned int keySize;
} key;

我有以下方法:

int createKeyChain(
  int id, 
  key_holder* keyHolders,
  keychain_handle* handle);

我需要我的 C# 来使用密钥创建密钥持有者,将其发送到 C++ 代码并接收句柄。

这是我的 C# 代码:

    /* Structs */
    [StructLayout(LayoutKind.Sequential)]
    public struct Key
    {
          public string key;
          public uint size;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct KeyHolder
    {
          public string name;
          public uint keys;
          public IntPtr keys;
    }


    /* Sync API */
    [DllImport("keys.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
    public static extern uint createKeyChain(uint id, KeyHolder[] keyHolders, ref IntPtr handle);


        Key[] myKeys = new Key[1];
        myKeys[0] = new Key { key = "tst", size = 5 };

        KeyHolder keyHolder = new DllWrapper.KeyHolder
        {
            name = "tst123",
            items = 1,
            keys = Marshal.AllocHGlobal(Marshal.SizeOf(typeof (Key))*myKeys.Length)
        };

        IntPtr c = new IntPtr(keyHolder.keys.ToInt32());

        for (int i = 0; i < myKeys.Length; i++)
        {
            Marshal.StructureToPtr(myKeys[i], c, false);
            c = new IntPtr(c.ToInt32() + Marshal.SizeOf(typeof(Key)));
        }

        Marshal.StructureToPtr(c, keyHolder.keys, false);

        IntPtr handle = IntPtr.Zero;

        var ret = createKeyChain(111, new []{keyHolder}, ref handle);

除了 Key 对象中的内部字符串损坏之外,一切都运行良好。我怀疑是 StructureToPtr 破坏了它。如何让字符串显示在 C++ 端?

谢谢。

【问题讨论】:

  • 你能展示一下你在 C# 中尝试过什么吗?
  • 请添加KeyKeyHolder定义
  • @nikis,抱歉忘记添加,现在添加。
  • 那是相当讨厌的一个。将需要相当多的手动编组。我认为您需要将键数组编组为IntPtr,然后单独编组每个键。需要为数组的每个元素调用一次Marshal.StringToHGlobalAnsi。 C++/CLI 是一种选择吗?
  • 是的,我想这是一个选项。

标签: c# c++ pinvoke marshalling


【解决方案1】:

如果不更改非托管代码,您别无选择,只能自己编组。看起来像这样:

[StructLayout(LayoutKind.Sequential)]
public struct _Key
{
    public IntPtr keyName;
    public uint keySize;
}

[StructLayout(LayoutKind.Sequential)]
public struct _KeyHolder
{
    public string name;
    public uint numKeys;
    public IntPtr keys;
}

public struct Key
{
    public string keyName;
    public uint keySize;
}

public static _KeyHolder CreateKeyHolder(string name, Key[] keys)
{
    _KeyHolder result;
    result.name = name;
    result.numKeys = (uint)keys.Length;
    result.keys = Marshal.AllocHGlobal(keys.Length * Marshal.SizeOf(typeof(_Key)));
    IntPtr ptr = result.keys;
    for (int i = 0; i < result.numKeys; i++)
    {
        _Key key;
        key.keyName = Marshal.StringToHGlobalAnsi(keys[i].keyName);
        key.keySize = keys[i].keySize;
        Marshal.StructureToPtr(key, ptr, false);
        ptr += Marshal.SizeOf(typeof(_Key));
    }
    return result;
}

public static void DestroyKeyHolder(_KeyHolder keyHolder)
{
    IntPtr ptr = keyHolder.keys;
    for (int i = 0; i < keyHolder.numKeys; i++)
    {
        _Key key = (_Key)Marshal.PtrToStructure(ptr, typeof(_Key));
        Marshal.FreeHGlobal(key.keyName);
        ptr += Marshal.SizeOf(typeof(_Key));
    }
    Marshal.FreeHGlobal(keyHolder.keys);
}

【讨论】:

  • 该函数实际上需要一个密钥持有者数组,但这并不是真正的问题。我剩下的唯一问题是密钥字符串没有进入 C++ 端。你能举一个你刚才解释的例子吗?
  • 我更新了我的帖子以包含我的最新代码,我在其中迭代所有键并编组它们。
  • 您对 Key 数组的建议不起作用:无法编组“KeyHolder”类型的字段“键”:托管/非托管类型组合无效(数组字段必须与 ByValArray 或 SafeArray 配对)。
  • 好的。我删除了那个。我现在不能再写代码了。如果你以后还卡住,我有时间。
  • 感谢您的宝贵时间!
【解决方案2】:

您仍然需要告诉 .NET 将字符串编组为 PChars:

[StructLayout(LayoutKind.Sequential)]
public struct Key
{
      [MarshalAs(UnmanagedType.LPStr)]
      public string key;
      public uint size;
}

[StructLayout(LayoutKind.Sequential)]
public struct KeyHolder
{
      [MarshalAs(UnmanagedType.LPStr)]
      public string name;
      public uint keyCount;

      public Key[] keys;
}

【讨论】:

  • 即使我添加了该属性,密钥字符串也已损坏。我认为整个 Key 对象的 Marshal.StructureToPtr 破坏了它。
  • @Tsury Aaah,当然,当您使用 Marshal.StructureToPtr 时,您不能这样做。要么让调用一直工作(例如 Key[] 而不是 IntPtr),要么手动处理字符串 - IntPtr keyMarshal.PtrToStringAnsi
  • PtrToStringAnsi 不是相反吗?我需要将字符串转换为 IntPtr,对吗?
  • @Tsury:哦,我以为你想读那些。如果你想通过一些,Marshal.StringToHGlobalAnsi 是你的人。不过,请确保您(通常是您,在方法调用之后)或库释放内存。
猜你喜欢
  • 2012-12-01
  • 1970-01-01
  • 2010-12-17
  • 2021-11-09
  • 1970-01-01
  • 1970-01-01
  • 2021-05-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多