【MFC】05.MFC六大机制:程序启动机制-笔记

MFC程序开发所谓是非常简单,但是对于我们逆向人员来说,如果想要逆向MFC程序,那么我们就必须了解它背后的机制,这样我们才能够清晰地逆向出MFC程序,今天这篇文章就来带领大家了解MFC的第一大机制:程序启动机制:

首先,我们创建一个单文档架构程序,我们来观察一下:

这里我创建的项目名称为:MFCApplication1

我们发现一共有三个类:(这里有一些是MFC自动为我们写好的类,类名称通常为项目名称,继承了MFC库中的一些类,我们只看MFC库中的类)我们发现大致三个类:

CFrame类:这个类是框架窗口类,封装了框架窗口的操作

CWinApp类:这个类是应用程序类,封装了流程的操作

CDocument类:这个类是文档类,封装了数据的处理,例如存储,转换等

CView类:这个类是视图类,封装了视图窗口的操作

我们主要讲解的是CFrame类和CWinApp类

这里我创建好的项目,我们先来看看部分源代码:

CMFCApplication1.h:


// MFCApplication1.h: MFCApplication1 应用程序的主头文件
//
#pragma once#ifndef __AFXWIN_H__#error "include 'pch.h' before including this file for PCH"
#endif#include "resource.h"       // 主符号// CMFCApplication1App:
// 有关此类的实现,请参阅 MFCApplication1.cpp
//class CMFCApplication1App : public CWinApp
{
public:CMFCApplication1App() noexcept;// 重写
public:virtual BOOL InitInstance();virtual int ExitInstance();// 实现afx_msg void OnAppAbout();DECLARE_MESSAGE_MAP()
};extern CMFCApplication1App theApp;

MianFrm.h:


// MainFrm.h: CMainFrame 类的接口
//#pragma onceclass CMainFrame : public CFrameWnd
{protected: // 仅从序列化创建CMainFrame() noexcept;DECLARE_DYNCREATE(CMainFrame)// 特性
public:// 操作
public:// 重写
public:virtual BOOL PreCreateWindow(CREATESTRUCT& cs);// 实现
public:virtual ~CMainFrame();
#ifdef _DEBUGvirtual void AssertValid() const;virtual void Dump(CDumpContext& dc) const;
#endifprotected:  // 控件条嵌入成员CToolBar          m_wndToolBar;CStatusBar        m_wndStatusBar;// 生成的消息映射函数
protected:afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);DECLARE_MESSAGE_MAP()};

看完这部分源码之后,我们来自己实现,不用MFC自动帮我们实现的类,来加深我们的理解:

我们创建一个控制台应用,修改项目属性:

  1. 常规:MFC的使用:在静态库中使用MFC
  2. 连接器->系统->子系统:窗口

接下来,我们删除掉main.cpp中的所有代码,我们来自己仿照MFC写一个窗口:

#include <afxwin.h>class CMyFrameWnd:public CFrameWnd{
public:
}class CMyApp:public CWinApp{
public:CMyApp(){};//必须要重写虚函数:virtual BOOL InitInstance(){CMyFrameWnd* pFrame = new CMyFrameWnd;pFrame->Create(NULL,L"FirstMFC");m_pMainWnd = pFrame;pFrame.ShouWindow(SW_SHOW);pFrame->UpdateWindow();return TRUE;}
};CMyWinApp theApp;

这里的CMyFrameWnd由于只是一个框架类,我们不需要做任何操作,只是继承MFC的框架类就可以了,狗仔函数会调用CFrameWnd中写好的构造函数

接下来我们创建一个自己的应用程序类,继承MFC的CWinApp类,这里我们写一个空的构造函数,当构造的时候,就会调用父类的构造函数了

我们在自己的应用程序类中还必须重写虚函数InitInstance,在这个函数中,我们创建窗口,显示窗口

最后,我们还需要一个应用程序类全局变量theApp

当我们写好这些之后,我们运行,发现窗口已经神奇的创建好了:

在这里插入图片描述

我们在创建Win32应用程序的时候,需要做的几个步骤:

  1. 定义窗口类,创建窗口
  2. 注册窗口
  3. 刷新窗口
  4. 显示窗口

但是在我们这样实现了MFC之后,我们貌似少做了很多操作,几行代码就完成了窗口的创建,甚至我们连WinMain函数都没有看见,那么MFC的程序启动机制到底是怎样的呢?我们首先就来深究一下MFC的程序运行机制:

我们知道,在C++中,全局变量的构造是优先于main函数的,那么我们少做的很多操作,肯定就是在这里全局变量的构造函数中了

我们在我们自己的应用程序类的构造函数上下断点,我们跟到CWinApp的构造函数中来看看:

这里我就写伪代码了:

