【MFC】06.MFC第二大机制:窗口创建机制-笔记

接上文【MFC】05.MFC第一大机制:程序启动机制-笔记,这一篇文章来带领大家逆向分析MFC第二大机制:窗口创建机制的源码。
我们知道,在Win32编程中,如果我们要创建一个窗口,基本步骤为:

  1. 注册窗口
  2. 创建一个窗口,必须要给一个类名称
  3. 消息处理回调函数

那么MFC的窗口创建机制又是怎样的呢?我们来回溯一下MFC源码:

首先,我们继续用上文中创建的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->ShowWindow(SW_SHOW);pFrame->UpdateWindow();return TRUE;}
};CMyApp theApp;

不难猜到,创建窗口的代码在这:pFrame->Create(NULL, L"FirstMFC");我们就在这行代码上下断点,跟过去:

我们知道我们重写的的InitInstance函数在WinMian函数转发的AfxwWimMian函数中会被调用:

这里给的参数,第一个NULL是lpszClassName(窗口类名),第二个参数是lpszWindowName(应用程序类名),但是我们了解Win32编程,我们知道如果想要创建窗口或者是注册窗口,都必须要窗口类名,但是这里给的是NULL,为什么还是能够正常执行过去呢?

virtual BOOL InitInstance(){CMyFrameWnd* pFrame = new CMyFrameWnd;//Create函数是框架类的方法://我们知道Create方法的参数肯定不止俩,我们跟过去之后,发现其他参数是有默认参数的pFrame->Create(NULL, L"FirstMFC"){//进入函数第一件事:判断是否包含菜单,如果包含菜单,则加载菜单HMENU hMenu = NULL;if (lpszMenuName != NULL){HINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, ATL_RT_MENU);if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL){TRACE(traceAppMsg, 0, "Warning: failed to load menu for CFrameWnd.\n");PostNcDestroy();return FALSE;}}m_strTitle = lpszWindowName;    // save title for later//这里的代码很明显是创建窗口了,但是没有窗口类名,怎么能创建窗口呢?我们跟进去看看://这里由于其他参数我们不关注,这里只留下窗口类名//这里源代码函数实现实在if语句的条件中执行的,这里直接拉出来CreateEx( lpszClassName, lpszWindowName,...){//可以看到在这里有断言:ASSERT(lpszClassName == NULL || AfxIsValidString(lpszClassName) ||AfxIsValidAtom(lpszClassName));ENSURE_ARG(lpszWindowName == NULL || AfxIsValidString(lpszWindowName));//这里是创建窗口所需要的类:CREATESTRUCT cs;cs.dwExStyle = dwExStyle;cs.lpszClass = lpszClassName;cs.lpszName = lpszWindowName;cs.style = dwStyle;cs.x = x;cs.y = y;cs.cx = nWidth;cs.cy = nHeight;cs.hwndParent = hWndParent;cs.hMenu = nIDorHMenu;cs.hInstance = AfxGetInstanceHandle();cs.lpCreateParams = lpParam;//这里隐含的this指针:框架类窗口PreCreateWindow(cs){//跟进去函数也能发现是框架类窗口的方法BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs){if (cs.lpszClass == NULL){//发现这里有一个貌似MFC注册窗口的函数:跟进去看看VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)){//这些是所需要的类,相信大家都很熟悉了WNDCLASS wndcls;memset(&wndcls, 0, sizeof(WNDCLASS));   // start with NULL defaultswndcls.lpfnWndProc = DefWindowProc;wndcls.hInstance = AfxGetInstanceHandle();wndcls.hCursor = afxData.hcurArrow;//接下来发现了好多类似的代码,应该是判断一些东西,我们的代码进入了这个if语句,我们就跟进去if (fToRegister & AFX_WNDFRAMEORVIEW_REG){//这里是对wndcls做一些赋值操作wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);//代码跟到这里,应该是注册窗口了,因为吧注册窗口所需要的类传进去了,为了以防万一,我们跟进去看看:if (_AfxRegisterWithIcon(&wndcls, _afxWndFrameOrView, AFX_IDI_STD_FRAME)){//代码前部分做了一些赋值操作,加载图标//我们跟到这个函数中看看注册操作:return AfxRegisterClass(pWndCls){WNDCLASS wndcls;//这里的GetClassInfo是判断窗口是否已经注册过GetClassInfo(lpWndClass->hInstance, lpWndClass->lpszClassName,&wndcls)//在这里发现了Win32API:注册窗口if (!RegisterClass(lpWndClass))}}}//在注册玩窗口后,发现了这段代码//这里的_afxWndFrameOrView是一个宏或者数组,跟过去看看,发现是个数组//跟到最后,发现是FrameOrViewcs.lpszClass = _afxWndFrameOrView;}}...return TRUE;}}}//在创建窗口之前,发现埋下了一个钩子AfxHookWindowCreate(this){void AFXAPI AfxHookWindowCreate(CWnd* pWnd){//MFC的第三个全局变量_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();if (pThreadState->m_pWndInit == pWnd)return;//这里是埋下了一个创建窗口的消息钩子pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,_AfxCbtFilterHook, NULL, ::GetCurrentThreadId());//如果失败的话,抛出异常if (pThreadState->m_hHookOldCbtFilter == NULL)AfxThrowMemoryException();}//这里保存了我们的框架类窗口对象pThreadState->m_pWndInit = pWnd;}}//发现这里是创建窗口HWND hWnd = CreateWindowEx(cs.dwExStyle, cs.lpszClass,cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);}
}

