關於如何在C#中呼叫C++的DLL,以及如何在C++中呼叫C#的DLL
一、關於如何在C#中呼叫C++的DLL,以及如何在C++中呼叫C#的DLL
注:clr指公共語言執行庫
CLR是一門非常惡搞的語言,就好像是在C++裡面寫C#的檔案一樣,也就是一種所謂的“託管模式”,把C++的程式碼丟到.net中去執行。
C#和C++形成的DLL有一層天然的屏障,並不能簡單地互相呼叫,想要C#工程呼叫c++dll,需要先在其外部包裹上clr c++的外殼。
公共語音執行庫提供了一系列能夠使託管程式碼與非託管程式碼進行互動的解決方案
主要包含三類互操作技術:
- 平臺呼叫(P/Invoke):主要用於處理在託管程式碼中呼叫C庫函式以及Win32 API 函式等非託管函式情形。
2.C++ Interop:適用在託管程式碼與C++ 類庫、核心演算法庫之間進行高效、靈活的互操作過程。
3.COM Interop:該技術用於處理託管程式碼與COM之間的互動過程。
當然如果你的dll是那種類C的介面,就是聲明瞭extern "C" 的,單純的某個方法的函式(就是純粹的在Cpp裡面寫C語言模式的介面,沒有例項化物件,沒有面向物件,沒有類,只有結構體和麵向過程),當然你的類大概率是不可能以這種形式去呼叫的,至於這樣的dll 該怎麼去呼叫,聰明的你肯定已經想到了,在主程式裡面有很多類似[DllImport "xxx.dll"] extern xxx 的方法,去看吧,那裡有你想要的。
我們大部分情況下呼叫的dll都是c++那種帶類的dll(當然了,不帶類的c++ DLL,聽上去就像一個冷笑話),在c#中呼叫這種dll則需要用到託管程式碼與C++類庫,也就是我們的第二種方法
至於COM元件是什麼我現在還沒弄懂,之後如果搞懂了我會單獨開一篇文章來專門介紹,如果我還記得的話,我會補充到這裡。
因為Qt 中的訊號和槽機制是無法通過CLR C++直接封裝的(原因1:CLR C++ 繼承的Object,Qt 中的類大部分繼承QObject,CLR C++不支援多繼承,所以無法將Qt訊號和槽轉換為 委託的形式,CLR C++可以封裝沒有使用訊號和槽的Qt類,但是為了結構上統一不建議這樣做)
所以如果需要向外部使用訊號槽機制的話,最好是換成回撥函式或者是委託(也不建議這麼做),最好是回撥函式,因為你這個dll不可能生來就是為了被C#呼叫而生的對吧。
呼叫過程:
- 一般C# 呼叫 C++ 情況
C# ------> 託管 C++----->非託管C++
- C# 呼叫Qt
C#---->託管C++--->非託管C++--->Qt
故 C# 呼叫Qt 多了一層 通過非託管C++ 封裝Qt的過程(此過程主要是為了遮蔽Qt的訊號和槽,將訊號和槽轉換為C++中的回撥)
這裡以我的一個完整工程為例:
part1:原始輪子
我這裡有一個第三方輪子 -Mixerwrap,這個類是一個純純的qt類,用來進行麥克風控制的
原來的這個類是沒有繼承QObject的,這裡需要記得加上QObject
然後我們生成,那麼這個類就可以用來當成我們已經存在的Qt庫了,甚至說可以不需要這個工程檔案,只需要這個dll就可以了。
part2 : 輪子套殼
C#---->託管C++--->非託管C++--->Qt
ok,現在我們完成了第一步Qt,現在我們要給qt套上非託管c++的殼,為了方便起見,我們非託管和託管C++的殼就放在一個工程檔案內操作,首先建立一個MixerwrapHelper,這個工程就是我們兩層套殼的地方。
這裡方便起見,我直接在工程內部引用了Mixerwrap的工程
當然了一般情況下是通過.lib檔案去連線.dll檔案完成引用,但想必你已經看到這了,不會還不知道怎麼通過 lib 連線 dll吧,想必你已經非常清楚了。
開始套殼,我們建立一個新的工程檔案,就叫Mixerwrap
然後我們來套第一層殼,新增一個非託管C++的類,就叫他MixerwrapStdWrapper
注:目前託管類C++資料夾還是空的。
然後現在我們開始寫這個非託管C++類,大概就是這樣
注:1.不一定需要匯入這個_global檔案,這個檔案是一個匯出宣告的預編譯標頭檔案,類似這樣
可以照著抄一份,這個全大寫的這個是匯出標誌而已...置於這個預編譯不會看不懂吧...
然後我們在這個匯出類內實現功能,就如圖,只是把方法搬運了過來而已,這樣我們就得到了一個純C++的無託管匯出類MixerwrapStdWrapper
這樣做我們第一層套殼就成功了,現在開始套第二層殼MixerwrapCLRWrapper
我們新建一個C++的類,初始設定沒有什麼特別的,就像建立一個普通的C++類一樣即可,我們給第二層殼MixerwrapCLRWrapper生成一下
ok,這個類我們要做一點小小的設定,右鍵MixerwrapCLRWrapper.cpp->屬性->c++->常規->公共語言執行時支援->公共語言執行時支援 (/clr)
套殼方式與第一層類似,如圖所示
注意四點:
1.引用的是非託管類C++ 檔案的標頭檔案
2.namespace 不是必須的,但是我建議你寫,不要問為什麼,.net寫多了你就明白了
3.public ref class MixerwrapCLRWrapper 這個頭要注意 一個是public 不加就讀不到這個類,一個是ref,代表引用傳遞,這裡不明白就去百度
4.建立的是非託管類C++的指標,不是原始DLL的指標,相當於是在非託管類DLL上方套了一層殼。
實現方法同套的第一層殼
這裡需要注意的是,這裡開始已經是CLR語法了,即在C++中寫.net 或者說C# 程式的語法,有點怪怪的,但是並沒有什麼難理解的。
最後我們要對整個工程的性質修改一下,將其改為CLR匯出DLL即可,我們在右鍵工程->屬性->配置屬性->常規->公共語言執行時支援->公共語言執行時支援(/clr)
注意是整個工程的,不是某個cpp檔案的。
part3.呼叫
ok,最噁心的套殼結束了之後,接下來就是呼叫了。.net程式的託管方式使得程式的dll呼叫起來比C++的DLL呼叫舒服很多,我們建立一個新的C#程式,來呼叫試一下:
建立一個交MicphoneControlTest的視窗工程,簡單寫個視窗,可以呼叫兩個功能
嘗試引用這個dll:右鍵引用->新增引用->瀏覽-》找到對應DLL->勾選並確定
當然我在這裡偷懶就直接引用這個工程專案了
物件瀏覽器裡應該就能看到這些東西了吧
這就是我們從CLR這個類中匯出的方法什麼的了,我們來嘗試一下呼叫
嘗試一下,點選,注意先將C#程式設為啟動項,另外兩個工程都是dll 是沒法啟動的,會提示是非標準的win32程式,需要注意
二、如何在Qt中呼叫C#的DLL
這部分內容不再更新,方案類似,只不過是反向套殼罷了,不會有人不理解吧?