• 【笔记】《深入浅出MFC》第6章 MFC程序的生死因果


    一、头文件说明

    STDAFX.H

    这个文件用来作为Precompile header file,其内只是载入其他的MFC头文件。应用程序通常会准备自己的头STDAFX.H。

    AFXWIN.H

    每一个Windows MFC程序都必须载入它,因为它以及它所载入的文件声明了所有的MFC类。

    在WINDEF.H中有CALLBACK的定义

    #define CALLBACK _stdcall     //是一种函数调用习惯

    在AFXWIN.H中有afx_msg的定义

    #define afx_msg        //故意安排一个空位置,也许以后版本会用到。

    所有MFC头文件均置于MSVCMFCINCLUDE中。这些文件在编译过程中耗费大量时间,所以有必要设定Precompiled header。一个应用程序在常需要不断地编译。Windows程序载入的.H文件非常巨大但内容不变,编译器浪费在上面的时间非常多。Precompiled header就是将.H文件第一次编译后的结果存储起来,第二次再编译时就可以直接从磁盘中取出来用。

    二、MFC程序的来龙去脉

    CWinApp代表程序本体。CFrameWnd代表一个主框架窗口。必须以这两个类为基础,派生自己的类,并改写其中一部分成员函数。

    全局对象theAPP,就是application object。每一个应用程序都应该改写CWinApp::InitInstance()函数。

    MFC把有着相当固定行为的WinMain内部操作封装在CWinApp中,把有着相当固定行为的WinProc内部操作封装在CFrameWnd中。

    传统SDK程序WinMain完成的工作,现在由CWinApp的三个函数完成。

    Virtual BOOL InitApplication();

    Virtual BOOL InitInstance();

    Virtual int Run();

    CWinApp继承CWinThread了成员变量m_pMainWnd,代表主窗口。CFrameWnd主要用来掌握一个窗口,它是用来取代SDK程序中的窗口函数的地位。

    我们并未写WinMain程序代码,这是MFC早已准备好并由链接器直接加到应用程序代码中的。_tWinMain函数的_t是为了支持UniCode而准备的一个宏。

    下面是AfxWinMain代码。

    将以上代码整理一下就得到下面这段代码。

    AfxGetApp 是一个全局函数,它取得CMyWinApp 对象指针。AfxWinInit 是继CWinApp 构造式之后的第一个动作。AfxWinInit 之后的动作是pApp->InitApplication。

    以上代码这些动作都是MFC 为了内部管理而做的。继InitApplication 之后, AfxWinMain 调用pApp->InitInstance。

    一般而言,CMyWinApp只改写CWinApp中的InitInstance,通常它不改写InitApplication和Run。

    注意:应用程序一定要改写虚拟函数InitInstance,因为它在CWinApp 中只是个空函数,没有任何内建(预设)动作。

    CMyWinApp::InitInstance 一开始new 了一个CMyFrameWnd 对象,准备用作主框窗口的C++ 对象。CFrameWnd::Create 在产生窗口之前,会先引发窗口类别的注册动作。

    下面是CreateEx代码。

    调用的PreCreateWindow 是虚拟函数, CWnd 和CFrameWnd 之中都有定义。由于this 指针所指对象的缘故,这里应该调用的是CFrameWnd::PreCreateWindow。

    利用AfxDeferRegisterClass宏注册窗口类。不同类别的PreCreateWindow 成员函数都是在窗口产生之前一刻被调用,准备用来注册窗口类别。

    CMyFrameWnd::CMyFrameWnd 结束后, 窗口已经诞生出来;程序流程又回到CMyWinApp::InitInstance ,于是调用ShowWindow 函数令窗口显示出来,并调用UpdateWindow 函数令Hello 程序送出WM_PAINT 消息。

    窗口类别注册好了,窗口诞生并显示出来了, UpdateWindow 被调用,使得消息队列中出现了一个WM_PAINT 消息,等待被处理。现在,执行pApp->Run。

    Message Map 机制是为了提供更方便的程序接口(例如宏或表格),让程序员很方便就可以建立起消息与处理例程的对应关系。

    MFC 提供给应用程序使用的「很方便的接口」是两组宏。以Hello 的主窗口为例,第一个动作是在HELLO.H 的CMyFrameWnd 加上DECLARE_MESSAGE_MAP:

    第二个动作是在HELLO.CPP 的任何位置(当然不能在函数之内)使用宏如下:

    MFC 把消息主要分为三大类, Message Map 机制中对于消息与函数间的对映关系也明定以下三种:

    1、标准Windows 消息(WM_xxx)的对映规则:

    2、命令消息( WM_COMMAND)的一般性对映规则是:ON_COMMAND(<id>,<memberFxn>)

    例如:

    ON_COMMAND(IDM_ABOUT, OnAbout)

    ON_COMMAND(IDM_FILENEW, OnFileNew)

    ON_COMMAND(IDM_FILEOPEN, OnFileOpen)

    ON_COMMAND(IDM_FILESAVE, OnFileSave)

    3、Notification 消息(由控制组件产生,例如BN_xxx)的对映机制的宏分为好几种(因为控制组件本就分为好几种),以下各举一例做代表:

    各个消息处理函数均应以afx_msg void 为函数类型。如果某个消息在Message Map 中找不到对映记录,它会往基础类别流窜,这个消息流窜动作称为Message Routing。如果一直窜到最基础的类别仍找不到对映的处理例程,由默认函数来处理。

    【总结】

    凡是由你设计而却由Windows 系统调用的函数,统称为callback 函数。这些函数都有一定的类型,以配合Windows的调用动作。

    callback 函数是给Windows 调用的, Windows 并不经由任何对象调用这个函数,也就没有传递this 指针给callback 函数,于是导至堆栈中有一个随机变量会成为this 指针,而其结果当然是程序的崩溃了。

    要把某个函数用作callback 函数,两个方法可以做到这一点:

    (1)不要使用类的成员函数(也就是说,要使用全局函数)做为callback 函数。

    (2)使用static 成员函数。也就是在函数前面加上static 修饰词。

  • 相关阅读:
    jsp第四次
    jsp第二次作业
    jsp第一次作业
    软件测试课堂练习
    11.11日
    10.28
    10.25
    10.21
    jsp第七周作业
    JSP第六周作业
  • 原文地址:https://www.cnblogs.com/mengwang024/p/4927378.html
Copyright © 2020-2023  润新知