目錄
Qt DLL總結【一】-鏈接庫預備知識
Qt DLL總結【二】-創建及調用QT的 DLL??
Qt DLL總結【三】-VS2008+Qt 使用QPluginLoader訪問DLL
開發環境:VS2008+Qt4.7.4
?
最近看了不少Qt的DLL例子,總結一下如何創建和調用QT 動態鏈接庫。
?
先講一下對QT動態鏈接庫的調用方法,主要包括:
1、顯式鏈接DLL,調用DLL的全局函數,采用Qt的QLibrary方法
2、顯式鏈接DLL,調用DLL中類對象、成員函數。(通過對象即可實現類成員函數的調用)
?
①用虛函數表的方法,這也是COM使用的方法,利用Qt的QLibrary技術調用;
②用GetProcAddress直接調用。
③用Qt的QPluginLoader類直接調用生成的DLL插件類對象
3、隱式鏈接DLL:也是采用Qt的Qlibrary方法
關于這種三種方法,下面詳細敘說
?
詳細分類敘述
?
前提:兩個項目文件目錄
1、TestDLL項目:testdll_global.h, ? TestDll.h, ? ?TestDll.cpp
2、TestMain exe應用項目:main.cpp
?
testdll_global.h 文件源代碼一直不變
?

- #ifndef?TESTDLL_GLOBAL_H??
- #define?TESTDLL_GLOBAL_H??
- ??
- #include?<QtCore/qglobal.h>??
- ??
- #ifdef?TESTDLL_LIB??
- #?define?TESTDLL_EXPORT?Q_DECL_EXPORT??
- #else??
- #?define?TESTDLL_EXPORT?Q_DECL_IMPORT??
- #endif??
- ??
- #endif?//?TESTDLL_GLOBAL_H??
? ? ? DLL的顯式鏈接在某些時候比隱式鏈接具有更大的靈活性。比如,如果在運行時發現DLL無法找到,程序可以顯示一個錯誤信息并能繼續運行。當你想為你的程序提供插件服務時,顯式鏈接也很有用處
?
1、采用顯式鏈接,調用DLL中全局函數,【只需要一個TestDLL.dll】
? ? ? ? 通常Windows下程序顯示調用dll的步驟分為三步(三個函數):LoadLibrary()、GetProcAdress()、FreeLibrary()
? ? ? ? 其中,LoadLibrary() 函數用來載入指定的dll文件,加載到調用程序的內存中(DLL沒有自己的內存!)
?? ? ? ? GetProcAddress() 函數檢索指定的動態鏈接庫(DLL)中的輸出庫函數地址,以備調用
?? ? ? ? FreeLibrary() 釋放dll所占空間?
? ? ? 而QT的QLibrary類顯示鏈接調用DLL的步驟:load()、resolve(const char * symbol )、unload()和VC步驟類似
?
TestDll.dll項目中的TestDLL.h源碼
?

- #ifndef?TESTDLL_H??
- #define?TESTDLL_H??
- ??
- #include?"testdll_global.h"??
- ??
- class?TESTDLL_EXPORT?TestDll??
- {??
- public:??
- ????TestDll();??
- ????~TestDll();???
- private:??
- ??
- ??
- };??
- extern?"C" ??TESTDLL_EXPORT?void?helloWorld();???????
- extern?"C" ??TESTDLL_EXPORT?int?add(int?a,int?b);????
- #endif?//?TESTDLL_H??
?
TestDll.dll項目中的TestDLL.cpp源碼
?

