MFC程序的执行流程

一、WinMain()函数在哪儿

因为看不到WinMain()函数,许多初学者不知道程序究竟从哪儿开始,编程从哪儿下手,编写的代码何时运行等等,另外,还有很多看不懂的语句,最大的困难恐怕还是MFC的编程思想不太理解。自己也曾为此而烦恼了几年。不过,饭还得一口一口地吃,最好的办法是先照葫芦画瓢,从容易的入手,再一步一步地深入。

MFC把千篇一律的WinMain()函数写在crtexec.c中,在编译完后链接时才组装到你的exe程序中。运行时,这个WinMain()函数调用MFC的全局函数AfxWinMain(),这个全局函数AfxWinMain()是写在appmodul.cpp中的。在AfxWinMain()函数中做三件事,注册窗口类、调用应用程序类的初始化函数InitInstance()、调用应用程序类的Run()函数(实际上是父类的父类CWinThread的Run()成员函数)。

上面这个Run()函数中包含消息循环,而InitInstance()函数又将框架类、文档类、视图类结合在一起,于是所有的类全部集成起来了。编程的重点在文档类和视图类,什么样的消息,执行什么样的处理函数,这样就有效地将数据和控制分离开来。

二、消息处理

消息处理的回调函数在mfc70d.dll中的CWnd::WindowProc(...),由上面讲的Run()函数调用,然后在主应用程序类、框架类、文档类、视图类里面接受这些消息并处理。问题是:某一消息究竟在哪个类里面处理?

与窗口有关的消息(即以“WM_”开头的消息),只能在框架类和视图类中处理;来自菜单或工具条的命令事件,则框架类、视图类、主应用程序类、文档类全部可以处理,究竟在哪个类中处理,根据具体情况再决定在哪个类中处理。这其中有优先级别高低的问题,一个类接受到命令消息时,先传给比自己级别高的类,如果未处理再自己处理。如果自己也不处理,则传给比自己级别低的类。

这个优先级别是:视图类>文档类>文档模板类>框架类>主应用程序类。例如,框架类收到“编辑”菜单的“Clear All”命令消息时,首先传给视图类,如果视图类处理便结束,否则就传给文档类。如果文档类处理便结束,否则就传给文档模板类。如果文档模板类处理则结束,否则就回到了框架类。如果框架类处理则结束,否则就传给主应用程序类。如果主应用程序类还不处理,就回到MFC缺省的处理程序中。

三、添加、删除消息处理

添加一个消息处理,一般要改动三个地方,以WM_LBUTTONDOWN消息为例,①在视图类的定义(test2View.h)中添加一个成员函数,②在视图类(test2View.c)中添加一条消息映射,③在视图类(test2View.c)中实现消息处理的成员函数。

// test2View.h : Ctest2View 类的接口

......(省略)

// 生成的消息映射函数
protected:
    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
};

......(省略)
// test2View.cpp : Ctest2View 类的实现
//

#include "stdafx.h"
#include "test2.h"

#include "test2Doc.h"
#include "test2View.h"
#include ".\test2view.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// Ctest2View

IMPLEMENT_DYNCREATE(Ctest2View, CView)

BEGIN_MESSAGE_MAP(Ctest2View, CView)
    // 标准打印命令
    ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
    ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
    ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
    ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()

......(省略)

// Ctest2View 消息处理程序

void Ctest2View::OnLButtonDown(UINT nFlags, CPoint point)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    AfxMessageBox("劝学网 http://www.quanxue.cn/", MB_OK);

    CView::OnLButtonDown(nFlags, point);
}

上面虽然很简单,但不建议手动添加,应该在“类视图”中选中“Ctest2View”,再在“属性”工具中选择“WM_LBUTTONDOWN”消息,同时选上“OnLButtonDown”后回车就可以了。

用同样方法在“属性”工具中删除“WM_LBUTTONDOWN”消息时,前面添加的三处代码将被用“//”注释在那儿,成了“垃圾”代码。这时最好手动删除一下这三处“垃圾”代码。

四、程序的入口

学过C++语言或Java语言的人都明白,类相当于自定义的数据类型,如果没有实例,自身是不会运行的。那么,上面一直在解说这4个类,当前生成的5个.cpp文件中,WinMain启动后究竟先运行什么语句,后运行什么语句?

要明白MFC的细节,除了C++的基础之外,最好学一点SDK编程,了解Win32编程中的基本内容。小雅在SDK教程中的第1章到第3章的内容请参考。MFC生成的4个类中,主应用程序类是从CWinApp类继承的,作用相当于WinMain程序;CMainFrame是从CFrameWnd类继承的,其作用相当于WndProc程序。也就是说,消息首先由框架类接受,然后按优先顺序传给其它的类。文档类和视图类由消息来驱动(视图类的PreCreateWindow()除外)。

