1. 程式人生 > >轉--C#呼叫C++DLL傳遞結構體陣列的終極解決方案

轉--C#呼叫C++DLL傳遞結構體陣列的終極解決方案

在專案開發時,要呼叫C++封裝的DLL,普通的型別C#上一般都對應,只要用DllImport傳入從DLL中引入函式就可以了。但是當傳遞的是結構體、結構體陣列或者結構體指標的時候,就會發現C#上沒有型別可以對應。這時怎麼辦,第一反應是C#也定義結構體,然後當成引數傳弟。然而,當我們定義完一個結構體後想傳遞引數進去時,會拋異常,或者是傳入了結構體,但是返回值卻不是我們想要的,經過除錯跟蹤後發現,那些值壓根沒有改變過,程式碼如下。

  1. [DllImport("workStation.dll")]  
  2.        privatestaticexternbool fetchInfos(Info[] infos);  
  3.        publicstruct Info  
  4.        {  
  5.            publicint OrderNO;  
  6.            publicbyte[] UniqueCode;  
  7.            publicfloat CpuPercent;                    
  8.        };  
  9.        privatevoid buttonTest_Click(object sender, EventArgs e)  
  10.        {  
  11.            try
  12.            {  
  13.             Info[] infos=new
     Info[128];  
  14.                if (fetchInfos(infos))  
  15.                {  
  16.                    MessageBox.Show("Fail");  
  17.                }  
  18.             else
  19.             {  
  20.                    string message = "";  
  21.                    foreach (Info info in infos)  
  22.                    {  
  23.                        message += string
    .Format("OrderNO={0}\r\nUniqueCode={1}\r\nCpu={2}",  
  24.                                               info.OrderNO,  
  25.                                               Encoding.UTF8.GetString(info.UniqueCode),  
  26.                                               info.CpuPercent  
  27.                                               );  
  28.                    }  
  29.                    MessageBox.Show(message);  
  30.             }  
  31.            }  
  32.            catch (System.Exception ex)  
  33.            {  
  34.                MessageBox.Show(ex.Message);  
  35.            }  
  36.        }  

後來,經過查詢資料,有文提到對於C#是屬於託管記憶體,現在要傳遞結構體陣列,是屬性非託管記憶體,必須要用Marsh指定空間,然後再傳遞。於是將結構體變更如下。
  1. [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]  
  2.      publicstruct Info  
  3.      {  
  4.          publicint OrderNO;  
  5.          [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]  
  6.          publicbyte[] UniqueCode;  
  7.          publicfloat CpuPercent;                    
  8.      };  

但是經過這樣的改進後,執行結果依然不理想,值要麼出錯,要麼沒有被改變。這究竟是什麼原因?不斷的搜資料,終於看到了一篇,裡面提到結構體的傳遞,有的可以如上面所做,但有的卻不行,特別是當引數在C++中是結構體指標或者結構體陣列指標時,在C#呼叫的地方也要用指標來對應,後面改進出如下程式碼。
  1. [DllImport("workStation.dll")]  
  2.     privatestaticexternbool fetchInfos(IntPtr infosIntPtr);  
  3.     [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]  
  4.     publicstruct Info  
  5.     {  
  6.         publicint OrderNO;  
  7.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]  
  8.         publicbyte[] UniqueCode;  
  9.         publicfloat CpuPercent;  
  10.     };  
  11.     privatevoid buttonTest_Click(object sender, EventArgs e)  
  12.     {  
  13.         try
  14.         {  
  15.             int workStationCount = 128;  
  16.             int size = Marshal.SizeOf(typeof(Info));  
  17.             IntPtr infosIntptr = Marshal.AllocHGlobal(size * workStationCount);  
  18.             Info[] infos = new Info[workStationCount];  
  19.             if (fetchInfos(infosIntptr))  
  20.             {  
  21.                 MessageBox.Show("Fail");  
  22.                 return;  
  23.             }  
  24.             for (int inkIndex = 0; inkIndex < workStationCount; inkIndex++)  
  25.             {  
  26.                 IntPtr ptr = (IntPtr)((UInt32)infosIntptr + inkIndex * size);  
  27.                 infos[inkIndex] = (Info)Marshal.PtrToStructure(ptr, typeof(Info));  
  28.             }  
  29.             Marshal.FreeHGlobal(infosIntptr);  
  30.             string message = "";  
  31.             foreach (Info info in infos)  
  32.             {  
  33.                 message += string.Format("OrderNO={0}\r\nUniqueCode={1}\r\nCpu={2}",  
  34.                                        info.OrderNO,  
  35.                                        Encoding.UTF8.GetString(info.UniqueCode),  
  36.                                        info.CpuPercent  
  37.                                        );  
  38.             }  
  39.             MessageBox.Show(message);  
  40.         }  
  41.         catch (System.Exception ex)  
  42.         {  
  43.             MessageBox.Show(ex.Message);  
  44.         }  
  45.     }  
