Directory
- Message processing procedures
- How the message arrives wxwidgets
- Win32 the conversion of messages to wxwidgets messages
- Menu Message Processing
- Message processing chain (based on Wxevthandler)
- Message processing chain (based on Wxwindow)
- Summarize
How message processing messages arrive wxwidgets
Windows program has its own set of rules, ::SendMessage
is the MS provided by the Windows Messaging interface, the user calls this interface will enter into the MS System library program, this interface specifies the target HWND and message parameters, the Windows system will find the specified HWND, and then through the gapfnScSendMessage
the interface invokes the user's message handler function.
So every time we see a message handler, it gapfnScSendMessage
goes through the call.
Win32 the conversion of messages to wxwidgets messages
Wxwidgets the window handler function is specified when the window is registered wxWndProc
, the system calls this function to process the message when the message is received.
The process is as follows:
- Invokes the
wxFindWinFromHandle
hwnd specified by the current message to find the corresponding Wxwindow, and if not, it needs to be associated with a recently created window.
- The window's
MSWWindowProc
method is then invoked to process the message.
// Main window procLRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){ wxWindowMSW *wnd = wxFindWinFromHandle(hWnd); // 关联窗口 if ( !wnd && gs_winBeingCreated ) { wxAssociateWinWithHandle(hWnd, gs_winBeingCreated); wnd = gs_winBeingCreated; gs_winBeingCreated = NULL; wnd->SetHWND((WXHWND)hWnd); } LRESULT rc; if ( wnd && wxGUIEventLoop::AllowProcessing(wnd) ) rc = wnd->MSWWindowProc(message, wParam, lParam); else rc = ::DefWindowProc(hWnd, message, wParam, lParam); return rc;}
MSWWindowProc
is a virtual function that is unique to the Windows platform, and for the frame class wxFrame::MSWWindowProc
, the message executes different functions based on the message type:
- wm_close: Exit operation, call
wxframe::close
directly;
- wm_size: Call
wxframe::handlesize
;
- WM_COMMAND: Special handling is required, it is clear in the comments that Wxwidgets provides a set of its own mechanisms for invoking messages between the parent window and the child window, so the functionality provided by the WIN32 is not used anymore.
- If it does not handle it, the parent class
Wxframebase::mswwindowproc
is processed;
WXLRESULT wxFrame::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam){ WXLRESULT rc = 0; bool processed = false; switch ( message ) { case WM_CLOSE: processed = !Close(); break; case WM_SIZE: processed = HandleSize(LOWORD(lParam), HIWORD(lParam), wParam); break; case WM_COMMAND: { WORD id, cmd; WXHWND hwnd; UnpackCommand((WXWPARAM)wParam, (WXLPARAM)lParam, &id, &hwnd, &cmd); HandleCommand(id, cmd, (WXHWND)hwnd); processed = true; } break; } if ( !processed ) rc = wxFrameBase::MSWWindowProc(message, wParam, lParam); return rc;}
Take the wxFrame::Close
function example, wxFrame
directly use the method of the parent class, the wxWindowBase::Close
content is constructs the wxwidgets message type, then invokes the HandleWindowEvent
method carries on the message processing.
bool wxWindowBase::Close(bool force){ wxCloseEvent event(wxEVT_CLOSE_WINDOW, m_windowId); event.SetEventObject(this); event.SetCanVeto(!force); // return false if window wasn‘t closed because the application vetoed the // close event return HandleWindowEvent(event) && !event.GetVeto();}
For WM_SIZE
, the way to go more, call the relationship as follows, the final wxWindowMSW::HandleSize
processing, here will generate a wxSizeEvent
message, and then submit the message to the HandleWindowEvent
processing:
wxFrame::MSWWindowProc() -> wxTopLevelWindowMSW::MSWWindowProc() -> wxWindowMSW::MSWWindowProc() -> wxWindowMSW::MSWHandleMessage() -> wxWindowMSW::HandleSize()bool wxWindowMSW::HandleSize(int WXUNUSED(w), int WXUNUSED(h), WXUINT wParam){ switch ( wParam ) { case SIZE_RESTORED: wxSizeEvent event(GetSize(), m_windowId); event.SetEventObject(this); processed = HandleWindowEvent(event); }}
That is, no matter what the message, the end is to convert the wxevent message, and then call the current window of the HandleWindowEvent
function processing, it is important to note that the message is now the wxwidgets internal message type.
Menu Message Processing
Next we verify the menu message processing in wxwidgets, first add the static message map table in the Wxwidgets project, and implement the corresponding code:
const long ID_MenuUser = wxNewId();BEGIN_EVENT_TABLE(debugWXFrame,wxFrame) EVT_MENU(ID_MenuUser, debugWXFrame::OnCheckMenu) EVT_UPDATE_UI(ID_MenuUser, debugWXFrame::OnCheckMenuUI)END_EVENT_TABLE()void debugWXFrame::OnCheckMenu(wxCommandEvent& event) {}void debugWXFrame::OnCheckMenuUI(wxUpdateUIEvent& event) {}
We then look at the command message processing, which is used very much, received command type message after HandleCommand
processing, the frame class only handles toolbars, menus and accelerator key commands, the implementation process:
- The invocation
FindItemInMenuBar
is based on the ID specified in the message to find the corresponding wxMenuItem
, the implementation of this function is to obtain the current wxFrame
menubar, and then loop query, the more the menu query speed is also slower;
- When MenuItem is found, it is called
wxFrameBase::ProcessCommand(mitem)
to continue processing:
bool Wxframe::handlecommand (wxword ID, wxword cmd, Wxhwnd control) {#if wxuse_menus#if defined (wince_without _commandbar) if (Gettoolbar () && Gettoolbar ()->findbyid (ID)) return Gettoolbar ()->mswcommand (cmd, ID), #endif///need to handle the menu and accelerator commands from the items//on our menu bar, base Wxwin Dow class already handles the rest if (!control && (cmd = = 0/* Menu */| * cmd = 1//accel */)) {#if Wxu Se_menus_native if (!wxcurrentpopupmenu) #endif//wxuse_menus_native {Wxmenuitem * const Mitem = Finditeminmenubar ((signed short) ID); if (Mitem) return ProcessCommand (Mitem); }} #endif//Wxuse_menus return Wxframebase::handlecommand (ID, cmd, control);;}
wxFrame
The class itself is not implemented ProcessCommand
, so the method of the parent class is called wxFrameBase::ProcessCommand
, and the key process code part calls Wxmenu's SendEvent
function to continue processing.
The focus is on what is called here menu->SendEvent
, so the next call switches to the Wxmenu class.
bool wxFrameBase::ProcessCommand(wxMenuItem *item){ ... wxMenu* const menu = item->GetMenu(); return menu->SendEvent(item->GetId(), checked);}
The implementation of Wxmenu SendEvent
is a wxMenuBase::SendEvent
method, at which point we are in the Wxmenu object, so the call GetEventHandler()
obtains the Evnthandler of the Wxmenu.
Focusing on win and MB two variables, Wxmenu first uses its own wxevthandler for processing, and then checks whether it is associated with win or menubar, if any, it also adds a tag event.SetWillBeProcessedAgain()
, That is, commands need to be wen or menubar handled.
Win and MB Two variables represent different menu types, MB is the menu in the menu bar, and Win is the context menu.
Here we call the Yes mb->HandleWindowEvent(event)
;
bool Wxmenubase::sendevent (int itemid, int checked) {wxcommandevent event (wxevt_menu, Itemid); Event. Seteventobject (this); Event. Setint (checked); wxwindow* Const WIN = GetWindow (); wxmenubar* const MB = GetMenuBar (); Wxevthandler *handler = Geteventhandler (); if (handler) {if (Win | | mb) event. Setwillbeprocessedagain (); Didn't think of invoking this function's scene? if (Handler->safelyprocessevent (event)) return true; }//If This menu was part of the menu bar, process the event There:this'll//also propagate it upwards to the WI Ndow containing the menu bar. if (MB) return mb->handlewindowevent (event); Try The window The menu is popped up from. if (win) return Win->handlewindowevent (event); Not processed. return false;}
At this point, we will switch to the owner of the, wxMenuBar::HandleWindowEvent
wxMenuBar
wxMenuBar
inherit from the wxWindow
class, it is also a separate window, so the function of this call is wxWindowBase::HandleWindowEvent
, the calling process is as follows:
GetEventHandler()
The method returns itself, wxFrame
itself is inherited from wxEvtHandler
;
ProcessEvent
method is provided by the parent class wxEvtHandler
;
bool wxWindowBase::HandleWindowEvent(wxEvent& event) const{ return GetEventHandler()->SafelyProcessEvent(event);}bool wxEvtHandler::SafelyProcessEvent(wxEvent& event){ return ProcessEvent(event);}
Then call ProcessEvent
, this function is universal, as long as it is inherited from the wxEvtHandler
call here, below we are in two cases to illustrate the situation:
General handling:
- Processing the global filter, the
wxEvtHandler
internal creation of static variables ms_filterList
, to save the wxEventFilter
list, the user can call the static function to wxEvtHandler::AddFilter
add filters to the system, in particular, refer to the 过滤器使用
chapter;
- Called
TryBeforeAndHere
only for this object processing, call this function need to rely on a token ShouldProcessOnlyIn
, the token will be set only in, that is DoTryChain
, only enter the DoTryChain
function will have this tag;
- Called to
ProcessEventLocally
perform this object processing;
- Called
TryAfter
to perform the processing of the parent;
The first case: the wxMenuBar
processing in:
At this point we wxMenuBar
are in, this class inherits from wxEvtHandler
, so here's the actual call wxEvtHandler::ProcessEvent
, the process:
- There is no mark at this time
ShouldProcessOnlyIn
, so it will not execute TryBeforeAndHere
;
ProcessEventLocally
wxMenuBar
is not processed because the handler for this menu is not bound in the object ProcessEventLocally
;
- Into the
TryAfter
process of executing the parent;
The second case: wxFrame
processing in:
The process for Wxframe ProcessEvent
has the same effect, but it is ProcessEventLocally
processed in.
bool wxEvtHandler::ProcessEvent(wxEvent& event){ // 处理过滤器 if ( !event.WasProcessed() ) { for ( wxEventFilter* f = ms_filterList; f; f = f->m_next ) { int rc = f->FilterEvent(event); if ( rc != wxEventFilter::Event_Skip ) { return rc != wxEventFilter::Event_Ignore; } } } // 只有执行了 DoTryChain() 之后,ShouldProcessOnlyIn()方法才会返回true // 具体可以参考 wxEventProcessInHandlerOnly 辅助类 if ( event.ShouldProcessOnlyIn(this) ) return TryBeforeAndHere(event); // Try to process the event in this handler itself. if ( ProcessEventLocally(event) ) { return !event.GetSkipped(); } if ( TryAfter(event) ) return true; // No handler found anywhere, bail out. return false;}
It is divided into two cases, namely:
The first case: the wxMenuBar
processing in:
wxEvtHandler::TryBeforeAndHere
Will invoke TryBefore
| | TryHereOnly
, TryBefore
We temporarily ignore, the point is TryHereOnly
, in the TryHereOnly
function, the first to find the dynamic binding table, and then find the static binding table, if there is a handler function in the table call it, otherwise will not be called, for this process to be, wxMenubar
Does not bind any handler functions, so TryHereOnly
return false, and the TryBeforeAndHere
function returns false, so you need to continue calling DoTryChain
.
The second case: wxFrame
processing in:
For Wxframe, the message handler for the menu in this example is bound to a static bound, so it is if ( GetEventHashTable().HandleEvent(event, this) )
processed in and returns True.
bool wxEvtHandler::ProcessEventLocally(wxEvent& event){ return TryBeforeAndHere(event) || DoTryChain(event);}bool wxEvtHandler::TryBeforeAndHere(wxEvent& event){ return TryBefore(event) || TryHereOnly(event);}bool wxEvtHandler::TryHereOnly(wxEvent& event){ // Handle per-instance dynamic event tables first if ( m_dynamicEvents && SearchDynamicEventTable(event) ) return true; // Then static per-class event tables if ( GetEventHashTable().HandleEvent(event, this) ) return true; return false;}
Continue to enter wxMenuBar
DoTryChain
, here is the purpose of the message delivery by setting Eventhandlerchain, but in the wxwidgets system, the wxWindow
process of inheritance is explicitly not used in this way for message passing, but through wxWindow
Its own parent-child relationship for message passing, so for wxMenuBar
that, this GetNextHandler
must return is empty, so DoTryChain
return false, and wxMenuBar
ProcessEventLocally
return FALSE.
bool wxEvtHandler::DoTryChain(wxEvent& event){ for ( wxEvtHandler *h = GetNextHandler(); h; h = h->GetNextHandler() ) { wxEventProcessInHandlerOnly processInHandlerOnly(event, h); if ( h->ProcessEvent(event) ) { event.Skip(false); return true; } if ( !event.ShouldProcessOnlyIn(h) ) { event.Skip(); return true; } } return false;}
Back to the first two steps, at this point we can only pass wxMenuBar
the continuation of the call TryAfter(event)
, the previous article has described the wxMenuBar
inheritance from wxWindows
, so here is called wxWindowBase::TryAfter
, in the following call, the window as long as it is possible to receive the message normally, the parent will be found, and then call the ProcessEvent
continue processing.
In this example, wxMenuBar
the parent is wxFrame
, so the continuation of the call continues to be wxFrame
ProcessEvent
processed
bool wxWindowBase::TryAfter(wxEvent& event){ if ( event.ShouldPropagate() ) { if ( !(GetExtraStyle() & wxWS_EX_BLOCK_EVENTS) ) { wxWindow *parent = GetParent(); if ( parent && !parent->IsBeingDeleted() ) { wxPropagateOnce propagateOnce(event, this); return parent->GetEventHandler()->ProcessEvent(event); } } } return wxEvtHandler::TryAfter(event);}
wxFrame
Is the same as the ProcessEvent
call order wxMenuBar
, except that wxFrame
it ProcessEventLocally
returns true in the method, causing the entire processing process to complete.
Message processing chain (based on Wxevthandler)
Wxwidgets provides a means by which users can inject message handlers into the Wxevthandler class without using inheritance, and the implementation method is to customize a Wxeventhandler class, and then call to add the wxEvtHandler::SetNextHandler()
message processing code to the specified wxEvtHandler
object, use an example:
The following code is used to put the menu processing into a separate wxEvtHandler
class by wxEvtHandler::SetNextHandler
means of linking this handler object to wxFrame
the top:
Note: You must use wxEvtHandler::SetNextHandler
method injection and cannot be called directly SetNextHandler
, because wxWindow
this method is overloaded and the direct call does not take effect.
const long ID_MenuUser = wxNewId();class CMyEvtHandler : public wxEvtHandler {public: bool ProcessEvent(wxEvent& event) { if (event.GetEventType() == wxEVT_MENU && event.GetId() == ID_MenuUser) { wxMessageBox("Menu processed in chain"); return true; } event.Skip(); return false; }};debugWXFrame::debugWXFrame(wxWindow* parent,wxWindowID id){ ... Menu1->Append(ID_MenuUser, _("Chain Menu")); wxEvtHandler::SetNextHandler(new CMyEvtHandler);}
When actually called, it goes into the wxFram::DoTryChain
function, and as we add to the Wxframe wxEvtHandler
, we take out the call-by-line on the relationship chain wxEvtHandler
.
Notice that when invoked in this way, it is preset to process the tag only in the current object, and by wxEventProcessInHandlerOnly processInHandlerOnly(event, h);
implementation, when the processInHandlerOnly
mark disappears after the destruction, the scope is only within the body of the loop.
bool wxEvtHandler::DoTryChain(wxEvent& event){ for ( wxEvtHandler *h = GetNextHandler(); h; h = h->GetNextHandler() ) { wxEventProcessInHandlerOnly processInHandlerOnly(event, h); if ( h->ProcessEvent(event) ) { event.Skip(false); return true; } if ( !event.ShouldProcessOnlyIn(h) ) { event.Skip(); return true; } } return false;}
Message processing chain (based on Wxwindow)
In addition to injecting the message-processing class into the current object, there is another way to wxEvtHandler
invoke the wxWindow::PushEventHandler
message-processing class into the current Windows stack, which differs in two ways:
- Injected into
wxEvtHandler
, Wxwidgets will first handle the current object wxEvtHandler
, and then check whether the current object wxEvtHandler
has links to other wxEvtHandler
, if any, call it;
- By
wxWindow::PushEventHandler
injecting the current message processing object that overwrites the Wxwindow class, when it finds the message-handling object of the Wxwindow object, only the last inserted one is called, so in order to ensure that the normal message can be processed, we must ProcessEvent()
method to invoke the next Wxevthandler in the
Example:
const long ID_MenuUser_wxWinChain = wxNewId();class CMyWinEvtHandler : public wxEvtHandler {public: bool ProcessEvent(wxEvent& event) { if (event.GetEventType() == wxEVT_MENU && event.GetId() == ID_MenuUser_wxWinChain) { wxMessageBox("Menu processed in chain, id="+wxString::Format("%d", event.GetId())); return true; } if (GetNextHandler()) return GetNextHandler()->ProcessEvent(event); return false; }};debugWXFrame::debugWXFrame(wxWindow* parent,wxWindowID id){ ... Menu1->Append(ID_MenuUser_wxWinChain, _("Chain Menu")); PushEventHandler(new CMyWinEvtHandler);}
Take the menu processing example, wxMenuBar
when calling to wxWindowBase::TryAfter
find the parent class call, will call the parent class directly, for our example, will call the CMyWinEvtHandler::ProcessEvent
method directly, so we ProcessEvent
must pay attention to the implementation, we need to call GetNextHandler()->ProcessEvent(event)
to ensure the normal processing of other messages.
if ( !(GetExtraStyle() & wxWS_EX_BLOCK_EVENTS) ){ wxWindow *parent = GetParent(); if ( parent && !parent->IsBeingDeleted() ) { wxPropagateOnce propagateOnce(event, this); return parent->GetEventHandler()->ProcessEvent(event); }}
Note: In the test process found there will always be a the last handler of the wxWindow stack should have this window as next handler
hint, this is the wxwidgets library itself code bug, window chain does not need a doubly linked list, the window itself wxEvtHandler
does not need to point to any wxEvtHandler
, because it is the last.
Summarize
This section mainly describes the message processing process for wxwidgets.
Wxwidgets Source Analysis (4)-Message processing process