首頁 後端開發 C#.Net教程 詳解C#呼叫C++DLL傳遞結構體數組的解決辦法

詳解C#呼叫C++DLL傳遞結構體數組的解決辦法

Mar 28, 2017 am 11:52 AM

這篇文章主要介紹了C#呼叫C++DLL傳遞結構體數組的終極解決方案的相關資料,需要的朋友可以參考下

C#呼叫C++DLL傳遞結構體數組的終極解決方案

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

[DllImport("workStation.dll")] 
    private static extern bool fetchInfos(Info[] infos); 
    public struct Info 
    { 
      public int OrderNO; 
 
      public byte[] UniqueCode; 
 
      public float CpuPercent;          
 
    }; 
    private void buttonTest_Click(object sender, EventArgs e) 
    { 
      try 
      { 
      Info[] infos=new Info[128]; 
        if (fetchInfos(infos)) 
        { 
          MessageBox.Show("Fail"); 
        } 
      else 
      { 
          string message = ""; 
          foreach (Info info in infos) 
          { 
            message += string.Format("OrderNO={0}\r\nUniqueCode={1}\r\nCpu={2}", 
                       info.OrderNO, 
                       Encoding.UTF8.GetString(info.UniqueCode), 
                       info.CpuPercent 
                       ); 
          } 
          MessageBox.Show(message); 
      } 
      } 
      catch (System.Exception ex) 
      { 
        MessageBox.Show(ex.Message); 
      } 
    }
登入後複製

後來,經過查找資料,有文提到對於C#是屬於託管內存,現在要傳遞結構體數組,是屬性非託管內存,必須要用Marsh指定空間,然後再傳遞。於是將結構體變更如下。

StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] 
   public struct Info 
   { 
     public int OrderNO; 
 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] 
     public byte[] UniqueCode; 
 
     public float CpuPercent;          
 
   };
登入後複製

但是經過這樣的改進後,運行結果依然不理想,值要麼出錯,要麼沒有被改變。這究竟是什麼原因?不斷的搜資料,終於看到了一篇,裡面提到結構體的傳遞,有的可以如上面所做,但有的卻不行,特別是當參數在C++中是結構體指標或結構體數組指標時,在C#呼叫的地方也要用指標來對應,後面改進出如下程式碼。

[DllImport("workStation.dll")] 
  private static extern bool fetchInfos(IntPtr infosIntPtr); 
  [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] 
  public struct Info 
  { 
    public int OrderNO; 
 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] 
    public byte[] UniqueCode; 
 
    public float CpuPercent; 
 
  }; 
  private void buttonTest_Click(object sender, EventArgs e) 
  { 
    try 
    { 
      int workStationCount = 128; 
      int size = Marshal.SizeOf(typeof(Info)); 
      IntPtr infosIntptr = Marshal.AllocHGlobal(size * workStationCount); 
      Info[] infos = new Info[workStationCount]; 
      if (fetchInfos(infosIntptr)) 
      { 
        MessageBox.Show("Fail"); 
        return; 
      } 
      for (int inkIndex = 0; inkIndex < workStationCount; inkIndex++) 
      { 
        IntPtr ptr = (IntPtr)((UInt32)infosIntptr + inkIndex * size); 
        infos[inkIndex] = (Info)Marshal.PtrToStructure(ptr, typeof(Info)); 
      } 
 
      Marshal.FreeHGlobal(infosIntptr); 
 
      string message = ""; 
      foreach (Info info in infos) 
      { 
        message += string.Format("OrderNO={0}\r\nUniqueCode={1}\r\nCpu={2}", 
                    info.OrderNO, 
                    Encoding.UTF8.GetString(info.UniqueCode), 
                    info.CpuPercent 
                    ); 
      } 
      MessageBox.Show(message); 
 
    } 
    catch (System.Exception ex) 
    { 
      MessageBox.Show(ex.Message); 
    } 
  }
登入後複製

要注意的是,這時介面已經改成IntPtr了。透過以上方式,終於把結構體數組傳進去了。不過,這裡要注意一點,不同的編譯器對結構體的大小會不一定,例如上面的結構體

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

#pragma pack(push,1) 
  struct Info 
{ 
  int OrderNO; 
       
  char UniqueCode[32]; 
 
  float CpuPercent; 
}; 
#pragma pack(pop)
登入後複製

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

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

