MFC消息处理
1、MFC窗口如何与AfxWndProc建立联系。
当一个新的CWnd派生类创建时,在调用CWnd::CreateEx()过程中,MFC都会安装AfxCbtFilterHook()。这个Hook将拦截HCBT_CREATEWND,将窗体的消息处理函数设置为AfxWndProc()。
- // wincore.cpp 651
- // CWnd::CreateEx函数通过AfxHookWindowCreate函数安插Hook
- BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
- LPCTSTR lpszWindowName, DWORD dwStyle,
- int x, int y, int nWidth, int nHeight,
- HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
- {
- // ...
- // 如果cs中窗体类名为空,PreCreateWindow (见wincore.cpp 714) 将调用
- // AfxEndDeferRegisterClass (见wincore.cpp 4431)注册默认窗口类,
- // 默认注册的窗口消息处理函数是DefWindowProc
- if (!PreCreateWindow(cs))
- {
- PostNcDestroy();
- return FALSE;
- }
- // 安插AfxCbtFilterHook
- AfxHookWindowCreate(this);
- HWND hWnd = ::AfxCtxCreateWindowEx(cs.dwExStyle, cs.lpszClass,
- cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
- cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
- if (!AfxUnhookWindowCreate())
- PostNcDestroy(); // cleanup if CreateWindowEx fails too soon
- // ...
- }
- // wincore.cpp 609
- void AFXAPI AfxHookWindowCreate(CWnd* pWnd)
- {
- _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
- if (pThreadState->m_pWndInit == pWnd)
- return;
- if (pThreadState->m_hHookOldCbtFilter == NULL)
- {
- // 安插_AfxCbtFilterHook,因为_AfxCbtFilterHook()是一个基于计算机训练的hook,
- // 所以windows在激活、创建、销毁、最大化、最小化、移动窗口或改变窗口大小之前,
- // 会调用_AfxCbtFilterHook()
- pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,
- _AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
- if (pThreadState->m_hHookOldCbtFilter == NULL)
- AfxThrowMemoryException();
- }
- pThreadState->m_pWndInit = pWnd;
- }
// wincore.cpp 651
// CWnd::CreateEx函数通过AfxHookWindowCreate函数安插Hook
BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
int x, int y, int nWidth, int nHeight,
HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
{
// ...
// 如果cs中窗体类名为空,PreCreateWindow (见wincore.cpp 714) 将调用
// AfxEndDeferRegisterClass (见wincore.cpp 4431)注册默认窗口类,
// 默认注册的窗口消息处理函数是DefWindowProc
if (!PreCreateWindow(cs))
{
PostNcDestroy();
return FALSE;
}
// 安插AfxCbtFilterHook
AfxHookWindowCreate(this);
HWND hWnd = ::AfxCtxCreateWindowEx(cs.dwExStyle, cs.lpszClass,
cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
if (!AfxUnhookWindowCreate())
PostNcDestroy(); // cleanup if CreateWindowEx fails too soon
// ...
}
// wincore.cpp 609
void AFXAPI AfxHookWindowCreate(CWnd* pWnd)
{
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
if (pThreadState->m_pWndInit == pWnd)
return;
if (pThreadState->m_hHookOldCbtFilter == NULL)
{
// 安插_AfxCbtFilterHook,因为_AfxCbtFilterHook()是一个基于计算机训练的hook,
// 所以windows在激活、创建、销毁、最大化、最小化、移动窗口或改变窗口大小之前,
// 会调用_AfxCbtFilterHook()
pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,
_AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
if (pThreadState->m_hHookOldCbtFilter == NULL)
AfxThrowMemoryException();
}
pThreadState->m_pWndInit = pWnd;
}
_AfxCbtFilterHook通过SetWindowLongPtr函数将窗口的处理函数替换成AfxWndProc(),同时,在CWnd::m_pfnSuper中保存原来的窗口消息处理函数指针。
- // wincore.cpp 472
- LRESULT CALLBACK
- _AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)
- {
- _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
- if (code != HCBT_CREATEWND)
- {
- // 如果不是HCBT_CREATEWND消息,直接忽略。接收到该消息意味着窗口要创建了
- return CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code,
- wParam, lParam);
- }
- LPCREATESTRUCT lpcs = ((LPCBT_CREATEWND)lParam)->lpcs;
- CWnd* pWndInit = pThreadState->m_pWndInit;
- BOOL bContextIsDLL = afxContextIsDLL;
- if (pWndInit != NULL || (!(lpcs->style & WS_CHILD) && !bContextIsDLL))
- {
- // ...
- HWND hWnd = (HWND)wParam;
- WNDPROC oldWndProc;
- if (pWndInit != NULL)
- {
- AFX_MANAGE_STATE(pWndInit->m_pModuleState);
- // connect the HWND to pWndInit...
- pWndInit->Attach(hWnd);
- // allow other subclassing to occur first
- pWndInit->PreSubclassWindow();
- // 消息处理函数指针,用于存储原来的消息处理函数
- WNDPROC *pOldWndProc = pWndInit->GetSuperWndProcAddr();
- // 将窗口的消息处理函数设置成AfxWndProc()
- WNDPROC afxWndProc = AfxGetAfxWndProc();
- oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,
- (DWORD_PTR)afxWndProc);
- if (oldWndProc != afxWndProc)
- *pOldWndProc = oldWndProc; // 保存原来的指针
- pThreadState->m_pWndInit = NULL;
- }
- // ...
- }
- // ...
- }
- // wincore.cpp 1048
- WNDPROC* CWnd::GetSuperWndProcAddr()
- {
- // Note: it is no longer necessary to override GetSuperWndProcAddr
- // for each control class with a different WNDCLASS.
- // This implementation now uses instance data, such that the previous
- // WNDPROC can be anything.
- return &m_pfnSuper;
- }
- // wincore.cpp 392
- WNDPROC AFXAPI AfxGetAfxWndProc()
- {
- // 静态库版本的消息处理函数
- return &AfxWndProc;
- }
// wincore.cpp 472
LRESULT CALLBACK
_AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)
{
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
if (code != HCBT_CREATEWND)
{
// 如果不是HCBT_CREATEWND消息,直接忽略。接收到该消息意味着窗口要创建了
return CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code,
wParam, lParam);
}
LPCREATESTRUCT lpcs = ((LPCBT_CREATEWND)lParam)->lpcs;
CWnd* pWndInit = pThreadState->m_pWndInit;
BOOL bContextIsDLL = afxContextIsDLL;
if (pWndInit != NULL || (!(lpcs->style & WS_CHILD) && !bContextIsDLL))
{
// ...
HWND hWnd = (HWND)wParam;
WNDPROC oldWndProc;
if (pWndInit != NULL)
{
AFX_MANAGE_STATE(pWndInit->m_pModuleState);
// connect the HWND to pWndInit...
pWndInit->Attach(hWnd);
// allow other subclassing to occur first
pWndInit->PreSubclassWindow();
// 消息处理函数指针,用于存储原来的消息处理函数
WNDPROC *pOldWndProc = pWndInit->GetSuperWndProcAddr();
// 将窗口的消息处理函数设置成AfxWndProc()
WNDPROC afxWndProc = AfxGetAfxWndProc();
oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,
(DWORD_PTR)afxWndProc);
if (oldWndProc != afxWndProc)
*pOldWndProc = oldWndProc; // 保存原来的指针
pThreadState->m_pWndInit = NULL;
}
// ...
}
// ...
}
// wincore.cpp 1048
WNDPROC* CWnd::GetSuperWndProcAddr()
{
// Note: it is no longer necessary to override GetSuperWndProcAddr
// for each control class with a different WNDCLASS.
// This implementation now uses instance data, such that the previous
// WNDPROC can be anything.
return &m_pfnSuper;
}
// wincore.cpp 392
WNDPROC AFXAPI AfxGetAfxWndProc()
{
// 静态库版本的消息处理函数
return &AfxWndProc;
}
微软不将AfxWndProc()做为注册窗口过程的原因是DefWindowPorc()可以支持3D控件。这些控件都在微软的CTL3D.dll中。如果系统具有CTL3D功能已经是一种迫切需要,那么应用程序就要覆盖CTL3D的功能(在处理WM_CTLCOLOR消息方面)。为了确保这一点,MFC必须按照以下顺序调用:AfxWndProc()、CTL3D的WndProc()和最后的DefWindowProc()。可见为了确保这一点,微软不得不允许CTL3D在AfxWndProc()之前分类,这就意味着延迟AfxWndProc()的引入。
- // wincore.cpp 375
- LRESULT CALLBACK
- AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
- {
- // special message which identifies the window as using AfxWndProc
- if (nMsg == WM_QUERYAFXWNDPROC)
- return 1;
- // all other messages route through message map
- CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
- if (pWnd == NULL || pWnd->m_hWnd != hWnd)
- return ::DefWindowProc(hWnd, nMsg, wParam, lParam);
- return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam); // 调用AfxCallWndProc
- }
- // wincore.cpp 208
- LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
- WPARAM wParam = 0, LPARAM lParam = 0)
- {
- _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
- MSG oldState = pThreadState->m_lastSentMsg; // 保存最后发送消息
- pThreadState->m_lastSentMsg.hwnd = hWnd;
- pThreadState->m_lastSentMsg.message = nMsg;
- pThreadState->m_lastSentMsg.wParam = wParam;
- pThreadState->m_lastSentMsg.lParam = lParam;
- // Catch exceptions thrown outside the scope of a callback
- // in debug builds and warn the user.
- LRESULT lResult;
- #ifndef _AFX_NO_OCC_SUPPORT
- // special case for WM_DESTROY
- if ((nMsg == WM_DESTROY) && (pWnd->m_pCtrlCont != NULL))
- pWnd->m_pCtrlCont->OnUIActivate(NULL);
- #endif
- // special case for WM_INITDIALOG
- CRect rectOld;
- DWORD dwStyle = 0;
- if (nMsg == WM_INITDIALOG)
- _AfxPreInitDialog(pWnd, &rectOld, &dwStyle); // 使对话框自动置于窗口中间
- // 委托给对象的WindowProc,覆盖该函数,可以在MFC查看某个消息之前处理这个消息
- lResult = pWnd->WindowProc(nMsg, wParam, lParam);
- // more special case for WM_INITDIALOG
- if (nMsg == WM_INITDIALOG)
- _AfxPostInitDialog(pWnd, rectOld, dwStyle);
- pThreadState->m_lastSentMsg = oldState;
- return lResult;
- }
- // wincore.cpp 1737
- LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
- {
- // OnWndMsg does most of the work, except for DefWindowProc call
- LRESULT lResult = 0;
- if (!OnWndMsg(message, wParam, lParam, &lResult)) // wincore.cpp 1746
- lResult = DefWindowProc(message, wParam, lParam);
- return lResult;
- }
// wincore.cpp 375
LRESULT CALLBACK
AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
// special message which identifies the window as using AfxWndProc
if (nMsg == WM_QUERYAFXWNDPROC)
return 1;
// all other messages route through message map
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
if (pWnd == NULL || pWnd->m_hWnd != hWnd)
return ::DefWindowProc(hWnd, nMsg, wParam, lParam);
return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam); // 调用AfxCallWndProc
}
// wincore.cpp 208
LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
WPARAM wParam = 0, LPARAM lParam = 0)
{
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
MSG oldState = pThreadState->m_lastSentMsg; // 保存最后发送消息
pThreadState->m_lastSentMsg.hwnd = hWnd;
pThreadState->m_lastSentMsg.message = nMsg;
pThreadState->m_lastSentMsg.wParam = wParam;
pThreadState->m_lastSentMsg.lParam = lParam;
// Catch exceptions thrown outside the scope of a callback
// in debug builds and warn the user.
LRESULT lResult;
#ifndef _AFX_NO_OCC_SUPPORT
// special case for WM_DESTROY
if ((nMsg == WM_DESTROY) && (pWnd->m_pCtrlCont != NULL))
pWnd->m_pCtrlCont->OnUIActivate(NULL);
#endif
// special case for WM_INITDIALOG
CRect rectOld;
DWORD dwStyle = 0;
if (nMsg == WM_INITDIALOG)
_AfxPreInitDialog(pWnd, &rectOld, &dwStyle); // 使对话框自动置于窗口中间
// 委托给对象的WindowProc,覆盖该函数,可以在MFC查看某个消息之前处理这个消息
lResult = pWnd->WindowProc(nMsg, wParam, lParam);
// more special case for WM_INITDIALOG
if (nMsg == WM_INITDIALOG)
_AfxPostInitDialog(pWnd, rectOld, dwStyle);
pThreadState->m_lastSentMsg = oldState;
return lResult;
}
// wincore.cpp 1737
LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// OnWndMsg does most of the work, except for DefWindowProc call
LRESULT lResult = 0;
if (!OnWndMsg(message, wParam, lParam, &lResult)) // wincore.cpp 1746
lResult = DefWindowProc(message, wParam, lParam);
return lResult;
}
OnWndMsg函数很大,它首先过滤出WM_COMMAND、WM_NOTIFY、WM_ACTIVE和WM_SETCURSOR。对于这几个消息,框架有自己的处理方法。如果不是这几个,OnWndMsg会在消息映射表中查找消息。MFC维护着一个消息映射表入口缓存,可以通过散列值访问它
MFC消息处理的更多相关文章
- 【转】MFC消息处理(一)
原文网址:http://blog.csdn.net/hyhnoproblem/article/details/6182120 1.MFC窗口如何与AfxWndProc建立联系. 当一个新的CWnd派生 ...
- 深入剖析MFC中对于Windows消息处理、运行机制
序: 本人对Windows系统.MFC谈不上有深入的了解,但对MFC本身包装API的机制很有兴趣,特别是读了候老师的<深入浅出MFC>后,感觉到VISUAL C++的Application ...
- MFC下WM_NOTIFY消息处理流程
参考文章:MFC的消息反射机制 在前一篇文章:MFC消息处理流程概述中描述了MFC消息处理的大体流程.由CWnd::OnWndMsg函数可知,当消息为WM_NOTIFY消息时,调用的是virtual ...
- windows消息机制(MFC)
消息分类与消息队列 Windows中,消息使用统一的结构体(MSG)来存放信息,其中message表明消息的具体的类型, 而wParam,lParam是其最灵活的两个变量,为不同的消息类型时,存放数据 ...
- MFC中的几个虚函数
1.PreTranslateMessage()和WindowProc() PreTranslateMessage是消息在送给TranslateMessage函数之前被调用的,通过函数名也可以猜出来.绝 ...
- [转]windows消息机制(MFC)
消息分类与消息队列 Windows中,消息使用统一的结构体(MSG)来存放信息,其中message表明消息的具体的类型, 而wParam,lParam是其最灵活的两个变量,为不同的消息类型时,存放数据 ...
- VC2008中如何为MFC应用程序添加和删除消息响应函数
最近重温<MFC Windows应用程序设计>第二版这本书,里面的代码全部是使用VC6.0写的,我Win7下安装的是VS2008开发环境. VC2008下添加和删除常见的消息响应函数有两种 ...
- C语言中的sizeof()
sizeof,一个其貌不扬的家伙,引无数菜鸟竟折腰,小虾我当初也没少犯迷糊,秉着"辛苦我一个,幸福千万人"的伟大思想,我决定将其尽可能详细的总结一下. 但当我总结的时候才发现,这个 ...
- 全面总结sizeof的用法(定义、语法、指针变量、数组、结构体、类、联合体、位域位段)
一.前言 编译环境是vs2010(32位). <span style="font-size:18px;">#include<iostream> #inclu ...
随机推荐
- 利用GoAccess分析Nginx访问日志
原文链接:https://blog.csdn.net/yown/article/details/56027112 需求:及时得到线上用户访问日志分析统计结果,以便给开发.测试.运维.运营人员提供决策! ...
- angular2配置使用ng2-bootstrap
第一步,安装.进入项目目录 npm install ng2-bootstrap bootstrap --save 第二步,angular-cli 配置 ng2-bootstrap src/.ang ...
- openwrt利用openvpn两网互通
目录 创建证书文件服务器端配置防火墙配置客户端配置uvs-001(远端PC)uvs-002(网关下属设备)测试连接 创建证书文件 安装证书工具 opkg openvpn-easy-rsa 创建证书 b ...
- 解决WinScp连接被拒绝的问题
尝试以下方法: 1) 开启|关闭防火墙(这里需要关闭) sudo ufw enable|disable 2) 开启远程服务 在终端界面输入:service sshd start.结果显示:ssh:un ...
- Java中syso的封装
将syso设置为由开关切换确实是个很不错的思想,这样就不用傻傻的一个个去找不用的syso进行删除或者注释 当然了,由网友所说,这叫日志系统情何以堪,这里想说的是这个开关的思想值得我这小白学习 此为看 ...
- 20145202马超《网络对抗》Exp7 网络欺诈技术防范
本实践的目标理解常用网络欺诈背后的原理,以提高防范意识,并提出具体防范方法.具体有(1)简单应用SET工具建立冒名网站(2)ettercap DNS spoof(3)结合应用两种技术,用DNS spo ...
- 问题集 - console.log在IE下不可用
js中添加如下一段代码即可. if(!window.console){ window.console = {}; } if(!window.console.log){ window.console.l ...
- 基于Spring的最简单的定时任务实现与配置(二)
接上一篇,原本我以为我实现的方式很简单了,在准备写(一)的时候,就去查了查别人是怎么实现定时任务的.不查还好,这一查,发现还有更简单的.所以就会有这篇文章. 本文主要是讨论,在完成Spring 项目搭 ...
- 使用 Fiddler工具模拟post四种请求数据
post请求主体详解: 对于get请求来说没有请求主体entity-body.对于post请求而言,不会对发送请求的数据格式进行限制,理论上你可以发任意数据,但是服务器能不能处理就是另一回事了.服务器 ...
- TPO-11 C1 Use the gym pass
TPO-11 C1 Use the gym pass 第 1 段 1.Listen to a conversation between a student and a university emplo ...