目錄
Qt DLL總結【一】-鏈接庫預備知識
Qt DLL總結【二】-創(chuàng)建及調用QT的 DLL
Qt DLL總結【三】-VS2008+Qt 使用QPluginLoader訪問DLL
開發(fā)環(huán)境:VS2008+Qt4.7.4
最近看了不少Qt的DLL例子,總結一下如何創(chuàng)建和調用QT 動態(tài)鏈接庫。
先講一下對QT動態(tài)鏈接庫的調用方法,主要包括:
1、顯式鏈接DLL,調用DLL的全局函數(shù),采用Qt的QLibrary方法
2、顯示鏈接DLL,調用DLL中類對象、成員函數(shù)。(通過對象即可實現(xiàn)類成員函數(shù)的調用)
①用虛函數(shù)表的方法,這也是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
#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的顯式鏈接在某些時候比隱式鏈接具有更大的靈活性。比如,如果在運行時發(fā)現(xiàn)DLL無法找到,程序可以顯示一個錯誤信息并能繼續(xù)運行。當你想為你的程序提供插件服務時,顯式鏈接也很有用處
1、采用顯示鏈接,調用DLL中全局函數(shù),只需要一個TestDLL.dll。
通常Windows下程序顯示調用dll的步驟分為三步(三個函數(shù)):LoadLibrary()、GetProcAdress()、FreeLibrary()
其中,LoadLibrary() 函數(shù)用來載入指定的dll文件,加載到調用程序的內存中(DLL沒有自己的內存!)
GetProcAddress() 函數(shù)檢索指定的動態(tài)鏈接庫(DLL)中的輸出庫函數(shù)地址,以備調用
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
#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;
- }
#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++編譯器會產生一個修飾過的函數(shù)名,這樣導出函數(shù)的名字將不再是helloworld,而是一個形如' ?helloWorld@TestDll@@UAEXXZ”的名字。為什么名字不是helloworld呢?這是因為C++為了支持函數(shù)的重載,會在編譯時將函數(shù)的參數(shù)類型信息以及返回值類型信息加入到函數(shù)名中,這樣代碼中名字一樣的重載函數(shù),在經過編譯后就互相區(qū)分開了,調用時函數(shù)名也經過同樣的處理,就能找到對應的函數(shù)了。詳細可以看這篇文章動態(tài)鏈接庫(Dynamic Link Library)學習筆記
TestMain項目 main.cpp
- #include <QtCore/QCoreApplication>
- #include <iostream>
- #include <QLibrary>
-
- typedef int (*Fun)(int,int); //定義函數(shù)指針,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;
- //調用外部函數(shù) add()
- Fun add = (Fun)mylib.resolve('add');
- //是否成功連接上 add() 函數(shù)
- if (add)
- {
- std::cout << 'Link to add Function is OK!'<<std::endl;
- //這里函數(shù)指針調用dll中的 add() 函數(shù)
- 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();
- }
#include <QtCore/QCoreApplication> #include <iostream> #include <QLibrary>
typedef int (*Fun)(int,int); //定義函數(shù)指針,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; //調用外部函數(shù) add() Fun add = (Fun)mylib.resolve('add'); //是否成功連接上 add() 函數(shù) if (add) { std::cout << 'Link to add Function is OK!'<<std::endl; //這里函數(shù)指針調用dll中的 add() 函數(shù) 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++類中的類對象、成員函數(shù)
如果你想導出并顯式鏈接一組C++類中的成員函數(shù)又該怎么辦呢?這里有兩個問題。第一是C++成員函數(shù)名是經過修飾的(即使指定extern 'C'標記也是這樣);第二是C++不允許將指向成員函數(shù)的指針轉換成其它類型。這兩個問題限制了C++類的顯式鏈接。下面介紹兩種方法來解決這個問題:
①用虛函數(shù)表的方法,這也是COM使用的方法,利用Qt的QLibrary技術調用;
②用GetProcAddress直接調用。
③用Qt的QPluginLoader類直接調用生成的DLL插件類對象
①虛函數(shù)表的方法,QLibrary 技術調用
TestDll.h代碼
- #ifndef TESTDLL_H
- #define TESTDLL_H
-
- #include 'testdll_global.h'
-
- class TESTDLL_EXPORT TestDll
- {
- public:
- TestDll();
- virtual~TestDll();
- virtual void helloWorld(); //類成員函數(shù)
- private:
-
-
- };
- extern 'C' TESTDLL_EXPORT TestDll* getTestDll(); //獲取類TestDll的對象
- #endif // TESTDLL_H
#ifndef TESTDLL_H #define TESTDLL_H
#include 'testdll_global.h'
class TESTDLL_EXPORT TestDll { public: TestDll(); virtual~TestDll(); virtual void helloWorld(); //類成員函數(shù) 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();
- }
#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)();//定義函數(shù)指針,獲取類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();
- }
#include <QtCore/QCoreApplication> #include <iostream> #include <QLibrary> #include '../TestDll/TestDll.h' //頭文件還是需要加的,否則無法解析TestDll類 typedef TestDll* (*GetTestDll)();//定義函數(shù)指針,獲取類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(); }
這個方法的使用得用戶可以很容易地為你的程序制作插件。它的缺點是創(chuàng)建對象的內存必須在dll中分配
②用GetProcAddress直接調用類對象中的成員函數(shù)
這個方法,我沒測試,對我沒對大作用,還得用def導出DLL函數(shù),有興趣的就參考一下這篇文章。DLL中類的顯式鏈接
③用Qt的QPluginLoader類直接調用生成的DLL插件類對象
這個方法,我單獨寫一篇總結,請看QPluginLoader的簡單小例子VS2008+Qt 使用QPluginLoader訪問DLL
3、采用隱式鏈接方法,通過QLibrary類對DLL中類對象、全局函數(shù)的調用
TestDll.h
- #ifndef TESTDLL_H
- #define TESTDLL_H
-
- #include 'testdll_global.h'
-
- class TESTDLL_EXPORT TestDll
- {
- public:
- TestDll();
- ~TestDll();
- void helloWorld(); //類成員函數(shù)
- private:
-
-
- };
- extern 'C' TESTDLL_EXPORT int add(int a,int b); //自定義的外部函數(shù)
- #endif // TESTDLL_H
#ifndef TESTDLL_H #define TESTDLL_H
#include 'testdll_global.h'
class TESTDLL_EXPORT TestDll { public: TestDll(); ~TestDll(); void helloWorld(); //類成員函數(shù) private:
}; extern 'C' TESTDLL_EXPORT int add(int a,int b); //自定義的外部函數(shù) #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;
- }
#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();
- }
#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(); }
結果即可編譯成功
|