我们应用程序类的父类:CWinApp的构造函数:
CWinApp::CWinApp(LPCTSTR lpszAppName){//首先判断lpszAppName是否为空,做了一些操作,我们没必要关注//AFX_MODULE_STATE这个结构体是MFC中的 程序模块状态信息结构体//这里是获取当前应用程序模块状态信息AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();//这里是获取当前程序线程状态信息,可以看出来,当前程序状态信息是模块状态信息中的一个成员AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;//这里是给线程信息的某个成员赋值,给的是this指针,也就是theApp的地址pThreadState->m_pCurrentWinThread = this;//这里是给theApp的成员赋值,我们自己的应用程序类没有写成员,这里都是CWinApp中写好的,我们只是继承过来了//获取当前应用程序线程句柄m_hThread = ::GetCurrentThread();//获取当前应用程序线程IDm_nThreadID = ::GetCurrentThreadId();//给当前应用程序模块状态信息的某个成员赋值//不难看出这里保存的是当前应用程序类的地址,也就是theApp的地址pModuleState->m_pCurrentWinApp = this;//之后就是一些给类中的其他成员赋初值,很多都是NULL,我们不再关注}

但是跟完构造函数后,我们也没有找到WinMain,更没有找到注册窗口等操作

既然在应用程序类的构造中我们没有找到,我们来看看接下来我们写的pFrame->Create(NULL, L"FirstMFC");这一段代码,但是这一段代码很明显是框架类的方法,我们猜测很多操作就在这里完成

我们在这一段代码上下断点,断下来之后,我们F11跟进,然后查看调用对战,我们就发现了wWinMian和AfxWInMain,由于我们写过win32程序,我们知道底层就是wWinMain函数,我们就跟到这个函数中来看:

这里还是写伪代码

_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,_In_ LPTSTR lpCmdLine, int nCmdShow)
{//我们发现在wWinMain函数中,做了转发,而且根据名称来看,是MFC的wWimMain函数,我们跟进去看看://这里注意,有些人到这里就不会往进去跟了,我们在这行代码上下断点,然后去掉之前的断点,重新调试,就能跟进去了return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow){//这里根据名称来看,是获取当前应用程序线程信息,我们跟进去看看:CWinThread* pThread = AfxGetThread(){//这里跟前面的应用程序类的构造函数中一样,获取当前应用程序的模块状态信息AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();//取出得到的模块状态信息中的成员->当前线程CWinThread* pThread = pState->m_pCurrentWinThread;//返回线程return pThread;}//这里根据名称来看,是获取了当前应用程序的应用程序类的地址,我们还是跟进去看看CWinApp* pApp = AfxGetApp(){//这里afx开头的函数return afxCurrentWinApp{//这里可以肯定,是返回了应用程序类地址return pResult = _afxBaseModuleState.GetData();}}这里这两步跟完之后,大家有没有发现,这里实现了C++的多态?CWinThread,是爷类指针,指向派生类类对象CWinApp是父类的指针,指向了派生类对象,theApp是我们自己实现的应用程序类嘛//下一步:执行了这个函数,在源代码中,是在if的条件中实现的,这里直接拉出来了AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow){//根据名称,这个是初始化的函数pApp->m_hInstance = hInstance;hPrevInstance; // Obsolete.pApp->m_lpCmdLine = lpCmdLine;pApp->m_nCmdShow = nCmdShow;pApp->SetCurrentHandles();}程序启动初始化操作 虚函数我们可以重写,貌似是关于文档的一些初始化操作pApp->InitApplication()//接下来这个函数也是在if条件中进行的,这里直接拉出来pThread->InitInstance(){这是虚函数,我们在我们自己的类中已经实现}}
}

代码跟到这里,我们已经解决了很多前面我们提出来的问题:WinMain函数等但是还有一个问题:消息循环呢?我们知道,如果没有消息循环,那么这个窗口根本运行不了,那既然我们能够关闭窗口等操作,那肯定是已经写好了消息循环,我们来接着上面的代码来跟:

上面的函数紧接着,我们就看到了一行代码:
这就是MFC的消息循环,我们来跟进去看一下:
pThread->Run(){//这里也相当于做了转发,在转发之前有一些判断,这里省略int CWinApp::Run(){//跟到这里之后,我们就发现了消息循环:for (;;)//死循环{//这里的PeekMessage是到消息列表中检查是否有消息while (bIdle &&!::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE)){//这里是说:如果没有消息的话,就会进行空闲操作,比如刷新窗口,检查定时器等等if (!OnIdle(lIdleCount++))bIdle = FALSE;}//死循环里面还有一个do——while循环,我们来看看:do{//这里,如果是WM_QUIT消息,才会返回false,才会进去退出程序if (!PumpMessage(){//在这个PumpMessage中,发现了翻译消息,派发消息if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur))){::TranslateMessage(&(pState->m_msgCur));::DispatchMessage(&(pState->m_msgCur));}})return ExitInstance();if (IsIdleMessage(&(pState->m_msgCur))){bIdle = TRUE;lIdleCount = 0;}} while (::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE));}}
}