触发的钩子函数
LRESULT CALLBACK _AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)
{
//MFC第三个全局变量的地址
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
//取出结构体参数
LPCREATESTRUCT lpcs = ((LPCBT_CREATEWND)lParam)->lpcs;
//多态 父类指针 指向子类
CWnd* pWndInit = pThreadState->m_pWndInit

//创建的窗口句柄
HWND hWnd = (HWND)wParam;//窗口句柄传入了进去
//内部this指针是我们的对象
pWndInit->Attach(hWnd);
{//MAP对象CHandleMap* pMap = afxMapHWND(TRUE); {//MFC 第二个全局变量 线程模块状态 AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();//把映射类对象 保存到了 第二个全局变量里面pState->m_pmapHWND = new CHandleMapreturn pState->m_pmapHWND;}//内部this指针是谁 pMap映射类对象pMap->SetPermanent(this->m_hWnd = hWndNew, 框架类对象);{inline void CHandleMap::SetPermanent(HANDLE h, CObject* permOb){//pMap映射类对象this->m_permanentMap[窗口句柄] = 框架类对象 pFrame;//stl map  map[key] = val}}WNDPROC afxWndProc = AfxGetAfxWndProc();oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,(DWORD_PTR)afxWndProc);
}LRESULT lResult = CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code,wParam, lParam);

}

//真正的窗口过程处理函数
AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
{
CHandleMap* pMap = afxMapHWND();
{
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
pState->m_pmapHWND
}

    return (CObject*)m_permanentMap.GetValueAt((LPVOID)h);        
}AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam)
{//父类指针 指向了子类 lResult = pWnd->WindowProc(nMsg, wParam, lParam);
}

}

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

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

相关文章

C#中的yield return详细解释

C# 中的 yield return 是一个强大的关键字&#xff0c;它可以帮助我们在不创建临时集合的情况下&#xff0c;实现可枚举的值的生成。yield return 可以实现延迟执行&#xff08;Lazy Evaluation&#xff09;, 更具可读性和优化内存的使用。 yield return 的工作原理 在 C# 中…

滑动窗口(全面清晰/Java)

数组模拟单调队列 分析 以k3举例&#xff1a; (1)利用单调队列的性质&#xff1a; <1>最小值&#xff1a;确保队列单调递增&#xff0c;处理后&#xff0c;队头即是最小值。 <2>最大值&#xff1a;确保队列单调递减&#xff0c;处理后&#xff0c;队头即是最大值…

线程魔法:用Spring Boot的@Async注解开启异步世界

在现代的应用程序开发中&#xff0c;异步调用已成为提高性能和响应性的重要策略之一。通过使用Spring Boot框架&#xff0c;我们可以轻松地实现异步调用&#xff0c;从而在处理请求时能够同时执行耗时的操作&#xff0c;如发送电子邮件、处理文件等&#xff0c;而不会阻塞主线程…

做接口测试如何上次文件

在日常工作中&#xff0c;经常有上传文件功能的测试场景&#xff0c;因此&#xff0c;本文介绍两种主流编写上传文件接口测试脚本的方法。 首先&#xff0c;要知道文件上传的一般原理&#xff1a;客户端根据文件路径读取文件内容&#xff0c;将文件内容转换成二进制文件流的格式…

在HTML页面中引入vue组件

1. 什么是组件化开发&#xff1f; 组件化开发是一种软件开发的方法论&#xff0c;旨在通过将复杂的系统分解为独立的、可重用的组件来提高开发效率和代码的可维护性。组件化开发将系统按照功能、模块或界面的不同部分进行拆分&#xff0c;每个部分都对应一个独立的组件&#x…

小程序如何使用防抖和节流?

防抖&#xff08;Debounce&#xff09;和节流&#xff08;Throttle&#xff09;都是用来优化函数执行频率的技术&#xff0c;特别在处理用户输入、滚动等频繁触发的情况下&#xff0c;它们可以有效减少函数的执行次数&#xff0c;从而提升性能和用户体验。但它们的工作方式和应…

yolov2检测网数据集标注_labelme使用_json2txt格式转换

yolov2检测网数据集标注_labelme使用_json2txt格式转换 一、安装Anaconda二、创建labelme虚拟环境三、使用labelme标注健康非健康猫狗数据3.1 打开数据集所在文件夹3.2 进行标注数据集3.3 json2txt3.4 按文件目录和训练测试数据集重分配 四、数据喂给服务器网络参考链接 一、安…

