Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
将C结构编组到C#_C#_.net_Dll_Interop_Pinvoke - Fatal编程技术网

将C结构编组到C#

将C结构编组到C#,c#,.net,dll,interop,pinvoke,C#,.net,Dll,Interop,Pinvoke,假设我有一个结构: 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;

假设我有一个结构:

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。我正在使用VisualStudio2010构建一个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结构(反之亦然)?

应该很简单:

[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也不知道传递的Vertices数组的长度,因此如果该长度发生变化,则希望将其传递给该方法

要恢复阵列,请执行以下操作:

[DllImport("library.dll", CallingConvention=CallingConvention.StdCall)]
public static extern Vertex[] another(float a);
封送拆收器在传入参数时处理所有内存问题,但返回数组时却无能为力。由于内存是在非托管堆上分配的,因此GC对此一无所知,最终会导致内存泄漏。封送拆收器只需将本机结构复制到托管结构数组,但它无法释放您使用
malloc
分配的内存


<>最容易的方法,如果你能改变C++代码,那就是改变代码的代码>另一个< /C> >,以输入一个垂直数组(数组的长度),而不是返回一个数组。我不需要为您编写任何这样做的代码,@DavidHeffernan已经在他的回答中这样做了,这是中断的一部分。

结构可以这样声明:

[StructLayout(LayoutKind.Sequential)]
public struct Vertex {
    public float x;
    public float y;
    public float z;
    public int ID;
}
[DllImport("library.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void PopulateVertices(Vertex[] vertices, int count);
接下来,你需要确定一个通话约定。您的C++代码几乎肯定是用<代码> CCDL> <代码>编译的。让我们坚持下去

第一个函数很容易从C#调用:

请注意,此处不应使用
SetLastError
——这是针对Windows API函数的。因为这里没有文本,所以不需要设置
CharSet


现在,对于另一个,事情变得更复杂了。如果你能在C代码中分配内存,那么这绝对是一条可行之路

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

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

如果您不想在围栏的C#侧进行分配,请执行以下操作:

[StructLayout(LayoutKind.Sequential)]
public struct Vertex {
    public float x;
    public float y;
    public float z;
    public int ID;
}
[DllImport("library.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void PopulateVertices(Vertex[] vertices, int count);
< L>从C++代码返回指针并将其分配给<代码> COTASKMeMeloCals> />代码>
  • 在C中,将导入函数的返回值声明为
    IntPtr
  • 使用
    Marshal.PtrToStructure
    和一些指针算法将返回数组封送到C#数组中
  • 调用
    Marshal.FreeCoTaskMem
    释放本机模块中分配的内存

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

    另一种方法呢?内存分配没有问题吗?指针?返回一个用C++ MALOC分配的数组根本不起作用。我猜,封送器可能试图用< COT> COTASKMEMFEXE/COD>释放内存。也许用
    CoTaskMemAlloc
    分配它就可以了。“我总是喜欢在篱笆的C#一侧分配。”戴维德费弗南——我怀疑这一点。我认为封送拆收器尽可能少地处理非托管内存,特别是当它分配了
    malloc
    时,以防止其他代码仍然期望该指针有效。是的,这是一个问题。这两个声明的另一个大问题是无法计算数组的大小。这是行不通的。因此,如果可能的话,最好(更容易?)在C端分配内存,然后将分配的空间传递给函数?这样当然容易得多。为了选择该解决方案,您需要提前知道阵列的大小。有时,您可以在DLL中调用一个函数(您提供的)来找出所需的数组大小。但通常情况下,您只需要从程序中的其他信息了解数组的大小。谢谢,我将尝试此解决方案。结构并不总是保留在堆栈上。有一种常见的误解,认为值类型(结构是值类型)总是驻留在堆栈上。不是真的。在您的情况下,当您分配一个数组时,数组的内容(它是一个对象)将位于堆上。这里没什么好担心的。在结构的C#中声明它为
    float[]info
    并用此属性标记它:
    [marshallas(UnmanagedType.ByValArray,SizeConst=3)]
    在此处阅读所有关于UnmanagedType属性的内容: