duilib库分析日志 (二) : 消息流程分析

看下CWindowWnd类与CPaintManagerUI类是咋进行消息分发的吧.


1. 先看下CPaintManagerUI类的MessageLoop函数:

void CPaintManagerUI::MessageLoop()
{
    MSG msg = { 0 };
    while( ::GetMessage(&msg, NULL, 0, 0) ) {    // 获取消息
        if( !CPaintManagerUI::TranslateMessage(&msg) ) { // 消息过滤
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg); // 分发到窗口的消息处理窗口中. 也就是调用CWindowWnd类的__WndProc函数或是__ControlProc函数.
        }
    }
}
消息第一次会由CPaintManagerUI类的TranslateMessage消息接收到.


2. 调用CWindowWnd::Create创建窗口.  完成以下操作:

1) 如果要子类下Window的控件(就是系统的控件, 而不是duilib的模拟控件), 就设置__ControlProc函数为消息回调函数.

2)不子类化, 就注册窗口类. 此时设置__WndProc为窗口消息处理回调函数.  

3)用CreateWindowEx API函数创建窗口.

这里先不看子类化相关的, 我要先看明白标准的窗口创建过程.  这也操作后消息就会分发到__WndProc了, 

3. 看下__WndProc函数的定义: 

LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CWindowWnd* pThis = NULL;
    if( uMsg == WM_NCCREATE ) {	// 要在此消息中把类于窗口进行绑定
        LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);	// 来自于CreateWindowEx函数的最后一个参数( 也就是CWindowWnd对象指针了 )
        pThis = static_cast<CWindowWnd*>(lpcs->lpCreateParams);
        pThis->m_hWnd = hWnd;
        ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis));	// 设置到窗口的用户数据中
    } 
    else {
        pThis = reinterpret_cast<CWindowWnd*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));
        if( uMsg == WM_NCDESTROY && pThis != NULL ) {
            LRESULT lRes = ::CallWindowProc(pThis->m_OldWndProc, hWnd, uMsg, wParam, lParam);	// 收到窗口能处理到的最后一个消息了, 要进行收尾工作了.
            ::SetWindowLongPtr(pThis->m_hWnd, GWLP_USERDATA, 0L);	// 取消类对象与窗口的绑定关系
            if( pThis->m_bSubclassed ) pThis->Unsubclass();
            pThis->m_hWnd = NULL;
            pThis->OnFinalMessage(hWnd);
            return lRes;
        }
    }
    if( pThis != NULL ) {
        return pThis->HandleMessage(uMsg, wParam, lParam);  // 在此调用继承类的消息处理函数
    } 
    else {
        return ::DefWindowProc(hWnd, uMsg, wParam, lParam); // 未绑定类对象, 就调用默认的窗口消息处理函数
    }
}


消息第二次就由__WndProc接收到, 然后再传到CWindowWnd类的HandlerMessage函数中.


3. 看看CWindowWnd类的继承类对于HandlerMessage虚函数的实现.

LRESULT CMainWnd::HandleMessage( UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	LRESULT	lRes		= 0;	// 消息处理返回值.
	BOOL	bHandled	= TRUE;	// 消息是否要继续往下传.
	switch ( uMsg )
	{
	case WM_CREATE:	lRes	= OnInitResource( bHandled ); break;  // 进行初始化工作. 比如最重要的XML加载解析工作.
	default:
		bHandled	= FALSE;
	}

	if ( bHandled )
	{
		return lRes;
	}

	if ( m_pm.MessageHandler( uMsg, wParam, lParam, lRes ) )	// 传给CPaintManagerUI::MessageHandler函数进行具体的控件处理工作
	{
		return lRes;
	}

	return CWindowWnd::HandleMessage( uMsg, wParam, lParam );  // 没处理过的就调用CWindowWnd类的默认消息处理函数吧.
}


在这里就是用户要按消息进行具体的处理了. 之后要传到CPaintManagerUI类对象的MessageHandler函数. 未处理的消息就要返回给CWindowWnd类的默认消息处理函数来处理了. 

4.  CPaintManagerUI类的TranslateMessage, MessageHandler函数的内容.

BOOL CPaintManagerUI::TranslateMessage(const LPMSG pMsg)
{
    HWND hwndParent = ::GetParent(pMsg->hwnd); // 获取消息接收窗口的父窗口
    UINT uStyle = GetWindowStyle(pMsg->hwnd); // 获取窗口的样式
    LRESULT lRes = 0;
    for( int i = 0; i < m_aPreMessages.GetSize(); i++ ) { // 这个m_aPreMessage保存着CPaintManagerUI类对象. 
        CPaintManagerUI* pT = static_cast<CPaintManagerUI*>(m_aPreMessages[i]);
        if( pMsg->hwnd == pT->GetPaintWindow() // 消息是否属于当前CPaintManagerUI绑定的窗口
         || (hwndParent == pT->GetPaintWindow() && ((uStyle & WS_CHILD) != 0)) ) // 消息是否为当前窗口中窗口的消息, (如ActiveX控件 )
        {
            if( pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ) return TRUE; // 此时就调用PreMessageHandler过滤函数.
        }
    }
    return FALSE;
}
m_aPreMessage为静态成员变量, 在CPaintManagerUI::Init进行窗口与此类绑定时添加到此变量中. 


5. CPaintManagerUI::PreMessageHandler消息过滤函数.

BOOL CPaintManagerUI::PreMessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& /*lRes*/)
{
    // 遍历当前的消息过滤列表. m_aPreMessageFilter的元素为IMessageFilterUI接口.只一个虚函数MessageHandler. 
    // 用户可以添加此接口的继承类变量到m_aPreMessageFilters列表中. ( 调用AddMessageFilter函数实现 )
    for( int i = 0; i < m_aPreMessageFilters.GetSize(); i++ ) 
    {
        BOOL bHandled = FALSE;
        LRESULT lResult = static_cast<IMessageFilterUI*>(m_aPreMessageFilters[i])->MessageHandler(uMsg, wParam, lParam, bHandled);
        if( bHandled ) {
            return TRUE;
        }
    }
    // 以下是对几个按键消息的过滤.
    // WM_KEYDOWN     检查是否为VK_TAB键, 要进行控件焦点的移动.
    // WM_SYSCHAR     获取与wParam中的字符加速键匹配的控件, 并激活它.
    // WM_SYSKEYDOWN  生成控件事件( 用TEventUI来模拟 )
}



5. CPaintManagerUI::MessageHandler函数.

1) 遍历m_aMessageFilters列表中的IMessageFilterUI接口, 并调用MessageHandler函数, 再次进行相关的消息过滤功能.(与上面的m_aPreMessageFilters类似)

2) 在此会处理窗口的WM_PAINT消息. 显示所有控件的外观与状态. 

3) 处理鼠标事件, 实现控件激活和相关事件.

4) 处理WM_TIMER消息, 所有控件要用CPaintManagerUI的SetTimer, KillTimer等函数实现计时器功能. 

5) 处理CPaintManagerUI类的自定消息, WM_APP + 1与 +2,

WM_APP + 1是用于控件延迟销毁控件对象

WM_APP + 2销毁异步消息的处理. 

( 异步控件消息用CPaintManagerUI::SendNotify函数, 把消息对象添加到m_aAsyncNotify列表中, 再PostMessage函数WM_APP + 2 )

5) 其它基本的窗口相关消息的处理.

CPaintManagerUI把DUILIB内部的事件都是用TEventUI结构的形式调用CControlUI类的Event函数来投递的.








  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值