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

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;
}

遇见报错,是程序类型的问题

窗口创建过程分析

  1. 加载菜单
  2. 调用cWnd::CreateEx函数创建窗口
    1. 调用PreCreateWindow函数设计和注册窗口类调用AfxDeferRegisterClass函数,在这个函数中设计窗口类∶
      1. WNDCLASS wndcls;//设计窗口类
      2. 定义窗口的处理函数为DefWindowProcwndcls.lpfnWndProc = DefWindowProc;调用_AfxRegisterWithlcon函数
      3. 在函数内部,加载图标,并调用AfxRegisterClass函数,在函数内部,调用::RegisterClass win32 ApI函数注册窗口类
    2. 调用AfxHookWindowCreate 函数。
    3. 在函数内部,调用SetWindowsHookEx创建WH_CBT类型的钩子,钩子的处理函数是_AfxCbtFilterHook。
      1. 将框架类对象地址(pFrame)保存到当前程序线程信息中
      2. 调用CreateWindowEx函数创建窗口,马上调用钩子处理函数
      3. 钩子处理函数_AfxCbtFilterHook
      4. 将窗口句柄和框架类对象地址建立一对一的绑定关系。
      5. 使用SetWindowLong函数,将窗口处理的函数设置AfxWndProc

下断点,分析 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 窗口创建过程与消息处理

目录 钩子简介 代码编写 窗口创建过程分析 消息处理 钩子简介 介绍几个钩子函数&#xff0c;因为它们与窗口创建工程有关 安装钩子函数 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问题解决这种情况是因为动态链接库位置错误导致的&#xff0c;alpine镜像使用的是musl libc而不是gun libc。因而动态链接库的位置不一致。在基础镜像内执行&…...

深入了解网络基础:从背景到协议

这里写自定义目录标题 1. 什么是协议呢&#xff1f;2. 什么是网络协议&#xff1f;5. OSI七层网络模型6. 网络传输基本流程1. 数据准备&#xff1a;2. 数据传输&#xff1a;3. 数据接收和重新组装&#xff1a;4. 数据处理与应用&#xff1a; 7. 数据的发送&#xff08;封装&…...

针对这两个趋势,3.0全新新零售商业模式可以采取以下策略:

国内市场确实存在“消费升级”和“消费降级”两个趋势&#xff0c;这是由于不同消费者群体的需求和购买力存在差异。消费升级主要发生在高端市场&#xff0c;消费者愿意为高品质、高价值、高价格的商品和服务付出更多。而消费降级则主要发生在中低端市场&#xff0c;消费者更加…...

鸿蒙HarmonyOS开发用什么语言

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

气象数据预测分析与可视化:天气趋势预测揭秘

气象数据预测分析与可视化&#xff1a;天气趋势预测揭秘 引言数据获取数据分析可视化展示Flask框架实现创新点&#xff1a;空气质量预测结论 引言 天气对我们日常生活和工作有着重要的影响&#xff0c;因此天气预测与分析变得愈发重要。本文将介绍如何通过爬取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&#xff0c;都可以。#delelt sudo apt --purge remove nvidia-* CUDA Toolkit Archiv…...

Vue 3 + Vite 4 移动端低版本白屏处理

vue3打包后在低版本浏览器或webview中出现白屏&#xff0c;原因就是因为语法兼容问题。根据vite官方文档描述&#xff0c;build.target 默认支持 Chrome >87、Firefox >78、Safari >14、Edge >88 传送&#xff0c;所以需要我们手动兼容低版本。 方法&#xff1a; …...

Python爬虫-解决使用requests,Pyppeteer,Selenium遇到网站显示“您的连接不是私密连接”的问题|疑难杂症解决(2)

前言 本文是该专栏的第13篇,后面会持续分享python爬虫案例干货,记得关注。 相信很多同学在处理爬虫项目的时候,会遇到一些网站出现如下图所示的情况: 就是当你不论是使用requests进行协议请求,还是使用自动化框架pyppeteer或者selenium都会出现上图中的情况。这相信会或多…...

机场信息集成系统系列介绍(5):机场运行资源管理系统

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

JavaEE:线程池精讲

目录 一.什么是线程池 二.线程池的实现原理 &#x1f388;为什么要有工厂模式&#xff1f; 三.线程池的构造方法解读 &#x1f388;线程池的拒绝策略 四.自己实现一个线程池 一.什么是线程池 简单来说&#xff0c;线程池就好比一块鱼塘&#xff0c;鱼塘中的每条鱼就是一个线程…...

spring-cloud-starter-gateway-mvc的网关实现

一 概括 最近&#xff0c;我也一直在使用SpringCloudGateway开发我们自己的网关产品。根据我对官网文档&#xff1a;https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway-server-mvc.html 的理解&#xff0c;内容如下&#xff1a; SpringCloudGatew…...

《PySpark大数据分析实战》-11.Spark on YARN模式安装Hadoop

&#x1f4cb; 博主简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是wux_labs。&#x1f61c; 热衷于各种主流技术&#xff0c;热爱数据科学、机器学习、云计算、人工智能。 通过了TiDB数据库专员&#xff08;PCTA&#xff09;、TiDB数据库专家&#xff08;PCTP…...

多架构容器镜像构建实战

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

通过层进行高效学习:探索深度神经网络中的层次稀疏表示

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

