本系列文章后续部分主要来自《Visual C++编程实战宝典 清华大学出版社》学习笔记
后续部分学习基于VS2010+
简介
微软基础类库(Microsoft Foundation Class Library,MFC)是一个编写Windows应用程序的框架类库。使用MFC类库编写 C++ 程序,可以便捷地实现界面功能、网络功能、多媒体功能和数据访问功能等各种常用功能。
MFC 类层次结构
CObject类是所有类的基类。在此基础之上,派生了CCmdTarget类,用于表示命令目标对象,CWnd类就是继承于此类。而在CWnd类上又派生了包括对话框、视图、控件、属性页、窗体框架和控制栏等在内的扩展对话框类。
MFC 全局函数
MFC中除了MFC类外,还包括部分宏和全局成员,这些都不属于类的成员,如全局函数和全局变量。常用的MFC全局函数有:
- AfxAbort:MFC提供的默认终止函数
- AfxBeginThread:开始一个新的线程
- AfxCheckError():检测代码是否为错误代码
- AfxCheckMemory():检测是否发生有关内存的错误
- AfxDaoInit():初始化DAO数据库引擎
- AfxDaoTerm():终止DAO数据库引擎
- AfxDbInitModule():初始化MFC数据库DLL
- AfxDoForAllClasses():在应用程序内存空间中,枚举所有序列化派生类
- AfxDump():调试程序时,列出对象所有的状态
- AfxDumpStack():列出当前堆栈的情况
- AfxEndThread:结束一个旧的线程
- AfxFormatString1:类似printf一般地将字符串格式化
- AfxFormatString2:类似printf一般地将字符串格式化
- AfxMessageBox:类似Windows API 函数 MessageBox
- AfxOuputDebugString:将字符串输往除错装置
- AfxGetApp:获得application object (CwinApp派生对象)的指针
- AfxGetMainWnd:获得程序主窗口的指针
- AfxGetInstance:获得程序的instance handle
MFC应用程序框架分析
入口函数 WinMain()
基于MFC框架的应用程序必有有一个派生自CWinApp类的对象,此对象在创建窗体前进行初始化。而CWinApp类派生自CWinThread类,代表应用程序执行的主线程,但一个应用程序可能具有一个或多个线程。CWinThread类中具有InitInstance()、Run()、ExitInstance()和OnIdle()成员函数。这些函数在CWinApp中也被重载使用,但是它们是作为应用程序对象执行而不是主线程。
MFC 应用程序也有一个WinMain()入口函数,我们一般不需要重写WinMain()函数,由类库提供。
派生自CWinApp的应用程序对象
CWinApp类是从Windows应用程序对象中派生而来的应用程序基类。应用程序提供初始化应用程序和实例的成员函数,并提供运行应用程序和终止应用程序的函数。每个使用MFC的应用程序只能包含一个派生自CWinApp的对象。当构造完C++全局对象后,框架会构造此对象,因此,当Windows调用MFC类库提供的WinMain()函数时,应用程序对象已经生效了。
当从CWinApp类派生应用程序类,重写InitInstance()成员函数创建应用程序的主对话框对象时,除了CWinApp的成员函数,MFC类库提供了几个全局函数来访问CWinApp对象和其它全局对象:
- AfxGetApp()
- AfxGetInstanceHandle()
- AfxGetResourceHandle()
- AfxGetAppName()
初始化应用程序的InitInstance()函数
Windows允许同时运行同一个程序的一个或多个实例。WinMain()函数在每次启动新的应用程序实例时,会调用InitInstance()函数。应用向导创建的标准的InitInstance()函数主要完成以下工作:
- 创建文档模板,依次创建文档对象、视图对象和框架对话框
- 从.ini文件中或windows注册表中装载标准文件选项,包括最近使用的文件名称等信息
- 注册一个或多个文档模板
- 对于MDI应用程序,创建一个主框架对话框
- 从命令行上处理命令行打开文档,或打开新的空文档。
InitInstace()示例:
BOOL CMFCApplication1App::InitInstance()
{
// 如果一个运行在 Windows XP 上的应用程序清单指定要
// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
//则需要 InitCommonControlsEx()。否则,将无法创建窗口。
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// 将它设置为包括所有要在应用程序中使用的
// 公共控件类。
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinAppEx::InitInstance();
// 初始化 OLE 库
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
}
AfxEnableControlContainer();
EnableTaskbarInteraction(FALSE);
// 使用 RichEdit 控件需要 AfxInitRichEdit2()
// AfxInitRichEdit2();
// 标准初始化
// 如果未使用这些功能并希望减小
// 最终可执行文件的大小,则应移除下列
// 不需要的特定初始化例程
// 更改用于存储设置的注册表项
// TODO: 应适当修改该字符串,
// 例如修改为公司或组织名
SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
LoadStdProfileSettings(4); // 加载标准 INI 文件选项(包括 MRU)
InitContextMenuManager();
InitKeyboardManager();
InitTooltipManager();
CMFCToolTipInfo ttParams;
ttParams.m_bVislManagerTheme = TRUE;
theApp.GetTooltipManager()->SetTooltipParams(AFX_TOOLTIP_TYPE_ALL,
RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams);
// 注册应用程序的文档模板。文档模板
// 将用作文档、框架窗口和视图之间的连接
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CMFCApplication1Doc),
RUNTIME_CLASS(CMainFrame), // 主 SDI 框架窗口
RUNTIME_CLASS(CMFCApplication1View));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
// 分析标准 shell 命令、DDE、打开文件操作的命令行
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// 调度在命令行中指定的命令。如果
// 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// 唯一的一个窗口已初始化,因此显示它并对其进行更新
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
框架程序的运行核心Run()函数
框架应用程序运行时就是运行CWinApp类的Run()函数。
MFC 的消息映射
在Windows系统中,消息一般由从CWnd派生而来的对象处理,包括CFrameWnd、CMDIFrameWnd、CMDIChildWnd、CView、CDialog和其它从这些类派生而来的对象,这些对象封装了代表Windows窗口句柄的HWND。
传统的Windows应用程序中,MFC使用switch语句处理发送给窗口的消息。
VC中使用消息映射来处理消息。
标准Windows消息
Windows系统提供了一组标准Windows消息,一般由对话框类和视图类根据参数的取值进行处理。 比如:创建窗口、销毁窗口和窗口重绘等。每个标准Windows消息都有一个以WM开头的消息ID和对应的宏,格式是ON_WM_xxx,其中xxx是消息名称,如ON_WM_CREATE宏表示创建对话框消息。
标准Windows消息对应的处理函数名根据消息宏派生而来,格式是onXxx,消息处理函数的参数顺序依次是wParam和lParam。
示例:
protected:
DECLARE_MESSAGE_MAP()
// CMFCApplication1App
BEGIN_MESSAGE_MAP(CMFCApplication1App, CWinAppEx)
ON_COMMAND(ID_APP_ABOUT, &CMFCApplication1App::OnAppAbout)
// 基于文件的标准文档命令
ON_COMMAND(ID_FILE_NEW, &CWinAppEx::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, &CWinAppEx::OnFileOpen)
// 标准打印设置命令
ON_COMMAND(ID_FILE_PRINT_SETUP, &CWinAppEx::OnFilePrintSetup)
END_MESSAGE_MAP()
DECLARE_MESSAGE_MAP宏表明消息映射。
触发菜单/快捷键产生的命令消息
MFC除了支持标准Windos消息外,还支持命令消息。
使用ON_COMMAND宏可以在消息映射表中指定命令消息对应的处理函数。
使用ON_UPDATE_COMMAND宏可以在消息映射表中指定命令更新消息对应的处理函数
宏的第一个参数是命令ID,第二个参数是命令消息的处理函数。
命令处理函数没有参数和返回值,命令更新处理函数只有一个CCmdUI类型的参数并且没有返回值,其定义方式为:
ON_COMAND(id,memberFxn) //命令消息宏
ON_UPDATE_COMMAND_UI(id,memberFxn) //命令更新消息定义。
使用ON_MESSAGE宏自定义消息
MFC还支持用户自定义消息。使用ON_MESSAGE宏可以在消息映射表中指定消息对应的处理函数。
示例:
#define WM_MYMESSAGE WM_USER + 100 //自定义消息值
//自定义消息处理函数
afx_msg LRESULT OnMyMessage(WPARAM wParam,LPARAM lParam);
//在类的实现文件中,自定义消息映射函数
BEGIN_MESSAGE_MAP(CMyWnd,CMyParentWndClass)
ON_MESSAGE(WM_MYMESSAGE,OnMyMessage)
END_MESSAGE_MAP()
发送自定义消息:
CWnd* pWnd = ...;
pWnd->SendMessage(WM_MYMESSAGE);
用户自定义消息的消息ID值的范围是从WM_USER~0x7fff。
注册系统消息
上面3种消息都是基于同一个窗口下的消息处理。要在系统中定义一个独立于窗口的唯一的消息处理,可以使用Windows注册消息。使用RegisterWindowMessage()函数可以创建在系统中唯一的消息ID。
使用ON_REGISTERED_MESSAGE宏可以在消息映射表中指定Windows注册消息对应的处理函数,宏的参数为使用RegisterWindowMessage()函数返回的UINT类型的消息ID。