将 C 结构编组到 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);
今天我需要传递 的数组 structs(如上面的示例)..也许还会收到一个返回..
如何将 C# 类“翻译”为 C 结构体(反之亦然)?
Suppose I have a structure:
typedef struct {
float x;
float y;
float z;
int ID;
} Vertex;
and a C++ function:
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;
}
First - my IDE. I'm using Visual Studio 2010, building a C# (4.0) application; The C++ part is also built in VS2010.
I know how to build a DLL of C/C++ code and use it in C# application, but until today I used only primitive arguments and return values like:
[DllImport("library.dll", CharSet = CharSet.Ansi, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern int simple(int a, int b);
Today I need to pass an array of structs (as in the example above).. and perhaps also receive one back..
How to I "translate" a C# class to a C struct (and vice versa) ??
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
该结构可以这样声明:
接下来,您需要确定调用约定。您的 C++ 代码几乎肯定是使用
cdecl
编译的。让我们坚持下去。首先,该函数很容易从 C# 调用:
请注意,您不应该在此处使用
SetLastError
– 这是针对 Windows API 函数的。并且无需设置CharSet
,因为此处没有文本。现在,
另一个
事情变得更加复杂。如果您可以在 C# 代码中分配内存,那么这绝对是正确的选择。在 C# 端,您可以这样声明它:
并这样调用它
如果您不想在栅栏的 C# 端进行分配,则可以这样做:
分配它CoTaskMemAlloc
。IntPtr
。但如果您需要我的建议,请尝试并坚持在托管代码中分配数组。
The struct can be declared like this:
Next you need to settle on a calling convention. Your C++ code is almost certainly compiled with
cdecl
. Let's stick with that.The function first is easy to call from C#:
Note that you should not use
SetLastError
here–that's for Windows API functions. And there's no need to set theCharSet
since there is no text here.Now, for
another
things get more complex. If you can allocate the memory in the C# code then that is definitely the way to go.On the C# side you declare it like this:
and call it like this
If you don't want to allocate on the C# side of the fence then do it like this:
CoTaskMemAlloc
.IntPtr
.Marshal.PtrToStructure
and some pointer arithmetic to marshal the return array into a C# array.Marshal.FreeCoTaskMem
to free the memory allocated in the native module.But if you want my advice, try and stick to allocating the array in the managed code.
应该这么简单:
您可能遇到的问题是,是否对 C 版本的结构进行了任何打包,也可能是对结构布局进行了打包。如果布局不匹配,您可能需要将其更改为
LayoutKind.Explicit
并在每个字段上使用[FieldOffset(0)]
属性。 C 也不知道传递的顶点数组的长度,因此如果它发生变化,您需要将其传递给该方法。要返回数组:
封送拆收器在传入参数时处理所有内存问题,但返回数组时它无法执行任何操作。由于内存是在非托管堆上分配的,因此 GC 对此一无所知,并且您将导致内存泄漏。编组器只会将本机结构复制到托管结构数组,但它无法释放使用
malloc
分配的内存。如果您可以更改 C++ 代码,解决此问题的最简单方法是更改
another
的签名以接受顶点数组(以及数组的长度),而不是返回一个。我不需要为您编写任何代码来执行此操作,@DavidHeffernan 已经在他的答案(中断部分)中完成了此操作。It should be this easy:
The issues you may run into would be if there is any packing done on the C version of the struct, and possibly the struct layout. If the layout doesn't match, you may want to change it to
LayoutKind.Explicit
and use the[FieldOffset(0)]
attribute on each field. C would also have no idea, the length of the verticies array passed, so if that changes, you'd want to pass that along to the method.To get an array back:
The marshaler handles all of the memory issues when passing arguments in, but returning the array it can't do anything. Since the memory is allocated on the unmanaged heap, the GC has no idea about it, and you will wind up with a memory leak. The marshaller will simply copy the native structs to the managed struct array, but it can't free the memory you've allocated with
malloc
.The easiest way to get around it, if you can change the C++ code, would be to change the signature of
another
to take in an array of verticies (and the length of the array) instead of returning one. I don't need to write out any code for you that does this, @DavidHeffernan has already done this in his answer, the part of the break.