Clion开发STM32之HAL库SPI封装(基础库)

前言 引用参考: Clion开发STM32之HAL库GPIO宏定义封装(最新版) 头文件 /******************************************************************************** Copyright (c) [scl]。保留所有权利。* 本文仅供个人学习和研究使用&#xff0c;禁止用于商业用途。******…

92 | Python 设计模式 —— 工厂模式

文章目录 什么是工厂模式?工厂模式的优点工厂模式的实现方式简单工厂模式(Simple Factory Pattern)抽象工厂模式(Abstract Factory Pattern)Python工厂模式示例1. 简单工厂模式2. 抽象工厂模式总结什么是工厂模式? 在软件设计中,工厂模式是一种创建型设计模式,它提供了…

网络安全---正则回溯

目录 一、题目引入 二、举出回溯例子进行分析 第一步&#xff1a; 正则往前匹配 第二步&#xff1a;匹配到头 第三步&#xff1a;往回匹配 第四步&#xff1a;直到分号结束 &#xff08;匹配上&#xff09; 原因&#xff1a; 三、进入正题一&#xff08;分析题型&#x…

【人工智能概述】python妙用 __str__()

【人工智能概述】python妙用 str() 文章目录 【人工智能概述】python妙用 __str__()一.python内置函数__str__() 一.python内置函数__str__() 通过自定义__str__()函数可以打印对象中相关的内容。 class Person(object):def __init__(self, name tom, age 10):self.name n…

MongoDB创建用户 、数据库、索引等基础操作

MongoDB的权限认证是相对来说比较复杂的&#xff0c;不同的库创建后需要创建用户来管理。 本机中的MongoDB是docker 启动的&#xff0c;所以先进入docker的镜像中 docker exec -it mongodb bash 这样就进入到了镜像MongoDB中&#xff0c;然后输入命令连接MongoDB数据库 注…

有效管理IT问题的5个原则

问题管理就是发现未知的、隐藏的问题&#xff0c;这是根本原因&#xff0c; 这是您 IT 帮助台无穷无尽的工单来源。当您实施有效的 问题管理&#xff0c;您的 IT 团队可以超越消防模式&#xff0c;专注于战略 IT 目标。以下是可以帮助您实现一流问题管理的五个原则&#xff1a;…

网络安全 Day30-运维安全项目-堡垒机部署

运维安全项目-堡垒机部署 1. 运维安全项目-架构概述2. 运维安全项目之堡垒机2.1 堡垒机概述2.2 堡垒机选型2.3 环境准备2.4 部署Teleport堡垒机2.4.1 下载与部署2.4.2 启动2.4.3 浏览器访问teleport2.4.4 进行配置2.4.5 安装teleport客户端 2.5 teleport连接服务器 1. 运维安全…

二叉树的前中后序遍历(Leetcode 94144145)

题目 ​​​​​​144. 二叉树的前序遍历 145. 二叉树的后序遍历 94. 二叉树的中序遍历 代码 分为三种方法1&#xff09;递归遍历&#xff1b;2&#xff09;迭代遍历&#xff1b;3&#xff09;统一迭代遍历 递归遍历 public List<Integer> preorderTraversal(TreeNod…

中介者模式(Mediator)

中介者模式是一种行为设计模式&#xff0c;可以减少对象之间混乱无序的依赖关系。该模式会限制对象之间的直接交互&#xff0c;迫使它们通过一个封装了对象间交互行为的中介者对象来进行合作&#xff0c;从而使对象间耦合松散&#xff0c;并可独立地改变它们之间的交互。中介者…

开源语言模型的历史和重要性;Edge浏览器将推出Bing AI重写文本功能

&#x1f989; AI新闻 &#x1f680; 微软即将推出桌面版Microsoft Edge浏览器的Bing AI重写文本功能 摘要&#xff1a;微软最近在桌面版Microsoft Edge浏览器中引入了一个新功能&#xff0c;允许用户使用Bing AI重写文本。用户可以选择不同的语气、格式和长度&#xff0c;然…

SpringMVC的架构有什么优势?——异常处理与文件上传(五)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…

oracle不小心将表update修改了如何回滚

oracle提供了一种闪回的方法&#xff0c;可以将某个时间的数据给还原回来 SELECT* FROMT_DIS_EVENT_RELATION_TYPE AS OF TIMESTAMP TO_TIMESTAMP (2023-08-08 15:31:00,yyyy-mm-dd hh24:mi:ss); T_DIS_EVENT_RELATION_TYPE 是误操作&#xff0c;需要闪回的表 2023-08-08 15…

java面试---字符串相关内容

字符串 1. 什么是Java中的字符串池&#xff08;String Pool&#xff09;&#xff1f;2. String、StringBuilder和StringBuffer之间的区别是什么&#xff1f;3. 如何比较两个字符串的内容是否相等&#xff1f;4、equals和的区别5. String类有哪些常用的方法&#xff1f; 1. 什么…