[英]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#侧分配,那么就这样做:
CoTaskMemAlloc
分配它。 IntPtr
。 Marshal.PtrToStructure
和一些指针算法将返回数组封送到C#数组中。 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.