文档章节

WINDOWS消息机制(五):消息推送

西昆仑
 西昆仑
发布于 2012/07/07 16:38
字数 1285
阅读 787
收藏 4

在上篇中,介绍了消息网络的整体布局,这篇要介绍的是,消息进来之后,如何顺着整个消息网络,找到自己对应的处理函数。

MFC 消息分类

1 命令消息(WM_COMMAND)

比如菜单项的选择,工具栏按钮点击等触发产生的消息。所有派生自CCmdTarget 的类都有能力接收WM_COMMAND 消息。

2 标准消息(WM_XXX)

比如窗口创建,窗口销毁等。所有派生自CWnd 的类才有资格接收标准消息。

3 通告消息(WM_NOTIFY)

这是有控件向父窗口发送的消息,标示控件本身状态的变化。比如下拉列表框选项的改变CBN_SELCHANGE 和树形控件的TVN_SELCHANGED 消息都是通告消息。

Window 9x 版及以后的新控件通告消息不再通过WM_COMMAND 传送,而是通过WM_NOTIFY 传送, 但是老控件的通告消息, 比如CBN_SELCHANGE 还是通过WM_COMMAND 消息发送。

4 自定义消息

在MFC 编程中,可以使用自定义消息。使用自定义消息需要遵循一定的规范,并编写消息响应函数,该例子在本系列文章《WINDOW消息机制(一):向窗体发送消息》中已有示例,此处不再赘述。

 

COMMOND消息引发的调用过程:

调用堆栈图:

函数AfxWindProc时MFC中消息推动引擎的入口点,所有的消息,在Dispatch后,由该函数进行推动,并找到匹配的处理函数。参数很简单:消息所属窗口的句柄、消息类型及其相关参数。该函数应该是运行在CWinApp的run函数中的,有证据: 在调用堆栈的最下面,就是Run函数。

mfc80d.dll!CWinThread::Run()
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);
	ASSERT(pWnd != NULL);					
	ASSERT(pWnd==NULL || pWnd->m_hWnd == hWnd);
	
        //若窗口指针为NULL或者句柄不匹配,则采用系统默认处理函数进行处理。
        if (pWnd == NULL || pWnd->m_hWnd != hWnd)
		return ::DefWindowProc(hWnd, nMsg, wParam, lParam);

        //信息匹配,调用AfxCallWndProc
	return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam); }
LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
	WPARAM wParam = 0, LPARAM lParam = 0)
{
	......
        //删除了一些特殊消息处理,只保留关键代码
	// delegate to object's WindowProc
	lResult = pWnd->WindowProc(nMsg, wParam, lParam);
        ......	
}

 这边就涉及到一个多态问题,我们调用pWnd->WindowProc,根据pWnd的具体类型,CDialog,CFrameWnd,CView等具体类型,调用具体的WindowProc.

CFrameWnd中没有定义WindowProc函数,所以调用CWnd::WindowProc
CView中没有定义WindowProc函数,所以调用CWnd::WindowProc
CDoc及其父类CCmdTarget肯定没有WindowProc函数,因为他们不是窗口哈
CDialog中没有定义WindowProc函数,所以调用CWnd::WindowProc

在CWnd中有如下声明:

// for processing Windows messages
virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);

 

基本逻辑:


定义如下:

BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{       
        //针对COMMAND类型的消息进行处理
	if (message == WM_COMMAND)
	{
		if (OnCommand(wParam, lParam))
		{
			lResult = 1;
			goto LReturnTrue;
		}
		return FALSE;
	}

	//针对NOTIFY类型的消息进行处理
	if (message == WM_NOTIFY)
	{
		NMHDR* pNMHDR = (NMHDR*)lParam;
		if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))
			goto LReturnTrue;
		return FALSE;
	}

	//各种SEPCIAL CASE,不一一列举,被我删除,以免占用太多的篇幅
        ......

        //获取消息MAP
	const AFX_MSGMAP* pMessageMap; 
        pMessageMap = GetMessageMap();

	UINT iHash; 
        iHash = (LOWORD((DWORD_PTR)pMessageMap) ^ message) & (iHashMax-1);
	winMsgLock.Lock(CRIT_WINMSGCACHE);
	AFX_MSG_CACHE* pMsgCache; pMsgCache = &_afxMsgCache[iHash];
	const AFX_MSGMAP_ENTRY* lpEntry;
	if (message == pMsgCache->nMsg && pMessageMap == pMsgCache->pMessageMap)
	{
		//在MSGCACHE中命中了消息
		lpEntry = pMsgCache->lpEntry;
		winMsgLock.Unlock();
		if (lpEntry == NULL) //若没有对应的处理函数,返回false,由DefWindowProc处理
			return FALSE;

		// 根据消息类型:标准WINDOWS消息以及用户自定义消息,分别进行处理
		if (message < 0xC000)
			goto LDispatch;
		else
			goto LDispatchRegistered;
	}else
	{
		//在当前MsgCache中未找到,则到BaseMessageMap中寻找
		pMsgCache->nMsg = message;
		pMsgCache->pMessageMap = pMessageMap;

		for (/* pMessageMap already init'ed */; pMessageMap->pfnGetBaseMap != NULL;
			pMessageMap = (*pMessageMap->pfnGetBaseMap)())
		{
			// Note: catch not so common but fatal mistake!!
			//      BEGIN_MESSAGE_MAP(CMyWnd, CMyWnd)
			ASSERT(pMessageMap != (*pMessageMap->pfnGetBaseMap)());
			if (message < 0xC000) //根据消息大小,判断消息为WINDOWS标准消息
			{
				// constant window message
				if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,
					message, 0, 0)) != NULL)
				{
					pMsgCache->lpEntry = lpEntry;
					winMsgLock.Unlock();
					goto LDispatch;
				}
			}
			else
			{
				//根据消息大小判断消息为用户自定义消息
				lpEntry = pMessageMap->lpEntries;
				while ((lpEntry = AfxFindMessageEntry(lpEntry, 0xC000, 0, 0)) != NULL)
				{
					UINT* pnID = (UINT*)(lpEntry->nSig);
					ASSERT(*pnID >= 0xC000 || *pnID == 0);
						// must be successfully registered
					if (*pnID == message)
					{
						pMsgCache->lpEntry = lpEntry;
						winMsgLock.Unlock();
						goto LDispatchRegistered;
					}
					lpEntry++;      // keep looking past this one
				}
			}
		}

		pMsgCache->lpEntry = NULL;
		winMsgLock.Unlock();
		return FALSE;
	}

