MFC是一种C++类库,利用面向对象的方法封装了Windows API,为Windows应用程序的开发带来了极大便利。本文总结了使用MFC进行编程的基本方法,编程环境为VS2008 SP1。
由于使用MFC应用程序向导
后VS会自动生成应用程序框架,因而本文先利用VS生成一个空项目,然后在此基础上一步步编写应用程序。
打开VS2008,从菜单中依次打开文件->新建->项目
,弹出新建项目
对话框,在左侧的项目类型
中选择Visual C++->Win32
,在右侧的模板
中选择Win32 项目
,并填写项目名称及路径,本文的项目名称为Hello
,如下图所示。
点击确定
按钮,进入Win32应用程序向导
对话框,点击下一步
。
应用程序类型
选择Windows应用程序
,附加选项
选择空项目
,点击完成
。
现在已经生成了一个Win32空项目,然后鼠标右键单击左侧工作空间窗口中的Hello
项目,选择属性
。在弹出的属性配置对话框中,打开配置属性->常规
,将MFC的使用
由使用标准Windows库
改为在共享DLL中使用MFC
,然后点击确定
按钮。
配置好该项属性后,项目就可以支持MFC类库了。
开发MFC应用程序的首要任务是定义一个基于CWinApp
类的应用程序对象。由于MFC已经对WinMain函数进行了封装,所以无需在程序中提供WinMain函数,而是采用对象来抽象表示应用程序,一个MFC应用程序有且仅有一个应用程序对象。
鼠标右键单击左侧工作空间窗口中的Hello
项目,依次选择添加->类
,在弹出的对话框中选择C++类
,点击添加
按钮。
在弹出的一般C++类向导
对话框中填写应用程序对象的相关信息,本文将类名定义为CMyApp
,基类一项填写CWinApp
,其他项保持默认即可。
点击完成
按钮,会提示项目中未找到基类"CWinApp"
,点击是
跳过即可。
为了解决提示中描述的问题,打开MyApp.h
,添加#include
语句,即可将CWinApp
以及其他MFC类引入应用程序中,并在CMyApp
类的定义中添加InitInstance
函数的声明。
#pragma once
#include
class CMyApp : public CWinApp
{
public:
CMyApp(void);
~CMyApp(void);
virtual BOOL InitInstance();
};
InitInstance
函数的主要作用是为应用程序创建一个窗口。窗口是Windows应用程序的核心,是实现人机交互的重要接口。与应用程序对象相同,MFC使用CWnd
类及其派生类对窗口进行抽象表示。在创建应用程序的过程中,有两个比较重要的窗口类,即CWnd
和CFrameWnd
。前者是所有窗口类的基类,后者是从前者派生而来,被称为框架窗口
,框架窗口在文档/视图体系结构中起着重要作用。
接下来打开MyApp.cpp
,定义一个CMyApp
类的对象代表应用程序,程序开始执行时该对象即在内存中实例化。
#include "MyApp.h"
// 应用程序对象
CMyApp myApp;
CMyApp::CMyApp(void)
{
}
CMyApp::~CMyApp(void)
{
}
首先用同样的方法添加窗口类,右键单击左侧工作空间窗口中的Hello
项目,依次选择添加->类
,在弹出的对话框中选择C++类
,点击添加
按钮。弹出一般C++类向导
对话框,类名填写CMainWindow
,基类填写CFrameWnd
,其他项保持默认即可。然后点击完成
按钮,会提示项目中未找到基类"CFrameWnd"
,点击是
跳过即可。同样地,打开MainWindow.h
,添加#include
语句,即可解决提示中出现的问题。
通过生成窗口对象并调用其Create
或CreateEx
函数,MFC应用程序可以创建一个窗口。打开MainWindow.cpp
,在CMainWindow
类的构造函数中添加Create
函数。
CMainWindow::CMainWindow(void)
{
Create(NULL, _T("The Hello Application"));
}
然后打开MyApp.cpp
,包含MainWindow.h
头文件并添加InitInstance
函数的定义。
#include "MainWindow.h"
BOOL CMyApp::InitInstance()
{
m_pMainWnd = new CMainWindow;
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
其中,m_pMainWnd
是指向CWnd
类型的指针。该变量是由MFC定义的,指向应用程序的主窗口。ShowWindow
函数的作用是显示窗口,其参数m_nCmdShow
是CWinApp
类的成员变量。m_nCmdShow
用于指定窗口的状态。通常情况下,m_nCmdShow
的值为SW_SHOWNORMAL
,即正常状态。UpdateWindow
可以使窗口立即重新绘制。
点击启动调试
运行程序,可以看到一个标题为The Hello Application
的空白窗口。
与过程化模型不同,Windows使用的是事件驱动编程模型,应用程序通过处理操作系统发送来的消息响应事件。以WM_PAINT
消息为例,程序启动时会触发该消息用以绘制窗口,这时可以通过其对应的消息处理函数OnPaint
对该消息进行处理。
首先打开MainWindow.cpp
添加消息映射及相应的命令响应函数。
BEGIN_MESSAGE_MAP(CMainWindow, CFrameWnd)
ON_WM_PAINT()
END_MESSAGE_MAP()
void CMainWindow::OnPaint()
{
CPaintDC dc(this);
CRect rect;
GetClientRect(&rect);
dc.DrawText(_T("Hello, MFC"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}
该函数用于在窗口中输出Hello, MFC
。然后在MainWindow.h
中为消息处理函数添加声明。
protected:
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
重新点击启动调试
运行程序,可以看到窗口中心输出了Hello, MFC
。
以上步骤阐述了框架窗口的使用,接下来将基于普通窗口创建应用程序。首先打开MainWindow.h
,将基类CFrameWnd
改为CWnd
。
然后打开MainWindow.cpp
,将BEGIN_MESSAGE_MAP
宏中的基类改为CWnd
,同时修改CMainWindow
类的构造函数。
CMainWindow::CMainWindow(void)
{
// 注册WNDCLASS
CString strWndClass = AfxRegisterWndClass(
CS_HREDRAW | CS_VREDRAW,
AfxGetApp()->LoadStandardCursor(IDC_ARROW),
(HBRUSH)(COLOR_WINDOW + 1),
AfxGetApp()->LoadStandardIcon(IDI_WINLOGO));
// 创建窗口
CreateEx(0, strWndClass, _T("The Hello Application"),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL);
// 调整窗口大小
CRect rect(0, 0, 352, 352);
CalcWindowRect(&rect);
SetWindowPos(NULL, 0, 0, rect.Width(), rect.Height(),
SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW);
}
可以看到,CMainWindow
类的构造函数首先使用AfxRegisterWndClass
函数注册了WNDCLASS
。如果窗口类是从CFrameWnd
派生的,并且将CFrameWnd::Create
函数的第一个参数指定为NULL
,MFC会完成WNDCLASS
的注册。然而从CWnd
派生窗口类时,需要自己注册WNDCLASS
。AfxRegisterWndClass
函数的原型如下:
LPCTSTR AFXAPI AfxRegisterWndClass(UINT nClassStyle,
HCURSOR hCursor = 0, HBRUSH hbrBackground = 0, HICON hIcon = 0);
第一个参数nClassStyle
指定了类样式,定义了窗口的某种操作特性。宏CS_HREDRAW
和CS_VREDRAW
分别表示窗口被水平与垂直缩放时使整个客户区无效。第二个参数hCursor
指定了该窗口中显示的光标形状。AfxGetApp
函数用于获取当前应用程序对象的指针,其返回类型为CWinApp*
。CWinApp::LoadStandardCursor
函数可以加载系统预定义的光标类型,IDC_ARROW
代表标准箭头光标。第三个参数hbrBackground
定义了窗口的默认背景色。(HBRUSH)(COLOR_WINDOW + 1)
定义了白色背景。hIcon
指定了Windows在桌面上、任务栏和其他地方代表应用程序的图标句柄。CWinApp::LoadStandardIcon
函数用于加载预定义的系统图标。IDI_WINLOGO
表示一个普通的应用程序图标。
注册了WNDCLASS
之后,就可以调用CWnd::CreateEx
函数创建主窗口。第一个参数指定窗口扩展样式,第二个参数是AfxRegisterWndClass
函数返回的WNDCLASS
名称,第三个参数是窗口标题,第四个参数是窗口样式,接下来的四个参数指定了窗口的初始位置和大小,最后两个参数指定父窗口和菜单。
CalcWindowRect
函数根据参数rect
计算窗口的尺寸,rect
代表所希望的窗口客户区的尺寸。SetWindowPos
函数使尺寸设置生效。
从CWnd
派生窗口类时,必须自己删除窗口对象。窗口在被销毁之前将收到最后一个消息WM_NCDESTROY
。CWnd
类包括一个默认的OnNcDestroy
函数,该函数首先执行一些清除任务,接着将调用PostNcDestroy
函数。CFrameWnd
类覆盖了PostNcDestroy
函数并执行一个delete this
语句删除自身。然而,CWnd
类中的PostNcDestroy
函数并不执行delete this
语句,因而从CWnd
派生窗口类时应该提供自己的PostNcDestroy
函数。打开MainWindow.cpp
,加入PostNcDestroy
函数。
void CMainWindow::PostNcDestroy()
{
delete this;
CWnd::PostNcDestroy();
}
然后打开MainWindow.h
,加入该函数的声明。
virtual void PostNcDestroy();