Comparison between ATL and MFC message distribution mechanism-thoughts from Kingsoft open source code (2)

Source: Internet
Author: User

The blog has been migrated to: http://kulv.sinaapp.com /.

Comparison between ATL and MFC message distribution mechanism-thoughts drawn from Kingsoft open source code (2)

Part of this article: http://blog.csdn.net/hw_henry2008/archive/2011/05/29/6453676.aspx

 

After finishing the message delivery method of ATL, we will continue the implementation mechanism of MFC.

II. Implementation of MFC message distribution:

First, let's look at the registration window:
Take the dialog box as an example. After calling domodal, enter:

Int_ptr cdialog: domodal () <br/>{< br/> // · Prepare resources <br/> // disable parent (before creating DIALOG) <br/> hwnd hwndparent = premodal (); // obtain the handle of the parent window and call afxhookwindowcreate. This is the key <br/> afxunhookwindowcreate (); <br/> // · <br/> afxhookwindowcreate (this); <br/> If (createdlgindirect (lpdialogtemplate, <br/> cwnd: fromhandle (hwndparent ), hinst) <br/> {<br/> // · <br/> verify (runmodalloop (dwflags) = m_nmodalresult ); <br/> // · <br/>}</P> <p> hwnd cdialog :: premodal () <br/>{< br/> // · <br/> hwnd = cwnd: getsafeowner _ (m_pparentwnd-> getsafehwnd (), & m_hwndtop); <br/> // The following function can be identified by name. It is a Windows hook function, let's see what it does. <br/> afxhookwindowcreate (this); <br/> return hwnd; // return window to use as parent for dialog <br/>}

In fact, the premodal has nothing to do, that is, the following afxhookwindowcreate function is called, and the parent window handle is obtained.

Void afxapi afxhookwindowcreate (cwnd * pwnd) <br/>{< br/> _ afx_thread_state * pthreadstate = _ afxthreadstate. getdata (); // see the following description <br/> If (pthreadstate-> m_pwndinit = pwnd) <br/> return; </P> <p> If (pthreadstate-> m_hhookoldcbtfilter = NULL) <br/> {// it can be seen that if comes in once and can be called multiple times. <Br/> pthreadstate-> m_hhookoldcbtfilter =: setwindowshookex (wh_cbt, <br/> _ afxcbtfilterhook, null,: getcurrentthreadid ()); <br/> If (pthreadstate-> m_hhookoldcbtfilter = NULL) <br/> afxthrowmemoryexception (); <br/>}< br/> // · <br/> pthreadstate-> m_pwndinit = pwnd; <br/>}

Afxhookwindowcreate plays a strange and interesting role in MFC. First look at the _ afxthreadstate. getdata ();
It can be seen from the literal meaning that some State information of the thread is obtained. However, if you look at its implementation, you will find some familiar words, such as: (cthreaddata *) tlsgetvalue (m_tlsindex); yes,TLS stores threads locally. It stores some variable data related to threads with the help of the thread local storage provided by the window operating system, because we know that message queues are thread-based, that is, each thread can have a message queue.
Next let's take a look at the highlights: setwindowshookex,: setwindowshookex (wh_cbt, _ afxcbtfilterhook, null,: getcurrentthreadid () is to install a thread hook for the current thread.
The type is wh_cbt, And the hook function is _ afxcbtfilterhook. So what does wh_cbt mean? Msdn:
Wh_cbt: installa hook procedure that includes ES specifications useful to a computer-based training (CBT)
Application. For more information, see the cbtproc hook procedure.
_ Afxcbtfilterhook is the so-called cbtproc hook function type.
The cbtproc hook procedure is an application-defined or library-defined callback function used with the setwindowshookex function. The system callthis function before activating, creating, destroying, minimizing, maximizing,
Moving, or sizing a window; before completing a system command; before removing a mouse or keyboard event from the system message queue; before setting the keyboard focus; or before synchronizing with the system message queue. A computer-based training (CBT)
Application uses this hook procedure to receive useful notifications from the system.

Actually, it means:The thread installs a wh_cbt hook. When activating, creating, and destroying a window, call this hook function to notify us.What does it mean, that is, when the thread creates a window, we will investigate this function before receiving the first message.
Next, don't rush to look at the processing method of the hook process, because we know that it will not be investigated now, and it will be called within the system during the creating window. Let's continue to return to the domodal code. The hw_cbt Hook has been installed. Should we create a window below?
Enter createdlgindirect, a long function ···

Bool cwnd: createdlgindirect (lpcdlgtemplate lpdialogtemplate, <br/> cwnd * pparentwnd, hinstance hinst) <br/> {<br/> // · <br/> afxhookwindowcreate (this); // This function was just seen, but this call has no practical use. <Br/> // I wonder why he called it twice. Although there will be no problems, he is not happy. <br/> hwnd =: createdialogindirect (hinst, lpdialogtemplate, <br/> pparentwnd-> getsafehwnd (), afxdlgproc); <br/> // · many lines are omitted here. Code irrelevant to us <br/>}

Well, it turns out that createdialogindirect creates a dialog box, and the window process provided is afxdlgproc! But we may not forget,Afxhookwindowcreate has a wh_cbt hook installed, which will synchronously call our hook function during the creation window.By the way, the difference between sendmessage and postmessage. The former is synchronous, which is actually a general call. It is returned only after the called function is completed;
The latter places the message in the thread's message queue and then returns immediately. Here, the operating system will call the hook process before sending the wm_create, wm_nccreate messages during the creation window. Therefore, the correct processing of the first message is ensured !!

Lresult callback _ afxcbtfilterhook (INT code, wparam, lparam) <br/>{< br/> _ afx_thread_state * pthreadstate = _ afxthreadstate. getdata (); <br/> If (code! = Hcbt_createwnd) {// we only want to secretly do something before the window is created. Release other messages <br/> return callnexthookex (pthreadstate-> m_hhookoldcbtfilter, code, wparam, lparam ); <br/>}< br/> // · <br/> cwnd * pwndinit = pthreadstate-> m_pwndinit; <br/> bool bcontextisdll = afxcontextisdll; <br/> If (pwndinit! = NULL | (! (Lpcs-> Style & ws_child )&&! Bcontextisdll) <br/>{< br/> // · <br/> If (pwndinit! = NULL) {<br/> // · <br/> assert (cwnd: fromhandlepermanent (hwnd) = NULL ); <br/> pwndinit-> attach (hwnd); <br/> wndproc afxwndproc = callback (); // actual return value: afxwndprocbase <br/> oldwndproc = (wndproc) setwindowlongptr (hwnd, gwlp_wndproc, <br/> (dword_ptr) afxwndproc); // reset the window handle. <Br/> // · <br/> pthreadstate-> m_pwndinit = NULL; // prepare the next window <br/>}< br/> // · <br/>}</P> <p> lcallnexthook: <br/> lresult = callnexthookex (pthreadstate-> m_hhookoldcbtfilter, code, wparam, lparam); <br/> return lresult; <br/>}

First, we are interested in fromhandlepermanent, whichReceives an hwnd handle and returns a pointer to a window class.!!! This is what we want to know:
How does MFC associate a window handle with a window instance?

Cwnd * Pascal cwnd: fromhandlepermanent (hwnd) {<br/> chandlemap * pmap = afxmaphwnd (); // This function obtains the m_pmaphwnd data member of the current thread. <Br/> cwnd * pwnd = NULL; <br/> If (pmap! = NULL) {<br/> // only look in the permanent map-does no allocations <br/> pwnd = (cwnd *) pmap-> lookuppermanent (hwnd ); // lookup, which should be a look-up table. A little disappointed... <br/> assert (pwnd = NULL | pwnd-> m_hwnd = hwnd); <br/>}< br/> return pwnd; // return the retrieved pointer <br/>}< br/> inline cobject * chandlemap: lookuppermanent (handle h) // fortunately, it is inline, otherwise, it would be too depressing. Does the table look up... not slow ?? <Br/> {return (cobject *) m_permanentmap.getvalueat (lpvoid) H) ;}< br/>

This is basically a hunch,The direct ing between the window handle of MFC and the corresponding window class instance is achieved through the Table query.. So the question is,When will these mappings be added????
Obviously:Before the wm_create message is received in the create window. Otherwise, how can I distribute this message without ing? To which category? I don't know. Isn't this critical period of establishing ing exactly the hook process of hook wh_cbt ?? Return to the _ afxcbtfilterhook hook function. The pwndinit-> attach (hwnd) statement is to add the current handle to the ing of the current thread.

Bool cwnd: attach (hwnd hwndnew) <br/>{< br/> assert (fromhandlepermanent (hwndnew) = NULL); // query table? Under normal circumstances, of course, it is impossible to establish a ing, that is, null is returned. <Br/> // · <br/> chandlemap * pmap = afxmaphwnd (true); // return this ing table <br/> assert (pmap! = NULL); <br/> pmap-> setpermanent (m_hwnd = hwndnew, this); // create a: <pwnd, cwnd *> that is, <window handle, this pointer> ing of window class instances !! <Br/> // · <br/> return true; <br/>}

I was quite disappointed to see that this was almost done,The powerful and complex MFC is used to create a ing in the form of a table query, but fortunately, it is a hash table.The basic data structure is cmapptrtoptr. You can view the implementation in the chandlemap. It is implemented using a hash table, so the efficiency is not satisfactory. However, the thunk technology is inferior to that of ATL.

Add the _ afxcbtfilterhook hook Process Handle settings. The afxgetafxwndproc function returnsAfxwndprocbaseThe latter's job is actually to turn off the afxwndproc function.

Wndproc afxapi afxgetafxwndproc () <br/>{< br/> # ifdef _ afxdll // _ afxdll is defined in general. <br/> return afxgetmodulestate ()-> m_pfnafxwndproc; <br/> # else <br/> return & afxwndproc; <br/> # endif <br/>}</P> <p> lresult callback <br/> afxwndprocbase (hwnd, uint nmsg, wparam, lparam) <br/> {<br/> afx_manage_state (_ afxbasemodulestate. getdata (); <br/> return afxwndproc (hwnd, nmsg, wparam, lparam); // return to the same path <br/>}

SoThe callback function is afxwndprocbase or afxwndproc.. Complete Message Delivery in afxwndproc. The following is simple:

Lresult callback <br/> afxwndproc (hwnd, uint nmsg, wparam, lparam) <br/>{< br/> // · <br/> cwnd * pwnd = cwnd: fromhandlepermanent (hwnd ); // This familiar function has been seen several times. The hash table is searched and the instance pointer of the specified window handle is returned. <br/> // · <br/> return afxcallwndproc (pwnd, hwnd, nmsg, wparam, lparam); // This pointer to the input window instance <br/>}</P> <p> lresult afxapi afxcallwndproc (cwnd * pwnd, hwnd, uint nmsg, <br/> wparam = 0, lparam = 0) <br/ >{< Br/> // · <br/> // The process of actually calling the window of each instance finally finds the corresponding dongjia @!! Now you can have private data. <Br/> lresult = pwnd-> windowproc (nmsg, wparam, lparam); <br/> // · <br/> return lresult; <br/>}

 

By now, the message ing of MFC is complete. The next thing is to carry out the long and complex message routing in the inheritance layers of various types.

You may have questions:Why must I install a wh_cbt hook?In fact, it is very simple. The hwing between hwnd and this must be set before wm_create and other messages arrive at the window, and our application obtains the window in createwindow, the createdialogindirect series functions are returned as return values to the application, but the API tells us that wm_create and other functions will be sent before this function is returned! There is no doubt that it is too late to set the ing when this function returns. The only way is to install the hook.

Summary:
Because the window operating system must require static or global callback to call the agreed function, pass in the hwnd parameter to indicate the corresponding window.
However, ATL/mfc strives to encapsulate the tedious Win32 process-oriented Window application development method with object-oriented encapsulation. However, a unified window process can only be provided to the operating system. Therefore, it is necessary to establish a ing directly between hwnd and this pointer of the window class instance. In the unified window callback process, the message is "distributed" to the member functions of the corresponding window class.

1. For ATL, it uses thunk technology to cleverly achieve this goal.
2. MFC uses a hash table to create a ing.
I personally feel that the results of the comparison between ATL and MFC are better than that of MFC. Although the latter hash table may achieve linear time speed, Thunk technology is better. Haha ···

Please give us some advice on the wrong part. Thank you! Welcome to join us!

 

 

 

 

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.