C#向C++封送结构体数组

在使用第三方的非托管API时,我们经常会遇到参数为指针或指针的指针这种情况,

一般我们会用IntPtr指向我们需要传递的参数地址;

 

但是当遇到这种一个导出函数时,我们如何正确的使用IntPtr呢,

extern "C" __declspec(dllexport) int GetClass(Class pClass[50]) ;

 

由于这种情况也经常可能遇到,所以我制作了2个示例程序来演示下如何处理这种非托管函数的调用!

 

首先创建一个C++ 的DLL  设置一个如上的导出函数

 1  #include  < Windows.h >
 2  #include  < stdio.h >
 3 
 4  typedef  struct  Student
 5  {
 6       char  name[ 20 ];
 7       int  age;
 8       double  scores[ 32 ];
 9  }Student;
10 
11  typedef  struct  Class
12  {
13       int  number;
14      Student students[ 126 ];
15  }Class;
16 
17  extern   " C "  __declspec(dllexport)  int  GetClass(Class pClass[ 50 ])
18  {
19       for ( int  i = 0 ;i < 50 ;i ++ )
20      {
21          pClass[i].number = i;
22           for ( int  j = 0 ;j < 126 ;j ++ )
23          {
24              memset(pClass[i].students[j].name, 0 , 20 );
25              sprintf(pClass[i].students[j].name, " name_%d_%d " ,i,j);
26              pClass[i].students[j].age = j % 2 == 0 ? 15 : 20 ;
27          }
28      }
29       return   0 ;
30  }

 

 

上面DLL 的导出函数要求传递的参数为它自定义的Class结构体数组, 那么我们在C#调用它时也要自定义对应的结构体了,

我们可以定义为如下:

 1   [StructLayout(LayoutKind.Sequential)]
 2           struct  Student
 3          {
 4              [MarshalAs(UnmanagedType.ByValTStr,SizeConst = 20 )]
 5               public   string  name;
 6               public   int  age;
 7              [MarshalAs(UnmanagedType.ByValArray,SizeConst = 32 )]
 8               public   double [] scores;
 9          }
10          [StructLayout(LayoutKind.Sequential)]
11           struct  Class
12          {
13               public   int  number;
14              [MarshalAs(UnmanagedType.ByValArray,SizeConst = 126 )]
15               public  Student[] students;
16 
17          }

 

 

需要注意的是,这2个结构体中的数组大小一定要跟C++中的限定一样大小哦,接下来如何使用这个API来正确的获取数据呢,大多数人可能想到像这样的处理方式

Class myclass  =   new  Class();
            IntPtr ptr
= Marshal.AllocHGlobal(Marshal.SizeOf( typeof (Class)));
            GetClass(ptr);
            Marshal.FreeHGlobal(ptr);

 

 

没错,这样的处理是没问题的,但是我们的API的参数是Class数组,这种处理方式只是传递一个Class结构体参数,所以这种方式在这里就不太合适了,!

 

 那大家就想到先Class[] myclass = new Class[MaxClass]; 然后在用Marshal.AllocHGlobal 来获取myclass 数据的指针,

 

其实这样也是错的, 因为 Class结构中包含了,不能直接封送的Student结构,所以无论如何上面的想法是错误的!

 

那要怎么办呢,其实很简单,就是先分配一段非托管内存,并调用API后,再将非托管内容数据读取到托管结构体数据中!

 

示例演示代码如下:

 1    static   void  Main( string [] args)
 2          {
 3               int  size  =  Marshal.SizeOf( typeof (Class))  *   50 ;
 4               byte [] bytes  =   new   byte [size];
 5              IntPtr pBuff  =  Marshal.AllocHGlobal(size);
 6              Class[] pClass  =   new  Class[ 50 ];
 7              GetClass(pBuff);
 8               for  ( int  i  =   0 ; i  <   50 ; i ++ )
 9              {
10                  IntPtr pPonitor  =   new  IntPtr(pBuff.ToInt64()  +  Marshal.SizeOf( typeof (Class))  *  i);
11                  pClass[i]  =  (Class)Marshal.PtrToStructure(pPonitor,  typeof (Class));
12              }
13              Marshal.FreeHGlobal(pBuff);
14              Console.ReadLine();
15          }

 

 

 有兴趣的不妨自己测试一下,看看输出结果是否正确!

 

你可能感兴趣的:(C++)