要注意的是,這時介面已經改成IntPtr了。通過以上方式,終於把結構體陣列給傳進去了。不過,這裡要注意一點,不同的編譯器對結構體的大小會不一定,比如上面的結構體

在BCB中如果沒有位元組對齊的話,有時會比一般的結構體大小多出2兩個位元組。因為BCB預設的是2位元組排序,而VC是預設1 個位元組排序。要解決該問題,要麼在BCB的結構體中增加位元組對齊,要麼在C#中多開兩個位元組(如果有多的話)。位元組對齊程式碼如下。

  1. #pragma pack(push,1)
  2.    struct Info  
  3. {  
  4.     int OrderNO;  
  5.     char UniqueCode[32];  
  6.     float CpuPercent;  
  7. };  
  8. #pragma pack(pop)


用Marsh.AllocHGlobal為結構體指標開闢記憶體空間,目的就是轉變化非託管記憶體,那麼如果不用Marsh.AllocHGlobal,還有沒有其他的方式呢?

其實,不論C++中的是指標還是陣列,最終在記憶體中還是一個一個位元組儲存的,也就是說,最終是以一維的位元組陣列形式展現的,所以我們如果開一個等大小的一維陣列,那是否就可以了呢?答案是可以的,下面給出了實現。

  1. [DllImport("workStation.dll")]  
  2.         privatestaticexternbool fetchInfos(IntPtr infosIntPtr);  
  3.         [DllImport("workStation.dll")]  
  4.         privatestaticexternbool fetchInfos(byte[] infos);  
  5.         [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]  
  6.         publicstruct Info  
  7.         {  
  8.             publicint OrderNO;  
  9.             [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]  
  10.             publicbyte[] UniqueCode;  
  11.             publicfloat

    相關推薦

    --C#呼叫C++DLL傳遞結構陣列終極解決方案

    在專案開發時,要呼叫C++封裝的DLL,普通的型別C#上一般都對應,只要用DllImport傳入從DLL中引入函式就可以了。但是當傳遞的是結構體、結構體陣列或者結構體指標的時候,就會發現C#上沒有型別可以對應。這時怎麼辦,第一反應是C#也定義結構體,然後當成引數傳弟。然而,當

    結構指標作函式引數(C# 呼叫C++ 的DLL

    1、C++結構體定義:   #pragma pack(1)  struct Person  {      #define Count_favoriteNumbers 6        int id;        fl

    c#呼叫C/C++ DLL,傳入指標陣列(指標指向自定的結構)

    來源:http://bbs.csdn.net/topics/380165851 依靠以下文章:解決問題。 、、、、、、、、、、、、、、、、、、、、 可以用Marshal.StruectToPtr哦。 、、、、、、、、、、、、、、 [StructLayout(Layo

    C# 呼叫dll 封送結構 結構陣列

    一. 結構體的傳遞 cpp 程式碼 #define JNAAPI extern "C" __declspec(dllexport) // C方式匯出函式 typedef struct { int osVersion; int majorVe

    C#呼叫c++Dll結構陣列指標的問題

    C#呼叫c++dll檔案是一件很麻煩的事情,首先面臨的是資料型別轉換的問題,相信經常做c#開發的都和我一樣把學校的那點c++底子都忘光了吧(語言特性類)。 網上有一大堆得轉換對應表,也有一大堆的轉換例項,但是都沒有強調一個更重要的問題,就是c#資料型別和c++資料型別佔

    python呼叫C++,傳遞結構結構指標,以及巢狀結構

    #include<iostream>using namespace std;//該檔名稱:cpptest.cpp//終端下編譯指令://g++ -o cpptest.so -shared -fPIC cpptest.cppstruct sub_struct{   

    C# 向 C++ DLL傳遞結構,包含二維陣列,一維陣列,VS2013下測試通過。

    需求是這樣的: C++中需要結構體如下: PS:使用的是MFC 靜態 DLL工程! // struct HCNetConnectData {//CHAR * HCNetServerIP;//INT    HCNetServerPort;//CHAR * HCNetServe

    C#呼叫C++編寫的DLL函式引數傳遞

                            &nb

    []C#呼叫C++ DLL

    在開發過程中經常需要在C#中呼叫C++編寫的DLL,中間碰到過一些問題,這裡做個總結,方便以後參考。 型別對照問題 記憶體釋放問題 版本問題(x86與x64) 編譯問題(靜態與動態) 資源載入問題 異常捕獲與問題定位 型別對照問題   c#呼叫c++方法時,首先要在類中定義

    C#呼叫C++的dll傳遞二維陣列

    1.C++中標頭檔案.h extern "C" MATHFUNCSDLL_API  int  __stdcall CallTest(int** arr, int rows, int cols); 2.C++中原始檔.cpp int __stdcall CallTest

    C#呼叫C++編寫的DLL函式各種引數傳遞問題

    [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously   [DllImport("User32.dll", CharSet=CharSet.Auto)]   public static extern

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

    一. 結構體的傳遞 Cpp程式碼    #define JNAAPI extern "C" __declspec(dllexport) // C方式匯出函式   typedef struct       {         int osVersion;  

    用JNI從C傳遞結構到JAVA

    typedef struct Foo {     int len;     char name[100]; } Foo_t; JNIEXPORT jint JNICALL Java_TestJNI_foo(JNIEnv *env, jobject obj, jobject fooObj) {     Foo

    golang日期字串,仿照C#中的日期格式結構

    1、日期格式集合、日期轉字串方法 package util import ( "strings" "time" ) //日期格式:模仿java中的結構體 type DateStyle string const ( MM_DD

    C#呼叫C/C++動態庫,封裝各種複雜結構

        現在公司要做一個使用C#程式呼叫C++的一個DLL庫,解析檔案的功能。所以在網上找了一些資料。     一、結構體傳遞 #define JNAAPI extern "C" __declspec(dllexport) // C方式匯出函式 typedef str

    C#呼叫C/C++ DLL 引數傳遞和回撥函式的總結

    Int型傳入: Dll端: extern "C" __declspec(dllexport) int Add(int a, int b) {     return a+b; } C#端: [DllImport("aeClient2.0.dll", CallingCo

    C#呼叫C++ 平臺呼叫P/Invoke 結構--輸入輸出引數、返回值、返出值、結構陣列作為引數【五】

    【1】結構體作為輸入輸出引數 C++程式碼: typedef struct _testStru1 { int iVal; char cVal; __int64 llVal; }testS

    C#呼叫C++ 平臺呼叫P/Invoke 結構--含有內建資料型別的一維、二維陣列、字串指標【六】

    【1】結構體中含有內建資料型別的一維陣列 C++程式碼: typedef struct _testStru3 { int iValArrp[30]; WCHAR szChArr[30];

    C++ 字串 15-- 18.41.結構與string string類的呼叫 引數通過引用的方式呼叫

    #include <iostream> #include <string> using namespace std; /*--------------------------------- 18.41.結構體與string string類的呼叫 引數通

    C語言中,隱藏結構的細節

    all printf span 包括 strcpy () 創建 提高 結構體指針 我們都知道,在C語言中,結構體中的字段都是可以訪問的。或者說,在C++ 中,類和結構體的主要區別就是類中成員變量默認為private,而結構體中默認為public。結構體的這一個特性,導致結構