從 C# 匯出 unmanaged DLL 詳細步驟

從 C# 匯出 unmanaged DLL 詳細步驟

雖然一年前寫過簡短的介紹,但最近使用又遇到一些挫折,那就再寫一篇更詳細的吧!

簡單介紹

如果你對這些基本概念沒興趣的話可以跳過,我沒正式學過、寫過 C#,對這些名詞概念頗陌生,因此記錄下來。

在談什麼是 unmanaged(非受控)之前,我們先了解什麼是 CLR(Common Language Runtime),這是微軟用來執行 .NET 程式的「虛擬機」,類似於 Java 的 JVM 都是用來將程式中間碼(CIL)即時編譯並且執行(JIT 技術),兩者間詳細的比較可以參考 [1]。

因此受到 CLR 管理的程式碼就稱為 managed code,泛指基於 .NET framework 的程式碼。

不受到 CLR 管理的成就就稱為 unmanaged code,如:MFC、VC++、VB 等。

更多內容可以參考前輩的介紹文章

「C# 那麼好寫,我可不可以用 C# 寫完輸出 DLL 給 unmanaged 程式使用?」答案是可以的!

實際操作

建立專案

這邊建立 [類別庫 (.NET Framework)],取個名字後直接建立專案。

/img/export_unmanaged_dll/Screen_Shot_2021-01-08_at_11.19.50_PM.png

使用 DllExport

使用開源專案 DllExport,最新的執行檔可以在這邊找到

相關資源:

首先,將下載的 DllExport.bat 放入專案資料夾,執行後可以看到下面操作介面:

/img/export_unmanaged_dll/Screen_Shot_2021-01-09_at_12.30.20_AM.png

  1. 勾選 [Installed] 安裝(反之則是解除安裝)
  2. 選擇對應的 Namespace,這邊採用 [Interop.Services](http://interop.Services) (需另外引入)
  3. 選擇想要輸出的格式(筆者只想輸出成 x86)
  4. 視情況勾選,預設都是取消的
    • 前者專給inf 錯誤(後面會詳述)
    • 後者專給 nan 錯誤(後面會詳述)

選好後,點擊右上角 [Apply] 即可套用到專案。

如果這時候你的專案是開啟的,會看到下面情況,請點選 [全部重新載入]

/img/export_unmanaged_dll/Screen_Shot_2021-01-09_at_12.43.09_AM.png

在專案中:

引入 InteropServices,並且在 static function 上方加入 [DllExport]

注意,簡單起見這邊限定使用於回傳 intvoid 的函數,其他型態頗複雜,後面說明。

using System.Runtime.InteropServices;

...

[DllExport]
public static void hello() { /* ... */ }

[DllExport]
public static int world() { /* ... */ }

編譯後,即可在 專案資料夾\bin\Debug 中(依照個人設定)看到輸出的 DLL,可能會有好幾個。

如果,你想將所有 DLL 合併成一個?

如果,你想使用於回傳其他型態函數?

後面都會說明處理方式!

檢測 DLL 匯出狀況

  1. 使用 CFF Explorer

    • 將 DLL 拖進去即可,在 [Export Directory] 可以看到兩個被匯出的函式

      /img/export_unmanaged_dll/Untitled.png

  2. 使用 dumpbin

    • 需要安裝 Visual C++

    • 執行檔路徑在這(不同環境版本略有差異)

      C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.28.29333\bin\Hostx64\x64
      
    • 執行 .\dumpbin.exe -Exports xxxx.dll

      /img/export_unmanaged_dll/Untitled%201.png

測試專案

在 Windows 上唯一會的 unmanaged 程式開發是 MFC,但那是我大二資演惡夢… Python 那麼棒,為什麼不用 Python 測試呢😆

由於我輸出的是 32 bit 程式,裝 Python 64-bit 版本會沒辦法執行,錯誤訊息如下:

%1 is not a valid win32 application

因此改裝 Python 32-bit,使用測試碼如下:

from ctypes import*

mydll = cdll.LoadLibrary("./mylib.dll")
result1 = mydll.world()

或是可以使用這種呼叫方式

合併 DLL

開啟 DllExport.bat

.\DllExport.bat -action Configure

選擇索引標籤 [Pre-processing],勾選 [Merge modules via ILMerge],並且在下方填入所有想要合併的 DLL 名稱(使用空格分開,不含最終 DLL),最後點選 Apply。

專案.csproj 中就可以看到 ILMergeConsolePath 相關字串

/img/export_unmanaged_dll/Screen_Shot_2021-01-09_at_12.57.48_AM.png

這時候回到專案編譯,正常來說不會有錯誤訊息,且可以發現目標 DLL 檔案大小明顯大了許多。

如果遇到錯誤訊息如:

-inf

/img/export_unmanaged_dll/Screen_Shot_2021-01-09_at_1.07.38_AM.png

錯誤		C:\jackkuo\Desktop\MyProj\Class1.cs(16707566) : error : syntax error at token '-' in: IL_0027: ldc.r8 -inf  MyProj

或是 -nan(ind)

/img/export_unmanaged_dll/Screen_Shot_2021-01-09_at_1.09.46_AM.png

錯誤		C:\jackkuo\Desktop\MyProj\Class1.cs(16707566) : error : syntax error at token '-' in: IL_0027:  ldc.r8  -nan(ind)	MyProj

那則需要根據不同的錯誤訊息把 “4” 開啟

/img/export_unmanaged_dll/Screen_Shot_2021-01-09_at_12.30.20_AM.png

想使用於回傳其他型態函數?

這部分複雜許多,請參考作者教學影片2

  • 需從 NuGet 中安裝 regXwildConari
  • 勾選 [Integrate Conari]

/img/export_unmanaged_dll/Screen_Shot_2021-01-09_at_1.15.49_AM.png

再來就是各種記憶體串接,這部分是作者自幹常見格式讓不同語言間可以互接,詳情請參考作者的影片。

其他方式

網路上有其他方式能夠從 C# 中輸出 unmanaged DLL,不過我都沒成功…

  1. .NET C# 建立 COM元件 (5) — UnManaged Exports dll
  2. C#的dll檔如何讓 Foxpro 呼叫
  3. HOW DO I EXECUTE A DLL IN VIUSUAL FOXPRO

參考資料:

  1. CLR vs JVM: How the Battle Between C# and Java Extends to the VM-Level
  2. 用.Net寫COM元件的經驗談(二)背景知識篇

comments powered by Disqus