至此,我们的消息循环就跟踪完了,这里,再总结一下MFC的程序启动机制:

  • 首先是theApp,也就是我们自己的窗口类(CMyApp)的构造,也就是CWinApp的构造,在构造函数中:
    1. 将theApp的地址,保存到了当前应用程序线程状态信息中
    2. 将theApp的地址,保存到了当前应用程序的模块状态信息中
  • 然后进入了WinMain函数,在WinMain函数中:
    1. 利用全局函数AfxGetThread()获取到了theApp的地址
    2. 利用theApp调用虚函数 InitApplication,进行文档类的一些初始化操作
    3. 利用theApp调用虚函数 InitInstance,也就是我们重写的虚函数,创建窗口
    4. 利用theApp调用虚函数 Run 消息循环
    5. 在消息循环中,我们跟踪到了获取消息,翻译消息,派发消息等
    6. 利用TheApp调用退出函数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/32844.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI:02-基于深度学习的动物图像检索算法的研究

文章目录 一、算法原理二、代码实现三、实验结果四、总结深度学习在计算机视觉领域中的应用越来越广泛,其中动物图像检索算法是一个重要的应用场景。本文将介绍一种基于深度学习的动物图像检索算法,并提供相应的代码实现。 一、算法原理 本算法采用卷积神经网络(Convolutio…

数据安全加固:深入解析滴滴ES安全认证技术方案

前文分别介绍了滴滴自研的ES强一致性多活是如何实现的、以及如何提升ES的性能潜力。由于ES具有强大的搜索和分析功能&#xff0c;同时也因其开源和易于使用而成为黑客攻击的目标。近些年&#xff0c;业界ES数据泄露事件频发, 以下是一些比较严重的数据泄露案件&#xff1a; 202…

Golang函数以及函数和方法的区别

在接触到go之前&#xff0c;我认为函数和方法只是同一个东西的两个名字而已&#xff08;在我熟悉的c/c&#xff0c;python&#xff0c;java中没有明显的区别&#xff09;&#xff0c;但是在golang中者完全是两个不同的东西。官方的解释是&#xff0c;方法是包含了接收者的函数。…

基于Dlib库+SVM+Tensorflow+PyQT5智能面相分析-机器学习算法应用(含全部工程源码)+训练及测试数据集

目录 前言总体设计系统整体结构图系统流程图模型流程 运行环境Python 环境TensorFlow环境界面编程环境 模块实现1. 数据预处理2. 模型构建1&#xff09;定义模型结构2&#xff09;交叉验证模型优化 3. 模型训练及保存4. 模型测试1&#xff09;摄像头调用2&#xff09;模型导入及…

springboot工程集成前端编译包,用于uni-app webView工程,解决其需独立部署带来的麻烦,场景如页面->画布->图片->pdf

前端工程 访问方式 http://127.0.0.1:8080/context/frontEnd/index放行 public class SecurityConfig extends WebSecurityConfigurerAdapter { "/frontEnd/**",SysFrontEndController import lombok.extern.slf4j.Slf4j; import nl.basjes.shaded.org.springfram…

分类预测 | MATLAB实现SMA-CNN-BiLSTM-Attention多输入分类预测

分类预测 | MATLAB实现SMA-CNN-BiLSTM-Attention多输入分类预测 目录 分类预测 | MATLAB实现SMA-CNN-BiLSTM-Attention多输入分类预测分类效果基本介绍模型描述程序设计参考资料 分类效果 基本介绍 1.MATLAB实现SMA-CNN-BiLSTM-Attention多输入分类预测&#xff0c;CNN-BiLSTM结…

解决android studio妙明奇妙出现的模拟器

1&#xff0c;查看设备 adb devices 2&#xff0c; adb命令断开指定的设备 要断开ADB与特定设备的连接&#xff0c;可以使用以下命令&#xff1a; adb disconnect <设备ID> 将 <设备ID> 替换为您要断开连接的设备的实际ID。设备ID可以在运行 adb devices 命令…

redis学习笔记(一)

文章目录 一、引言二、redis介绍2.1、定义2.2、Redis的数据类型及主要特性2.3、Redis的应用场景有哪些&#xff1f; 三、redis环境安装3.1、下载和安装 一、引言 在Web应用发展的初期&#xff0c;那时关系型数据库受到了较为广泛的关注和应用&#xff0c;原因是因为那时候Web站…

Python爬虫:抓取表情包的下载链接

Python爬虫:抓取表情包的下载链接 1. 前言2. 具体实现3. 实现代码 1. 前言 最近发现了一个提供表情包的网址&#xff0c;觉得上面的内容不错&#xff0c;于是就考虑用Python爬虫获取上面表情包的下载链接。整体而言&#xff0c;实现这个挺简单的&#xff0c;就是找到提供表情包…

Python ImportError报错:No module named ‘numpy.core_multiarray_umath‘

文章目录 背景Import 报错是版本问题吗&#xff1f;删除pandas在Visual Studio中设置Python 环境为什么要在Visual Studio IDE下继续安装Python package在Visual Studio安装numpy和pandasPYTHONPATHDebug模式下继续报错配置Release 工程优化不便之处 1不便之处 2后续 参考 背景…

前端渲染数据

在前端对接受后端数据处理后返回的接收值的时候&#xff0c;为了解决数据过于庞大&#xff0c;而对数据进行简化处理例如性别&#xff0c;经常会使用1&#xff0c; 0这俩个来代替文字的男&#xff0c;女。以下就是前端渲染的具体实现。 以下是部分代码 <el-table-columnpr…

Linux 共享内存mmap,进程通信

文章目录 前言一、存储映射 I/O二、mmap&#xff0c; munmap三、父子进程间 mmap 通信四、非血缘关系进程间 mmap 提通信五、mmap 匿名映射区总结 前言 进程间通信是操作系统中重要的概念之一&#xff0c;使得不同的进程可以相互交换数据和进行协作。其中&#xff0c;共享内存…

非关系型数据库---Redis安装与基本使用

一、数据库类型 关系数据库管理系统&#xff08;RDBMS&#xff09;非关系数据库管理系统&#xff08;NoSQL&#xff09; 按照预先设置的组织机构&#xff0c;将数据存储在物理介质上(即&#xff1a;硬盘上) 数据之间可以做无关联操作 (例如: 多表查询&#xff0c;嵌套查询&am…

详细记录Pycharm配置已安装好的Conda虚拟环境

当安装好conda环境之后&#xff0c;想要在Pycharm中使用&#xff0c;那么就要在Pycharm中导入&#xff0c;我这里使用的pycharm-professional-2023.2这个版本&#xff0c;下面是详细步骤&#xff1a; 1.打开File->Settings&#xff1a; 2.找到Project——>Python Inter…

【每日一题】1572. 矩阵对角线元素的和

【每日一题】1572. 矩阵对角线元素的和 1572. 矩阵对角线元素的和题目描述解题思路 1572. 矩阵对角线元素的和 题目描述 给你一个正方形矩阵 mat&#xff0c;请你返回矩阵对角线元素的和。 请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。 示例 1&a…

基于Gradio的GPT聊天程序

网上很多别人写的&#xff0c;要用账号也不放心。就自己写了一个基于gradio的聊天界面&#xff0c;部署后可以本地运行。 特点&#xff1a; 可以用openai的&#xff0c;也可以用api2d&#xff0c;其他api可以自己测试一下。使用了langchain的库 可以更改模型&#xff0c;会的…

【调整奇数偶数顺序】

调整奇数偶数顺序 1.题目 输入一个整数数组&#xff0c;实现一个函数&#xff0c; 来调整该数组中数字的顺序使得数组中所有的奇数位于数组的前半部分&#xff0c; 所有偶数位于数组的后半部分。 2.题目分析 这道题首先用到的方法是冒泡排序的思想&#xff0c;首先通过冒泡排序…

时序预测 | MATLAB实现BO-BiLSTM贝叶斯优化双向长短期记忆神经网络时间序列预测

时序预测 | MATLAB实现BO-BiLSTM贝叶斯优化双向长短期记忆神经网络时间序列预测 目录 时序预测 | MATLAB实现BO-BiLSTM贝叶斯优化双向长短期记忆神经网络时间序列预测效果一览基本介绍模型搭建程序设计参考资料 效果一览 基本介绍 MATLAB实现BO-BiLSTM贝叶斯优化双向长短期记忆…

Linux下C语言调用libcurl库获取天气预报信息

一、概述 当前文章介绍如何在Linux&#xff08;Ubuntu&#xff09;下使用C语言调用libcurl库获取天气预报的方法。通过HTTP GET请求访问百度天气API&#xff0c;并解析返回的JSON数据&#xff0c;可以获取指定城市未来7天的天气预报信息。 二、设计思路 【1】使用libcurl库进…

用MariaDB创建数据库,SQL练习,MarialDB安装和使用

前言&#xff1a;MariaDB数据库管理系统是MySQL的一个分支&#xff0c;主要由开源社区在维护&#xff0c;采用GPL授权许可 MariaDB的目的是完全兼容MySQL&#xff0c;包括API和命令行&#xff0c;使之能轻松成为MySQL的代替品。在存储引擎方面&#xff0c;使用XtraDB来代替MySQ…