Wxwidgets Source Analysis (4)-Message processing process

Source: Internet
Author: User
Tags wxwidgets

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 gapfnScSendMessagethe 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:

    1. 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.
    2. 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;}

MSWWindowProcis 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:

    1. wm_close: Exit operation, call wxframe::close directly;
    2. wm_size: Call wxframe::handlesize ;
    3. 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.
    4. 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:

    1. 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;
    2. 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);;}  

wxFrameThe 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:

    1. GetEventHandler()The method returns itself, wxFrame itself is inherited from wxEvtHandler ;
    2. ProcessEventmethod 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:

    1. 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;
    2. 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;
    3. Called to ProcessEventLocally perform this object processing;
    4. 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:

  1. There is no mark at this time ShouldProcessOnlyIn , so it will not execute TryBeforeAndHere ;
  2. ProcessEventLocally wxMenuBar is not processed because the handler for this menu is not bound in the object ProcessEventLocally ;
  3. 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::TryBeforeAndHereWill 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 ProcessEventcontinue 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);}

wxFrameIs 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 wxEvtHandlerobject, 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:

    1. 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;
    2. 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

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.