Qt DLL總結【二】-建立及呼叫QT的 DLL

pamxy發表於2013-07-27

轉自:http://qimo601.iteye.com/blog/1397936

目錄

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 檔案原始碼一直不變

 

Cpp程式碼  收藏程式碼
  1. #ifndef TESTDLL_GLOBAL_H  
  2. #define TESTDLL_GLOBAL_H  
  3.   
  4. #include <QtCore/qglobal.h>  
  5.   
  6. #ifdef TESTDLL_LIB  
  7. # define TESTDLL_EXPORT Q_DECL_EXPORT  
  8. #else  
  9. # define TESTDLL_EXPORT Q_DECL_IMPORT  
  10. #endif  
  11.   
  12. #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原始碼

 

Cpp程式碼  收藏程式碼
  1. #ifndef TESTDLL_H  
  2. #define TESTDLL_H  
  3.   
  4. #include "testdll_global.h"  
  5.   
  6. class TESTDLL_EXPORT TestDll  
  7. {  
  8. public:  
  9.     TestDll();  
  10.     ~TestDll();   
  11. private:  
  12.   
  13.   
  14. };  
  15. extern "C" TESTDLL_EXPORT void helloWorld();       
  16. extern "C" TESTDLL_EXPORT int add(int a,int b);    
  17. #endif // TESTDLL_H  

 

TestDll.dll專案中的TestDLL.cpp原始碼

 

Cpp程式碼  收藏程式碼
  1. #include <iostream>  
  2. #include "TestDll.h"  
  3.   
  4. TestDll::TestDll()  
  5. {  
  6.   
  7. }  
  8.   
  9. TestDll::~TestDll()  
  10. {  
  11.   
  12. }  
  13.   
  14. void helloWorld()  
  15. {  
  16.     std::cout << "hello,world!";  
  17. }  
  18. int add(int a,int b)  
  19. {  
  20.     return a + b;  
  21. }  

   注: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

 

Cpp程式碼  收藏程式碼
  1. #include <QtCore/QCoreApplication>  
  2. #include <iostream>  
  3. #include <QLibrary>  
  4.   
  5. typedef int (*Fun)(int,int); //定義函式指標,int add(int a,int b);      
  6. int main(int argc, char *argv[])  
  7. {  
  8.     QCoreApplication a(argc, argv);  
  9.       
  10.     QLibrary mylib("TestDll.dll");   //宣告所用到的dll檔案  
  11.     int result;  
  12.     //判斷是否正確載入  
  13.     if (mylib.load())                
  14.         {  
  15.             std::cout << "DLL load is OK!"<<std::endl;  
  16.             //呼叫外部函式 add()  
  17.             Fun add = (Fun)mylib.resolve("add");     
  18.             //是否成功連線上 add() 函式  
  19.             if (add)                    
  20.                 {  
  21.                     std::cout << "Link to add Function is OK!"<<std::endl;  
  22.                      //這裡函式指標呼叫dll中的 add() 函式  
  23.                     result = add(5,6);       
  24.                     std::cout << result;  
  25.                 }  
  26.             else  
  27.                 std::cout << "Link to add Function failed!!"<<std::endl;  
  28.   
  29.               
  30.     }  
  31.     //載入失敗  
  32.     else  
  33.         std::cout << "DLL is not loaded!"<<std::endl;  
  34.        
  35.   
  36.     return a.exec();  
  37. }   

2、採用顯示連結,呼叫C++類中的類物件、成員函式 

     如果你想匯出並顯式連結一組C++類中的成員函式又該怎麼辦呢?這裡有兩個問題。第一是C++成員函式名是經過修飾的(即使指定extern "C"標記也是這樣);第二是C++不允許將指向成員函式的指標轉換成其它型別。這兩個問題限制了C++類的顯式連結。下面介紹兩種方法來解決這個問題:

①用虛擬函式表的方法,這也是COM使用的方法,利用Qt的QLibrary技術呼叫;

②用GetProcAddress直接呼叫。

用Qt的QPluginLoader類直接呼叫生成的DLL外掛類物件

     ①虛擬函式表的方法,QLibrary 技術呼叫

TestDll.h程式碼

 

Cpp程式碼  收藏程式碼
  1. #ifndef TESTDLL_H  
  2. #define TESTDLL_H  
  3.   
  4. #include "testdll_global.h"  
  5.   
  6. class TESTDLL_EXPORT TestDll  
  7. {  
  8. public:  
  9.     TestDll();  
  10.     virtual~TestDll();    
  11.     virtual void helloWorld(); //類成員函式  
  12. private:  
  13.   
  14.   
  15. };     
  16. extern "C" TESTDLL_EXPORT TestDll* getTestDll(); //獲取類TestDll的物件  
  17. #endif // TESTDLL_H  

 

 TestDll.cpp原始碼

 

