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();这样创建的窗口就不会因为导出函数的结束而析构掉。