主应用程序类的程序test2.cpp中,跳过消息映射以及类的构造函数、成员函数后,实际只剩下1条语句,即“Ctest2App theApp;”,这便是整个应用程序的总入口。MFC的crtexec.c的WinMain()函数调用test2.cpp中的“Ctest2App theApp;”。theApp是类的实例,自然也是必然要调用主应用程序类的构造函数,包括父类的构造函数,之后再调用MFC的appmodul.cpp中的AfxWinMain()函数,由AfxWinMain()函数调用主应用程序类的InitInstance()成员函数。

在主应用程序类的InitInstance()成员函数中,定义了框架类、文档类、视图类的实例,从而间接地调用了框架类的PreCreateWindow()和OnCreate()成员函数,由OnCreate()函数再调用视图类的PreCreateWindow()成员函数。然后再回到主应用程序类的InitInstance()成员函数中,执行“m_pMainWnd->ShowWindow(SW_SHOW);”和“m_pMainWnd->UpdateWindow();”。


五,程生成后文件的作用

生成的文件的作用可以在ReadMe.txt便可知道。
================================================================================
    MICROSOFT 基础类库: Test 项目概述

应用程序向导已为您创建了此 Test 应用程序。此应用程序不仅介绍了使用 Microsoft 基
础类的基本知识,而且是编写应用程序的起点。

此文件包含组成 Test 应用程序的每个文件的内容摘要。

Test.vcproj
    这是使用“应用程序向导”生成的 VC++ 项目的主项目文件。 
    它包含有关生成文件的 Visual C++ 版本的信息,以及有关用“应用程序向导”所选择
    的平台、配置和项目功能的信息。

Test.h
    这是应用程序的主头文件。  它包含其他项目特定的头文件(包括 Resource.h),并声
    明 CTestApp 应用程序类。

Test.cpp
    这是包含应用程序类 CTestApp 的主应用程序源文件。

Test.rc
    这是程序使用的所有 Microsoft Windows 资源的列表。它包含存储在 RES 子目录中的图
    标、位图和光标。  可直接在 Microsoft Visual C++ 中编辑此文件。 项目资源包含在 
    2052 中。

res\Test.ico
    这是一个图标文件,用作应用程序的图标。  此图标包含在主资源文件 Test.rc 中。

res\Test.rc2
    此文件包含不由 Microsoft Visual C++ 编辑的资源。 应将所有不能由资源编辑器编辑的
    资源放在此文件中。
/////////////////////////////////////////////////////////////////////////////

对于主框架窗口:
    项目将包含标准的 MFC 界面。
MainFrm.h, MainFrm.cpp
    这些文件包含框架类 CMainFrame,该类派生自 CFrameWnd 并控制所有的 SDI 框架功能。
res\Toolbar.bmp
    此位图文件用于创建工具栏的平铺图像。初始工具栏和状态栏在 CMainFrame 类中构造。
    使用资源编辑器编辑此工具栏位图,并更新 Test.rc 中的 IDR_MAINFRAME TOOLBAR 数组
    以添加工具栏按钮。
/////////////////////////////////////////////////////////////////////////////

应用程序向导将创建一种文档类型和一个视图:

TestDoc.h、TestDoc.cpp - 文档
    这些文件包含 CTestDoc 类。  编辑这些文件以添加特殊的文档数据并实现文件的保存和
    加载(通过 CTestDoc::Serialize)。
TestView.h、TestView.cpp - 文档视图
    这些文件包含 CTestView 类。
    CTestView 对象用于查看 CTestDoc 对象。
/////////////////////////////////////////////////////////////////////////////

其他功能:

ActiveX 控件
    应用程序支持使用 ActiveX 控件。

打印支持和打印预览支持
    应用程序向导已生成了一些代码,通过从 MFC 库调用 CView 类中的成员函数来处理打印、
    打印设置和打印预览命令。
/////////////////////////////////////////////////////////////////////////////

其他标准文件:

StdAfx.h、StdAfx.cpp
    这些文件用于生成名为 Test.pch 的预编译头文件(PCH)和名为 StdAfx.obj 的预编译类型
    文件。

Resource.h
    这是标准头文件,它定义新资源 ID。
    Microsoft Visual C++ 将读取并更新此文件。

/////////////////////////////////////////////////////////////////////////////

其他说明:

应用程序向导使用“TODO:” 来指示应添加或自定义的源代码部分。

