繁体   English   中英

将C结构编组为C#

[英]Marshalling C structures to C#

假设我有一个结构:

typedef struct {
float x;
float y;
float z;
int   ID;
} Vertex;

和一个C ++函数:

float first(Vertex* ptr, int length){ //really silly function, just an example
    Vertex u,v;
    u.x = ptr[0].x; //...and so on, copy x,y,z,ID
    v.x = ptr[1].x; 
    return (u.x * v.x + u.y * v.y + u.z * v.z);
    }


Vertex* another(float a, int desired_size){
    Vertex v = (Vertex*)malloc(desired_size*sizeof(Vertex));
    v[0].x = a;
    v[1].x = -a; //..and so on.. make some Vertices.
    return v;
}

首先 - 我的IDE。 我正在使用Visual Studio 2010,构建一个C#(4.0)应用程序; C ++部分也是在VS2010中构建的。

我知道如何构建一个C / C ++代码的DLL并在C#应用程序中使用它,但直到今天我只使用原始参数并返回如下值:

    [DllImport("library.dll", CharSet = CharSet.Ansi, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
    public static extern int simple(int a, int b);

今天我需要传递一个结构 数组 (如上例所示)..也许还会收到一个结构..

如何将C#类“翻译”为C结构(反之亦然)?

结构可以像这样声明:

[StructLayout(LayoutKind.Sequential)]
public struct Vertex {
    public float x;
    public float y;
    public float z;
    public int ID;
}

接下来,您需要确定一个调用约定。 您的C ++代码几乎肯定是用cdecl编译的。 让我们坚持下去。

该函数首先很容易从C#调用:

[DllImport("library.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern float first(Vertex[] vertices);

请注意,您不应在此处使用SetLastError ,即Windows API函数。 并且没有必要设置CharSet因为这里没有文本。


现在,对于another件事情变得更加复杂。 如果您可以在C#代码中分配内存,那么这绝对是可行的方法。

void PopulateVertices(Vertex *vertices, int count)
{
    for (int i=0; i<count; i++)
    {
        vertices[i].x = ....
    }
}

在C#端你声明它是这样的:

[DllImport("library.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void PopulateVertices(Vertex[] vertices, int count);

并称之为这样

Vertex[] vertices = new Vertex[2];
PopulateVertices(vertices, vertices.Length);

如果你不想在围栏的C#侧分配,那么就这样做:

  1. 从C ++代码返回一个指针并使用CoTaskMemAlloc分配它。
  2. 在C#中,将导入函数的返回值声明为IntPtr
  3. 使用Marshal.PtrToStructure和一些指针算法将返回数组封送到C#数组中。
  4. 调用Marshal.FreeCoTaskMem释放本机模块中分配的内存。

但是,如果您需要我的建议,请尝试坚持在托管代码中分配数组。

它应该很容易:

[StructLayout(LayoutKind.Sequential)]
public struct Vertex {
    float x;
    float y;
    float z;
    int   ID;
}

[DllImport("library.dll", CallingConvention=CallingConvention.StdCall)]
public static extern float first(Vertex[] verticies, int arrLen);

可能遇到的问题是,如果在C版本的结构上进行了任何打包,可能还有结构布局。 如果布局不匹配,您可能希望将其更改为LayoutKind.Explicit并在每个字段上使用[FieldOffset(0)]属性。 C也不知道,传递了verticies数组的长度,所以如果改变了,你想要将它传递给方法。

要获得一个阵列:

[DllImport("library.dll", CallingConvention=CallingConvention.StdCall)]
public static extern Vertex[] another(float a);

编组器在传入参数时处理所有内存问题,但返回数组时它无法执行任何操作。 由于内存是在非托管堆上分配的,因此GC不知道它,并且最终会出现内存泄漏。 marshaller将简单地将本机结构复制到托管结构数组,但它无法释放您使用malloc分配的malloc

如果你可以改变C ++代码,解决它的最简单方法是更改another的签名以接受一个verticies数组(以及数组的长度)而不是返回一个。 我不需要为你写出任何代码,@ DavidHeffernan已经在他的答案中做了这个,这是休息的一部分。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM