当前位置: 首页 > news >正文

【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调用退出函数

相关文章:

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

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

Von Maur, Inc EDI 需求分析

Von Maur, Inc 是一家历史悠久的卖场&#xff0c;成立于19世纪&#xff0c;总部位于美国。作为一家知名的零售商&#xff0c;Von Maur 主要经营高端时装、家居用品和美妆产品。其使命是为顾客提供优质的产品和无与伦比的购物体验。多年来&#xff0c;Von Maur 凭借其卓越的服务…...

[深度学习入门]PyTorch深度学习[Numpy基础](上)

目录 一、前言二、Numpy概述三、生成Numpy数组3.1 从已有数据中创建数组3.2 利用random模块生成数组3.3 创建特定形状的多维数组3.4 利用arange和linspace函数生成数组 四、获取元素五、Numpy的算术运算5.1 对应元素相乘5.2 点积运算 六、后记 本文的目标受众&#xff1a; 对机…...

Excel vost 实现照光灯效果

如果你想要在 VSTO&#xff08;Visual Studio Tools for Office&#xff09;中实现在 Excel 中添加“照光灯”效果&#xff0c;你需要创建一个 VSTO 插件来实现这个功能。照光灯效果通常是指通过将非活动行或列进行高亮显示&#xff0c;以便更清楚地查看某一行或列的内容。以下…...

IntelliJ中文乱码问题

1、控制台乱码 运行时控制台输出的中文为乱码&#xff0c;解决方法&#xff1a;帮助 > 编辑自定义虚拟机选项… > 此时会自动创建出一个新文件&#xff0c;输入&#xff1a;-Dfile.encodingUTF-8&#xff0c;然后重启IDE即可&#xff0c;操作截图如下&#xff1a; 2、…...

【C++】红黑树模拟实现插入功能(包含旋转和变色)

红黑树模拟实现并封装为map和set 前言正式开始红黑树概念红黑树基本要求大致框架树节点树 调整红黑树使其平衡第一种&#xff1a;cur红&#xff0c;p红&#xff0c;g黑&#xff0c;u存在且为红第二种&#xff1a;cur红&#xff0c;p红&#xff0c;g黑&#xff0c;u不存在或为黑…...

Pads输出器件坐标文件时,如何更改器件坐标精度

相信对于用pads软件的工程师么&#xff0c;在完成PCB设计的时候都需要输出生产文件给板厂和贴片厂&#xff0c;今天我们需要给大家介绍的是如何在在pads软件上面输出器件坐标文件以及如何更改器件坐标文件的精度。 首先我们需要点击工具-基本脚本-基本脚本接下来会跳到下面这个…...

Vuejs3父组传值给子组件

父组件代码 <script setup> import TextProps from ./components/TextProps.vue; import { reactive } from vue;const queryobj reactive({"a":1, "b":1}); const aryobj reactive([1,2,3]);</script><template><div class"…...

竞赛项目 深度学习的智能中文对话问答机器人

文章目录 0 简介1 项目架构2 项目的主要过程2.1 数据清洗、预处理2.2 分桶2.3 训练 3 项目的整体结构4 重要的API4.1 LSTM cells部分&#xff1a;4.2 损失函数&#xff1a;4.3 搭建seq2seq框架&#xff1a;4.4 测试部分&#xff1a;4.5 评价NLP测试效果&#xff1a;4.6 梯度截断…...

【剑指 の 精选】热门状态机 DP 运用题

题目描述 这是 LeetCode 上的 「剑指 Offer II 091. 粉刷房子」 &#xff0c;难度为 「中等」。 Tag : 「状态机 DP」、「动态规划」 假如有一排房子&#xff0c;共 n 个&#xff0c;每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种&#xff0c;你需要粉刷所有的房子…...

自动化实践-全量Json对比在技改需求提效实践

