如何使用pinvoke将C struct数组指针指向C#

时间:2012-08-15 12:06:21

标签: c# c arrays struct pinvoke

我正在尝试使用pinvoke来编组从C到C#的另一个结构内的结构数组。 AFAIK,不能做。
所以相反,在C结构中,我向我的数组和malloc声明了一个ptr。问题:1)如何在C#端声明等效? 2)如何在C#端分配和使用等效文件?

//The C code
typedef struct {
       int a;
       int b; } A;
typedef struct {
      int c;
      // A myStruct[100];    // can't do this, so:
      A *myStruct; } B;

//The c# code:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class A{
    int a;
    int b;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class B{
      int c;
      // can't declare array of [100] A structures...
     ?
 }

[编辑]:不知何故,我误解了我在别处读到的关于c#侧固定数组对象的内容。 我可以修复C中的数组大小所以编译好了,但是在使用时我得到“对象引用没有设置为对象的实例”:

data.B[3].a = 4567;所以,在其他地方读到这个错误可能是什么,我添加了这个方法:

public void initA()
        {
          for (int i = 0; i < 100; i++) { B[i] = new A(); }
        }

再次编译好,但错误信息相同。

1 个答案:

答案 0 :(得分:5)

要在C和C#之间编组像这样的“复杂”结构,你有几个选择。

在这种情况下,我强烈建议您尝试将固定数组嵌入到C端结构中,因为它会大大简化C#端。您可以使用MarshalAs属性告诉C#在运行时需要在阵列中分配多少空间:

// In C:
typedef struct
{
 int a;
 int b;
} A;

typedef struct
{
 int c;
 A data[100];
} B;

// In C#:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct A 
{
  int a;
  int b;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct B
{
  int c;
  [MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
  A[] data = new data[100];
}

如果您不知道或无法指定数组的固定大小,那么您需要执行您所做的操作并将其声明为C中的指针。在这种情况下,您无法告诉C#阵列在运行时将使用多少内存,所以你几乎完全不习惯手工编写所有的编组。 This question对其运作方式有一个很好的概述,但基本思路是:

  1. 您应该在结构中添加一个字段,其中包含数组元素的数量(这将使您的生活更轻松)
  2. 将C#中的字段声明为:IntPtr data;,没有属性。
  3. 使用Marshal.SizeOf(typeof(A))在非托管内存中获取结构的大小。
  4. 使用Marshal.PtrToStructure将单个非托管结构转换为C#
  5. 使用IntPtr.Add(ptr, sizeofA)移动到数组中的下一个结构
  6. 循环直到你用完为止。