自然语言处理阅读第二弹

HuggingFace 镜像网站模型库 HuggingFace中bert实现 下游任务介绍重要源码解读 NLP中的自回归模型和自编码模型 自回归&#xff1a;根据上文内容预测下一个可能的单词&#xff0c;或者根据下文预测上一个可能的单词。只能利用上文或者下文的信息&#xff0c;不能同时利用上…...

利用canvas封装录像时间轴拖动(uniapp),封装上传uniapp插件市场

gitee项目地址,项目是一个空项目,其中包含了封装的插件,自己阅读,由于利用了canvas所以在使用中暂不支持.nvue,待优化; 项目也是借鉴了github上的一个项目,timeline-canvas,​​​​​​​ ​​​​​​​...

PDF转为图片

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

隐私计算介绍

这里只对隐私计算做一些概念性的浅显介绍&#xff0c;作为入门了解即可 目录 隐私计算概述隐私计算概念隐私计算背景国外各个国家和地区纷纷出台了围绕数据使用和保护的公共政策国内近年来也出台了数据安全、隐私和使用相关的政策法规 隐私计算技术发展 隐私计算技术安全多方计…...

HTML有哪些列表以及具体的使用!!!

文章目录 一、HTML列表二、列表的应用1、无序列表2、有序列表3、自定义列表 三、总结 一、HTML列表 html的列表有三种&#xff0c;一种是无序列表&#xff0c;一种是有序列表&#xff0c;还有一种为自定义列表。 二、列表的应用 1、无序列表 <ul> <li>无序列表…...

DriveWorks Solo捕获参数(二)

捕获参数-帧 顶门框 现在让我们捕获框架。它由2部分组成&#xff1b;两者都有一个需要捕捉的维度。 1.通过单击“捕获资源管理器”中的标题来激活“捕获的模型”部分。 2.展开框架组件。 3.双击任务窗格树中的模型顶门侧柱。 这将在SOLIDWORKS中打开模型顶门门框&#xff0c;并…...

基于开源的JAVA mongodb jdbc 驱动 使用教程

基于开源的JAVA mongodb jdbc 驱动 使用教程介绍 介绍 本文介绍一款开源的基于JAVA的 Mongodb JDBC 驱动使用教程 开源地址 https://gitee.com/bgong/jdbc-mongodb-driver功能价值 与mybaits融合&#xff1a;复用mybatis的功能特性&#xff0c;如:缓存,if动态判断标签等特…...

[RK-Linux] RK3399使用RK开源SPL,修改U-Boot为FIT打包方式,裁剪trust分区

文章目录 一、启动方式二、FIT打包三、RK3568相关配置参考四、RK3399支持与调试一、启动方式 RK3399平台根据前级Loader代码是否开源,目前有两套启动方式: // 前级loader闭源 BOOTROM => ddr bin => Miniloader => TRUST => U-BOOT => KERNEL // 前级loader…...

【网络安全】-Linux操作系统—VMWare软件

文章目录 VMWare软件的安装选择VMWare版本下载VMWare安装过程 VMWare的常用操作创建新的虚拟机配置虚拟机启动和关闭虚拟机安装VMWare Tools VMWare的克隆和快照克隆&#xff08;Clone&#xff09;快照&#xff08;Snapshot&#xff09; 总结 VMWare是一种流行的虚拟化软件&…...

关于chatgpt一点肤浅认识

001 词向量 用数字向量表示单词。它是计算机更好地理解单词 1、预训练 – 就是先训练一个模型&#xff0c;用于以后特定任务的微调&#xff0c;比如将 BERT这个模型用于特定的NLP任务&#xff0c;比如情感分析 2、one-hot: 用只有一个元素是1&#xff0c;其他是0的向量表示物体…...

Redis结合SpringBoot 基本使用

1.1 简介 1.1.1 概述 Spring Data 中有一个成员 Spring Data Redis&#xff0c;他提供了 RedisTemplate 可以在 Spring 应用中更简便的访问 Redis 以及异常处理及序列化&#xff0c;支持发布订阅等操作。 1.2 RedisTemplate 常见 API   RedisTemplate 针对 jedis 客户端中大…...

JAVA主流日志框架梳理学习及使用

前言&#xff1a;目前市面上有挺多JAVA的日志框架&#xff0c;比如JUL(JDK自带的日志框架),Log4j,Logback,Log4j2等&#xff0c;有人可能有疑问说还有slf4j&#xff0c;不过slf4j不是一种日志框架的具体实现&#xff0c;而是一种日志门面&#xff08;日志门面可以理解为是一种统…...

java多个设计模式解决大量if-else堆积

当面对大量的 if-else 语句时&#xff0c;可以考虑使用以下几种常见的设计模式来减少代码的复杂性和维护成本&#xff1a; 策略模式&#xff08;Strategy Pattern&#xff09;&#xff1a;将各个分支的逻辑封装成不同的策略类&#xff0c;然后通过一个上下文类来根据条件选择合…...

js DOM的一些小操作 获取节点集合Node( getElementsByClassName等)

1. getElementsByClassName(names) 返回文档中所有含有指定类名的节点 document.getElementsByClassName(a) 返回所有类名为a的节点 2.getElementsByName(name) 返回文档中所有指定name的节点。 标签可以有name属性。 3. querySelectorAll(selectors) 返回文档中所有匹配…...