在C#调用C++的DLL简析(二)—— 生成托管dll
寫操作之前,還是扼要的說(shuō)一下托管與非托管C++的區(qū)別好了,其實(shí)我也并沒(méi)有深入了解過(guò)托管C++的特點(diǎn)所在,其最大的特征就是可以由系統(tǒng)來(lái)調(diào)試回收相關(guān)的代碼資源,跟C#的特性一樣,只是編程風(fēng)格跟C++類似而已,因此,這決定了C#與托管C++是可以完美結(jié)合在一起的。托管C++生成的dll跟C#生成的dll應(yīng)該說(shuō)是沒(méi)區(qū)別的,之所以產(chǎn)生托管C++這種怪物,完全是因?yàn)槲④浽跇O力推崇C#,必須要兼顧不同語(yǔ)言間交互。
好吧,接下來(lái)正經(jīng)的寫一下過(guò)程。先擺出目的:我手上有一個(gè)C++寫的類(ClassA),想在C#下調(diào)用這個(gè)類,可是C#是沒(méi)有簡(jiǎn)單的像dllimport這樣的方法獲取非托管C++ dll里的類。我的解決方法是,生成一個(gè)托管C++的dll,因?yàn)橥泄艽a與非托管代碼是不能在一個(gè)文件里混編的,所以我必須將ClassA用托管C++的手段封裝一下,然后生成一個(gè)dll,以供C#調(diào)用。
也許我這里說(shuō)得很繞,請(qǐng)看下面的教程,會(huì)很明了的。
一、建立CLR類庫(kù)工程
其實(shí),我挺想忽略類似這些步驟的,一幅圖能說(shuō)明的問(wèn)題我就不多說(shuō),反正建立一個(gè)CLR類庫(kù)工程,其命名暫定為ManageClass,這是工程名,請(qǐng)勿混淆,如下圖,沒(méi)什么注意事項(xiàng)可言的。(抱怨一下,為啥51CTO沒(méi)法在編輯里縮小圖片的,PS好麻煩)
二、一個(gè)非托管C++的例子
我手上有一個(gè)用非托管C++寫的類NativeClass,它本身是屬于另外一個(gè)非托管C++工程,現(xiàn)在我直接將這個(gè)類文件拷貝到本工程的目錄下去,簡(jiǎn)單起見(jiàn),這個(gè)類我內(nèi)聯(lián)在一個(gè)頭文件里,如果是其他比較大型的類,必要將NativeClass.h里#include到的其他文件也一并拷貝到本新建工程目錄下,然后將這些文件添加到VS的資源管理器下,如下圖所示:
上圖中,除了NativeClass.h文件是我添加進(jìn)去的,其他都是工程自帶的東西,其中ManageClass.h及ManageClass.cpp是要生成dll所動(dòng)用到的東西,暫時(shí)先不管,我們看一下NativeClass.h里的內(nèi)容:
#pragma once class _declspec(dllexport) NativeClass { private:int nCount; public:NativeClass(void){this->nCount = 0;}~NativeClass(void){}int GetCount(void){return this->nCount;}void Increase(void){this->nCount++;}void Clear(void){this->nCount = 0;} };類的內(nèi)容簡(jiǎn)單到我不忍直視,像類頭的_declspec(dllexport)字段其實(shí)可要可不要的,只是我懶得刪除而已。
三、非裝成托管C++的內(nèi)容
這一步是很關(guān)鍵的,之所以有這么一步,是因?yàn)橥泄蹸++與非托管C++沒(méi)法混編,于是乎我將托管代碼將上面的NativeClass類封裝了一下,本來(lái)按規(guī)范而言我應(yīng)該將函數(shù)聲明與實(shí)現(xiàn)分開寫,但我承認(rèn)我又偷懶了,只在ManageClass.h里作修改,雖然沒(méi)有用到ManageClass.cpp,但無(wú)論如何也別將這個(gè)文件刪除,否則是沒(méi)法生成dll的。我的封裝代碼如下:
// ManageClass.h #pragma once #include "NativeClass.h" using namespace System; namespace ManageClass {public ref class NativeClassEx{// TODO: 在此處添加此類的方法。private:NativeClass * m_pnClass;public:NativeClassEx(void){this->m_pnClass = new NativeClass();}~NativeClassEx(void){delete this->m_pnClass;}int GetCount(void){return this->m_pnClass->GetCount();}void Increase(void){this->m_pnClass->Increase();}void Clear(void){this->m_pnClass->Clear();}protected:!NativeClassEx(void){delete this->m_pnClass;}}; }別告訴我上面的代碼你沒(méi)看懂,我會(huì)建議你找塊豆腐撞腦袋的。
四、生成托管C++的dll
其實(shí)到了這一步就結(jié)了,你直接點(diǎn)編譯,就會(huì)在工程外的Debug文件夾里生成ManageClass.dll了,務(wù)必要看清,經(jīng)過(guò)封裝后,我新的類名是叫NativeClassEx,請(qǐng)?jiān)谑褂脮r(shí)注意一下。
五、項(xiàng)目測(cè)試dll
調(diào)用托管C++的dll跟調(diào)用C#的dll沒(méi)任何區(qū)別,新建一個(gè)測(cè)試工程(我用的是WinForm的窗體工程),名字叫DllTest,在解決方案資源管理器里將剛剛生成的那個(gè)ManageClass.dll添加到引用里,使用using ManageClass,然后你就可以用了,其測(cè)試代碼就幾句話:
NativeClassEx testCalss = new NativeClassEx(); Debug.WriteLine("GetCount : " + testCalss.GetCount().ToString()); testCalss.Increase(); testCalss.Increase(); testCalss.Increase(); Debug.WriteLine("GetCount : " + testCalss.GetCount().ToString()); testCalss.Clear(); Debug.WriteLine("GetCount : " + testCalss.GetCount().ToString());編譯一下,看輸出窗口,類還是完美運(yùn)行得了的。
六、注意事項(xiàng)
1、盡管C#與托管C++很大程度上兼容,但還是要注意基本類型外的對(duì)齊問(wèn)題,像結(jié)構(gòu)體、string類這些,最好入口參數(shù)除了基本類型其他都別用,這點(diǎn)請(qǐng)參考我上一篇文章;
2、我嘗試用托管C++封裝我寫OpenCV類,類里再調(diào)用了OpenCV的dll(即C#調(diào)用托管dll,托管dll調(diào)用非托管dll),編譯通過(guò),但實(shí)際運(yùn)行不行,里面有什么問(wèn)題暫時(shí)不清楚;
3、建議,沒(méi)什么事別用這種方法來(lái)調(diào)用類,C#中調(diào)用dll的函數(shù)才是最具保障的。
4、示例工程請(qǐng)?jiān)谶@里下載,用前記得先編譯好dll,并確保添加了引用,可能會(huì)有一些關(guān)于CPU類型選擇的warning,請(qǐng)諸位自力更生了。
轉(zhuǎn)載于:https://blog.51cto.com/joeyliu/1297961
總結(jié)
以上是生活随笔為你收集整理的在C#调用C++的DLL简析(二)—— 生成托管dll的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 网络出问题了 网络出问题了打什么电话
- 下一篇: C#获取本机可用端口