Cpp程式碼  收藏程式碼
  1. #include <iostream>  
  2. #include "TestDll.h"  
  3.   
  4. TestDll::TestDll()  
  5. {  
  6.   
  7. }  
  8.   
  9. TestDll::~TestDll()  
  10. {  
  11.   
  12. }  
  13.   
  14. void TestDll::helloWorld()  
  15. {  
  16.     std::cout << "hello,world!";  
  17. }  
  18.   
  19. TestDll* getTestDll()  
  20. {  
  21.     return new TestDll();  
  22. }  

 

 TestMain專案中的main.cpp原始碼

 

Cpp程式碼  收藏程式碼
  1. #include <QtCore/QCoreApplication>  
  2. #include <iostream>  
  3. #include <QLibrary>  
  4. #include "../TestDll/TestDll.h"  //標頭檔案還是需要加的,否則無法解析TestDll類  
  5. typedef TestDll* (*GetTestDll)();//定義函式指標,獲取類TestDLL物件;    
  6. int main(int argc, char *argv[])  
  7. {  
  8.     QCoreApplication a(argc, argv);  
  9.   
  10.     QLibrary mylib("TestDll.dll");   //宣告所用到的dll檔案  
  11.     int result;  
  12.     //判斷是否正確載入  
  13.     if (mylib.load())                
  14.         {  
  15.             GetTestDll getTestDll = (GetTestDll)mylib.resolve("getTestDll");  
  16.             if(getTestDll)  
  17.             {  
  18.                 TestDll *testDll = getTestDll();  
  19.                 testDll->helloWorld();  
  20.                 delete testDll;  
  21.             }  
  22.     }  
  23.     //載入失敗  
  24.     else  
  25.         std::cout << "DLL is not loaded!"<<std::endl;  

  26.         // return a.exec();  (轉者註釋:這樣是看不到結果的,要改為下面這樣)
  27.         a.exec();
  28.        return 0;

  29. }  

        這個方法的使用得使用者可以很容易地為你的程式製作外掛。它的缺點是建立物件的記憶體必須在dll中分配(轉者注:就是需要用個得到這個類例項的函式,在這個函式裡new一個這個類的例項)

 

 

②用GetProcAddress直接呼叫類物件中的成員函式

這個方法,我沒測試,對我沒對大作用,還得用def匯出DLL函式,有興趣的就參考一下這篇文章。DLL中類的顯式連結

        ③用Qt的QPluginLoader類直接呼叫生成的DLL外掛類物件

           這個方法,我單獨寫一篇總結,請看QPluginLoader的簡單小例子VS2008+Qt 使用QPluginLoader訪問DLL

 

3、採用隱式連結方法,通過QLibrary類對DLL中類物件、全域性函式的呼叫

 

TestDll.h

 

Cpp程式碼  收藏程式碼
  1. #ifndef TESTDLL_H  
  2. #define TESTDLL_H  
  3.   
  4. #include "testdll_global.h"  
  5.   
  6. class TESTDLL_EXPORT TestDll  
  7. {  
  8. public:  
  9.     TestDll();  
  10.     ~TestDll();   
  11.     void helloWorld(); //類成員函式  
  12. private:  
  13.   
  14.   
  15. };     
  16. extern "C" TESTDLL_EXPORT int add(int a,int b);  //自定義的外部函式  
  17. #endif // TESTDLL_H  

TestDll.cpp原始碼

Cpp程式碼  收藏程式碼
  1. #include <iostream>  
  2. #include "TestDll.h"  
  3.   
  4. TestDll::TestDll()  
  5. {  
  6.   
  7. }  
  8.   
  9. TestDll::~TestDll()  
  10. {  
  11.   
  12. }  
  13.   
  14. void TestDll::helloWorld()  
  15. {  
  16.     std::cout << "hello,world!";  
  17. }  
  18. int add(int a,int b)  
  19. {  
  20.     return a + b;  
  21. }  

 

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原始碼

Cpp程式碼  收藏程式碼
  1. #include <QtCore/QCoreApplication>  
  2. #include <iostream>  
  3. #include <QLibrary>  
  4. #include "../TestDll/TestDll.h"  
  5. //引入TestDll.lib檔案,和上面的2,3步工作同理  
  6. //#pragma comment(lib, "../Debug/TestDll.lib")  
  7. int main(int argc, char *argv[])  
  8. {  
  9.     QCoreApplication a(argc, argv);  
  10.     int result = add(5,6);  
  11.     std::cout << result;  
  12.     TestDll dll;  
  13.     dll.helloWorld();  
  14.        // return a.exec();  (轉者註釋:這樣是看不到結果的,要改為下面這樣)
  15. a.exec();
  16. return 0;
  17. }  

 結果即可編譯成功

Qt DLL總結【二】-建立及呼叫QT的 DLL


相關文章