[DllImport("workStation.dll")] 
    private static extern bool fetchInfos(IntPtr infosIntPtr); 
    [DllImport("workStation.dll")] 
    private static extern bool fetchInfos(byte[] infos); 
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] 
    public struct Info 
    { 
      public int OrderNO; 
 
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] 
      public byte[] UniqueCode; 
 
      public float CpuPercent; 
 
    }; 
 
   
    private void buttonTest_Click(object sender, EventArgs e) 
    { 
      try 
      { 
        int count = 128; 
        int size = Marshal.SizeOf(typeof(Info)); 
        byte[] inkInfosBytes = new byte[count * size];         
        if (fetchInfos(inkInfosBytes)) 
        { 
          MessageBox.Show("Fail"); 
          return; 
        } 
        Info[] infos = new Info[count]; 
        for (int inkIndex = 0; inkIndex < count; inkIndex++) 
        { 
          byte[] inkInfoBytes = new byte[size]; 
          Array.Copy(inkInfosBytes, inkIndex * size, inkInfoBytes, 0, size); 
          infos[inkIndex] = (Info)bytesToStruct(inkInfoBytes, typeof(Info)); 
        } 
 
        string message = ""; 
        foreach (Info info in infos) 
        { 
          message += string.Format("OrderNO={0}\r\nUniqueCode={1}\r\nCpu={2}", 
                      info.OrderNO, 
                      Encoding.UTF8.GetString(info.UniqueCode), 
                      info.CpuPercent 
                      ); 
        } 
        MessageBox.Show(message); 
 
      } 
      catch (System.Exception ex) 
      { 
        MessageBox.Show(ex.Message); 
      } 
    } 
 
    #region bytesToStruct 
    /// <summary> 
    /// Byte array to struct or classs. 
    /// </summary> 
    /// <param name=”bytes”>Byte array</param> 
    /// <param name=”type”>Struct type or class type. 
    /// Egg:class Human{...}; 
    /// Human human=new Human(); 
    /// Type type=human.GetType();</param> 
    /// <returns>Destination struct or class.</returns> 
    public static object bytesToStruct(byte[] bytes, Type type) 
    { 
 
      int size = Marshal.SizeOf(type);//Get size of the struct or class.      
      if (bytes.Length < size) 
      { 
        return null; 
      } 
      IntPtr structPtr = Marshal.AllocHGlobal(size);//Allocate memory space of the struct or class.  
      Marshal.Copy(bytes, 0, structPtr, size);//Copy byte array to the memory space. 
      object obj = Marshal.PtrToStructure(structPtr, type);//Convert memory space to destination struct or class.      
      Marshal.FreeHGlobal(structPtr);//Release memory space.   
      return obj; 
    } 
    #endregion
登入後複製

對於實在想不到要怎麼傳資料的時候,可以考慮byte數組來傳(即便是整數也可以,只要是開闢4字節(在32位下)),只要開的長度對應的上,拿到數據後,要依照類型規則轉換成所需的數據,一般都能達到目的。

以上是詳解C#呼叫C++DLL傳遞結構體數組的解決辦法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

使用 C# 的活動目錄 使用 C# 的活動目錄 Sep 03, 2024 pm 03:33 PM

使用 C# 的 Active Directory 指南。在這裡,我們討論 Active Directory 在 C# 中的介紹和工作原理以及語法和範例。

C# 序列化 C# 序列化 Sep 03, 2024 pm 03:30 PM

C# 序列化指南。這裡我們分別討論C#序列化物件的介紹、步驟、工作原理和範例。

C# 中的隨機數產生器 C# 中的隨機數產生器 Sep 03, 2024 pm 03:34 PM

C# 隨機數產生器指南。在這裡,我們討論隨機數產生器的工作原理、偽隨機數和安全數的概念。

C# 資料網格視圖 C# 資料網格視圖 Sep 03, 2024 pm 03:32 PM

C# 資料網格視圖指南。在這裡,我們討論如何從 SQL 資料庫或 Excel 檔案載入和匯出資料網格視圖的範例。

C# 中的階乘 C# 中的階乘 Sep 03, 2024 pm 03:34 PM

C# 階乘指南。這裡我們討論 C# 中階乘的介紹以及不同的範例和程式碼實作。

C# 中的模式 C# 中的模式 Sep 03, 2024 pm 03:33 PM

C# 模式指南。在這裡,我們討論 C# 中模式的介紹和前 3 種類型,以及其範例和程式碼實作。

c#多線程和異步的區別 c#多線程和異步的區別 Apr 03, 2025 pm 02:57 PM

多線程和異步的區別在於,多線程同時執行多個線程,而異步在不阻塞當前線程的情況下執行操作。多線程用於計算密集型任務,而異步用於用戶交互操作。多線程的優勢是提高計算性能,異步的優勢是不阻塞 UI 線程。選擇多線程還是異步取決於任務性質:計算密集型任務使用多線程,與外部資源交互且需要保持 UI 響應的任務使用異步。

C# 中的質數 C# 中的質數 Sep 03, 2024 pm 03:35 PM

C# 質數指南。這裡我們討論c#中素數的介紹和範例以及程式碼實作。

See all articles