windows下如何使用QT編寫dll程式

pamxy發表於2013-06-13

轉自:http://blog.csdn.net/normallife/article/details/5397980

Windows 下如何使用 QT 編寫 dll 程式

因為 QT 必須有呼叫 QApplication 的 exec 方法,這樣才能產生訊息迴圈, QT 的程式才可以執行。所以說如果我們使用了 QT 編寫了 dll 程式,在普通的 windows 程式中是不能呼叫的。在呼叫的時候會出現錯誤。當然 QT 提供瞭解決方法:那就是 QTWinmigrate

這裡是 QT 官方網站對 QTWinmigrate 的介紹:

http://qt.nokia.com/products/appdev/add-on-products/catalog/4/Windows/qtwinmigrate

下面我來介紹一下使用 QTWinmigrate 來編寫 dll 的方法。

首先,我們要重寫 DllMain 函式:

  1. #include <qtwinmigrate/qmfcapp.h>   
  2. #include <qtwinmigrate/qwinwidget.h>   
  3. #include <qmessagebox.h>   
  4. #include <windows.h>   
  5. BOOL  WINAPI DllMain(  HINSTANCE  hInstance,  DWORD  dwReason,  LPVOID  lpvReserved )  
  6. {  
  7.     static   bool  ownApplication = FALSE;  
  8.     if  ( dwReason == DLL_PROCESS_ATTACH )  
  9.         ownApplication = QMfcApp::pluginInstance( hInstance );  
  10.     if  ( dwReason == DLL_PROCESS_DETACH && ownApplication )  
  11.         delete  qApp;  
  12.     return  TRUE;  
  13. }  
  1. #include <qtwinmigrate/qmfcapp.h>   
  2. #include <qtwinmigrate/qwinwidget.h>   
  3. #include <qmessagebox.h>   
  4. #include <windows.h>   
  5. BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpvReserved )  
  6. {  
  7.     static bool ownApplication = FALSE;  
  8.     if ( dwReason == DLL_PROCESS_ATTACH )  
  9.         ownApplication = QMfcApp::pluginInstance( hInstance );  
  10.     if ( dwReason == DLL_PROCESS_DETACH && ownApplication )  
  11.         delete qApp;  
  12.     return TRUE;  
  13. }  

大家都知道 DllMain 函式是 windows 動態庫的入口函式,如果在 dll 中使用了 QT 的 ui 介面前,全域性的 QApplication 必須首先要建立,並且應用程式必須建立 EventLoop 。

進入到 QmfcApp::pluginInstance 方法中去,

  1. bool  QMfcApp::pluginInstance(Qt:: HANDLE  plugin)  
  2. {  
  3.     if  (qApp)  
  4.     return  FALSE;  
  5.     QT_WA({  
  6.     hhook = SetWindowsHookExW(WH_GETMESSAGE, QtFilterProc, 0, GetCurrentThreadId());  
  7.     }, {  
  8.     hhook = SetWindowsHookExA(WH_GETMESSAGE, QtFilterProc, 0, GetCurrentThreadId());  
  9.     });  
  10.     int  argc = 0;  
  11.     (void ) new  QApplication(argc, 0);  
  12.     if  (plugin) {  
  13.     char  filename[256];  
  14.     if  (GetModuleFileNameA(( HINSTANCE )plugin, filename, 255))  
  15.         LoadLibraryA(filename);  
  16.     }  
  17.   
  18.     return  TRUE;  
  19. }  

  1. bool QMfcApp::pluginInstance(Qt::HANDLE plugin)  
  2. {  
  3.     if (qApp)  
  4.     return FALSE;  
  5.     QT_WA({  
  6.     hhook = SetWindowsHookExW(WH_GETMESSAGE, QtFilterProc, 0, GetCurrentThreadId());  
  7.     }, {  
  8.     hhook = SetWindowsHookExA(WH_GETMESSAGE, QtFilterProc, 0, GetCurrentThreadId());  
  9.     });  
  10.     int argc = 0;  
  11.     (void)new QApplication(argc, 0);  
  12.     if (plugin) {  
  13.     char filename[256];  
  14.     if (GetModuleFileNameA((HINSTANCE)plugin, filename, 255))  
  15.         LoadLibraryA(filename);  
  16.     }  
  17.     return TRUE;  
  18. }  

 

我們可以看到: Qapplication 被建立了出來。 QmfcApp::pluginInstanc 是為了保證程式中存在一個 Qapplication 物件,並且 dll 要把這個 Qapplication 的例項載入到記憶體中。

