http://doc.qt.digia.com/solutions/4/qtwinmigrate/winmigrate-walkthrough.html(原文地址)
(qtwinmigrate-2.8-opensource.zip可以到csdn資源內下載,點此下載 )
這個練習是基於MFC生成的程序遷移到Qt的例子,這個程序通過微軟Visual Studio的MFC應用程序嚮導生成。
入門(Getting Started)
(注:這個例子在文件qtwinmigrate-2.8-opensource\examples\mfc\step1中,而且這例子要用VC6.0或者用Qt Creator才能打開)
先把工程文件 qtmfc1.dsp 導入到VS的工作空間,並且確保這個VS工程可以正常的編譯和運行。(在step1中沒有找到qtmfc1.dsp,倒是有個qtmfc.dsp)
這個MFC程序有一個使用DLL提供的對話框接口,這個很簡接口很簡單:DLL導入一個名字是showDialog的C語言風格的函數,這個函數把整個窗口作爲父句柄。這個DLL以模態方式顯示它的對話框,並且在函數返回後卸載掉。
以下代碼是在MFC應用程序的OnAppAbout消息處理函數。(這個消息處理函數就是響應“關於。。。。。”的單擊,源代碼在step1目錄下qtmfc.cpp中)
void WindowsApp::OnAppAbout() //這部分的源代碼在step1目錄下
{
HMODULE mod = LoadLibrary("qtdialog.dll" );
//這個是測試Qt生成的Dll的部分,先動態載入,再使用導出的函數
if ( mod )
{
typedefBOOL(*pShowDialog)(HWND parent);
pShowDialog showDialog =(pShowDialog)GetProcAddress( mod, "showDialog" );
if ( showDialog )
showDialog(theApp.m_pMainWnd->m_hWnd );
FreeLibrary( mod );
}
else
{
CAboutDlg aboutDlg;
aboutDlg.DoModal();
}
}
如果這個DLL被正確載入並且showDialog()被正確導出,那麼導出的函數就會被調用,否則調用顯示默認的MFC關於對話框。
插件擴展
(這個工程生成的dll就是上個例子用的那個)
(注:這個例子在文件qtwinmigrate-2.8-opensource\examples\qtdll中,可以用Qt Creator 編譯生成dll,也可以添加到vs新建的工程當中去,已經測試過,可以正常使用)
示例目錄下的qtdll文件下的工程通過QMessageBox類實現了插件接口。爲了使用這個類,一個QApplication對象必須在當前進程中存在,而且除了mfc的標準事件派送外,還要有一個Qt的消息循環。
這個DLL也要確保它和其它的基於Qt的DLL能夠運行在同一個進程中,甚至進程中可能已經存在一個QApplication對象,並且這個創建QApplication對象的DLL還要繼續運行在內在中,防止其它DLL使用內存地址。
上述所列的事情全部被QMFC::pluginInstance()函數解決。這個函數創建一個QApplication對象,並且安裝一個能夠使Win32標準消息循環和Qt事件循共同工作的消息鉤子函數。如果DLL實例作爲參數被傳遞,那麼pluginInstance()也會增加這個DLL的使用計數,直到這個進程結束。
當DLL被加載的時候,這個pluginInstance()也可以用於DllMain入口函數的重載版本。一個靜態的變量被用於記錄DLL是否和QApplication對象關聯。當DLL被卸載後,這個QAplication對象就可以通過全局指針qApp刪除了。
爲了使用這個pluginInstance函數和跟它有相關的Qt類,我們需要包含一些頭文件。
#include <qmfcapp.h>
#include <qwinwidget.h>
#include <QtGui/QMessageBox>
#include <windows.h>
//用DllMain()函數就是爲了使用hInstance,作爲qwinwidget的參數
BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpvReserved*/ )
{
static bool ownApplication = FALSE; //這部分內容不用更改,使用時直接copy就行
if ( dwReason == DLL_PROCESS_ATTACH )
ownApplication = QMfcApp::pluginInstance( hInstance );
if ( dwReason == DLL_PROCESS_DETACH && ownApplication )
delete qApp;
return TRUE;
}
然後DLL的接口就通過導出C語言風格的showDialog函數來實現。QWinWidget類可以在這個時候使用,用於存放對話框,並且關聯上Qt 對話框。
extern "C" __declspec(dllexport) bool showDialog( HWND parent ) //這個是導出的函數
{
//到這個地方,看到QWinWidget了吧?它的參數就是使用Dll的主程序的句柄
QWinWidget win( parent );
win.showCentered();
QMessageBox::about( &win, "About QtMfc", "QtMfc Version 1.0\nCopyright (C) 2003" );
//這部分官方代碼原文中並沒有,是我添加的,已經測試過,可以在vs工程下正常使用
////////////////////////////////////////////////////////////////QMainWindow* mainwin=new QMainWindow();
QLineEdit *edit = new QLineEdit( widget );
mainwin->setCentralWidget(edit);
mainwin->show();
////////////////////////////////////////////////////////////////
win.show();
qApp->exec(); //有這個就可以把信號和槽添加進來,已經測試過,支持信號和槽
return TRUE;
}
(個人總結:我已經測試過qtwinmigrate-2.8-opensource\examples\qtdll目錄下的工程可以在Qt Creator上可以生成dll和lib文件,生成的dll在其它vs工程上完全正常使用。所以如果以後要再用Qt開發vs下用的dll,完全可以把這個作爲模板,DllMain()內的代碼不用動,只是更換掉要導入的函數即可。
完整的代碼如下圖:
在上面的代碼中,左邊的qtwinmingrate.pri提供了一個Qt/MFC 遷移框架,裏面有qmfcappp、qwinhost、qwinwidget三個類,就像上面說的,QMfcApp:: pluginInstance(hInstance)提供Qt和MFC消息循環共同工作的機制,並接受DllMian()函數傳來的句柄,然後使用qwinwidget類接受這個句柄,有了這個父類,其它的小部件,包括各種控件就可以向上放了,當然,也可以支持信號和槽機制。另外,如果加了信號和槽功能後,最好在return TRUE;前面加上一句 qApp->exec();這樣創建的窗口就不會因爲導出函數的結束而析構掉。