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

Source: Internet
Author: User
The blog has been migrated to: http://kulv.sinaapp.com /.
Comparison between ATL and MFC message distribution mechanisms-thoughts on the open source code of Kingsoft (1)

A few days ago, when I first read the Kingsoft open source code, I wrote a blog to analyze the implementation method of its message mechanism. Later I found that most of the information I wrote was in ATL, and the most ** was a serious error. I put an important technology in the ATL window message mechanism: the thunk technology that implements the hwing between hwnd and the corresponding window class this pointer is ignored. Later, Chen Kun Gg immediately reminded me. Thank you!

Now, let's start with the question. Today we will mainly compare how ATL and MFC map the Windows handle hwnd to the corresponding class's this pointer.

1. First explain why ing:
We have never mapped the Win32 program by ourselves. Generally, we only provide a window process when registering the window, and then we can do everything in the window process. Why do we need to map it?
We know that windows are written in C, and all APIs do not support object-oriented programming. The key is that ATL/mfc is a framework. In order to do its best to block the tedious steps in programming (register the window class, provide the window process, create a window, and display the window ···) and enable developers to use object-oriented methods for programming and enjoy great convenience, so they have to build a bridge between the process-oriented operating system API and the object-oriented programming framework, this is the beginning of the problem ···
The program we write is generally a window process corresponding to the main window! All do not need to care about this (and we generally do not fully process-oriented Win32 programming ). However, the ATL/mfc process is different, so we don't need to provide the window process. This makes programming easier. All the frameworks provide us with the window process. The problem is, can the framework provide different window procedures for each of our different windows and register with the control system? Neither can nor be implemented. Therefore, it can only provide a unified window process to the control system,
1. for ATL, It is a static function cwindowimplbaset <>: startwindowproc. Do not forget that the static function is actually similar to the global function. It is class-based and does not have the this pointer, the compiler will not add the this pointer for it. (In fact, this process is a bit tortuous and will be discussed later)
2. For MFC, It is afxdlgproc at registration, but the actual situation is more complicated than this.

 

No matter who the window process is provided during registration, it is clear that it must be a static function of a global function or class.

The above is actually very simple. Since all functions are provided with the same function, the operating system will call this function no matter which window has a message! The difference is that different parameters are provided, that is, the hwnd parameter! This parameter undoubtedly marks a window. The question is, how do we know who is the window class corresponding to the window handle ?? A simple method: If-else lookup. Yes, the window is too slow!
So we came to the focus of our discussion:How is ATL/mfc mapped?

 1. Let's talk about ATL first:
Or from the source, first look at how to register:
How to register the http://blog.csdn.net/hw_henry2008/archive/2011/05/22/6438153.aspx I mentioned in my previous article
Here is a brief review.
The dialog box is used as the basis, and multiple documents are similar. In the domodal function, this is the case when the dialog box is created.

