可能是最簡單的把C++Lib包裝成C#可用dll的方法
(想直接看結果的直接翻到最後)
之前對C++接觸不多,最近工作需要,第三方給了一個C++的lib庫,我們需要把它封裝一下在C#中呼叫。對方要是直接給Dll就省事了。。。
研究了一下,基本有三個方向:
1. 建立CLI型別的,或者叫Managed的基於.NET的dll,這樣c#可以直接進行引用。
2. 建立native的c++ dll,然後在C#用 dllimport的方式呼叫。
3. 建立com元件。
一開始感覺第一種很美好。直接加到reference中就能像引用一個C#的dll一樣使用了。而且有一個好處是,我可以把這個dll工程和我的呼叫的C#工程放在一個solution中,然後在除錯的時候,斷點能直接進入到這個C++的工程中。這點要除錯起來是很美好的哦。
方法可以參考這個:ofollow,noindex" target="_blank">https://docs.microsoft.com/en-us/cpp/dotnet/how-to-wrap-native-class-for-use-by-csharp?view=vs-2017
一個不錯的C#使用CLI/CLR 呼叫native C++ 的例子:http://www.dorodnic.com/blog/2014/12/10/calling-cpp-by-example/
基本上就是建立一個CLI型別的工程。注意工程的屬性,選擇一下CLR,以及Framework的版本。

在Header中引入標頭檔案,在Resources中引入Lib檔案。

這樣在GoWrapper.cpp中就可以把需要的函式封裝一下了。

這裡可以選擇更好的封裝方式,比如對於原有的C++函式使用指標來返回結果的方式,我們可以使用一個自定義的類來返回之類的。但是考慮到要包裝很多的函式,為了簡便,最好讓包裝好的函式看起來和原有的函式差不多,對於指標可以通過ref或者out的方式來呼叫。
這樣在呼叫的時候,就可以像下面這樣呼叫:

注意如果想要用ref的方式呼叫的話,用下面這種來宣告:

到現在,CLI的Managed dll方式基本已經完成了。可是想想如果要包裝的時候,這一百多個函式的型別轉換也將會是很大的一個工作量。我決定再嘗試一下native dll的方式,雖然不能在同一個solution中debug,但是畢竟包裝起來方便一些,只好忍忍了。
參考文件:https://msdn.microsoft.com/en-us/library/ms235636.aspx
主要就是要在native dll的函式前面加上 __declspec(dllexport) int __stdcall 的宣告(__stdcall非必須),它就可以export了。
MSDN的關於dllexport和dllimport的說明:https://msdn.microsoft.com/zh-cn/library/3y1sfaz2.aspx
還有下面這個也不錯:
https://docs.microsoft.com/zh-cn/cpp/build/importing-into-an-application-using-declspec-dllimport?view=vs-2017
為什麼要寫成 ifdef 就 export, 如果不,就import的方式,主要是為了便於同一個標頭檔案可以同時應用於客戶端和提供端。
根據上面那個walkthrough,native 的dll還是很好建立的。要記得在工程屬性裡面,CLR不要選擇,就是native 的dll,或者記得要在建立project的時候的模板就選擇native的。
不過,這樣生成的dll,我在import的時候遇到了問題。import的程式碼如下:

這裡我必須使用一個EntryPoint=“#1”來指定我這個函式的entrypoint,因為生成的dll裡面,export的函式的entrypoint的名稱後面有一串 @xxxx 的東西。這個entrypoint 可以使用depends開啟檢視,可以使用名稱或者序號。這樣很不方便對不對,我用過的dllimport沒有哪個是要這麼搞的。為了解決這個問題,我們需要用到一個DEF檔案。在properties中可以指定所使用的DEF檔案,不過如果你自己新增一個DEF檔案的話,它會被自動新增到Properties的設定中的,其實你不需要手動去指定它。

Def檔案的作用就是告訴編譯程式,我要把哪個函式用來export,用什麼樣的名稱來export。當然,有了這個DEF檔案,就可以不需要__declspec(dllexport) int的聲明瞭。
修改之後的標頭檔案:

修改之後的def檔案:

呼叫方:

到現在,應該還比較圓滿了。雖然我麼有了除錯C++庫的便利,但是包裝幾百個函式也容易一些。只要直接把那個函式return回去就好了。
正當這時,凝望著我可愛的標頭檔案。我忽然想起,當用depends檢視生成的dll的時候。在依賴中是可以看到第三方的函式的。它們貌似也都加過 __declspec的字首。那麼既然它都加過了。那我還再Wrap一遍幹啥???試了一下。把DEF檔案刪掉,把我加的Wrapper刪掉,把我加的標頭檔案也刪掉。試了一下,可以用!!!
最終,其實就只是建立了一個native的dll,在resources裡面加上了第三方的lib檔案而已。別的自己的標頭檔案和cpp檔案一個都不用加的。有一種“慕然回首,那人卻在燈火闌珊處”的感覺。雖然轉了一圈,但是也算對於各種DLL的知識都有了瞭解,也算是有很多的收穫了。而且這個方法適用的前提在於第三方已經把自己的函式都添加了 declspec的字首。如果沒有的話,可以簡單的通過新增 DEF檔案的方式來export出想要的方法。
附一個DEF檔案的文件:https://docs.microsoft.com/zh-cn/cpp/build/reference/module-definition-dot-def-files?view=vs-2017
轉載請註明出處!!