1. 程式人生 > >C#呼叫C/C++動態庫 封送結構體,結構體陣列

C#呼叫C/C++動態庫 封送結構體,結構體陣列

一. 結構體的傳遞

Cpp程式碼 

 收藏程式碼

  1. #define JNAAPI extern "C" __declspec(dllexport) // C方式匯出函式  
  2. typedef struct      
  3. {    
  4.     int osVersion;    
  5.     int majorVersion;    
  6.     int minorVersion;    
  7.     int buildNum;    
  8.     int platFormId;    
  9.     char szVersion[128];    
  10. }OSINFO;    
  11. // 1. 獲取版本資訊(傳遞結構體指標)    
  12. JNAAPI bool GetVersionPtr( OSINFO *info );    
  13. // 2.獲取版本資訊(傳遞結構體引用)    
  14. JNAAPI bool GetVersionRef(OSINFO &info);    

  可以通過二種方式來呼叫:

C#程式碼 

 收藏程式碼

  1. // OSINFO定義  
  2. [StructLayout(LayoutKind.Sequential)]  
  3. public struct OSINFO  
  4. {  
  5.     public int osVersion;  
  6.     public int majorVersion;  
  7.     public int minorVersion;  
  8.     public int buildNum;  
  9.     public int platFormId;  
  10.     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]  
  11.     public string szVersion;  
  12. }  

  1. 方式一(傳入結構體引用),在C#中,結構體是以傳值方式傳遞,類才是以傳地址方式傳遞,加關鍵字ref即可. C端傳遞了兩種不同型別的引數,都可以通過引用來解決.

C#程式碼 

 收藏程式碼

  1. [DllImport("jnalib.dll", EntryPoint = "GetVersionPtr")]  
  2. public static extern bool GetVersionPtr(ref OSINFO info);  
  3. public static extern bool GetVersionRef(ref OSINFO info);  

  2. 方式二(傳入IntPtr(平臺通用指標))

Cpp程式碼 

 收藏程式碼

  1. IntPtr pv = Marshal.AllocHGlobal(148); //結構體在使用時一定要分配空間(4*sizeof(int)+128)  
  2. Marshal.WriteInt32(pv,148); //向記憶體塊裡寫入數值  
  3. if (GetVersionPtr(pv)) //直接以非託管記憶體塊地址為引數  
  4. {  
  5.     Console.WriteLine("--osVersion:{0}", Marshal.ReadInt32(pv, 0));  
  6.     Console.WriteLine("--Major:{0}",Marshal.ReadInt32(pv, 4)); //移動4個位元組  
  7.     Console.WriteLine("--BuildNum: " + Marshal.ReadInt32(pv, 12));  
  8.     Console.WriteLine("--szVersion: "+Marshal.PtrToStringAnsi((IntPtr)(pv.ToInt32()+20)));  
  9. }  
  10. Marshal.FreeHGlobal(pv); //處理完記得釋放記憶體  

   二.結構體陣列的傳遞

Cpp程式碼 

 收藏程式碼

  1. // 傳遞結構體指標  
  2. JNAAPI bool GetVersionArray(OSINFO *info,int nLen);  

   呼叫程式碼:

C#程式碼 

 收藏程式碼

  1. /** 
  2.  * C#介面,對於包含陣列型別,只能傳遞IntPtr 
  3.  */   
  4. [DllImport("jnalib.dll", EntryPoint = "GetVersionArray")]  
  5. public static extern bool GetVersionArray(IntPtr p, int nLen);    
  6. // 源目標引數  
  7. OSINFO[] infos = new OSINFO[2];  
  8. for (int i = 0; i < infos.Length; i++)  
  9. {  
  10.     infos[i] = new OSINFO();  
  11. }  
  12. IntPtr[] ptArr = new IntPtr[1];  
  13. ptArr[0] = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(OSINFO)) * 2); //分配包含兩個元素的陣列  
  14. IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(OSINFO)));   
  15. Marshal.Copy(ptArr, 0, pt, 1); //拷貝指標陣列  
  16. GetVersionArray(pt, 2); //呼叫  
  17. //還原成結構體陣列  
  18. for (int i = 0; i < 2; i++)    
  19. {  
  20.     infos[i]=(OSINFO)Marshal.PtrToStructure((IntPtr)(pt.ToInt32()+i*Marshal.SizeOf(typeof(OSINFO))),typeof(OSINFO));  
  21.     Console.WriteLine("OsVersion:{0} szVersion:{1}", infos[i].osVersion, infos[i].szVersion);  
  22. }  

  三. 複雜結構體的傳遞

   1. 輸出引數,結構體作為指標傳出

Cpp程式碼 

 收藏程式碼

  1. typedef struct  
  2. {  
  3.     char name[20];  
  4.     int age;  
  5.     double scores[30];  
  6. }Student;  
  7. // Class中包含結構體陣列型別  
  8. typedef struct  
  9. {  
  10.     int number;  
  11.     Student students[50];  
  12. }Class;  
  13. // 傳入複雜結構體測試  
  14. JNAAPI int GetClass(Class *pClass,int len);  

Cpp程式碼 

 收藏程式碼

  1. // 介面定義   
  2. [DllImport("jnalib.dll", EntryPoint = "GetClass")]  
  3. public static extern int GetClass(IntPtr pv,int len);  
  4. // 結構體定義  
  5. // Student  
  6. [StructLayout(LayoutKind.Sequential)]  
  7. public struct Student  
  8. {  
  9.     [MarshalAs(UnmanagedType.ByValTStr,SizeConst=20)]  
  10.     public string name;  
  11.     public int age;  
  12.     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]  
  13.     public double[] scores;  
  14. }  
  15. // Class  
  16. [StructLayout(LayoutKind.Sequential)]  
  17. public struct Class  
  18. {  
  19.     public int number;  
  20.     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)] // 指定陣列尺寸   
  21.     public Student[] students; // 結構體陣列定義  
  22. }  
  23. // 呼叫複雜結構體測試  
  24. int size = Marshal.SizeOf(typeof(Class)) * 50;  
  25. IntPtr pBuff = Marshal.AllocHGlobal(size); // 直接分配50個元素的空間,比Marshal.copy方便多了  
  26. GetClass(pBuff, 50);  
  27. Class[] pClass = new Class[50];  
  28. for (int i = 0; i < 50; i++)  
  29. {  
  30.     IntPtr ptr = new IntPtr(pBuff.ToInt64() + Marshal.SizeOf(typeof(Class)) * i);  
  31.     pClass[i] = (Class)Marshal.PtrToStructure(ptr, typeof(Class));  
  32. }  
  33. Marshal.FreeHGlobal(pBuff); // 釋放記憶體  

   2. 輸入引數, 給複雜結構體賦值後作為輸入引數傳入

   對於比較大的結構體指標,無法直接應用結構體型別,轉化成IntPtr型別, 此時需要將原生型別轉化為指標,並給指標賦值

   呼叫方法: Marshal.StructureToPtr(stu, ptr1, true)