用了Qt大半年,略懂皮毛,对MFC有些偏见。认为是过时之物。最后发现不管用什么,还是需求驱动的。
Qt开源社区蓬勃发展、代码风格确实比MFC要好看得多。但是MFC并没有被淘汰,很多公司提供的SDK附带的是MFC的例子。比如海康SDK、Andor科研CCD的例程。说明在市场上,在正式的应用场合里MFC还,C++在Windows的简单桌面应用开发还是可以考虑一下MFC的,因为当年学MFC的那个群体的长辈现在就是各行各业的中坚啊。当然,我上面所说的各种产品的SDK,其提供的库只要支持,自然可以用Qt来做界面。
在数字图像处理、计算机视觉这块的上位机软件自然是C++的用武之地,因为OpenCV库最初提供的就是C++的编程借口。虽然现在OpenCV提供了其他语言的编程接口(Python、Android),传统应用场合还是选用C++。
MFC的消息处理与Qt的信号与槽机制很像。MFC的文档视图结构Document/View(将数据的保存与显示分离的思想)与Qt的模型视图Model-View编程的思想一致。MFC同样提供了对数据库的编程接口、还有套接字的网络编程。Qt同样有对数据库的编程支持、Qt网络编程。多线程编程、线程同步这些也是一应俱全。总之,MFC与Qt这样的编程框架、基本的写软件的东西都具备。
还是多做总结、多比较学习、体会编程之美吧。
1.MFC桌面应用官方参考:MFC Desktop Applications:点击打开链接https://msdn.microsoft.com/en-us/library/d06h2x6e.aspx
2.Visual C++例子点击打开链接
3.MFC入门Tutorial
MFC Tutorial:
https://www.tutorialspoint.com/mfc/index.htm
教程参考资料:参考文献:马石安. Visual C++程序设计与应用教程(第三版)[M]. 清华大学出版社, 2017.
参考教程里的代码例子:链接:https://pan.baidu.com/s/1gqX1YaCReVfO5vyHGWhg-g 密码:3xzo
下面对MFC、Windows程序设计做个小结:
1. MFC应用程序概述
MFC(Microsoft Foundation Class)是有微软提供的一套专门用于Windows编程的基础类库,它的类可以处理许多标准的Windows编程任务。
2.1Windows应用程序概述
Windows操作系统是一个多任务操作系统。所有Windows应用程序都是由消息驱动的,消息处理是所有Windows应用程序的核心。
2.1.1窗口、消息和事件
Windows应用程序的运行过程即是窗口内部、窗口与窗口之间、窗口与系统之间进行数据处理与数据交换的过程。
Windows是一个基于事件的消息驱动系统。(消息:Message)
Windows应用程序是按照“事件->消息->消息处理”的机制运行的。
当有某个事件(如单击鼠标、键盘输入、执行菜单命令)发生时,Windows会根据具体的事件产生对应的消息,并发送该消息到指定的应用程序的消息队列。应用程序从消息队列中取出消息,并根据不同的消息进行不同的处理。
2.1.2VC基本数据类型
数据类型 |
对应基本数据类型 |
说明 |
BOOL |
int |
布尔值 |
BSTR |
unsigned short* |
32位字符指针 |
BYTE |
unsigned char |
8位无符号整数 |
COLORREF |
unsigned long |
32位颜色值 |
DWORD |
unsigned long |
32位无符号整型(段地址与偏移地址) |
LONG |
long |
32位有符号整型数 |
LPARAM |
long |
32位值,传递给窗口或过程回调函数的参数 |
LPCSTR |
char* |
32位字符串指针 |
LPCTSTR |
const char* |
32位字符串常量指针,用于移植到双字节字集 |
LPTSTR |
char* |
32位字符串指针,用于移植到双字节字集 |
LPVOID |
void* |
指向未定义类型的32位指针 |
LPRESULT |
long |
来自窗口过程或回调函数的32位返回值 |
UINT |
unsigned int |
32位无符号整型数 |
WNDPROC |
long(__stdcall*)(void*, unsigned int,unsigned int,long) |
指向窗口过程的32位指针 |
WORD |
unsigned short |
16位无符号整数 |
WPARAM |
unsigned int |
作为参数传递给窗口过程或回调函数的32位值 |
句柄是Windows用来唯一标识应用程序所建立的或使用的对象的一个32位无符号整型数值。句柄用来标识应用程序的一个对象,如窗口、实例、菜单、内存、输出设备、控制或文件等。
2.2.2MFC应用程序框架类结构
1) Cobject类
Cobject类是MFC类库的根类,描绘了所有MFC类的公共特性,并给它的所有子类提供了三个重要特性:(1)串行化支持:串行化是对象的数据存入或者输出存储介质的过程。把CObject类作为基类,创建可以串行化的类,它的实例可以很容易地存储或重新创建。(2)运行时类信息(RTTI):允许用户在运行时检索对象的类名称以及对象的其他信息。(3)诊断和调试支持:允许用户对CObject派生类的实例执行有效性检测,并将状态信息存到一个调试窗口。
2) CCmdTarget类
命令类CCmdTarget是CObject的子类,它是MFC库中所有具有消息映射属性的类的公共基类。它的子类:CWinThread类、CWnd类、CDocument类、CDocTemplate类。从CCmdTarget类派生的派生的类能在程序运行时动态创建对象和处理命令消息。
3) CWinApp类
应用程序类CWinApp类是CWinThread的子类,封装了初始化、运行、终止应用程序的代码。可以由此派生自己的应用程序类。CWinApp类用4个成员函数来实现传统SDK应用程序WinMain()函数完成的工作。
表1 CWinApp类可重载的成员函数
成员函数 |
功能 |
InitInstance() |
应用程序的初始化;创建文档模板、文档、视图和主窗口 |
Run() |
处理消息循环 |
OnIdle() |
当没用窗口消息需要处理时,这个函数被窗口框架调用 |
ExitInstance() |
退出程序时,该函数被调用 |
窗口类提供了MFC中所有窗口类的基本功能。从CWnd派生的类可以拥有自己的窗口,并对它进行控制。
5) CFrameWnd类
CFrameWnd类是CWnd类的派生类。它是所有其他框架窗口类的基类,主要用来管理一个窗口。CFrameWnd类的对象是一个框架窗口,包括边框、标题栏、菜单、最大化按钮、最小化按钮、和一个激活的视图。CFrameWnd类支持单文档界面,对于多文档界面,使用它的两个派生类:CMDIFrameWnd和CMDIChildWnd。CMDIFrameWnd用于MDI的主窗口,CMDIChildWnd用于MDI的子窗口。CFrameWnd类提供了若干成员函数,用于获取和设置活动文档、视图、图文框、标题栏、状态栏。
6) CView类
视图CView是CWnd类的子类。视图类及其派生类用于管理框架窗口客户区。
7) CDocument类
文档类CDocument负责装载和维护文档。文档包括应用程序的工作成果或环境设置数据等,可以是程序需要保存的任何内容。
一个MFC应用程序并不直接操作上述类,而是以上述类为基类派生新的类,从而构建Windows应用程序的基本框架。
8)CDocTemplate类
AppWizard除了生成可在工作区中展示的应用程序类、窗口框架类、文档和视图类以外,还生成了文档模板类CDocTemplate。文档模板类定义了文档模板的基本功能,用户不能直接使用它,而只能使用它的派生类。该类用于创建一个新的文档/视图结构,以协调创建文档、视图和框架窗口对象。它提高了两个派生类:单文档模板CSingleDocTemplate和多文档模板CMultiDocTemplate。
2.2.3MFC应用程序分析
1)应用程序向导生成的文件:
(1)头文件和实现文件:
MainFrm.h 和MainFrm.cpp:定义和实现窗口框架类CMainFrame。
XXXDoc.h和XXXDoc.cpp:定义和实现文档类。
XXXView.h和XXXView.cpp:定义和实现类视图类。
XXX.h和XXX.cpp定义和实现应用程序类。
Resource.h:定义项目中所有资源标识符,给资源ID分配一个整数值
StdAfx.h和StdAfx.cpp:用于创建一个预编译的头文件.pch和一个预定义类型的文件StdAfx.obj
StdAfx.h预编译头文件包含了标准系统包含文件以及经常使用的项目特定的包含文件,这些文件一般情况下不需要进行修改。MFC体系结构非常庞大,包含文件很多,如果每次都重新编译,很费时。因此,常用的MFC头文件如afxwin.h、afxext.h、afxdisp.h、afxcmn.h等,都放在StdAfx.h中,然后使StdAfx.cpp包含StdAfx.h文件。由于编译器可以识别文件是否被编译过,所以StdAfx.cpp只需编译一次,并生成预编译头文件,用于存放头文件编译后的信息。采用预编译头文件可以加速编译过程。
(2)资源文件 .rc .rc2
(3)项目工作去文件.dsw:保存当前工作区所包含的项目信息
和项目文件.dsp:包含当前项目的设置、项目中的文件等信息。
(4)类信息文件.clw:保存了ClassWizard编辑现有类或增加新类时需要使用的类信息,同时还保存了创建、编辑消息映射和成员函数所需要的信息。
(5)ReadMe.txt项目自述文件。
2.3.2.2MFC程序的执行过程
与所有Windows应用程序一样,MFC应用程序框架也有一个作为程序入口点的WinMain()主函数,但在资源程序中看不见该函数。它在MFC中已经定义好并同应用程序相链接。该函数对于与MFC文件Winmain.cpp中的AfxWinMain()函数。
MFC应用程序启动时,首先创建应用程序对象theApp。这时将自动调用应用程序类的构造函数初始化对象theApp,然后由应用程序框架调用MFC提供的AfxWinMain()主函数。在AfxWinMain()主函数中,首先通过调用全局函数AfxGetApp()来获取theApp的指针pApp,然后通过该指针调用theApp的成员函数InitInstance()来初始化应用程序。在应用程序的初始化过程中,同时还构造了文档模板,产生最初的文件、视图和主框架窗口,并生成工具栏和状态栏。当InitInstance()函数执行完毕后,AfxWinMain()函数将调用成员函数Run(),进入消息循环,直到函数Run()收到WM_QUIT消息。MFC首先调用CWinApp类的成员函数ExitInstance(),然后调用静态对象的析构函数,包括CWinApp对象,最后退出应用程序,将控制权交给操作系统。
在初始化的最后,应用程序将收到WM_PAINT消息,框架会自动调用视图类的OnDraw()函数绘制程序客户区窗口。这时,应用程序基本窗口已经生成,应用程序准备接受系统或用户的消息,以便完成用户需要的功能。如果消息队列中由消息且不是WM_QUIT消息,则将消息分发给窗口函数,以便通过MFC消息映射宏调用指定对象的消息处理函数。如果消息队列中没用消息,函数Run()就调用OnIdle()函数进行空闲时间的处理。
// Standard WinMain implementation
// Can be replaced as long as 'AfxWinInit' is called first
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
// AFX internal initialization
if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
goto InitFailure;
// App global initializations (rare)
if (pApp != NULL && !pApp->InitApplication())
goto InitFailure;
// Perform specific initializations
if (!pThread->InitInstance())
{
if (pThread->m_pMainWnd != NULL)
{
TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");
pThread->m_pMainWnd->DestroyWindow();
}
nReturnCode = pThread->ExitInstance();
goto InitFailure;
}
nReturnCode = pThread->Run();
InitFailure:
#ifdef _DEBUG
// Check for missing AfxLockTempMap calls
if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
{
TRACE(traceAppMsg, 0, "Warning: Temp map lock count non-zero (%ld).\n",
AfxGetModuleThreadState()->m_nTempMapLock);
}
AfxLockTempMaps();
AfxUnlockTempMaps(-1);
#endif
AfxWinTerm();
return nReturnCode;
}