头文件
/*
MFC是一个封装了WindowsAPI且面向对象的C++类库,同时也是一个应用程序的框架结构。另外,并非所有MFC提供的函数都是类的成员,MFC也以
全局函数的形式,为用户提供了各类前缀为Afx的函数,可供用户在必要时随时随地进行调用。
由MFC创建的应用,我们不能够直接看到其程序的入口。入口函数AfxWinMain()被MFC深深的封装在其框架之中,最后直接由编译器编入应用程序。
因此,我们必须站在面向对象这个巨人的肩膀(高度)上,来分析、审视由MFC创建的Windows应用。
由MFC创建的应用其宏观执行的大致流程为:
1、在进入程序入口函数AfxWinMain()前,首先在全局范围内创建一个应用程序对象,即由CWinApp或其子类创建的对象。
2、在入口函数中调用应用程序对象的InitInstance()成员函数。
3、由InitInstance()负责创建窗口、显示窗口并向窗口发送第一个WM_PAINT消息。
4、最后,由入口函数调用应用程序对象的成员函数Run()启动消息循环,然后程序在消息循环中不断收取并响应(处理)属于自己的消息。
Windows的C++程序员习惯把类的定义放在.h文件中,把类的实现或程序的其它源代码放在.cpp文件中。我们这里也遵从这个习惯。
*/
#pragma once //在头文件开始处加入这条杂注,可保证头文件只被编译一次。
//这里的“afxwin.h”是用MFC创建窗口应用需要包含的头文件。
#include <afxwin.h>
//下面我们来讨论应用程序对象与框架(顶层)窗口对象的类定义。
/*
这里的CMyApp是从CWinApp继承而来(CWinApp->CWinThread->CCmdTarget->CObject,大多数MFC的类都直接或间接派生于CObject),其实例代表
上述的应用程序对象。
我们只需要覆盖CWinApp中的InitInstance()成员函数即可。上以述及,这个函数完成了应用窗口的创建、显示并向窗口发送WM_PAINT消息的功能。
*/
class CMyApp : public CWinApp {
public:
virtual BOOL InitInstance();
};
//这里的CMainWindow是从CFrameWnd继承而来(CFrameWnd->CWnd->CCmdTarget->CObject),其实例代表应用程序框架(顶层)窗口对象。
class CMainWindow : public CFrameWnd {
public:
CMainWindow(); //这是CMainWindow的构造函数。
protected:
afx_msg void OnPaint(); //这是响应WM_PAINT消息的处理程序(我们用afx_msg来标识消息处理程序)。
DECLARE_MESSAGE_MAP() //这是定义消息映射的宏,只要应用中需要进行消息映射,就必须有这个宏定义。
};
源文件
/*
这里是应用的原程序文件Hello.cpp。
下面我们来讨论应用程序对象与框架(顶层)窗口对象的类的实现代码(源程序)。
首先将应用的头文件"Hello.h"包含进来。然后创建一个全局范围的应用程序对象myApp。
*/
#include "Hello.h"
CMyApp myApp;
//这是CMyApp的InitInstance()成员函数的实现代码,其中的m_pMainWnd与m_nCmdShow为从其父类继承过来的两个数据成员。
BOOL CMyApp::InitInstance() {
/*
下面的程序首先在堆上创建一个框架窗口对象,并将指针赋予成员变量m_pMainWnd。
然后按照m_nCmdShow方式显示这个窗口,最后调用UpdateWindow()成员函数向窗口发送一个WM_PAINT消息。这和用WindowsAPI创建窗口时的
情形基本相同。
值得注意的是,程序中从未见到用delete m_pMainWnd来显式删除这个动态创建的框架窗口对象的语句。实际上,窗口在被清除之前接收的
最后一个消息是WM_NCDESTROY,在这个消息处理程序CWind::OnNcDestroy()中,调用了一个虚函数CWnd::PostNcDestroy(),而CFrameWnd正
好覆盖了这个虚函数并在其中执行了delete this语句。显然,这条语句的作用正是窗口在自己的程序中最终删除了自己(堆上创建的窗口)。
*/
m_pMainWnd = new CMainWindow;
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
//这是CMainWindow的构造函数的实现代码,其中覆盖了父类的虚函数Create()来实际创建具体的窗口。
CMainWindow::CMainWindow() {
//为简单起见,这里仅指定了函数前两个形参的值,其它参数(还有6个)全部采用缺省值。
Create(
nullptr, //第一个参数指定了窗口的WNDCLASS名称。这里使用空指针表示使用主结构注册的WNDCLASS默认值。
/*
下面这个参数是窗口的标题(在标题栏中显示的文本)。这里的_T是一个宏,用来把字符串常量设置为中性(即如果在预处理器中定义
了_UNICODE,编译器将字符看作UNICODE字符,否则将字符看作ANSI字符)。
*/
_T("The MFC Application")
);
}
//下面就是WM_PAINT消息处理程序的实现代码。
void CMainWindow::OnPaint() {
/*
首先构造一个CPaintDC对象实例。CPaintDC类是从MFC的更一般的CDC(CPaintDC->CDC->CObject)类派生而来。
MFC在CDC类中封装了Windows设备环境,其中包含绘制到屏幕、打印机和其它设备的成员函数。
CPaintDC分别在其构造函数和析构函数中调用WindowsAPI函数BeginPaint()和EndPaint()来获取和释放Windows设备句柄。
*/
CPaintDC dc(this);
CRect rt;
GetClientRect(&rt); //然后调用父类的GetClientRect()获得窗口客户区范围
/*
最后调用设备环境句柄的DrawText()成员函数在屏幕上输出字符串。这里的DrawText()成员函数是WindowsAPI的DrawText()函数在CPaintDC类
中直接包装的典型例子。成员函数的参数与WindowsAPI中的雷同,在此不再赘述。
*/
dc.DrawText(
_T("Hello, MFC"), //被输出的字符串。
-1, //被输出字符串的字符个数或-1(如果是-1,则被输出的字符串将以空字符结尾)。
&rt, //表示窗口客户区范围的CRect指针。
/*
下面的参数表示绘制选项的“|”运算组合。
这里的DT_SINGLELINE | DT_CENTER | DT_VCENTER三项组合,表示将单行文本水平且垂直居中显示。
*/
DT_SINGLELINE | DT_CENTER | DT_VCENTER
);
}
/*
最后来看实现消息映射的宏。
在BEGIN_MESSAGE_MAP()与END_MESSAGE_MAP()之间的每一条宏定义都代表一个消息映射。
*/
BEGIN_MESSAGE_MAP(CMainWindow, CFrameWnd)
ON_WM_PAINT() //这个宏实现了WM_PAINT消息与处理程序OnPaint()的映射。
END_MESSAGE_MAP()
/*
下面是在程序编译前的两个编译选项的设置,先在这里熟悉一下。
1、使用静态库还是动态链接库的设置:
在项目上,右键->属性->配置属性->常规—>项目默认值—>MFC的使用—>选择“在静态库中使用MFC”就好了,这是调用静态库的时候。
如果调用DLL,就要选择“在共享DLL中使用MFC”。
2、入口点的定义:
在项目上,右键->属性->链接器->系统->子系统,下拉框选择:窗口 (/SUBSYSTEM:WINDOWS)。
或者:在项目上,右键->属性->链接器->高级->入口点,设置成:WinMainCRTStartup。
我们现在就来启动VS2017的IDE,来编译和运行我们的窗口应用。
*/