
本文共 8253 字,大约阅读时间需要 27 分钟。
APP主循环
MainLoop
前面的wxApp的启动代码可以看到,执行完成wxApp::OnInit()
函数后,接着就执行wxApp::OnRun()
函数进入App的主循环,wxApp
继承自wxAppBase
,所以实际调用的是wxApp::OnRun()
,过程如下:
wxAppBase::OnRun()
-> wxAppConsole::OnRun()
-> wxAppConsoleBase::MainLoop()
调用关系如下:
// src/common/appbase.cppint wxAppBase::OnRun() { return wxAppConsole::OnRun(); }int wxAppConsoleBase::OnRun(){ return MainLoop();}
下面继续分析wxAppConsoleBase::MainLoop()
的代码:
- 构建消息循环辅助对象
wxEventLoopBaseTiedPtr
; - 调用当前App的
OnLaunched()
方法; - 调用主循环
Run()
函数,但是这个Run函数到底是谁的函数呢?
// src/common/appbase.cppint wxAppConsoleBase::MainLoop(){ wxEventLoopBaseTiedPtr mainLoop(&m_mainLoop, CreateMainLoop()); if (wxTheApp) wxTheApp->OnLaunched(); return m_mainLoop ? m_mainLoop->Run() : -1;}wxEventLoopBase *wxAppConsoleBase::CreateMainLoop(){ return GetTraits()->CreateEventLoop();}wxEventLoopBase* wxGUIAppTraits::CreateEventLoop(){ return new wxEventLoop;}
消息循环对象的创建
上文遗留一个问题,Run函数到底是哪个函数,这个问题肯定在wxEventLoopBaseTiedPtr
对象中,我们看看这个消息循环辅助对象的创建:
wxEventLoopBaseTiedPtr
从哪里来的呢,这里需要重点关注宏展开:
- 使用
wxDEFINE_TIED_SCOPED_PTR_TYPE
宏创建出来的,wxDEFINE_TIED_SCOPED_PTR_TYPE(wxEventLoopBase)
创建了wxEventLoopBaseTiedPtr类型;wxEventLoopBaseTiedPtr
继承自wxEventLoopBasePtr
,父类是通过wxDEFINE_SCOPED_PTR_TYPE
宏创建的,对于此对象来说创建的就是wxEventLoopBasePtr。
- 操作中需要重点关注的2个函数:
T * operator->() const
和T & operator*() const
:
- 参考构造函数可以得出,这个变了保存的就是
wxApp::m_mainLoop
变量的地址,同时在构造函数中将wxApp::CreateMainLoop()
赋值给了wxApp::m_mainLoop
变量;- 这两个函数是重载操作符,通过
->
操作实际使用的的是内部保存的*m_ptr
变量,这样在通过m_mainLoop->Run()
调用时,实际调用的是wxApp::m_mainLoop->Run()
函数;
源代码如下:
// src/common/appbase.cpp// 类型 wxEventLoopBaseTiedPtr 的定义wxDEFINE_TIED_SCOPED_PTR_TYPE(wxEventLoopBase)// include/wx/scopedptr.h// wxDEFINE_TIED_SCOPED_PTR_TYPE 宏定义,可以看到这里的定义:// 1. 定义了 wxEventLoopBaseTiedPtr 类型// 2. 对应的父类是 wxEventLoopBasePtr#define wxDEFINE_TIED_SCOPED_PTR_TYPE(T) \ wxDEFINE_SCOPED_PTR_TYPE(T) \ class T ## TiedPtr : public T ## Ptr \ { \ public: \ T ## TiedPtr(T **pp, T *p) \ : T ## Ptr(p), m_pp(pp) \ { \ m_pOld = *pp; \ *pp = p; \ } \ private: \ T **m_pp; \ T *m_pOld; \ };// 进而我们看下父类 wxEventLoopBasePtr 的定义// 改类中 重载*和->操作符#define wxDEFINE_SCOPED_PTR_TYPE(T) \ wxDECLARE_SCOPED_PTR(T, T ## Ptr) \ wxDEFINE_SCOPED_PTR(T, T ## Ptr) #define wxDECLARE_SCOPED_PTR(T, name) \class name \{ \ ... // 重载*和->操作符,很关键 T & operator*() const \ { \ wxASSERT(m_ptr != NULL); \ return *m_ptr; \ } \ T * operator->() const \ { \ wxASSERT(m_ptr != NULL); \ return m_ptr; \ } \};
再回头我们看下 wxAppConsoleBase::CreateMainLoop()
函数的实现,此对象返回了一个新创建的wxEventLoop
对象,过程如下:
- 调用
CreateMainLoop
这个函数是父类的函数; wxAppConsoleBase::GetTraits()
会调用CreateTraits
来创建traits,这里CreateTraits
是虚方法,而且wxApp的继承关系是wxAppw -> wxAppBase
,所以实际调用的是wxAppBase::CreateTraits()
,所以这个loopBase实际是调用wxGUIAppTraits::CreateEventLoop()
实现消息循环对象的创建,最终创建了 wxEventLoop 对象;
// src/common/appbase.cpp// wxEventLoopBase *wxAppConsoleBase::CreateMainLoop(){ return GetTraits()->CreateEventLoop();}// src/common/appcmn.cpp// 虚方法,调用最后一次的实现wxAppTraits *wxAppBase::CreateTraits(){ return new wxGUIAppTraits;}wxAppTraits *wxAppConsoleBase::GetTraits(){ // FIXME-MT: protect this with a CS? if ( !m_traits ) { m_traits = CreateTraits(); wxASSERT_MSG( m_traits, wxT("wxApp::CreateTraits() failed?") ); } return m_traits;}// src/common/app.cppwxEventLoopBase* wxGUIAppTraits::CreateEventLoop(){ return new wxEventLoop;}
消息循环
上面的代码分析,我们知道消息循环对象是wxEventLoop
类型的,此对象保存在wxAppConsoleBase::m_mainLoop
变量中,接着调用m_mainLoop->Run()
执行(下面的分析都是基于windows系统,其他的系统类似):
跟踪代码,先理清楚 wxEventLoop
的继承关系:
wxEventLoop -> wxGUIEventLoop -> wxMSWEventLoopBase -> wxEventLoopManual -> wxEventLoopBase
接着Run
函数实际调用的是wxEventLoopBase::Run()
:
// src/common/apploopcmn.cppint wxEventLoopBase::Run(){ // ...忽略非关键流程的代码 // Finally really run the loop. return DoRun();}
DoRun
函数是wxEventLoopBase
定义的纯虚方法,所以这个函数会调用最接近wxEventLoop
的实现,这里调用的是wxEventLoopManual::DoRun()
,继续看此函数的实现(只保留关键代码):
- 正常的消息循环,所有的用户消息都在这里接收和派发,如果收到
WM_QUIT
消息则退出此循环,进入到退出前剩余消息的循环; - 退出前剩余消息的循环,检查剩余的Pending状态的消息,如果有则处理之,所有Pending状态的消息都处理完成后,退出此循环,函数返回。
int wxEventLoopManual::DoRun(){ // this is the event loop itself for ( ;; ) { // a message came or no more idle processing to do, dispatch // all the pending events and call Dispatch() to wait for the // next message if ( !ProcessEvents() ) { // we got WM_QUIT break; } } for ( ;; ) { bool hasMoreEvents = false; if ( wxTheApp && wxTheApp->HasPendingEvents() ) { wxTheApp->ProcessPendingEvents(); hasMoreEvents = true; } if ( Pending() ) { Dispatch(); hasMoreEvents = true; } if ( !hasMoreEvents ) break; } return m_exitcode;}
继续跟踪 wxEventLoopManual::ProcessEvents()
的处理:
- 检查是否有Pending的消息,如果有则处理之,这些消息是在收到本消息前还未处理的消息,所以优先处理,如果是程序退出请求,则完成并退出;
- 否则继续调用
Dispatch
处理当前消息。
bool wxEventLoopManual::ProcessEvents(){ if ( wxTheApp ) { wxTheApp->ProcessPendingEvents(); if ( m_shouldExit ) return false; } return Dispatch();}
Dispatch
是虚方法,此方法是体系结构相关的,需要实现在各体系结构中,对于本流程来说,调用的是wxGUIEventLoop::Dispatch()
,这个函数的处理过程就是获取到下一条消息,然后处理。
函数中使用到了另外两个虚方法GetNextMessage
和ProcessMessage
,分别调用的是wxMSWEventLoopBase::GetNextMessage
和wxGUIEventLoop::ProcessMessage
// src/msw/evtloop.cpp// 体系结构相关的函数,用户派发消息bool wxGUIEventLoop::Dispatch(){ MSG msg; if ( !GetNextMessage(&msg) ) return false; ProcessMessage(&msg); return true;}// 调用Win32API执行windows系统的消息收发bool wxMSWEventLoopBase::GetNextMessage(WXMSG* msg){ const BOOL rc = ::GetMessage(msg, NULL, 0, 0); ... return true;}
消息派发
上文中讲述了消息循环的过程,接着我们看消息的派发过程:
消息派发是由wxGUIEventLoop::ProcessMessage
处理的,先预处理,也就是本窗口处理,如果是本窗口的消息则处理掉,否则再调用::DispatchMessage
分发给对应的窗口。
// 派发消息void wxGUIEventLoop::ProcessMessage(WXMSG *msg){ // give us the chance to preprocess the message first if ( !PreProcessMessage(msg) ) { // if it wasn't done, dispatch it to the corresponding window ::TranslateMessage(msg); ::DispatchMessage(msg); }}
我们先看看消息的预处理过程wxGUIEventLoop::PreProcessMessage
:
- 通过msg的HWND来获取wxWindow指针,通过调用
wxGetWindowFromHWND
实现,具体如何获取到的后文有介绍; - tooltip消息的处理,鼠标移动时调用;
- accelerators消息,这种消息处理优先级最高;
- 其他消息的处理。
注意到消息处理流程:从本窗口开始,如果本窗口不处理则丢给父窗口处理,直到最顶层窗口,如果还没有处理则返回失败,继续调用系统函数分发之。
bool wxGUIEventLoop::PreProcessMessage(WXMSG *msg){ HWND hwnd = msg->hwnd; wxWindow *wndThis = wxGetWindowFromHWND((WXHWND)hwnd); wxWindow *wnd;#if wxUSE_TOOLTIPS // we must relay WM_MOUSEMOVE events to the tooltip ctrl if we want it to // popup the tooltip bubbles if ( msg->message == WM_MOUSEMOVE ) { // we should do it if one of window children has an associated tooltip // (and not just if the window has a tooltip itself) if ( wndThis->HasToolTips() ) wxToolTip::RelayEvent((WXMSG *)msg); }#endif // wxUSE_TOOLTIPS // try translations first: the accelerators override everything for ( wnd = wndThis; wnd; wnd = wnd->GetParent() ) { if ( wnd->MSWTranslateMessage((WXMSG *)msg)) return true; if ( wnd->IsTopLevel() ) break; } // now try the other hooks (kbd navigation is handled here) for ( wnd = wndThis; wnd; wnd = wnd->GetParent() ) { if ( wnd->MSWProcessMessage((WXMSG *)msg) ) return true; if ( wnd->IsTopLevel() ) break; } // no special preprocessing for this message, dispatch it normally return false;}
总结
到此为止,消息的循环派发过程就已经可以正常使用了,在windows系统中,调用Win32的::DispatchMessage
可以将消息发送给指定的窗口,实际的过程就是消息派发线程调用该窗口的WinProc
函数。
发表评论
最新留言
关于作者