LDispatch:  //标准的WINDOWS消息
	ASSERT(message < 0xC000);
	mmf.pfn = lpEntry->pfn;
        ......
	goto LReturnTrue;

LDispatchRegistered:    // 处理用户自定义的消息
        ......
        go to LReturnTrue;

LReturnTrue:
	if (pResult != NULL)
		*pResult = lResult;
	return TRUE;
}

根据上文所述,如果是WINDOWS标准消息以及用户自定义消息,那么消息的流向是从子类流向父类(即当前类无法处理,则交由父类的消息MAP搞定,若还不行,继续上传,如果都不行,则由WINDOWS默认的处理函数进行处理)

注:本文说明了消息推送的一个整体过程,但是在此当中是有所区分的,标准的WINDOWS消息以及用户的自动以消息才去的是自底向上推送,但是命令消息涉及到一个横向的消息推送,就在下文说明吧。

© 著作权归作者所有

共有 人打赏支持
西昆仑

西昆仑

粉丝 137
博文 141
码字总数 102735
作品 0
南京
高级程序员
私信 提问
Push Notification (推送通知服务)

概述 通常情况下,用户主动向服务器发出请求,服务器才会向用户传送数据,推送服务(Push Notification)的出现改变了这一状况,其思想是将浏览器主动请求信息改变为服务器主动发送信息。服务...

失足处男的倒霉孩子
2013/12/26
5.1K
1
Windows Phone 7 学习笔记 - 推送通知服务

大家都知道windows phone值允许一个第三方的应用程序在前台运行,所以应用程序就不能在后台从服务器上取数据。所以微软提供推送通知服务给第三方应用程序取得更新通知的消息,让用户觉得这个...

虫虫
2012/02/21
1K
0
编程思想之消息机制

1.编程思想之消息机制 什么是消息? 何为消息?消息就是带有某种信息的信号,如你用鼠标点击一个窗口会产生鼠标的消息,键盘输入字符会产生键盘的消息,一个窗口大小的改变也会产生消息。 消...

火力全開
2016/10/09
10
0
海鑫智圣:物联网漫谈之MQTT协议

什么是MQTT协议   MQTT(消息队列遥测传输协议)是IBM在1999年专门针对物联网等应用场景来制订的轻量级双向消息传输协议,它主要是为了解决物联网上使用到的设备的互相通信的问题,以及这些...

数通畅联
2016/06/27
160
0
从消息机制谈到观察者模式

从简单的例子开始 同样,我们还是先看一个简单例子:创建一个窗口实现加法的计算功能。其效果如下: 图1: 加法计算 Calculator.java: import javax.swing.;import javax.swing.border.Bevel...

Sheamus
2016/03/14
34
0

没有更多内容

加载失败,请刷新页面

加载更多

mogodb服务

部署MongoDB 官网: https://www.mongodb.com/download-center/community 创建mongo数据目录 mkdir /data/mongodb 二进制部署 wget -c https://fastdl.mongodb.org/linux/mongodb-linux-x8......

以谁为师
昨天
3
0
大神教你Debian GNU/Linux 9.7 “Stretch” Live和安装镜像开放下载

Debian项目团队于昨天发布了Debian GNU/Linux 9 "Stretch" 的第7个维护版本更新,重点修复了APT软件管理器中存在的安全漏洞。在敦促每位用户尽快升级系统的同时,Debian团队还发布了Debian ...

linux-tao
昨天
3
0
PHP 相关配置

1. php-fpm的pool 编辑php-fpm配置文件php-fpm.con vim /usr/local/php/etc/php-fpm.conf //在[global]部分增加以下内容 include = etc/php-fpm.d/*.conf # 相当与Nginx的虚拟主机文件 “vho......

Yue_Chen
昨天
2
0
EOS主网数据同步指南

本文介绍如何安装EOS节点软件并接入EOS主网,主要包括以下内容: 如何安装EOS节点软件 如何配置EOS节点软件接入主网 如何启动EOS节点软件与主网数据同步 如何检查主网数据同步进度 如何正确地...

汇智网教程
昨天
3
0
matlab-线性代数 齐次方程组 基础解系和通解

  matlab : R2018a 64bit     OS : Windows 10 x64 typesetting : Markdown    blog : my.oschina.net/zhichengjiu    gitee : gitee.com/zhichengjiu   code clearclc% x1+2*......

志成就
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部