【问题标题】:How to create an array of structures in C# using DLL (written in C)?如何使用 DLL(用 C 编写)在 C# 中创建结构数组?
【发布时间】:2015-09-14 22:45:47
【问题描述】:

我想用数据填充一个结构,这样我就可以将它传递给我的 DLL 函数。 C# 和 C 实现中的结构相似。例如(在 C 中):

typedef struct{
    apple[] apples;
    leaf[] leaves;
} tree;

typedef struct{
    int taste;
} apple;

typedef struct{
    int color;
} leaf;

我想在 C# 上创建一个树结构,用苹果和树叶填充它,然后将其发送到我在 DLL 上的函数。

怎么做?

【问题讨论】:

  • 为了做这样的事情,您需要通过为结构指定顺序布局类型来编组非托管数据类型,并将数组专门编组为 C 样式数组。查看 MSDN 文章“平台调用教程”,特别是标记为 "Specifying Custom Marshaling for User-Defined Structs" 的部分。
  • 如果有人更熟悉这个过程并且可以写出完整的答案,欢迎您来代表。
  • 在尝试 pinvoke 之前先让它在 C 中工作。你不禁发现自己遇到了重大问题。就像 C 代码不知道结构中存储了多少元素一样。还有棘手的内存管理问题,谁负责释放这些数组的内存?这些问题在你 pinvoke 时并没有好转。
  • 如果我理解 weel,在像你这样的情况下,我曾经开发过一个小的 C++ dll 包装器。这种“技术”允许您在 C++ 中使用指针,并且您可以简单地定义在包装器中添加苹果等的方法。

标签: c# c visual-studio dll dllexport


【解决方案1】:

首先,我认为你应该稍微改变一下 C 结构:

typedef struct{
    UINT cApples;
    const apple *pApples;
    UINT cLeaves;
    const leaf *pLeaves;
} tree;

在 C# 方面:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct Tree
{
    internal uint cApples;
    internal IntPtr pApples;
    internal uint cLeaves;
    internal IntPtr pLeaves;
}

您可以类似地定义LeafApple。然后你可以在 C# 端填充它们,如下所示:

private Tree CreateTree(Leaf[] leaves, Apple[] apples)
{
    var result = new Tree();
    result.cLeaves = (uint)leaves.Length;
    result.cApples = (uint)apples.Length;
    result.pLeaves = AllocateNativeMemory(leaves);
    result.pApples = AllocateNativeMemory(apples);
    return result;
}

private IntPtr AllocateNativeMemory<T>(T[] elements) where T : struct
{
    int elementSize = Marshal.SizeOf(typeof(T));
    IntPtr result = Marshal.AllocHGlobal(elements.Length * elementSize);

    for (int i = 0; i < elements.Length; i++)
    {
        Marshal.StructureToPtr(
            elements[i], new IntPtr((long)result + i * elementSize), false);
    }

    return result;
}

现在可以将CreateTree方法的结果传递给调用C端的extern方法了。

备注:分配的内存应该被释放;否则,您的应用程序将泄漏。如果您决定 C# 端负责释放分配的内存,则应在最后执行以下操作:

private static void FreeTree(Tree tree)
{
    FreeNativeMemory<Leaf>(tree.pLeaves, tree.cLeaves);
    FreeNativeMemory<Apple>(tree.pApples, tree.cApples);
}

private static void FreeNativeMemory<T>(IntPtr arrayPtr, uint arrayLen) where T : struct
{
    int elementSize = Marshal.SizeOf(typeof(T));
    for (uint i = 0; i < arrayLen; i++)
    {
        Marshal.DestroyStructure(new IntPtr((long)arrayPtr + i * elementSize), typeof(T));
    }

    Marshal.FreeHGlobal(arrayPtr);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多