MFC 窗口创建过程与消息处理
目录
钩子简介
代码编写
窗口创建过程分析
消息处理
钩子简介
介绍几个钩子函数,因为它们与窗口创建工程有关
安装钩子函数
HHOOK SetWindowsHookExA([in] int idHook,[in] HOOKPROC lpfn,[in] HINSTANCE hmod,[in] DWORD dwThreadId
);
参数说明:
- idHook:指定要安装的钩子类型,例如鼠标钩子、键盘钩子等。
- lpfn:指向钩子过程(HookProc)的指针,即要安装的钩子处理函数。
- hmod:指定包含钩子过程的DLL模块句柄。如果是本地钩子或全局钩子,则此参数可以为NULL。
- dwThreadId:指定关联的线程ID。对于全局钩子,如果此参数为0,则表示将钩子应用到所有线程。
钩子处理函数
LRESULT CALLBACK CBTProc(_In_ int nCode,_In_ WPARAM wParam,_In_ LPARAM lParam
);
参数说明:
- nCode:指示钩子过程收到的通知代码,用于确定如何处理钩子。
- wParam:指定与钩子相关的消息的附加消息信息。
- lParam:指定与钩子相关的消息的附加消息信息。
更改窗口处理函数
LONG_PTR SetWindowLongPtrA([in] HWND hWnd,[in] int nIndex,[in] LONG_PTR dwNewLong
);
参数说明:
- hWnd:指定要设置额外窗口内存的窗口句柄。
- nIndex:指定要设置的值的偏移量。可以是一个负偏移量,也可以是预定义值之一。
- dwNewLong:指定的一个32位或64位的新值,取决于窗口的32位或64位。
代码编写
还是创建一个空白的Winodws应用程序
- 修改为多字节编码
- 使用静态MFC库,方便调试
#include <afxwin.h>class CMyFrameWnd : public CFrameWnd {
public:virtual LRESULT WindowProc(UINT msgID, WPARAM wParam, LPARAM);
};
LRESULT CMyFrameWnd::WindowProc(UINT msgID, WPARAM wParam, LPARAM lParam) {//此函数内部的this为pFrameswitch (msgID) {case WM_CREATE:AfxMessageBox("WM_CREATE消息被处理");break;case WM_PAINT:{PAINTSTRUCT ps = { 0 };HDC hdc = ::BeginPaint(this->m_hWnd, &ps);::TextOut(hdc, 100, 100, "hello", 5);::EndPaint(m_hWnd, &ps);}break;}return CFrameWnd::WindowProc(msgID, wParam, lParam);
}
class CMyWinApp : public CWinApp {
public:virtual BOOL InitInstance();
};CMyWinApp theApp;//爆破点BOOL CMyWinApp::InitInstance() {CMyFrameWnd* pFrame = new CMyFrameWnd;pFrame->Create(NULL, "MFCCreate");m_pMainWnd = pFrame;pFrame->ShowWindow(SW_SHOW);pFrame->UpdateWindow();return TRUE;
}
遇见报错,是程序类型的问题
窗口创建过程分析
- 加载菜单
- 调用cWnd::CreateEx函数创建窗口
- 调用PreCreateWindow函数设计和注册窗口类调用AfxDeferRegisterClass函数,在这个函数中设计窗口类∶
- WNDCLASS wndcls;//设计窗口类
- 定义窗口的处理函数为DefWindowProcwndcls.lpfnWndProc = DefWindowProc;调用_AfxRegisterWithlcon函数
- 在函数内部,加载图标,并调用AfxRegisterClass函数,在函数内部,调用::RegisterClass win32 ApI函数注册窗口类
- 调用AfxHookWindowCreate 函数。
- 在函数内部,调用SetWindowsHookEx创建WH_CBT类型的钩子,钩子的处理函数是_AfxCbtFilterHook。
- 将框架类对象地址(pFrame)保存到当前程序线程信息中
- 调用CreateWindowEx函数创建窗口,马上调用钩子处理函数
- 钩子处理函数_AfxCbtFilterHook
- 将窗口句柄和框架类对象地址建立一对一的绑定关系。
- 使用SetWindowLong函数,将窗口处理的函数设置AfxWndProc
- 调用PreCreateWindow函数设计和注册窗口类调用AfxDeferRegisterClass函数,在这个函数中设计窗口类∶
下断点,分析 Create() 函数,F11进入分析
第一个参数为 NULL,第二个参数是一个字符串 MFCCreate,进入 Create 函数内部
前面是针对 第一个参数不为空的处理
if (lpszMenuName != NULL){// load in a menu that will get destroyed when window gets destroyedHINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, ATL_RT_MENU);if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL){TRACE(traceAppMsg, 0, "Warning: failed to load menu for CFrameWnd.\n");PostNcDestroy(); // perhaps delete the C++ objectreturn FALSE;}}
参数为空的处理 ,进入CreateEx函数,NULL 作为第二个参数
if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))
CREATESTRUCT结构体在窗口创建过程中提供了创建窗口所需的各种信息,这个结构体会作为函数 CreateWindowEx 的参数创建窗口
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;
进入PreCtreateWinodow函数,创建窗口之前的处理函数
if (!PreCreateWindow(cs)){PostNcDestroy();return FALSE;}
再进入AfxEndDeferRegisterClass函数,AfxGetModuleState() 是类库的全局函数,为全局变量 当前程序模块信息类 服务
通过定义WNDCLASS结构体并填充相应成员的值,开发人员可以注册一个新的窗口类,并使用该类创建窗口。可以通过 AfxGetInstanceHandle() 这个全局函数获取当前应用程序实例句柄。
WNDCLASS wndcls;memset(&wndcls, 0, sizeof(WNDCLASS)); // start with NULL defaultswndcls.lpfnWndProc = DefWindowProc; // 窗口处理函数wndcls.hInstance = AfxGetInstanceHandle();wndcls.hCursor = afxData.hcurArrow;
一直按F11,程序执行流程走到这个函数中
if (fToRegister & AFX_WNDFRAMEORVIEW_REG){// SDI Frame or MDI Child windows or views - normal colorswndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);if (_AfxRegisterWithIcon(&wndcls, _afxWndFrameOrView, AFX_IDI_STD_FRAME))fRegisteredClasses |= AFX_WNDFRAMEORVIEW_REG;}
进入函数
_AfxRegisterWithIcon(&wndcls, _afxWndFrameOrView, AFX_IDI_STD_FRAME)
为窗口类 lpszClassName 赋值
之后调用 AfxRegisterClass(pWndCls) ,这个函数就是注册窗口了,之后没有必要再跟下去了
AFX_STATIC BOOL AFXAPI _AfxRegisterWithIcon(WNDCLASS* pWndCls,LPCTSTR lpszClassName, UINT nIDIcon)
{pWndCls->lpszClassName = lpszClassName;HINSTANCE hInst = AfxFindResourceHandle(ATL_MAKEINTRESOURCE(nIDIcon), ATL_RT_GROUP_ICON);if ((pWndCls->hIcon = ::LoadIconW(hInst, ATL_MAKEINTRESOURCEW(nIDIcon))) == NULL){// use default iconpWndCls->hIcon = ::LoadIcon(NULL, IDI_APPLICATION);}return AfxRegisterClass(pWndCls);
}
完成了窗口注册,程序流程一路返回
跟进到 AfxHookWindowCreate(this)
获取当前程序线程信息
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
利用Win32的API函数,埋下一个类型为WH_CBT的钩子,钩子处理函数是 _AfxCbtFilterHook
WH_CBT钩子是一种全局的系统事件钩子,它允许拦截一系列与计算机、窗口、任务和其他系统相关的事件。这些事件包括窗口的创建、激活、移动、销毁等,通过使用WH_CBT钩子,应用程序可以介入并对这些系统事件做出响应或修改。
::SetWindowsHookEx(WH_CBT,_AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
将自己new的框架类对象pFrame保存到pThreadState->m_pWndInit
pThreadState->m_pWndInit = pFrame;
之后开始创建窗口,当窗口创建成功后,钩子就会钩到WM_CREATE消息,之后调用钩子处理函数,函数第二个参数 wParam 是窗口句柄
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData(); // 程序线程信息
CWnd* pWndInit = pThreadState->m_pWndInit; // 框架窗口对象赋值
之后调用,attch 函数的this是框架窗口对象
pWndInit->Attach(hWnd);
进入到 afxMapHWND 函数
CHandleMap* pMap = afxMapHWND(TRUE);
函数内部,创建了一个CHandleMap返回给 CHandleMap* pMap
pState->m_pmapHWND = new CHandleMap(RUNTIME_CLASS(CWnd)
调用SetPermanent,第一个参数是窗口句柄,另一个是框架窗口对象
pMap->SetPermanent(m_hWnd = hWndNew, this);
m_permanentMap是一个数组,根据下标窗口句柄,就能拿到窗口框架对象,也就是说建立了一个窗口句柄到框架窗口对象的映射
void CHandleMap::SetPermanent(HANDLE h, CObject* permOb)
{BOOL bEnable = AfxEnableMemoryTracking(FALSE);m_permanentMap[(LPVOID)h] = permOb;AfxEnableMemoryTracking(bEnable);
}
将窗口处理函数更改为AfxWndProc(才是真正的窗口处理函数)
oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,(DWORD_PTR)afxWndProc);
接下来就是窗口处理函数来处理消息
消息处理
接下尝试调试WM_CREATE消息
- 当收到消息时,进入AfxWndProc函数。
- AfxWndProc 函数根据消息的窗口句柄,查询对应框架类对象的地址( pFrame ) 。
- 利用框架类对象地址( pFrame)调用框架类成员虚函数WindowProc,完成消息的处理。
重写虚函数,消息处理函数
LRESULT CMyFrameWnd::WindowProc(UINT msgID, WPARAM wParam, LPARAM lParam) {//此函数内部的this为pFrameswitch (msgID) {case WM_CREATE:AfxMessageBox("WM_CREATE消息被处理");break;case WM_PAINT:{PAINTSTRUCT ps = { 0 };HDC hdc = ::BeginPaint(this->m_hWnd, &ps);::TextOut(hdc, 100, 100, "hello", 5);::EndPaint(m_hWnd, &ps);}break;}return CFrameWnd::WindowProc(msgID, wParam, lParam);
}
下个断点,看看调用堆栈
在这里下个断点,分析一下执行过程
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
进入FromHandlePermanent函数
CWnd* PASCAL CWnd::FromHandlePermanent(HWND hWnd)
{CHandleMap* pMap = afxMapHWND();CWnd* pWnd = NULL;if (pMap != NULL){// only look in the permanent map - does no allocationspWnd = (CWnd*)pMap->LookupPermanent(hWnd);ASSERT(pWnd == NULL || pWnd->m_hWnd == hWnd);}return pWnd;
}
CHandleMap* pMap = afxMapHWND(); 进入看看,返回就是之前保存在程序模块线程信息中的映射类对象地址
根据窗口句柄,拿到框架窗口句柄,这之间关系就好比 洗衣机与洗衣机类,通过类来管理句柄
通过这个函数进一步调用到重写的虚函数
return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
lResult = pWnd->WindowProc(nMsg, wParam, lParam);
相关文章:

MFC 窗口创建过程与消息处理
目录 钩子简介 代码编写 窗口创建过程分析 消息处理 钩子简介 介绍几个钩子函数,因为它们与窗口创建工程有关 安装钩子函数 HHOOK SetWindowsHookExA([in] int idHook,[in] HOOKPROC lpfn,[in] HINSTANCE hmod,[in] DWORD dwThreadId ); 参数说明…...

基于JavaWeb+SSM+Vue微信小程序的移动学习平台系统的设计和实现
基于JavaWebSSMVue微信小程序的移动学习平台系统的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 Lun文目录 第1章 绪论 1 1.1 课题背景 1 1.2 课题意义 1 1.3 研究内容 2 第2章 开发环…...

解决docker alpine /bin/sh: ./main: not found
解决docker alpine /bin/sh: ./main: not found golang中编译之后的二进制文件部署在alpine镜像中出现了not found问题解决这种情况是因为动态链接库位置错误导致的,alpine镜像使用的是musl libc而不是gun libc。因而动态链接库的位置不一致。在基础镜像内执行&…...

深入了解网络基础:从背景到协议
这里写自定义目录标题 1. 什么是协议呢?2. 什么是网络协议?5. OSI七层网络模型6. 网络传输基本流程1. 数据准备:2. 数据传输:3. 数据接收和重新组装:4. 数据处理与应用: 7. 数据的发送(封装&…...

针对这两个趋势,3.0全新新零售商业模式可以采取以下策略:
国内市场确实存在“消费升级”和“消费降级”两个趋势,这是由于不同消费者群体的需求和购买力存在差异。消费升级主要发生在高端市场,消费者愿意为高品质、高价值、高价格的商品和服务付出更多。而消费降级则主要发生在中低端市场,消费者更加…...

鸿蒙HarmonyOS开发用什么语言
1.网上流行一句有中国底蕴的话:鸿蒙系统方舟框架盘古大模型。都方舟框架了肯定主推的是ArkUI框架。其实还能使用C、Java和Js开发。 2.从API8开始,Java语言已经从鸿蒙开发剔除了,而官方推荐的是ArkTs.下图是ArkTS与TS、JS的关系。 ArkTs 是TS的…...

气象数据预测分析与可视化:天气趋势预测揭秘
气象数据预测分析与可视化:天气趋势预测揭秘 引言数据获取数据分析可视化展示Flask框架实现创新点:空气质量预测结论 引言 天气对我们日常生活和工作有着重要的影响,因此天气预测与分析变得愈发重要。本文将介绍如何通过爬取2345天气网的数据…...

install cuda cudnn tersorRT
1, dark view 2,470-server cant install 11.4 3,cuda.run and tensorRT.dpkg cant # 安装 $ ubuntu-drivers devices$ sudo apt-get install nvidia-driver-470-server # 推荐是server,都可以。#delelt sudo apt --purge remove nvidia-* CUDA Toolkit Archiv…...
Vue 3 + Vite 4 移动端低版本白屏处理
vue3打包后在低版本浏览器或webview中出现白屏,原因就是因为语法兼容问题。根据vite官方文档描述,build.target 默认支持 Chrome >87、Firefox >78、Safari >14、Edge >88 传送,所以需要我们手动兼容低版本。 方法: …...

Python爬虫-解决使用requests,Pyppeteer,Selenium遇到网站显示“您的连接不是私密连接”的问题|疑难杂症解决(2)
前言 本文是该专栏的第13篇,后面会持续分享python爬虫案例干货,记得关注。 相信很多同学在处理爬虫项目的时候,会遇到一些网站出现如下图所示的情况: 就是当你不论是使用requests进行协议请求,还是使用自动化框架pyppeteer或者selenium都会出现上图中的情况。这相信会或多…...

机场信息集成系统系列介绍(5):机场运行资源管理系统
目录 一、简介 二、主要功能 1、 航班资源管理模块 2、甘特图资源模块 3、规划管理模块 4、资源基础数据定义及配置管理模块 5、系统功能设置模块 6、 审计管理模块 一、简介 机场运行资源管理系统(ORMS) 是一种专门用于管理和优化机场运行资源的系统。它能够实现机场航…...

JavaEE:线程池精讲
目录 一.什么是线程池 二.线程池的实现原理 🎈为什么要有工厂模式? 三.线程池的构造方法解读 🎈线程池的拒绝策略 四.自己实现一个线程池 一.什么是线程池 简单来说,线程池就好比一块鱼塘,鱼塘中的每条鱼就是一个线程…...
spring-cloud-starter-gateway-mvc的网关实现
一 概括 最近,我也一直在使用SpringCloudGateway开发我们自己的网关产品。根据我对官网文档:https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway-server-mvc.html 的理解,内容如下: SpringCloudGatew…...

《PySpark大数据分析实战》-11.Spark on YARN模式安装Hadoop
📋 博主简介 💖 作者简介:大家好,我是wux_labs。😜 热衷于各种主流技术,热爱数据科学、机器学习、云计算、人工智能。 通过了TiDB数据库专员(PCTA)、TiDB数据库专家(PCTP…...

多架构容器镜像构建实战
最近在一个国产化项目中遇到了这样一个场景,在同一个 Kubernetes 集群中的节点是混合架构的,也就是说,其中某些节点的 CPU 架构是 x86 的,而另一些节点是 ARM 的。为了让我们的镜像在这样的环境下运行,一种最简单的做法…...

通过层进行高效学习:探索深度神经网络中的层次稀疏表示
一、介绍 深度学习中的层次稀疏表示是人工智能领域日益重要的研究领域。本文将探讨分层稀疏表示的概念、它们在深度学习中的意义、应用、挑战和未来方向。 最大限度地提高人工智能的效率和性能:深度学习系统中分层稀疏表示的力量。 二、理解层次稀疏表示 分层稀疏表…...

自然语言处理阅读第二弹
HuggingFace 镜像网站模型库 HuggingFace中bert实现 下游任务介绍重要源码解读 NLP中的自回归模型和自编码模型 自回归:根据上文内容预测下一个可能的单词,或者根据下文预测上一个可能的单词。只能利用上文或者下文的信息,不能同时利用上…...

利用canvas封装录像时间轴拖动(uniapp),封装上传uniapp插件市场
gitee项目地址,项目是一个空项目,其中包含了封装的插件,自己阅读,由于利用了canvas所以在使用中暂不支持.nvue,待优化; 项目也是借鉴了github上的一个项目,timeline-canvas, ...

PDF转为图片
PDF转为图片 背景pdf展示目标效果 发展过程最终解决方案:python PDF转图片pdf2image注意:poppler 安装 背景 最近接了一项目,主要的需求就是本地的文联单位,需要做一个电子刊物阅览的网站,将民族的刊物发布到网站上供…...

隐私计算介绍
这里只对隐私计算做一些概念性的浅显介绍,作为入门了解即可 目录 隐私计算概述隐私计算概念隐私计算背景国外各个国家和地区纷纷出台了围绕数据使用和保护的公共政策国内近年来也出台了数据安全、隐私和使用相关的政策法规 隐私计算技术发展 隐私计算技术安全多方计…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...