• 6.2 窗口句柄映射


    6.2.1 向CWnd对象分发消息

    一个线程中可能(很可能)有不止一个窗口,因此也会有多个对应的CWnd对象。每个CWnd对象只响应发送给本窗口的消息,那么,如何将线程接受到的消息交给不同的CWnd对象呢?

    Windows是通过窗口函数将消息发送给应用程序的。窗口函数的第一个参数hWnd指示了接收此消息的窗口,我们只能通过窗口句柄hWnd的值找到对应的CWnd对象的地址。这就要求:

    (1)只安排一个窗口函数。窗口函数的作用仅仅是找到处理该消息的CWnd对象的地址,再把它交给此CWnd对象。增加窗口函数对寻找CWnd对象不会有帮助,因为窗口函数的参数是固定的。

    (2)记录窗口句柄到CWnd对象指针的映射关系。

    窗口函数是全局函数,将它命名为AfxWndProc,其实现代码在CWnd类的实现文件WINCORE.CPP中。假设CWnd类用于接收消息的成员函数的名称是WindowProc,则AfxWndProc的伪代码如下。

    LRESULT __stdcall AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
     { 
         CWnd* pWnd = ... // 通过hWnd找到对应的CWnd指针;  
         ASSERT(pWnd != NULL); 
         ASSERT(pWnd->m_hWnd == hWnd);  
         ... // 将消息交给CWnd对象处理return pWnd->WindowProc(nMsg, wParam, lParam); 
     } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    AfxWndProc是程序中所有窗口的消息处理函数,它先找到管理窗口的CWnd对象,再将消息交给该对象处理,并返回消息的处理结果。图6.1显示了此函数的功能。

    解决问题(2),只要使用CHandleMap类就可以了。由于Windows为每个线程维护一个消息队列,如图6.1所示,线程1执行过程中消息处理函数AfxWndProc只能收到本线程中的窗口发来的消息,所以窗口的句柄映射应该是线程私有的。CWnd类对象和它所控制的窗口都在同一个模块中,因此窗口句柄映射是模块线程私有的。所以最终我们将记录窗口句柄映射的CHandleMap对象定义在模块线程状态类AFX_MODULE_THREAD_STATE中。

    class AFX_MODULE_THREAD_STATE : public CNoTrackObject // _AFXSTAT_.H文件 
    { 
         …… // 其他成员 
         // 窗口句柄映射  
         CHandleMap* m_pmapHWND; 
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述
    m_pmapHWND指针所指向的CHandleMap对象记录了本模块内当前线程的窗口句柄映射,这里的当前线程是指访问此变量的线程。下面的函数afxMapHWND用于访问当前线程中窗口句柄映射。

    CHandleMap* afxMapHWND(BOOL bCreate = FALSE) // 定义在WINCORE.CPP文件 
    {
    	AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
    	if(pState->m_pmapHWND == NULL && bCreate)
    	{
    		pState->m_pmapHWND = new CHandleMap();
    	}
    	return pState->m_pmapHWND;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    需要访问当前线程的窗口句柄映射时,只要调用afxMapHWND函数即可。如果仅仅是查询,就将bCreate参数的值设置为FALSE;如果是向映射中添加新项,就要将TRUE传给bCreate参数,此时,afxMapHWND会检查当前线程中的CHandleMap对象是否创建,如果没有就创建它。

    CWnd类提供以下4个成员函数来管理窗口句柄映射,这些函数都是先调用afxMapHWND函数得到CHandleMap指针,然后再进行相关操作。

    class CWnd : public CCmdTarget // _AFXWIN.H文件 
    { 
         …… // 其他成员  
         static CWnd* FromHandle(HWND hWnd);  
         static CWnd* FromHandlePermanent(HWND hWnd);  
         BOOL Attach(HWND hWndNew);  
         HWND Detach(); 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    给定窗口句柄hWnd,FromHandle和FromHandlePermanent函数都会试图返回指向CWnd对象的指针。如果没有CWnd对象附加到此窗口句柄上,FromHandle函数会创建一个临时的CWnd对象,并附加到hWnd上,而FromHandlePermanent函数只返回NULL。但是,我们的CHandleMap类并没有实现自动创建临时对象的功能,所以这两个函数的功能没有区别。函数的实现代码如下。

    CWnd* CWnd::FromHandle(HWND hWnd)
    {
    	CHandleMap* pMap = afxMapHWND(TRUE); // 如果不存在则创建一个CHandleMap对象
    	ASSERT(pMap != NULL);
    	return (CWnd*)pMap->FromHandle(hWnd);
    }
    
    CWnd* CWnd::FromHandlePermanent(HWND hWnd)
    {
    	CHandleMap* pMap = afxMapHWND();
    	CWnd* pWnd = NULL;
    	if(pMap != NULL)
    	{
    		// 仅仅在永久映射(非临时映射)中查找——不创建任何新的CWnd对象
    		pWnd = (CWnd*)pMap->LookupPermanent(hWnd);
    	}
    	return pWnd;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这两个函数的实现不与任何CWnd类的对象有关,而且又是负责查询全局(相对于线程)窗口句柄映射的,所以将它们声明为static类型,做为全局函数来使用。

    Attach函数附加一个窗口句柄到当前CWnd对象,即添加一对映射项;Detach函数将窗口句柄从当前CWnd对象分离,即移除一对映射项。这些操作都是在永久映射中进行的,其实现代码如下。

    BOOL CWnd::Attach(HWND hWndNew)
    {
    	ASSERT(m_hWnd == NULL);	 // 仅仅附加一次
    	ASSERT(FromHandlePermanent(hWndNew) == NULL); // 必须没有在永久映射中
    
    	if(hWndNew == NULL)
    		return FALSE;
    
    	CHandleMap* pMap = afxMapHWND(TRUE);	// 如果不存在则创建一个CHandleMap对象
    	ASSERT(pMap != NULL);
    
    	pMap->SetPermanent(m_hWnd = hWndNew, this); // 添加一对映射
    	return TRUE;
    }
    
    HWND CWnd::Detach()
    {
    	HWND hWnd = m_hWnd;
    	if(hWnd != NULL)
    	{
    		CHandleMap* pMap = afxMapHWND(); // 如果不存在不去创建
    		if(pMap != NULL)
    			pMap->RemoveHandle(hWnd);
    		m_hWnd = NULL;
    	}
    	return hWnd;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    每创建一个窗口,就调用Attach函数将新的窗口句柄附加到CWnd对象,在此窗口销毁的时候再调用Detach函数取消上面的附加行为。这样,在整个窗口的生命周期内,就会存在一个此窗口句柄hWnd到CWnd对象指针pWnd的映射项,在消息处理函数AfxWndProc中,能够轻易地完成图6.1所示的消息分发的功能,如下代码所示。

    CWnd* pWnd = CWnd::FromHandlePermanent(hWnd); // 通过hWnd找到对应的CWnd指针; 
    return pWnd->WindowProc(nMsg, wParam, lParam); // 将消息交给CWnd对象处理 
    
    • 1
    • 2
  • 相关阅读:
    C. Building Permutation
    了解MySQL InnoDB多版本MVCC(Multi-Version Concurrency Control)
    Netty简介及简单客户端/服务端示例代码
    【数据结构】八大排序算法(内含思维导图和画图分析)
    《嵌入式 – GD32开发实战指南》第19章 程序加密
    【BurpSuite】插件学习之Software Vulnerability Scanner
    【C语言】病人信息管理系统
    好心情精神心理科医生:轻度抑郁症需要治疗吗?
    java计算机毕业设计天津城建大学校友录管理系统源程序+mysql+系统+lw文档+远程调试
    HTTP学习
  • 原文地址:https://blog.csdn.net/qq_36314864/article/details/125516688