下面是 dll 中的匯出函式:

  1. extern   "C"   __declspec ( dllexport )  bool  showDialog(  HWND  parent )  
  2. {  
  3.     QWinWidget win( parent );  
  4.     win.showCentered();  
  5.     QMessageBox::about( &win, "About QtMfc" ,  "QtMfc Version 1.0/nCopyright (C) 2003"  );  
  6.   
  7.     return  TRUE;  
  8. }  

  1. extern "C" __declspec(dllexport) bool showDialog( HWND parent )  
  2. {  
  3.     QWinWidget win( parent );  
  4.     win.showCentered();  
  5.     QMessageBox::about( &win, "About QtMfc", "QtMfc Version 1.0/nCopyright (C) 2003" );  
  6.     return TRUE;  
  7. }  

dll 中的匯出函式要用 extern "C" 形式, QwinWidget 為 native win32 視窗提供堆疊等等。

 

這樣還沒有寫完程式。不行你拿這個程式來

qmake -project

qmake

nmake

這樣是無論如何也編譯不過的。

如果你仔細看qtwinmigrate的example的話,你就會注意到:

include(D:/qt4.4.3/qtwinmigrate-2.8-opensource/src/qtwinmigrate.pri)

 

編譯的時候一定要在*.pro檔案中加上這一句!切記,切記!

 

參考: http://doc.trolltech.com/solutions/qtwinmigrate/winmigrate-qt-dll-example.html

 

讀後感:

 

 

我發現一個問題啊:從bool QMfcApp::pluginInstance(Qt::HANDLE plugin) 的實現可以知道三件事:
1.Qt注入了鉤子函式,去過濾host application(主程式)的訊息,如果碰到Qt的訊息,就讓Qt add-in(.dll etc)工程去處理。
2.建立了一個QtApplication物件。
3.載入此Qt add-in(.dll etc)。
對於第一件事和第三件事,我不敢苟同Qt的做法。
第一件事,既然注入了鉤子函式,那麼什麼時候free掉(關掉)?如果不free(關掉),那麼鉤子函式就一直存在。大家有可能認為在Qt dll unload/free(解除安裝)的時候去關掉鉤子函式不就行了嗎,可是大家看:
if ( dwReason == DLL_PROCESS_DETACH && ownApplication )  
        delete qApp;  
這裡僅僅delete qApp; 沒有去解除安裝鉤子函式啊。也許你覺得解除安裝鉤子與否無所謂,可是如果連Qt dll都不存在了,而鉤子函式還存在,那麼QtFilterProc去那裡執行,crash也就發生了。
對於第三件事,也許是Qt想到了上面提到的情況,所以就直接loadlibrary Qt add-in(.dll etc),也沒有freelibrary,意思就是讓Qt add-in(.dll etc)一直存在(和host application一樣的生命週期),這樣來避免crash.可是這種做法比較牽強,因為對於add-in(.dll etc)而言,程式設計師當然是想什麼時候unload(解除安裝)就什麼時候unload(解除安裝),可以節省記憶體。

請教高人後的解答:

對於第一件事:

在QMfxApp的解構函式中解除安裝了鉤子函式
QMfcApp::~QMfcApp()
{
    if (hhook) {
        UnhookWindowsHookEx(hhook);
        hhook = 0;
    }

#ifdef QTWINMIGRATE_WITHMFC
    for (int a = 0; a < mfc_argc; ++a) {
        char *arg = mfc_argv[a];
        delete[] arg;
    }
    delete []mfc_argv;

    mfc_argc = 0;
    mfc_argv = 0;
    mfc_app = 0;
#endif
}

對於第三件事,是Qt 的一個bug.

參考文章:

http://blog.csdn.net/tingsking18/archive/2009/12/28/5091580.aspx

 

過了些天后,自己又想了想覺得還有幾點需要考慮:

  1. #include <qtwinmigrate/qmfcapp.h>   
  2. #include <qtwinmigrate/qwinwidget.h>   
  3. #include <qmessagebox.h>   
  4. #include <windows.h>   
  5. BOOL  WINAPI DllMain(  HINSTANCE  hInstance,  DWORD  dwReason,  LPVOID  lpvReserved )  
  6. {  
  7.     static   bool  ownApplication = FALSE;  
  8.     if  ( dwReason == DLL_PROCESS_ATTACH )  
  9.         ownApplication = QMfcApp::pluginInstance( hInstance );   
  10.     if  ( dwReason == DLL_PROCESS_DETACH && ownApplication )  
  11.         delete  qApp;  
  12.     return  TRUE;  

上面給的例子有許多對於C++設計原則相悖的地方(紅色標出來的語句)。

首先,QMfcApp::pluginInstance(hInstance);這個語句的實現裡有loadlibrary的操作,而對於DllMain裡的DLL_PROCESS_ATTACH,去呼叫有loadlibrary的語句實在是不好的設計(容易產生loadlibrary死鎖)。

再者,delete qApp;這樣子的呼叫也是不可取的。

 

相關文章