windows下如何使用QT编写dll程序

来源:互联网 发布:php 动态实例化对象 编辑:程序博客网 时间:2024/05/17 08:52

转自: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 函数:

view plaincopy to clipboardprint?
  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. }  
[cpp] view plaincopyprint?
  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 方法中去,

view plaincopy to clipboardprint?
  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. }  

[cpp] view plaincopyprint?
  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 中的导出函数:

view plaincopy to clipboardprint?
  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. }  

[cpp] view plaincopyprint?
  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;这样子的调用也是不可取的。