上一篇《一个Demo初识MFC》用一个例程带入对MFC的初探,本篇紧接着上篇,首先对代码进行讲解,然后继续向前进。
《一个Demo初识MFC》代码解析
概念一览:
MFC提供数百个类,最重要的、也是编写任何VC++应用程序都必不可少的两个类 CWinApp 和 CFrameWnd ,这两个类是编写复杂庞大应用程序的基石。
- 封装特性:构成MFC框架的是MFC类库而MFC类库又是C++的一个类库。这些类封装WIN32应用程序编程接口,OLE(Object
Link Embed 对象链接嵌入)特性,ODBC和DAO数据访问的功能。 - 继承特性:MFC抽象出了众多类的共同特性,并设计出一些基类作为实现其他类的基础,这些类中最重要的类是CObject类和CCmdTarget类(如图),程序员可以从适当的MFC类中派生出自己的类,实现特定的功能达到编程的目的。
- 虚拟和消息映射:MFC是以C++为基础,当然支持虚函数,但作为一个编程框架必须要解决的是效率问题:如果MFC仅仅通过虚函数来支持动态约束必然会产生大量的虚函数表这样编程框架过于臃肿而且消耗更多的内存。但是MFC建立了消息映射机制这样降低了内存的使用却大大提高了效率。消息映射是一个将消息和成员函数相互关联的表,当应用程序的框架窗口接收到一个消息时,MFC将搜索该窗口的消息映射,如果存在一个处理消息的处理程序,那么就调用该处理程序.它通过宏来实现消息到成员函数的映射,而且这些函数不必是虚拟的成员函数,这样不需要为消息映射函数生成一个很大的虚拟函数表(V表),节省内存。
- MFC消息映射机制
将消息与消息处理函数联系起来,形成一一对应的机制。
消息映射宏
声明: DECLARE_MESSAGE_MAP
定义:
BEGIN_MESSAGE_MAP
ON_COMMANDON_CONTROL
ON_MESSAGE
END_MESSAGE_MAP
- MFC主要组成部分:类、宏和全局函数。
类是MFC中最主要的内容。MFC类是以层次结构方式组织起来的。MFC中的类分成两部分,除了一些辅助类,大多数的MFC类是直接或间接从根类CObject派生而来。
- MFC宏主要功能:消息映射、运行时对象类型服务、诊断服务、异常处理。
- MFC约定:全局函数以“Afx”为前缀,全局变量以“afx”为前缀
- MFC类的层次关系
CObject项目类)->CCmdTarget(消息响应类)->
{
CWinThread(线程类)->CWinApp(Window应用程序类)
CDocument(文档类)
CWnd(窗体类)->[
CFrameWnd(框架类)
CView(视图类)
]
}
代码解析
- CWinApp是应用程序类,在MFC应用程序中必须从这个类派生出一个类,该派生类是MFC应用程序的入口
必须定义这个派生类的对象,并且只能有一个这个对象代表整个应用程序。
成员函数:InitInstance()
功能:初始化应用程序实例和窗口实例,
虚函数CWinApp::InitInstance必须在派生类中重写。在InitInstance函数中,编写初始化代码,如:
创建一个窗口
显示一个窗口
- CFrameWnd类
作用:为应用程序提供一个窗口,同时实现消息处理功能。
成员函数: Create()
功能:创建窗体,将之赋于CFrameWnd对象上。
BOOL Create(窗口类型, 窗口标题,显示风格,显示区域,符窗口句柄,菜单,扩展显示风格,上下文对象)共有8个参数,前两个必须给出,后6个可以默认。
- MFC应用程序的核心就是基于CWinApp类的应用程序对象,CWinApp提供了消息循环来检索消息并将消息调度给应用程序的窗口.我们在编写MFC应用程序时,要包含afxwin.h,
一个MFC应用程序可以有且仅有一个应用程序对象,对象必须声明为在全局范围内有效(也就是全局对象),以便它在程序开始时即在内存中被实例化
我们的Hello MFC的应用程序类被命名为CMyApp,它在hello.cpp中用如下语句进行了实例化:
CMyApp myApp;
- CMyApp的类声明在hello.h中代码如下:
class CMyApp:public CWinApp
{
public:
virtual BOOL InitInstance();
};
CMyApp没有声明任何的数据成员,只是重写了一个从CWinApp类中继承来的函数,在应用程序的生存期内InitInstance的调用比较早,是在应用程序开始运行以后而窗口创建之前,除非InitIstance创建一个窗口,否则应用程序是不会有窗口,这正是为什么即使最小的MFC应用程序也必须从CWinApp派生出一个类并重写CWinApp::InitIstance的原因
InitInstance函数:CWinApp::InitInstance是一个虚函数,其默认操作仅包含一条语句:return TRUE;
InitInstance是用来执行程序每次开始时都需要进行的初始化工作最好的地方。
- 在hello.cpp中,CMyApp的InitInstance通过实例化hello的CMainWindow类来创建hello窗口,语句:
m_pMainWnd = new CMainWindow;
构造了一个CMainWindow对象指针,并将其地址复制到了应用程序对象的m_pMainWnd数据成员中,窗口创建以后,InitInstance就会通过CMainWindow指针调用ShowWindow和UpdateWindow函数显示它:
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
ShowWindow和UpdateWindow是所有窗口对象共用的CWnd成员函数其中包括CFrameWnd类的对象,CMainWindow就是从CFrameWnd派生出来的.
- 在CMyApp::InitInstance中,hello创建了一个CMainWindow对象,CMainWindow的构造函数生成在屏幕上看到的窗口:
Create(NULL,”我的第一个MFC应用程序”);
CPaintDC dc(this);
- MFC的CPaintDC类是从MFC的CDC类派生的,CDC类封装了Windows设备环境,以及包含了绘制到屏幕、打印机和其他设备的几十个成员函数。(这有关于
“设备上下文的内容”会在以后的篇章中写道)
从API编程到MFC编程的过渡
接下来给出一通过Window API 过程实现的代码例程
建立Win32控制平台代码,设定->项目属性
源代码
// TestWinAPI.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include<windows.h> /* 声明窗口过程 */ LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); /* 窗口类名 */ TCHAR szClassName[]=TEXT("WindowsApp"); /* 入口函数 */ int WINAPI WinMain ( HINSTANCE hInstance, // 实例句柄 HINSTANCE hPrevInstance, // 先前的实例, Win32下始终为NULL LPSTR lpCmdLine, // 命令行参数 int nCmdShow // 窗口显示命令参数 ) { HWND hwnd; /* 窗口句柄 */ MSG messages; /* 消息结构变量 */ WNDCLASS wincl; /* 窗口类结构变量 */ /* 填充窗口类结构变量 */ wincl.hInstance = hInstance; wincl.lpszClassName = szClassName; wincl.lpfnWndProc = WindowProcedure; /* 设置窗口过程 */ wincl.style = CS_DBLCLKS; /* 窗口类样式 */ /* 设置默认图标光标 */ wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION); wincl.hCursor = LoadCursor (NULL, IDC_ARROW); wincl.lpszMenuName = NULL; /* 菜单 */ wincl.cbClsExtra = 0; /* 窗口类额外数据字节数 */ wincl.cbWndExtra = 0; /* 窗口额外数据字节数 */ /* 窗口默认背景 */ wincl.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); /* 注册窗口类 */ if (!RegisterClass(&wincl)) return 0; /* 创建窗口 */ hwnd = CreateWindow ( szClassName, /* 类名 */ TEXT("我的Windows API应用程序"),/* 标题文本 */ WS_OVERLAPPEDWINDOW, /* 窗口样式 */ CW_USEDEFAULT, /* X坐标 */ CW_USEDEFAULT, /* Y坐标 */ CW_USEDEFAULT, /* 宽度px */ CW_USEDEFAULT, /* 高度px */ HWND_DESKTOP, /* 父窗口句柄 */ NULL, /* 菜单 */ hInstance, /* 程序实例句柄 */ NULL /* 窗口创建参数 */ ); /* 按照参数显示窗口 */ ShowWindow (hwnd, nCmdShow); /* 进入消息循环,直到GetMessage()返回0. 即WM_QUIT消息发出 */ while (GetMessage(&messages, NULL,0,0)) { /* 翻译虚拟键消息成字符消息 */ TranslateMessage(&messages); /* 发送消息给窗口过程 */ DispatchMessage(&messages); } return (int)messages.wParam; } /* 窗口过程实现 */ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_PAINT: { PAINTSTRUCT ps; RECT rect; HDC hDC = BeginPaint(hwnd,&ps); //TCHAR sz[]=TEXT("我的Windows API应用程序"); //TextOut(hDC,10,10,sz,lstrlen(sz)); GetClientRect (hwnd, &rect) ; DrawText(hDC,TEXT("Hello Windows API"),-1, &rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER); EndPaint(hwnd,&ps); } break; case WM_DESTROY: PostQuitMessage(0); /* 向消息队列发出WM_QUIT消息 */ break; default: /* 不感兴趣的消息交给DefWindowProc()处理 */ return DefWindowProc(hwnd, message, wParam, lParam); } return 0; }
编译运行结果:
WIindows API和MFC的比较见下一篇——《论烦琐与简单:WIindows
API和MFC》