MFC 窗口创建过程与消息处理

news2024/9/30 5:40:05

目录

钩子简介

代码编写

窗口创建过程分析

消息处理


钩子简介

介绍几个钩子函数,因为它们与窗口创建工程有关

安装钩子函数

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为pFrame
	switch (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 destroyed
		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();            // perhaps delete the C++ object
			return 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 defaults
	wndcls.lpfnWndProc = DefWindowProc;    // 窗口处理函数
	wndcls.hInstance = AfxGetInstanceHandle();
	wndcls.hCursor = afxData.hcurArrow;

一直按F11,程序执行流程走到这个函数中

	if (fToRegister & AFX_WNDFRAMEORVIEW_REG)
	{
		// SDI Frame or MDI Child windows or views - normal colors
		wndcls.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 icon
		pWndCls->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为pFrame
	switch (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 allocations
		pWnd = (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);

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

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

相关文章

Kafka 安装与部署

目录 Kafka 下载 &#xff08;1&#xff09;将 kafka_2.11-2.4.1.tgz 上传至 /opt/software/ &#xff08;2&#xff09;解压安装包至 /opt/module/ [huweihadoop101 ~]$ cd /opt/software/ [huweihadoop101 software]$ tar -zxvf kafka_2.11-2.4.1.tgz -C ../module/&#…

PB开发Windows服务方案

1、项目简介 ​ PB作为一门客户端开发语言&#xff0c;虽然官方并未提供标准的Windows服务开发方案&#xff0c;但使用PB开发Windows服务并非无法实现。自PB9开始&#xff0c;PB提供了PBNI接口&#xff0c;PB与C可以通过这个接口互相调用&#xff0c;而C可以开发Windows服务。…

Java智慧工地数字化云平台源码(SaaS模式)

智慧工地是智慧城市理念在建筑工程行业的具体体现&#xff0c;智慧工地解决方案是建立在高度信息化基础上一种支持人事物全面感知、施工技术全面智能、工作互通互联、信息协同共享、决策科学分析、风险智慧预控的新型信息化手段。围绕人、机、料、法、环等关键要素&#xff0c;…

【算法与数据结构】376、LeetCode摆动序列

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题难点在于要考虑到不同序列的情况&#xff0c;具体来说要考虑一下几种特殊情况&#xff1a; 1、上…

python学习,2.简单的数据类型

1.了解数及运算 整数&#xff1a;1&#xff0c;2&#xff0c;3。 运算符&#xff1a;加减乘除&#xff0c;**(乘方) 浮点数&#xff1a;python将所有带小数点的数称为浮点数。 这一块和别的语言有些不一样&#xff0c; 像C&#xff0c;分为float&#xff0c;double&#x…

河南开放大学形成性考核 平时作业 统一参考资料

试卷代号&#xff1a;1288 现代管理原理 参考试题&#xff08;开卷&#xff09; 一、单项选择&#xff08;下列选项中只有一个答案是准确的&#xff0c;请将其序号填入括号中。每小题2分&#xff0c;共20分&#xff09; 1.“凡事预则立&#xff0c;不预则废”&#xff0c;说…

极简Windows本机下载安装启动zookeeper

1.下载zookeeper Apache Download Mirrors 注意&#xff01;&#xff01;&#xff01;&#xff1a;安装的路径不要用中文 2.生成zoo.cfg文件 复制zookeeper的conf目录下的zoo_simple.cfg文件&#xff0c;并重命名为zoo.cfg 修改zoo.cfg文件中的路径(data&#xff0c;log…

Navicat 16最新操作

Navicat 16最新操作 1 知识小课堂1.1 Navicat 161.2 其他数据库连接工具 2 下载和安装2.1 下载2.2 安装 1 知识小课堂 1.1 Navicat 16 Navicat 16是一款功能强大的数据库管理工具&#xff0c;可以创建多个连接&#xff0c;方便管理不同类型的数据库&#xff0c;包括MySQL、Ora…

如何利用研发效能度量工具分析代码评审的效率、质量与瓶颈?

代码评审&#xff08;Code Review&#xff09;是保障代码质量中一个非常重要的环节&#xff0c;也是保证项目交付质量的关键一环。代码评审的开展对于产品质量提升、工程素养提升、研发团队的技术分享交流&#xff0c;或是完善团队代码规范&#xff0c;都能起到重要的促进作用。…

​【EI会议征稿通知】第三届智能系统、通信与计算机网络国际学术会议(ISCCN 2024)

第三届智能系统、通信与计算机网络国际学术会议&#xff08;ISCCN 2024&#xff09; 2024 3rd International Conference on Intelligent Systems, Communications and Computer Networks 第三届智能系统、通信与计算机网络国际学术会议&#xff08;ISCCN 2024&#xff09;将…

期货股市联动(期股联动助推资本市场上扬)

期股联动——期货股市助推资本市场上扬 随着我国资本市场的不断发展&#xff0c;期货和股票这两个市场也在逐渐紧密地联系起来。期货和股票的相互作用是一种“期股联动”&#xff0c;它能够促进资本市场的上扬。 期货与股票市场 期货市场是一种标准化的场外交易市场&#xf…

OpenAI发布AGI安全风险框架!董事会可随时叫停GPT-5等模型发布,奥特曼也得乖乖听话

OpenAI 再次强调模型安全性&#xff01;AGI 安全团队 Preparedness 发布模型安全评估与监控框架&#xff01; 这两天关注 AI 圈新闻的小伙伴们可能也有发现&#xff0c;近期的 OpenAI 可谓进行了一系列动作反复强调模型的“安全性”。 前有 OpenAI 安全系统&#xff08;Safety…

HarmonyOS(十五)——状态管理之@Prop装饰器(父子单向同步)

上一篇文章我们认识了状态管理的State装饰器&#xff08;组件内状态&#xff09;&#xff0c;接下来我们学习另外一个状态管理装饰器Prop装饰器。 Prop装饰的变量可以和父组件建立单向的同步关系。Prop装饰的变量是可变的&#xff0c;但是变化不会同步回其父组件。 说明&#…

可视化数据监控大屏网页界面,数据大屏模版PS资料(免费UI源文件)

数据大屏模板在大数据领域被广泛应用&#xff0c;其优势在于能够将复杂的数据通过图形、图表等方式呈现出来&#xff0c;使数据更易于理解。数据大屏模板可以用来进行数据分析。通过对数据的比较、趋势分析、异常检测等&#xff0c;可以发现数据中的规律和问题&#xff0c;为决…

SQL进阶理论篇(十):数据库中的锁

文章目录 简介按照锁的粒度进行划分从数据库管理的角度进行划分从程序员的角度进行划分为什么共享锁会发生死锁&#xff1f;参考文献 简介 索引和锁&#xff0c;是数据库中的两个核心知识点。 索引的相关知识点&#xff0c;在之前的几章里我们已经介绍的差不多了。接下来我们…

CEC2013(python):五种算法(HHO、WOA、GWO、DBO、PSO)求解CEC2013(python代码)

一、五种算法简介 1、哈里斯鹰优化算法HHO 2、鲸鱼优化算法WOA 3、灰狼优化算法GWO 4、蜣螂优化算法DBO 5、粒子群优化算法PSO 二、5种算法求解CEC2013 &#xff08;1&#xff09;CEC2013简介 参考文献&#xff1a; [1] Liang J J , Qu B Y , Suganthan P N , et al. P…

安卓开发学习---kotlin版---笔记(三)

网络 安卓主页的网络框架&#xff1a;OkHttp 在OkHttp的基础上进行封装的&#xff1a;Retrofit框架&#xff0c;更常使用 OkHttp学习 在使用网络请求的时候&#xff0c;先添加网络访问权限&#xff1a; <uses-permission android:name"android.permission.INTERNET&…

【数据结构】栈的使用|模拟实现|应用|栈与虚拟机栈和栈帧的区别

目录 一、栈(Stack) 1.1 概念 1.2 栈的使用 1.3 栈的模拟实现 1.4 栈的应用场景 1. 改变元素的序列 2. 将递归转化为循环 3. 括号匹配 4. 逆波兰表达式求值 5. 出栈入栈次序匹配 6. 最小栈 1.5 概念区分 一、栈(Stack) 1.1 概念 栈&#xff1a;一种特殊的线性表&…

数据库——水果商店进阶

智能2112杨阳 一、目的与要求&#xff1a; 综合运用SQL语言相关知识如变量、游标、函数、触发器等解决实际问题。 二、内容&#xff1a; 设计并完成以下实验&#xff0c;要求附上源码&#xff08;非截图&#xff09;&#xff0c;测试效果截图 在订单详情表orderitems插入新…