如果应用程序在共享 DLL 中使用 MFC,且应用程序使用的语言不是操作系统的当前语言,则需
要从 Microsoft Visual C++ 光盘上 Win\System 目录下将相应的本地化资源 MFC70XXX.DLL 
复制到计算机的 system 或 system32 目录下,并将其重命名为 MFCLOC.DLL。 (“XXX”代表
语言缩写。  例如,MFC70DEU.DLL 包含翻译成德语的资源。)  如果不这样做,应用程序的某
些 UI 元素将保留为操作系统的语言。

/////////////////////////////////////////////////////////////////////////////


公共对话框MessageBox

前面章节都是让人脑细胞高度紧张,这一章放松一下。对于公共对话框MessageBox大家并不陌生,在MFC的CWnd类中封装了一个MessageBox()成员函数,但MSDN并不推荐使用,一般用AfxMessageBox()全局函数,任何地方都可使用。
int AfxMessageBox(           int AFXAPI AfxMessageBox(
    LPCTSTR lpszText,               UINT nIDPrompt,             //要表示的字符串
    UINT    nType = MB_OK,          UINT nType = MB_OK,         //画面形状(按钮、模式、图标、缺省值4个值组合使用)
    UINT    nIDHelp = 0             UINT nIDHelp = (UINT) -1    //帮助ID,缺省为0
);                             );
返回值 说明
0 不能正常表示
IDYES 被按下
IDNO 被按下
IDOK 被按下
IDCANCEL 被按下或【ESC】被按下
IDABORT 被按下
IDIGNORE 被按下
IDRETRY 被按下
显示按钮 说明
ID_ABORTRETRYIGNORE  
ID_OK
ID_OKCANCEL  
ID_RETRYCANCEL  
ID_YESNO  
ID_YESNOCANCEL  
模式 说明
ID_APPMODAL 模态(缺省值)即不关闭就不能回到父窗口,但可以切换到其它应用程序。
ID_SYSTEMMODAL 系统模态即不关闭就不能回到任何窗口。
ID_TASKMODAL 特殊用途而备用。
显示图标 说明
ID_ICONEXCLAMATION 感叹符号
ID_ICONINFORMATION 情报符号
ID_ICONquestion 问号
ID_ICONSTOP 停止符号
缺省按钮 说明
ID_DEFBUTTON1 第一个探针为缺省按钮。
ID_DEFBUTTON2 第二个探针为缺省按钮。
ID_DEFBUTTON3 第三个探针为缺省按钮。


二、MFC特有的规则
类型:MFC将C++的关键字用宏定义成以下内容。
MFC类型 意思 C++类型
BOOL,BOOLEAN 布尔型 boolean
BYTE 1字节数值型 char
WORD 2字节数值型 short
DWORD 4字节数值型 int
UINT 无符号整型 unsigned int
VOID void型 void
LPDWORD DWORD的指针型 int*
LPCSTR 常量字符串 const char*
LPSTR 字符串 char*
LPCVOID 常量void指针 const void*
LPVOID void指针 void*
常量:MFC将C++的常量用宏定义成以下内容。
MFC常量 意思 C++常量
NULL 空指针 0
TRUE 布尔值:真 true
FALSE 布尔值:假 false
命名规则:MFC有以下命名规则。
类名:以大写的C开头,单词的首字母大写,单词之间不用下划线。
成员函数和全局函数:以单词的首字母大写,单词之间不用下划线。全局函数前再加Afx
成员变量:匈牙利命名法,以“m_”开头,单词的首字母大写。
宏、类型名:全部使用大写。
三、用TRACE宏调试
可以在程序的任何位置插入TRACE()宏函数来调试程序,TRACE()宏函数只在DEBUG状态有效,即按<F5>运行时有效,<Ctrl + F5>时无效。另外,断点也可以设置条件。下面例子是一个没有消息循环的MFC程序(控制台程序)。断点的条件是“i>5”。
// test5.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "test5.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 唯一的应用程序对象

CWinApp theApp;
using namespace std;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    int nRetCode = 0;

    // 初始化 MFC 并在失败时显示错误
    if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
    {
        // TODO: 更改错误代码以符合您的需要
        _tprintf(_T("致命错误: MFC 初始化失败\n"));
        nRetCode = 1;
    }
    else
    {
        // TODO: 在此处为应用程序的行为编写代码。
        for (int i=0; i<10; i++) {
            TRACE("i = [%d]\n", i);

            //按钮:[是(Y)][否(N)][取消],图标为:[i],
            //模式:不能切换到其它窗口,缺省:[否(N)]
            AfxMessageBox("这是小雅的MFC教程。" , 
            MB_YESNOCANCEL|MB_SYSTEMMODAL|MB_ICONINFORMATION|MB_DEFBUTTON2);

            printf("劝学网:测试AfxMessageBox功能。\n");
        }
    }


    return nRetCode;
}

你可能感兴趣的:(框架,Microsoft,command,文档,mfc,工具)