在使用第三方的非托管API時,我們經常會遇到參數為指針或指針的指針這種情況,
一般我們會用IntPtr指向我們需要傳遞的參數地址;
?
但是當遇到這種一個導出函數時,我們如何正確的使用IntPtr呢,
extern "C" __declspec(dllexport) int GetClass(Class pClass[50])?;
?
由于這種情況也經常可能遇到,所以我制作了2個示例程序來演示下如何處理這種非托管函數的調用!
?
首先創建一個C++ 的DLL ?設置一個如上的導出函數
#include?<Windows.h>?
#include?<stdio.h>typedef?struct?Student?
{?char?name[20];?int?age;?double?scores[32];
}Student;typedef?struct?Class
{int?number;Student?students[126];?
}Class;extern?"C"?__declspec(dllexport)int?GetClass(Class?pClass[50])
{?????for(int?i=0;i<50;i++)?????{????????pClass[i].number=i;????????for(int?j=0;j<126;j++)????????{????????????memset(pClass[i].students[j].name,0,20);???????sprintf(pClass[i].students[j].name,"name_%d_%d",i,j);???pClass[i].students[j].age=j%2==0?15:20;???????}?????}?????return?0;30?
}
?
上面DLL 的導出函數要求傳遞的參數為它自定義的Class結構體數組, 那么我們在C#調用它時也要自定義對應的結構體了,
我們可以定義為如下:
[StructLayout(LayoutKind.Sequential)]struct Student{[MarshalAs(UnmanagedType.ByValTStr,SizeConst=20)]public string name;public int age;[MarshalAs(UnmanagedType.ByValArray,SizeConst=32)]public double[] scores;}[StructLayout(LayoutKind.Sequential)]struct Class{public int number;[MarshalAs(UnmanagedType.ByValArray,SizeConst=126)]public Student[] students;}
?
需要注意的是,這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后,再將非托管內容數據讀取到托管結構體數據中!
?
static?void?Main(string[]?args)
{int?size?=?Marshal.SizeOf(typeof(Class))?*?50;byte[]?bytes?=?new?byte[size];IntPtr?pBuff?=?Marshal.AllocHGlobal(size);Class[]?pClass?=?new?Class[50];GetClass(pBuff);for?(int?i?=?0;?i?<?50;?i++){IntPtr?pPonitor?=?new?IntPtr(pBuff.ToInt64()?+?Marshal.SizeOf(typeof(Class))?*?i);pClass[i]?=?(Class)Marshal.PtrToStructure(pPonitor,?typeof(Class));}Marshal.FreeHGlobal(pBuff);Console.ReadLine();
}
?