Hwnd c0000wimpl: Public cwindowimplbaset <tbase, twintraits> <br/>: Create (hwnd hwndparent, _ u_rect rect = NULL, lpctstr szwindowname = NULL, <br/> DWORD dwstyle = 0, DWORD dwexstyle = 0, <br/> _ u_menuorid menuorid = 0u, lpvoid lpcreateparam = NULL) <br/> {<br/> // · <br/> atom = T: getwndclassinfo (). register (& m_pfnsuperwindowproc); <br/> // · the register function is registered, and the global registerclassw function <br/> return cwindowimplbaset <tbase, twintraits> :: create (hwndparent, rect, szwindowname, <br/> dwstyle, dwexstyle, menuorid, Atom, lpcreateparam); <br/>}

The above code calls getwndclassinfo to obtain the basic information of the window structure. It is necessary to paste the code of getwndclassinfo when the window handler is set. It returns the basic information of a window class, this includes WINDOW processing functions, which are the callback functions we use to register with the operating system.

Static ATL: cwndclassinfo & cbkdialogimpl: getwndclassinfo () <br/>{< br/> static ATL: cwndclassinfo WC ={< br/> {sizeof (wndclassex ), <br/> cs_hredraw | cs_vredraw | cs_dblclks | (iswinxpandlater ()? Cs_dropshadow: 0), <br/> startwindowproc, 0, 0, null, null, <br/> (hbrush) (color_window + 1), null, null, null },< br/> null, null, idc_arrow, true, 0, _ T ("") <br/>}; <br/> return WC; <br/>}

 

From the code above, we can see that the window process provided when registering with the control system is startwindowproc, which is in VC/atlmfc/include/atlwin. h. In this way, when the first message comes, the operating system will undoubtedly call our startwindowproc. The last time I saw this, I did not go into detail and skipped the important thunk technology. Next we will continue to look at the window Process

Lresult callback cwindowimplbaset <tbase, twintraits >:: startwindowproc (hwnd, uint umsg, wparam, lparam) <br/> {<br/> // the following code is actually obtained from the this pointer in a previously saved window process. Because this function is called for the first time, that is, the hwnd corresponds to this <br/> // but I suspect that this will cause problems when multithreading occurs, how do you ensure that the stored this pointer does not correspond to hwnd? If the two threads run to this simultaneously, <br/> // will not compete? See it in another day. Please give me some advice. ^) <br/> cwindowimplbaset <tbase, twintraits> * pthis = (cwindowimplbaset <tbase, twintraits> *) _ atlwinmodule. extractcreatewnddata (); <br/> // · <br/> pthis-> m_hwnd = hwnd; // Save the handle and use it later. Why, because the thunk code will overwrite the data in this place. <Br/> // · // getwindowproc returns windowproc, which is also a static function <br/> pthis-> m_thunk.init (pthis-> getwindowproc (), pthis ); // here we will talk about <br/> wndproc pproc = pthis-> m_thunk.getwndproc (); // actually, the first address of A _ stdcallthunk struct is returned. <br/> wndproc poldproc = (wndproc): setwindowlongptr (hwnd, gwlp_wndproc, (long_ptr) pproc ); <br/> // set the first address of the struct to The Window Process !!!??? See <br/> // · <br/> return pproc (hwnd, umsg, wparam, lparam); <br/>}

 

Question: What is m_thunk? Each window instance has such a data member, which is defined as follows:

Class cwndprocthunk <br/>{< br/> Public: <br/> _ atlcreatewnddata CD; <br/> cstdcallthunk thunk; // This is the focus. Thunk is actually a piece of assembly code. </P> <p> bool Init (wndproc proc, void * pthis) {<br/> return thunk. init (dword_ptr) proc, pthis); <br/>}< br/> wndproc getwndproc () {<br/> return (wndproc) thunk. getcodeaddress (); <br/>}< br/>}; </P> <p> # pragma pack (push, 1) <br/> struct _ stdcallthunk <br/> {<br/> DWORD m_mov; // mov dword ptr [esp + 0x4], pthis (esp + 0x4 is hwnd) <br/> DWORD m_this; // <br/> byte m_jmp; // JMP wndproc <br/> DWORD m_relproc; // REL Ative JMP <br/> bool Init (dword_ptr proc, void * pthis) <br/> {// initialize this assembly code, it will be forcibly converted into the address of a window procedure function. Only do not perform operations such as stack pressure <br/> m_mov = 0x0da-44c7; // C7 44 24 0C // The following comments should be // C7 44 24 04 <br/> m_this = ptrtoulong (pthis ); <br/> // move the this pointer to the starting position of the four bytes of the stack. From the call rule callback of startwindowproc, this location is exactly the window handle <br/> m_jmp = 0xe9; <br/> m_relproc = DWORD (int_ptr) proc-(int_ptr) this + sizeof (_ stdcallthunk); <br/> // The previous jump <br/> flushinstructioncache (getcurrentprocess (), this, sizeof (_ stdcallthunk )); // refresh the CPU pre-read command <br/> return true; <br/>}< br/> void * getcodeaddress () {<br/> return this; // return the first address of the struct. Fortunately, there is no virtual function, you know. <Br/>}< br/> // · <br/> };

The above code will talk about it again. init actually initializes the thunk structure and initializes it as follows:
First, change the 4-byte content on the stack to the this pointer of the corresponding hwnd window class passed in, and then jump to the proc pointer for execution, which is actually windowproc.
Why can this be implemented?
We know that startwindowproc initializes thunk with the windowproc and this pointer of the corresponding window process, and then initializes the "content" of the thunk (in fact, it is a well-arranged assembly code) forcibly convert to a window procedure function and register it with the operating system! Think about the effect this time ?? Right. Since then, whenever a message arrives in the "window", the operating system will take the following parameters:
(Hwnd, umsg, wparam, lparam) call the "function" at this address "!! The following actions are performed in the operating system:
(This is related to the call convention of the function, please refer to the http://blog.csdn.net/hw_henry2008/archive/2011/05/29/6453257.aspx if you are not clear)
--------------------------------------------------------------
 01111: <br/> push lparam <br/> push wparam <br/> push umsg <br/> push hwnd // The stack pressure rules stipulated by callback: from right to left <br/> push Cs: EIP // Save the return address, that is, the command pointer <br/> call 0x112345678 // assume this is the base address of the thunk struct. </P> <p> 0x112345678: <br/> move this [esp + 0x04] // check the stack status when the stack is pressed, this window class overwrites the hwnd! In the parameter !! <Br/> JMP windowproc // then, if nothing happens, jump to windowproc for execution. </P> <p>

-----------------------------------------------------------------
The stack status is:
Lparam
Wparam
Umsg
Hwnd <---- ESP + 4
CS: EIP <---- ESP register

------------------------------------------------------------------

We can see that Thunk is actually used here: Change the hwnd in the stack to the corresponding window class this pointer. Note that the thunk structure is one for each window instance, in fact, different thunks have different source addresses, that is, the window instance's this pointer is different. Therefore, the operating system no longer calls the startwindowproc function for every message in the future, instead, call the code at thunk.
We will continue to look at how windowproc obtains this, after all, it is also a static function.

Lresult callback cwindowimplbaset <tbase, twintraits>: windowproc (hwnd, uint umsg, wparam, lparam) <br/>{< br/> // directly convert the hwnd parameter to a pointer of the window class !! Why is it successful? <Br/> // because moving this [esp + 0x4] In the thunk code just now overwrites this parameter !! In addition, the thunk code is used for each window instance. <Br/> cwindowimplbaset <tbase, twintraits> * pthis = (cwindowimplbaset <tbase, twintraits> *) hwnd; <br/> // cleverly obtain the this pointer of the corresponding window process, so the member function is called in a big swing, <br/> // Of course, The m_hwnd handle is saved only once it enters startwindowproc. <Br/> bool Bret = pthis-> processwindowmessage (pthis-> m_hwnd, umsg, wparam, lparam, lres, 0 ); <br/> // · <br/>}

The message distribution process of ATL is what we mentioned above. For details about thunk, refer to my reposted blog.
In addition, you need to know a little about the Assembly Language and call conventions. These are for reference in my reposted blog.

The basic diagram is as follows:

 

In addition, the Code in startwindowproc is as follows:
_ Atlwinmodule. extractcreatewnddata () I started to think there was a thread competition problem. I just looked at the implementation in it and it was okay. The lock was continuously applied to it, it also uses the processing like "Every thread variable.

Atlinline atlapi _ (void *) atlwinmoduleextractcreatewnddata (_ atl_win_module * pwinmodule) <br/>{< br/> // · // The following shackles, exit this function to unlock <br/> ccomcritseclock <ccomcriticalsection> lock (pwinmodule-> m_cswindowcreate, false); <br/> If (failed (lock. lock () <br/> {// · <br/>}< br/> _ atlcreatewnddata * pentry = pwinmodule-> m_pcreatewndlist; <br/> If (pentry! = NULL) {<br/> DWORD dwthreadid =: getcurrentthreadid (); <br/> _ atlcreatewnddata * pprev = NULL; <br/> while (pentry! = NULL) {<br/> If (pentry-> m_dwthreadid = dwthreadid) <br/> {// the key is to process only the current thread. For multithreading, they access different elements <br/> If (pprev = NULL) <br/> pwinmodule-> m_pcreatewndlist = pentry-> m_pnext; <br/> else <br/> pprev-> m_pnext = pentry-> m_pnext; <br/> Pv = pentry-> m_pthis; <br/> break; <br/>}< br/> pprev = pentry; <br/> pentry = pentry-> m_pnext; <br/>}< br/> return PV; <br/>}

 

II. Implementation of MFC:

Limited space, please refer to the next page: http://blog.csdn.net/hw_henry2008/archive/2011/05/29/6453730.aspx

 

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.