- #include?<iostream>??
- #include?"TestDll.h"??
- ??
- TestDll::TestDll()??
- {??
- ??
- }??
- ??
- TestDll::~TestDll()??
- {??
- ??
- }??
- ??
- void?helloWorld()??
- {??
- ????std::cout?<<?"hello,world!";??
- }??
- int?add(int?a,int?b)??
- {??
- ????return?a?+?b;??
- }??
? ?注:1)建立成功DLL項目后,可以在VS命令提示行中用命令"dumpbin -exports DllTest.dll"來查看(也可以用VC工具包中的depends使用程序來查看)??
? ?注:2)必須使用extern "C"鏈接標記,否則C++編譯器會產生一個修飾過的函數名,這樣導出函數的名字將不再是helloworld,而是一個形如"??helloWorld@TestDll@@UAEXXZ”的名字。
為什么名字不是helloworld呢?這是因為C++為了支持函數的重載,會在編譯時將函數的參數類型信息以及返回值類型信息加入到函數名中,這樣代碼中名字一樣的重載函數,在經過編譯后就互相區分開了,調用時函數名也經過同樣的處理,就能找到對應的函數了。詳細可以看這篇文章動態鏈接庫(Dynamic Link Library)學習筆記
?TestMain項目 main.cpp
?

- #include?<QtCore/QCoreApplication>??
- #include?<iostream>??
- #include?<QLibrary>??
- ??
- typedef?int?(*Fun)(int,int);?//定義函數指針,int?add(int?a,int?b);??????
- int?main(int?argc,?char?*argv[])??
- {??
- ????QCoreApplication?a(argc,?argv);??
- ??????
- ????QLibrary?mylib("TestDll.dll");???//聲明所用到的dll文件??
- ????int?result;??
- ????//判斷是否正確加載??
- ????if?(mylib.load())????????????????
- ????????{??
- ????????????std::cout?<<?"DLL?load?is?OK!"<<std::endl;??
- ????????????//調用外部函數?add()??
- ????????????Fun?add?=?(Fun)mylib.resolve("add");?????
- ????????????//是否成功連接上?add()?函數??
- ????????????if?(add)????????????????????
- ????????????????{??
- ????????????????????std::cout?<<?"Link?to?add?Function?is?OK!"<<std::endl;??
- ?????????????????????//這里函數指針調用dll中的?add()?函數??
- ????????????????????result?=?add(5,6);???????
- ????????????????????std::cout?<<?result;??
- ????????????????}??
- ????????????else??
- ????????????????std::cout?<<?"Link?to?add?Function?failed!!"<<std::endl; ?
- ????}??
- ????//加載失敗??
- ????else??
- ????????std::cout?<<?"DLL?is?not?loaded!"<<std::endl; ?
- ????return?a.exec();??
- }???
2、采用顯式鏈接,調用C++類中的類對象、成員函數 ?【需要TestDLL.dll、TestDll.h】
? ? ? 如果你想導出并顯式鏈接一組C++類中的成員函數又該怎么辦呢?
這里有兩個問題。
第一是C++成員函數名是經過修飾的(即使指定extern "C"標記也是這樣);
第二是C++不允許將指向成員函數的指針轉換成其它類型。這兩個問題限制了C++類的顯式鏈接。
下面介紹兩種方法來解決這個問題:
①用虛函數表的方法,這也是COM使用的方法,利用Qt的QLibrary技術調用;
②用GetProcAddress直接調用。
③用Qt的QPluginLoader類直接調用生成的DLL插件類對象
? ? ?
①虛函數表的方法,QLibrary 技術調用
TestDll.h代碼
?

- #ifndef?TESTDLL_H??
- #define?TESTDLL_H??
- ??
- #include?"testdll_global.h"??
- ??
- class?TESTDLL_EXPORT?TestDll??
- {??
- public:??
- ????TestDll();??
- ????virtual~TestDll();????
- ????virtual?void?helloWorld();?//類成員函數??
- private:??
- ??
- ??
- };?????
- extern?"C"?TESTDLL_EXPORT?TestDll*?getTestDll();?//獲取類TestDll的對象??
- #endif?//?TESTDLL_H??
?
?TestDll.cpp源碼
?

- #include?<iostream>??
- #include?"TestDll.h"??
- ??
- TestDll::TestDll()??
- {??
- ??
- }??
- ??
- TestDll::~TestDll()??
- {??
- ??
- }??
- ??
- void?TestDll::helloWorld()??
- {??
- ????std::cout?<<?"hello,world!";??
- }??
- ??
- TestDll*?getTestDll()??
- {??
- ????return?new?TestDll();??
- }??
?
?TestMain項目中的main.cpp源碼
?