1 背景 随着自动化测试左移实践深入&#xff0c;越来越多不同类型的需求开始用自动化测试左移来实践&#xff0c;在实践的过程中也有了新的提效诉求&#xff0c;比如技改类的服务拆分项目或者BC流量拆分的项目&#xff0c;在实践过程中&#xff0c;这类需求会期望不同染色环境…...

【Matlab】PSO优化(单隐层)BP神经网络

上一篇博客介绍了BP-GA&#xff1a;BP神经网络遗传算法(BP-GA)函数极值寻优——非线性函数求极值&#xff0c;本篇博客将介绍用PSO&#xff08;粒子群优化算法&#xff09;优化BP神经网络。 1.优化思路 BP神经网络的隐藏节点通常由重复的前向传递和反向传播的方式来决定&#…...

创建型模式-原型模式

文章目录 一、原型模式1. 概述2. 结构3. 实现4. 案例1.5 使用场景1.6 扩展&#xff08;深克隆&#xff09; 一、原型模式 1. 概述 用一个已经创建的实例作为原型&#xff0c;通过复制该原型对象来创建一个和原型对象相同的新对象。 2. 结构 原型模式包含如下角色&#xff1a; …...

JS逆向系列之猿人学爬虫第11题 - app抓取 - so文件协议破解

题目地址 http://match.yuanrenxue.com/match/11这是个app题目,先下载下来安装到测试手机上 安装完成后的app界面长这样 打开之后是这样的: 要求已经简单明了了。 二话不说先反编译app 不出意外的是没出意外,源代码里面没啥混淆,所有东西都展示的明明白白的。 "…...

c基础扫雷

和三子棋一样&#xff0c;主函数先设计游戏菜单界面&#xff0c;这里就不做展示了。 初始化棋盘 初级扫雷大小为9*9的棋盘&#xff0c;但排雷是周围一圈进行排雷(8格)&#xff0c;而边界可能会越界。数组扩大了一圈,行和列都加了2&#xff0c;所以我们用一个11*11的数组来初始化…...

端点中心(Endpoint Central)的软件许可证管理

软件许可证管理 &#xff08;SLM&#xff09; 是从单个控制台管理整个组织中使用的软件许可证的过程。软件许可证是由软件发行商或分销商制作的法律文件&#xff0c;提供有关软件使用和分发的规则和指南&#xff0c;本文档通常包含条款和条件、限制和免责声明。 软件许可证管理…...

SpringCloud源码探析(九)- Sentinel概念及使用

1.概述 在微服务的依赖调用中&#xff0c;若被调用方出现故障&#xff0c;出于自我保护的目的&#xff0c;调用方会主动停止调用&#xff0c;并根据业务需要进行对应处理&#xff0c;这种方式叫做熔断&#xff0c;是微服务的一种保护方式。为了保证服务的高可用性&#xff0c;…...

nodejs+vue+elementui美食网站的设计与实现演示录像2023_0fh04

本次的毕业设计主要就是设计并开发一个美食网站软件。运用当前Google提供的nodejs 框架来实现对美食信息查询功能。当然使用的数据库是mysql。系统主要包括个人信息修改&#xff0c;对餐厅管理、用户管理、餐厅信息管理、菜系分类管理、美食信息管理、美食文化管理、系统管理、…...

Mysql 数据库增删改查

MySQL是目前最流行的关系型数据库。以下是MySQL数据库的增删改查操作。 1.数据库连接 在进行增删改查操作之前&#xff0c;需要先连接MySQL数据库。使用以下命令进行连接&#xff1a; import mysql.connectormydb mysql.connector.connect(host"localhost",user&…...

【深度学习注意力机制系列】—— ECANet注意力机制(附pytorch实现)

ECANet&#xff08;Efficient Channel Attention Network&#xff09;是一种用于图像处理任务的神经网络架构&#xff0c;它在保持高效性的同时&#xff0c;有效地捕捉图像中的通道间关系&#xff0c;从而提升了特征表示的能力。ECANet通过引入通道注意力机制&#xff0c;以及在…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...