• MFC框架程序解析


    写在前面

    MFC(Microsoft Foundation Class,微软基础类库)是微软为了简化程序员的开发工作所开发的一套C++类的集合,是一套面向对象的函数库,以类的方式提供给用户使用。利用这些类,可以有效地帮助程序员完成Windows应用程序的开发。

    即MFC是一个封装库, 该库封装了 Windows程序运行机制 一文中手动设计、注册、创建、显示刷新、进入消息循环等代码实现,使得开发人员开发Windows程序更加方便简洁。

    MFC框架解析

    这里使用VS2019新创建一个单文档MFC程序, 如下:
    1
    2
    这里创建了一个基于 单个文档 类型, 样式是MFC Standard 的MFC程序, 创建后可以看到VS2019自动创建了以下5个类:
    3
    在MFC中,类的命名都以字母“C”开头, 对于一个单文档应用程序(即我们在创建项目时第二步应用程序类型中选择的“单个文档”),都有一个CMainFrame类、一个以**“C+项目名+App”为名字的类(例CSingleDocMFCApp)、一个以“C+项目名+Doc”为名字的类(例CSingleDocMFCDoc)和一个以“C+项目名+View”**为名字的类(例CSingleDocMFCView)。

    这里可以直接编译运行生成一个窗口:
    4
    和之前的Windows程序相比,这里新建项目后并没有手动的进行一般Windows程序的开发步骤,即设计、注册、创建、显示刷新、消息循环等步骤开发,但运行后依旧创建显示了一个窗口,因为创建项目后自动生成了几个类, 因此这里就会认为是MFC这个库在创建项目后自动给我们完成了设计、注册、创建、显示刷新、消息循环等步骤。

    但通过全局搜索几个自动创建的类发现,并没有Windows程序的入口函数,即WinMain。

    我们之所以看不见这些,是因为微软在MFC的底层框架类中封装了这些每一个窗口应用程序都需要的步骤,目的主要是为了简化程序员的开发工作,但这也给我们在学习和掌握MFC程序时造成了很多不必要的困扰。

    入口点函数WinMain

    这里可以在VS安装目录下的MFC的源码文件appmodul.cpp中找到WinMain, 例我安装在D盘,右键用vs2019打开:
    5
    可以看到WinMain函数,重新编译运行,可以看到会运行到断点的WinMain函数中:
    6
    这里的问题是,如何通过MFC框架自动创建的代码进入到WinMain的?
    其实每个MFC程序,都会有一个唯一的全局的应用程序对象,如下:
    7
    初始化该对象时,首先递归调用CSingleDocMFCApp的父类的构造函数,而CSingleDocMFCApp继承自CWinApp,因此这里我们断点到CWinApp构造函数中,发现确实如此:
    8
    m_pCurrentWinThread对象的类型是CWinThread,该类是CWinApp的父类。根据C++继承机制,可以确定这个this对象代表的是子类CTestApp的对象,即theApp。这会在后面的WinMain中引用。

    当程序调用了CWinApp类的构造函数,并执行了CTestApp类的构造函数,且产生了theApp 对象之后,接下来就进入 WinMain 函数。

    回到之前的WinMain函数中,发现里面只调用了一个AfxWinMain, 在MFC程序中, 以Afx开头的都是全局函数,该函数在winmain.cpp中实现:
    9
    断点①:调用pApp->InitApplilcation(), 这里的pApp指向的就是唯一的全局对象(在上面的CWinApp构造中断点部分指定),该函数完成MFC内部管理方面的工作。

    断点②:调用pThread->InitInstance(), 这里的pThread同理也在上面CWinApp中的断点部分指定,因此会调用CSingleDocMFCApp::InitApplication(),在该函数中会构建MFC核心的Doc/View结构:
    10
    其中会CMainFrame的构造中完成窗口的设计、注册、创建、显示刷新,这里会在其创建之前调用CMainFrame::PreCreateWindow, 在该函数内又会调用其父类的版本CFrameWnd::PreCreateWindow, 会在该接口中完成窗口的注册。

    因篇幅有限不再深入讲解,可参考上述步骤找到对应代码断点验证。

    断点③:pThread->Run(), 即该应用程序(theApp)的消息循环。

    至此,可以看到在没有手动添加代码的情况下,使用MFC向导生成的代码,便可代替此前 Windows程序运行机制 一文中的设计、注册、创建、显示刷新、进入消息循环等步骤。

    总结

    最后,在总结下MFC框架创建窗口的整体过程:
    ■ 首先利用全局应用程序对象theApp 启动应用程序。正是产生了这个全局对象,基类CWinApp中的this指针才能指向这个对象。如果
    没有这个全局对象,则程序在编译时不会出错,但在运行时就会出错。
    ■ 调用全局应用程序对象的构造函数,从而就会先调用其基类CWinApp的构造函数。后者完成应用程序的一些初始化工作,并将应用
    程序对象的指针保存起来。
    ■ 进入WinMain函数。在AfxWinMain函数中可以获取子类(即唯一的全局对象theApp)的指针,利用此指针调用虚函数:InitInstance,根据多态性原理,实际上调用的是子类(CSingleDocMFCApp)的 InitInstance 函数。后者完成应用程序的一些初始化工作,包括窗口类的注册、创建,窗口的显示和更新。
    ■ 进入消息循环。虽然也设置了默认的窗口过程函数,但是,MFC 应用程序实际上是采用消息映射机制来处理各种消息的。当收到
    WM_QUIT 消息时,退出消息循环,程序结束。

    可以看到窗口的创建虽然都由MFC框架处理,但其实现原理依旧和Windows程序一样,即设计、注册、创建、显示刷新、进入消息循环这一步骤。

    优点就是前面提到的简化程序员的开发工作,但可以明显感觉到MFC框架对Windows系统的依赖性,这就限制了其对跨平台的支持。

  • 相关阅读:
    java 找不到或无法加载主类
    原型和原型对象
    l8-d7 实现TCP通信
    java毕业设计园林公司OA系统Mybatis+系统+数据库+调试部署
    python绘制piper三线图
    技术分享 | 常见接口协议解析
    如何在 Windows 10 上安装 Ubuntu 操作系统?
    如此简单易懂的方式 让网站支持PWA
    莫队 从零基础到入门 超详细
    Pulsar3.0 升级指北
  • 原文地址:https://blog.csdn.net/SNAKEpc12138/article/details/126859149