- #include?<QtCore/QCoreApplication>??
- #include?<iostream>??
- #include?<QLibrary>??
- #include?"../TestDll/TestDll.h"??//頭文件還是需要加的,否則無法解析TestDll類??
- typedef?TestDll*?(*GetTestDll)();//定義函數指針,獲取類TestDLL對象;????
- int?main(int?argc,?char?*argv[])??
- {??
- ????QCoreApplication?a(argc,?argv);??
- ??
- ????QLibrary?mylib("TestDll.dll");???//聲明所用到的dll文件??
- ????int?result;??
- ????//判斷是否正確加載??
- ????if?(mylib.load())????????????????
- ????????{??
- ????????????GetTestDll?getTestDll?=?(GetTestDll)mylib.resolve("getTestDll");??
- ????????????if(getTestDll)??
- ????????????{??
- ????????????????TestDll?*testDll?=?getTestDll();??
- ????????????????testDll->helloWorld();??
- ????????????????delete?testDll;??
- ????????????}??
- ????}??
- ????//加載失敗??
- ????else??
- ????????std::cout?<<?"DLL?is?not?loaded!"<<std::endl;??
- ????return?a.exec();??
- }??
? ? ? ? 這個方法的使用得用戶可以很容易地為你的程序制作插件。它的缺點是創建對象的內存必須在dll中分配
?
?
②用GetProcAddress直接調用類對象中的成員函數
這個方法,我沒測試,對我沒對大作用,還得用def導出DLL函數,有興趣的就參考一下這篇文章。DLL中類的顯式鏈接
? ? ? ? ③用Qt的QPluginLoader類直接調用生成的DLL插件類對象
? ? ? ? ? ?這個方法,我單獨寫一篇總結,請看QPluginLoader的簡單小例子VS2008+Qt 使用QPluginLoader訪問DLL
?
3、采用隱式鏈接方法,通過QLibrary類對DLL中類對象、全局函數的調用 ?【需要TestDll.h、TestDll.cpp、TestDll.dll】
?
TestDll.h
?

- #ifndef?TESTDLL_H??
- #define?TESTDLL_H??
- ??
- #include?"testdll_global.h"??
- ??
- class?TESTDLL_EXPORT?TestDll??
- {??
- public:??
- ????TestDll();??
- ????~TestDll();???
- ????void?helloWorld();?//類成員函數??
- private:??
- ??
- ??
- };?????
- extern?"C"?TESTDLL_EXPORT?int?add(int?a,int?b);??//自定義的外部函數??
- #endif?//?TESTDLL_H??
TestDll.cpp源碼

- #include?<iostream>??
- #include?"TestDll.h"??
- ??
- TestDll::TestDll()??
- {??
- ??
- }??
- ??
- TestDll::~TestDll()??
- {??
- ??
- }??
- ??
- void?TestDll::helloWorld()??
- {??
- ????std::cout?<<?"hello,world!";??
- }??
- int?add(int?a,int?b)??
- {??
- ????return?a?+?b;??
- }??
?
TestMain項目中的main.cpp ,需要稍微配置頭文件和lib文件
1、在項目中主程序引入TestDll.h頭文件,
2、配置項目屬性:加入TestDLL.lib的文件目錄,在Linker/General/Additional Library Diretories里面選擇TestDll.lib的文件目錄D:\VSWorkSpace\Test\Debug
3、配置項目屬性:加入TestDll.lib文件,在Linker/Input/Additional Dependencies 中加入 TestDll.lib
?
main.cpp源碼

- #include?<QtCore/QCoreApplication>??
- #include?<iostream>??
- #include?<QLibrary>??
- #include?"../TestDll/TestDll.h"??
- //引入TestDll.lib文件,和上面的2,3步工作同理??
- //#pragma?comment(lib,?"../Debug/TestDll.lib")??
- int?main(int?argc,?char?*argv[])??
- {??
- ????QCoreApplication?a(argc,?argv);??
- ????int?result?=?add(5,6);??
- ????std::cout?<<?result;??
- ????TestDll?dll;??
- ????dll.helloWorld();??
- ????????return?a.exec();??
- }??
?結果即可編譯成功