3消息循环中的消息处理
来源:互联网 发布:网络维护实训周报 编辑:程序博客网 时间:2024/05/21 22:25
1.简介
duilib中消息的流程十分复杂,窗口想处理消息,可以通过重载以下几个函数来实现:
- virtual LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled);- virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);- virtual void Notify(TNotifyUI& msg)
2.消息类型
(参考:http://www.cppblog.com/suiaiguo/archive/2009/07/18/90412.html)
从消息的发送途径来看,消息可以分成2种:队列消息和非队列消息。消息队列可以分成系统消息队列和线程消息队列。系统消息队列由Windows维护,线程消息队列则由每个GUI线程自己进行维护。队列消息送到系统消息队列,然后到线程消息队列,一般来讲,系统总是将消息Post在消息队列的末尾(有时为了效率,也会有些小聪明:如同一个窗口的多个 WM_PAINT被合并成一个 WM_PAINT 消息)。非队列消息直接送给目的窗口过程。
对于队列消息,最常见的是鼠标和键盘触发的消息,例如WM_MOUSERMOVE, WM_CHAR等消息,还有一些其它的消息,例如:WM_PAINT、 WM_TIMER和WM_QUIT。当鼠标、键盘事件被触发后,相应的鼠标或键盘驱动程序就会把这些事件转换成相应的消息,然后输送到系统消息队列,由 Windows系统去进行处理。Windows系统则在适当的时机,从系统消息队列中取出一个消息,然后把取出的消息送往创建窗口的线程的相应队列。线程看到自己的消息队列中有消息,就从队列中取出来,发送到合适的窗口过程去处理。
非队列消息将会绕过系统队列和消息队列,直接将消息发送到窗口过程,。系统发送非队列消息通知窗口,系统发送消息通知窗口。例如,当用户激活一个窗口系统发送WM_ACTIVATE, WM_SETFOCUS, WM_SETCURSOR。这些消息通知窗口它被激活了。非队列消息也可以由当应用程序调用系统函数产生。例如,当程序调用SetWindowPos系统发送WM_WINDOWPOSCHANGED消息。一些函数也发送非队列消息,如SendMessage()。
3.消息循环
注意:CPaintManagerUI::MessageLoop() 函数为静态成员函数
void CPaintManagerUI::MessageLoop(){ MSG msg = { 0 }; while( ::GetMessage(&msg, NULL, 0, 0) ) { if( !CPaintManagerUI::TranslateMessage(&msg) ) { ::TranslateMessage(&msg); //将按键转化为ASCII字符 ::DispatchMessage(&msg); //将消息分发到特定窗口的窗口过程中,消息处理完成才会返回 } }}
3.1 分发到窗口过程之前
消息到来时,首先由 CPaintManagerUI::TranslateMessage(&msg) 函数进行过滤,只有通过过滤后的消息,才能分发到各个窗口。 TranslateMessage() 函数同样为静态成员函数,其实现如下(简化):
bool CPaintManagerUI::TranslateMessage(const LPMSG pMsg){ //1.当窗口有父窗口时,循环搜索当前的hwnd所以对应的窗口或其长辈窗口(父窗口、祖父窗口); //2.当窗口无父窗口时,循环搜索当前的hwnd所以对应的窗口; //如果找到,则处理;否则直接返回false CPaintManagerUI* pT = static_cast<CPaintManagerUI*>(m_aPreMessages[i]); if (pT->TranslateAccelerator(pMsg)) return true; if( pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ) return true; return false;}
其中 m_aPreMessages 为CPaintManagerUI数组,每个元素对应于每个窗口。下面将分别介绍TranslateAccelerator() 函数和 PreMessageHandler() 函数。
3.1.1 函数 TranslateAccelerator()
win32提供的同名函数 TranslateAccelerator() 有两个功能:进行消息翻译,并将消息投递到消息队列中,故如果 TranslateAccelerator(msg) 返回为true时,将不能再进行 ::TranslateMessage() 和 ::DispatchMessage(&msg)。 CPaintManagerUI的TranslateAccelerator直接对消息进行了处理,与win32提供的同名函数效果基本相同。
bool CPaintManagerUI::TranslateAccelerator(LPMSG pMsg){ for (int i = 0; i < m_aTranslateAccelerator.GetSize(); i++) { LRESULT lResult = static_cast<ITranslateAccelerator *>(m_aTranslateAccelerator[i])->TranslateAccelerator(pMsg); if( lResult == S_OK ) return true; } return false;}
3.1.2 函数 PreMessageHandler()
PreMessageHandler()函数用于消息过滤,过滤器实现 IMessageFilterUI 接口中的MessageHandler()接口函数;通过 CPaintManagerUI::AddMessageFilter(IMessageFilterUI* pFilter) 函数实现过滤器的添加。
bool CPaintManagerUI::PreMessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& /*lRes*/){ //使用过滤器对消息过滤 for( int i = 0; i < m_aPreMessageFilters.GetSize(); i++ ) { bool bHandled = false; IMessageFilterUI filter = static_cast<IMessageFilterUI*>(m_aPreMessageFilters[i]); LRESULT lResult = filter->MessageHandler(uMsg, wParam, lParam, bHandled); if( bHandled ) return true; } //常规过滤一些消息 switch( uMsg ) { case WM_KEYDOWN: case WM_SYSCHAR: case WM_SYSKEYDOWN: } return false;}
将 IMessageFilterUI 列于此处:
class IMessageFilterUI{public: virtual LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled) = 0;};
将 AddMessageFilter() 列于此处:
bool CPaintManagerUI::AddMessageFilter(IMessageFilterUI* pFilter){ ASSERT(m_aMessageFilters.Find(pFilter)<0); return m_aMessageFilters.Add(pFilter);}
3.2 分发到窗口过程之后
分发到窗口后,将由窗口的窗口过程回调函数进行处理。在窗口创建之前,即WM_NCCREATE消息时,将CWindowWnd对象指针保存在GWLP_USERDATA中,在处理其他消息中,利用此分别调用不同窗口的处理函数:pThis->HandleMessage(uMsg, wParam, lParam)。
继承CWindowWnd的窗口可以通过重载HandleMessage()函数从而个性化处理各种消息。
LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ CWindowWnd* pThis = NULL; if( uMsg == WM_NCCREATE ) { //将CWindowWnd指针保存到hwnd所指向的内核对象中(即窗口) } else { pThis = reinterpret_cast<CWindowWnd*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA)); if( uMsg == WM_NCDESTROY && pThis != NULL ) { //重置hwnd所指向的内核对象(即窗口) } } if( pThis != NULL ) { return pThis->HandleMessage(uMsg, wParam, lParam); } else { return ::DefWindowProc(hWnd, uMsg, wParam, lParam); }}
从上可以看出,每个窗口对象的 HandleMessage() 函数基本上可理解为该窗口的窗口过程函数;其中 HandleMessage() 为CWindowWnd中定义的虚函数。
下面将 WindowImplBase 重载的 HandleMessage() 列出:
LRESULT WindowImplBase::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam){ LRESULT lRes = 0; BOOL bHandled = TRUE; switch (uMsg) { case WM_CREATE: lRes = OnCreate(uMsg, wParam, lParam, bHandled); break; case WM_CLOSE: lRes = OnClose(uMsg, wParam, lParam, bHandled); break; case WM_DESTROY: lRes = OnDestroy(uMsg, wParam, lParam, bHandled); break; #if defined(WIN32) && !defined(UNDER_CE) case WM_NCACTIVATE: lRes = OnNcActivate(uMsg, wParam, lParam, bHandled); break; case WM_NCCALCSIZE: lRes = OnNcCalcSize(uMsg, wParam, lParam, bHandled); break; case WM_NCPAINT: lRes = OnNcPaint(uMsg, wParam, lParam, bHandled); break; case WM_NCHITTEST: lRes = OnNcHitTest(uMsg, wParam, lParam, bHandled); break; case WM_GETMINMAXINFO: lRes = OnGetMinMaxInfo(uMsg, wParam, lParam, bHandled); break; case WM_MOUSEWHEEL: lRes = OnMouseWheel(uMsg, wParam, lParam, bHandled); break; #endif case WM_SIZE: lRes = OnSize(uMsg, wParam, lParam, bHandled); break; case WM_CHAR: lRes = OnChar(uMsg, wParam, lParam, bHandled); break; case WM_SYSCOMMAND: lRes = OnSysCommand(uMsg, wParam, lParam, bHandled); break; case WM_KEYDOWN: lRes = OnKeyDown(uMsg, wParam, lParam, bHandled); break; case WM_KILLFOCUS: lRes = OnKillFocus(uMsg, wParam, lParam, bHandled); break; case WM_SETFOCUS: lRes = OnSetFocus(uMsg, wParam, lParam, bHandled); break; case WM_LBUTTONUP: lRes = OnLButtonUp(uMsg, wParam, lParam, bHandled); break; case WM_LBUTTONDOWN: lRes = OnLButtonDown(uMsg, wParam, lParam, bHandled); break; case WM_MOUSEMOVE: lRes = OnMouseMove(uMsg, wParam, lParam, bHandled); break; case WM_MOUSEHOVER: lRes = OnMouseHover(uMsg, wParam, lParam, bHandled); break; default: bHandled = FALSE; break; } if (bHandled) return lRes; lRes = HandleCustomMessage(uMsg, wParam, lParam, bHandled); if (bHandled) return lRes; if (m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes)) return lRes; return CWindowWnd::HandleMessage(uMsg, wParam, lParam);}
HandleCustomMessage()函数为 WindowImplBase 定义的虚函数,继承WindowImplBase的窗口可以重写该函数。
注意最后的 m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes),CPaintManagerUI中的 MessageHandler() 函数已经帮我们实现了大量的功能。 所有的notify都是在此处理的。 后面将对notify的流程进行分析。现将 m_PaintManager.MessageHandler() 简单粘贴如下:
bool CPaintManagerUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes){ //消息的notify TNotifyUI* pMsg = NULL; while( pMsg = static_cast<TNotifyUI*>(m_aAsyncNotify.GetAt(0)) ) { m_aAsyncNotify.Remove(0); if( pMsg->pSender != NULL ) { if( pMsg->pSender->OnNotify ) pMsg->pSender->OnNotify(pMsg); } for( int j = 0; j < m_aNotifiers.GetSize(); j++ ) { static_cast<INotifyUI*>(m_aNotifiers[j])->Notify(*pMsg); } delete pMsg; } // Cycle through listeners for( int i = 0; i < m_aMessageFilters.GetSize(); i++ ) { bool bHandled = false; LRESULT lResult = static_cast<IMessageFilterUI*>(m_aMessageFilters[i])->MessageHandler(uMsg, wParam, lParam, bHandled); if( bHandled ) { lRes = lResult; return true; } } // Custom handling of events switch( uMsg ) { case WM_APP + 1: case WM_CLOSE: case WM_ERASEBKGND: case WM_PAINT: //重要,负责绘制窗口中的各个控件 .... } //消息的notify pMsg = NULL; while( pMsg = static_cast<TNotifyUI*>(m_aAsyncNotify.GetAt(0)) ) { m_aAsyncNotify.Remove(0); if( pMsg->pSender != NULL ) { if( pMsg->pSender->OnNotify ) pMsg->pSender->OnNotify(pMsg); } for( int j = 0; j < m_aNotifiers.GetSize(); j++ ) { static_cast<INotifyUI*>(m_aNotifiers[j])->Notify(*pMsg); } delete pMsg; } return false;}
4.总结
通过上面的分析,目前适合窗口重载的消息处理函数有以下几个:
//窗口过程之前virtual LRESULT IMessageFilterUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, bool& bHandled) = 0;//进入窗口过程中之后virtual LRESULT CWindowWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);virtual LRESULT WindowImplBase::HandleCustomMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
当然可能还会有一些其他函数,只是上面的这几个比较重要。记住!!!
- 3消息循环中的消息处理
- 3消息循环中的消息处理附图
- juce中的消息循环及其处理
- 消息和消息处理之消息循环
- MFC中的消息循环
- MFC中的消息循环
- Chrome中的消息循环
- chromium中的消息循环
- iOS中的消息循环
- Windows OS 消息泵(消息循环处理)
- Windows OS 消息泵(消息循环处理)
- VC++中的消息处理
- MFC中的消息处理
- powerplant中的消息处理
- Delphi中的消息处理
- BADI中的消息处理
- Delphi中的消息处理
- 消息处理中的TranslateMessage
- 如何在Spring官网下载jar包
- add custom font on iOS
- 分享几个小小的python爬虫供大家娱乐(人民日报要闻---to be continued )
- Swift-单例
- [React Native混合开发]React Native for iOS之布局实战
- 3消息循环中的消息处理
- [React Native混合开发]React Native for iOS之布局实战(二)
- X264函数功能总结
- Linux 磁盘管理
- 【u004】数列
- [React Native混合开发]React Native for iOS之UI组件
- Partition List
- [React Native混合开发]React Native for iOS之应用
- Jersey采用模板Freemarker输出