WTL process analysis - premium

zhaozj2021-02-16  43

WTL process analysis

Welcome to my Personal Home http://www.noasia.net/taoen

A window is created to destruction, there are such a few main processes.

In WinMain

Registration window class

Create a window

Enter the message loop

In WNDPROC

Handle message

Now we have to dig out where to handle these things in the WTL, how to deal with it. first of all:

Where is WinMain?

WinMain is in the same CPP file as the engineering name. The name is called _twinmain

Int WinApi _twinmain (Hinstance Hinstance, Hinstance / * Hprevinstance * /, LPTSTR LPSTRCMDLINE, INT NCMDSHOW)

{

HRESULT HRES = :: Coinitialize (NULL);

// if you are running on nt 4.0 or higher you can use the following call instead to

// Make The Exe Free Threaded. This Means That Calls Come in on a random rpc thread.

// HRESULT HRES = :: CoinitializeEx (null, coinit_multithread);

Atlassert ("succeeded (hres));

// this Resolves ATL WINDOW THUNKING Problem When Microsoft Layer For Unicode (MSLU) IS Used

:: DefWindowProc (NULL, 0, 0, 0L);

AtlinitcommonControls (ICC_COOL_CLASS | ICC_BAR_CLASSES); // Add Flags to Support Other Controls

HRES = _Module.init (null, hinstance);

Atlassert ("succeeded (hres));

Int nret = Run (LPSTRCMDLINE, NCMDSHOW);

_Module.Term ();

:: Couninitialize ();

Return nret;

}

From this function, you can't see anything, basically substantially allocated in other functions. The other functions mention here are run (lpstrcmdline, ncmdshow); this function is written by our own, just above this _twinmain.

Run's role

INT Run (LPTSTR / * LPSTRCMDLINE * / = null, int ncmdshow = sw_showdefault)

{

CMessageloop theloop;

_Module.AddMessageloop (& Theloop);

CMAINFRAME WNDMAIN;

IF (WNDMAIN.CREATEX () == NULL)

{

ATLTRACE (_T ("Main Window Creation Failed! / N")));

Return 0;

}

WNDMAIN.SHOWINDOW (NCMDSHOW);

INT nret = theloop.run ();

_Module.RemoveMessageloop ();

Return nret;

}

From the name Messageloop and CreateEx, you can guess this Run is where you create a window and enter the message loop. and so

WinMain is necessary to initialize, the main work is in RUN

RUN Create a window and enter the message loop.

Creation of the window

It is easy to know that this section is created in the window.

CMAINFRAME WNDMAIN;

IF (WNDMAIN.CREATEX () == null) {

ATLTRACE (_T ("Main Window Creation Failed! / N")));

Return 0;

}

CMAINFRAME is defined in Mainfrm.h.

Class CMAINFRAME: PUBLIC CFRAMEWINDOWIMPL , Public CUPDATEUI , PUBLIC CMESSAGEFILTER, PUBLIC CIDEHANDLER

It can be seen here to use more inheritance, which is a universal behavior. Mainly inherited in CfraMewindowImpl, and this is a template, the parameters provided are CMAINFRAME. It can be found later that this parameter is used in the base class for mandatory type conversion, as a downward conversion.

Creating a call is wndmain.createex (), which cannot be found in CMAINFRAME, naturally there is in its base class. This is createex (): in CFrameWindowImpl ():

HWnd createex (hwnd hwndparent = null, _u_rect = null, dword dwstyle = 0, dword dwexstyle = 0, lpvoid lpcreateparam = null)

{

Tchar Szwindowname [256];

Szwindowname [0] = 0;

:: LoadString (_Module.getResourceInstance (), T :: getWndclassInfo (). M_UCommonResourceId, Szwindowname, 256);

HMENU HMENU = :: loadmenu (_Module.getResourceInstance (), makeintresource (t :: getWndclassInfo (). M_ucommonResourceId);

T * pt = static_cast (this);

HWND HWND = Pt-> Create (HWndParent, Rect, Szwindowname, DWStyle, DwexStyle, HMenu, LpCreateParam);

IF (hwnd! = null)

M_haccel = :: loadaccelerators (_Module.getResourceInstance (), makeintresource (t :: getWndclassInfo (). m_ucommonResourceId);

Return hwnd;

}

Wait, we have found a singular behavior here.

T * pt = static_cast (this);

What is this, forced type conversion, and is based on type conversion of template parameters. Well, this is the simulation dynamic binding of the ATL development group. Using the base class to provide derived class as a template parameter, the mandatory type conversion is enforced when the function call is used to determine which function is called during compilation. This makes we can rewrite the function in the base class in the derived class and except for the cost of virtual functions. so

Pt-> Create (HWndParent, Rect, Szwindowname, DWStyle, DwexStyle, HMenu, LpCreateParam);

Calling is a Create function of the derived class, although the derived class does not rewrite this function, but you can do this and get flexibility.

Let's continue tracking this Create behavior, don't look for it, this function is at the top of CreateEx. Derived class has not been rewritten, the call is the version in the base class. HWnd create (hwnd hwndparent = null, _u_rect = null, lpctstr szwindowname = null, dword dWStyle = 0, dword dwexstyle = 0, HMENU HMENU = NULL, LPVOID LPCREATEPARAM = NULL)

{

Atom atom = t :: getWndclassInfo (). Register (& m_pfnsuperwindowproc);

DWStyle = T :: getWndStyle (DWStyle);

DWEXStyle = T :: getWndexStyle (dwexstyle);

IF (Rect.m_lprect == Null)

Rect.m_lpRect = & TBASE :: rcdefault;

Return CFrameWindowImplbase :: Create (HWndParent, Rect.m_lpRect, Szwindowname, DWStyle, DwexStyle, HMenu, Atom, LpCreateParam);

}

Red labeled two important processes, a registration window class, a window created. First pay attention to the registration of the window class.

Window class with registration

T :: getWndclassInfo (). Register (& M_PFNSUPERWINDOWPROC);

This code completes the registration of the window class.

T is the parameter passed to the base class, that is, derived classes. So T is CMAINFRAME. T :: getWndClassInfo () indicates that the class's static function is called. So where is this function defined? We must notice this line in the CMAINFRAME definition:

Declare_frame_wnd_class (null, idr_mainframe)

Obviously, this is a macro (you look at there without a semicolon behind). So continue to search this macro definition

#define declare_frame_wnd_class (wndclassname, ucommonResourceId) /

Static cframewndclassinfo & getWndclassInfo () /

{/

Static CFrameWndClassInfo WC = /

{/

{SizeOf (Wndclassex), 0, StartWindowProc, /

0, 0, NULL, NULL, NULL, (HBRUSH) (Color_Window 1), NULL, WNDCLASSNAME, NULL}, /

NULL, NULL, IDC_ARROW, TRUE, 0, _T (""), UcommonResourceId /

}; /

Return WC; /

}

^ _ ^, I caught you. It is this macro to make a static function. This static function is a static variable of type CFraMewndClassInfo based on parameters and returns it. This static variable contains WNDCLASS information and some of the information you need to provide when you create a window. It's so

Register (& M_PFNSUPERWINDOWPROC);

The call is the MEMBER FUNCTION in CFrameWndClassInfo. So, we have to see the definition of CFraMewndClassInfo: Class CframeWndClassInfo

{

PUBLIC:

WNDCLASSEX M_WC;

LPCTSTR M_LPSZORIGNAME;

WndProc PWNDPROC;

LPCTSTR M_LPSZCURSORID;

BOOL M_BSYSTEMCURSOR;

Atom m_atom;

TCHAR M_SZAUTONAME [5 SIZEOF (VOID *) * 2]; // sizeof (void *) * 2 is the number of digits% P Outputs

Uint M_UCommonResourceId;

Atom Register (WNDPROC * PPROC)

{

IF (m_atom == 0)

{

:: EntercriticalSection (& _ Module.m_cswindowcreate);

IF (m_atom == 0)

{

Hinstance hinst = _Module.getModuleInstance ();

IF (m_lpszorigname! = null)

{

Atlassert (PPROC! = Null);

LPCTSTR LPSZ = m_wc.lpszclassname;

WndProc Proc = m_wc.lpfnwndproc;

WNDCLASSEX WC;

wc.cbsize = sizeof (wndclassex);

// Try Process Local Class First

IF (! :: getclassinfoex (_module.getmoduleInstance (), m_lpszorigname, & wc))

{

// Try Global Class

IF (! :: getclassinfoex (null, m_lpszorigname, & wc))

{

:: LeavecriticalSection (& _ Module.m_cswindowcreate);

Return 0;

}

}

Memcpy (& M_WC, & WC, SIZEOF (WNDCLASSEX));

PWndProc = m_wc.lpfnwndproc;

m_wc.lpszclassname = lps;

m_wc.lpfnwndproc = proc;

}

Else

{

M_wc.hcursor = :: loadcursor (m_bsystemcursor? null: hinst, m_lpszcursorid);

}

m_wc.hinstance = hinst;

m_wc.style & = ~ cs_globalclass; // We don't register global classes

IF (m_wc.lpszclassname == NULL)

{

WSPrintf (m_szautoname, _t ("ATL:% P"), & m_wc);

m_wc.lpszclassname = m_szautoname;

}

WNDCLASSEX WCTEMP;

Memcpy (& WCTEMP, & M_WC, SIZEOF (WNDCLASSEX));

m_atom = (atom) :: getClassInfoEx (m_wc.hinstance, m_wc.lpszclassname, & wctemp);

IF (m_atom == 0)

{

IF (M_UCOMMONRESOURCEID! = 0) // use it if not zero {

M_wc.hicon = (hic) :: loadimage (_Module.getResourceInstance (), makeintresource (m_ucommonresourceid), Image_ICON, 32, 32, LR_DEFAULTCOLOR);

M_wc.hiconsm = (hicon) :: loadImage (_Module.getResourceInstance (), makeintresource (m_ucommonresourceid), image_icon, 16, 16, lr_defaultcolor;

}

m_atom = :: registerclassex; & m_wc

}

}

:: LeavecriticalSection (& _ Module.m_cswindowcreate);

}

IF (m_lpszorigname! = null)

{

Atlassert (PPROC! = Null);

Atlassert (PWndProc! = Null);

* pproc = pwndproc;

}

Return m_atom;

}

}

Don't take a lot of seven-eight-tunda, the key part is m_atom = :: registerclassex (& m_wc); obvious, this sentence completed the registration of the true window class. And use the m_atom tag to be registered. With regard to the registration of the window class, we must also pay attention to a key point, that is the address of WndProc. Out of the way, it is STARTWINDOWPROC. Ok, here, the registration of the window class has been completed. the following:

Creation of the window

CframeWindowImplbase :: Create (HWndParent, Rect.m_LPRect, Szwindowname, DWStyle, DwexStyle, HMenu, Atom, LpCreateParam);

This is the definition of this function:

HWnd Create (HWND HWNDPARENT, _U_RECT RECT, LPCTSTSTSZWINDOWNAME, DWORD DWSTYLE, DWORD DWEXSTYLE, _U_MENUORID MENUORID, Atom Atom, LPVOID LPCREATEPARAM)

{

Atlassert (m_hwnd == null);

IF (atom == 0)

Return NULL;

_Module.addcreateWnddata (& m_thunk.cd, this);

IF (menuorid.m_hmenu == null && (dwstyle & ws_child))

Menuorid.m_hmenu = (hmenu) (uint_ptr) this;

IF (Rect.m_lprect == Null)

Rect.m_lpRect = & TBASE :: rcdefault;

HWND hWnd = :: CreateWindowEx (dwExStyle, (LPCTSTR) (LONG_PTR) MAKELONG (atom, 0), szWindowName, dwStyle, rect.m_lpRect-> left, rect.m_lpRect-> top, rect.m_lpRect-> right - rect.m_lpRect -> Left, Rect.m_lpRect-> Bottom-Rect.m_lpRect-> Top, hwndparent, menuorid.m_hmenu, _Module.getModuleInstance (), lpcreateparam); atlassert (m_hwnd == hwnd);

Return hwnd;

}

IF (atom == 0)

Return NULL;

Check if the window class is registered correctly. Then the creation work of CreateWindowex substance. The parameter window in it is (LPCTSTR) (long_ptr) makelong (atom, 0). So here, the window class name is not used. The Atom returned when the registration window class is used as the corresponding function. This is very different from MFC practices.

So far, the window class has been registered and created a window. Let's go in RUN:

INT Run (LPTSTR / * LPSTRCMDLINE * / = null, int ncmdshow = sw_showdefault)

{

CMessageloop theloop;

_Module.AddMessageloop (& Theloop);

CMAINFRAME WNDMAIN;

IF (WNDMAIN.CREATEX () == NULL)

{

ATLTRACE (_T ("Main Window Creation Failed! / N")));

Return 0;

}

WNDMAIN.SHOWINDOW (NCMDSHOW);

INT nret = theloop.run ();

_Module.RemoveMessageloop ();

Return nret;

}

Message loop

AddMessageloop and RemoveMessageloop hook the theloop to the module (program) object or remove.

The core of the problem now is the processing of the message loop. THELOOP.Run (); let's see the definition of CMessageloop's Run:

Int run ()

{

Bool bdoidle = true;

INT NIDECOUNT = 0;

BOOL BRET;

For (;;)

{

While (! :: PeekMessage (& M_MSG, NULL, 0, 0, PM_NOREMOVE) && bdoidle)

{

IF (! OnIdle (NidleCount ))

BDOIDLE = FALSE;

}

Bret = :: getMessage (& M_MSG, NULL, 0, 0);

IF (BRET == -1)

{

Atltrace2 (Atltraceui, 0, _t (":: getMessage returned -1 (error) / n"));

Continue; // Error, Don't Process

}

Else if (! BRET)

{

ATLTRACE2 (Atltraceui, 0, _T ("cMessageloop :: run - exiting / n"));

Break; // wm_quit, EXIT Message Loop

}

IF (! PretranslateMessage) {

:: TranslateMessage (& M_MSG);

:: DispatchMessage (& M_MSG);

}

IF (ISIDEMESSAGE))

{

BDOIDLE = True;

Nidlecount = 0;

}

}

Return (int) m_msg.wparam;

}

Very simple, use PeekMessage to determine if there is a message that needs to be processed, and then performs regular translation and distribution of messages that need to be processed. Among them, there is an opportunity to handle free time.

Then the message loop has started, now I have to pay attention to where to deal with the message?

Message process

The front is a small dish, which is very clear. I have encountered a big problem here. We recall the WndProc recorded in WNDCLASS is StartWndProc. No matter how, the message is entered at the beginning is this function. Grab it, there is hope:

Template

LResult Callback CWindowImplbaset ::

StartWindowProc (HWND HWND, UINT UMSG, WPARAM WPARAM, LPARAM LPARAM)

{

CWindowImplbaset * pthis = (CWindowImplbaset *) _ module.extractCreateWndData ();

Atlassert (PTHIS! = Null);

Pthis-> m_hwnd = hwnd;

Pthis-> m_thunk.init (pthis-> getwindowproc (), pthis);

WndProc Pproc = (WndProc) & (PTHIS-> m_thunk.thunk);

WndProc PoldProc = (WndProc) :: SetWindowlong (hwnd, gwl_wndproc, (long) pproc

#ifdef _Debug

// Check if somebody Has Subclassed US Already Since We Discard IT

IF (PoldProc! = StartWindowProc)

ATLTRACE2 (AtltraceWindowing, 0, _T ("Subclassing Through a hook discarded./n"));

#ELSE

PoldProc; // avoid unused warning

#ENDIF

Return PPROC (HWND, UMSG, WPARAM, LPARAM);

}

First let's see SetWindowlong, know what this is to do? Setwindowlong changes some of the basic properties of the window. GWL_WNDPROC indicates that the address of WndProc is to be changed. ^ _ ^, I know why I want to see this first. This step is to change WndProc to "correct". That is, PPROC.

WndProc Pproc = (WndProc) & (PTHIS-> m_thunk.thunk);

This is the function of WndProc will be changed after the startWindowProc is executed. PTHIS is taken from _Module. The information taken is recorded when the window is created. For clarity, no matter it, it is a CWindowImplbaset type pointer.

Now I have to understand that M_thunk is a scorpion. m_thunk defines a variable of CWndProctHunk in the base class of CWindowImplbaset. Let's see CWndProcthunk: Class CWndProcthunk

{

PUBLIC:

union

{

_Atlcreatewnddata cd;

_WndProcthunk Thunk;

}

Void Init (WndProc Proc, Void * Pthis)

{

#if defined (_m_ix86)

Thunk.m_mov = 0x042444c7; file: // c7 44 24 0C

Thunk.m_this = (dword) PTHIS;

Thunk.m_jmp = 0xE9;

Thunk.m_relproc = (int) proc - ((int) this sizeof (_wndprocthunk));

#elif defined (_M_ALPHA)

Thunk.ldah_at = (0x279f0000 | HiWord (PROC)) (Loword >> 15);

Thunk.ldah_a0 = (0x261F0000 | HiWord (PTHIS)) (Loword (PTHIS) >> 15);

Thunk.lda_at = 0x239c0000 | Loword (Proc);

Thunk.lda_a0 = 0x22100000 | Loword (PTHIS);

Thunk.jmp = 0x6bfc0000;

#ENDIF

// Write Block from data cache and

File: // flush from instruction cache

FlushinstructionCache (GetCurrentProcess (), & Thunk, Sizeof (Thunk);

}

}

Horror, actually appeared. Basic ideas are to prepare a machine code through init, and then put the address of the machine code as a function address. This machine code does two things, one is to replace the WndProc's HWND parameter to pTHIS, and the other is to jump to the true WndProc in the corresponding window.

PTHIS-> getWindowProc ()

This code returns to the place where the actual processing message is actually processed. Now look at this function:

Virtual WndProc getWindowProc ()

{

Return windowProc;

}

Template

LResult Callback CWindowImplbaset ::

WindowProc (HWND HWND, UINT UMSG, WPARAM WPARAM, LPARAM LPARAM)

{

CWindowImplbaset * Pthis = (CWindowImplbaset *) hwnd;

// set a ptr to this message and save the old value

Msg msg = {pthis-> m_hwnd, umsg, wparam, lparam, 0, {0, 0}};

Const msg * Poldmsg = pthis-> m_pcurrentmsg;

PTHIS-> M_PCURRENTMSG = & msg;

// Pass to the message map to process

LRESULT LRES;

Bool Bret = Pthis-> ProcessWindowMessage (PTHIS-> M_HWND, UMSG, WPARAM, LPARAM, LRES, 0); // RESTORE SAVED VALUE for the CURRENT Message

Atlassert (pthis-> m_pcurrentmsg == & msg);

PTHIS-> M_PCURRENTMSG = POLDMSG;

// do the default processing if message was not handled

IF (! bret)

{

IF (UMSG! = WM_NCDESTROY)

Lres = pthis-> DefWindowProc (UMSG, WPARAM, LPARAM);

Else

{

// unsubclass, if nesered

Long PfnWndProc = :: getwindowlong (pthis-> m_hwnd, gwl_wndproc);

Lres = pthis-> DefWindowProc (UMSG, WPARAM, LPARAM);

IF (pthis-> m_pfnsuperwindowproc! = :: DefwindowProc &&: getWindowlong (pthis-> m_hwnd, gwl_wndproc) == PfnWndProc)

:: setWindowlong (pthis-> m_hwnd, gwl_wndproc, (long) pthis-> m_pfnsuperwindowproc);

// Clear Out Window Handle

HWND HWND = PTHIS-> M_HWND;

PTHIS-> M_HWND = NULL;

// Clean Up After Window Is Destroyed

PTHIS-> OnfinalMessage (hwnd);

}

}

Return Lres;

}

It can be seen that several twists and turns will eventually fall into the processWindowMessage of derived class. The simulated virtual function is also used here. There is also a question is that I didn't write processWindowMessage in CMAINFRAME? But you wrote

Begin_MSG_MAP (CMAINFRAME)

Message_handler (WM_CREATE, ONCREATE)

Command_id_handler (ID_APP_EXIT, ONFILEEXIT)

Command_id_handler (id_file_new, onfilenew)

Command_id_handler (ID_FILE_OPEN, ONFILEOPEN)

Command_id_handler (id_view_toolbar, onviewtoolbar)

Command_id_handler (id_view_status_bar, onviousstatusbar)

Command_id_handler (ID_APP_ABOUT, ONAPPABOUT)

CHAIN_MSG_MAP (CUPDATEUI )

CHAIN_MSG_MAP (CFrameWindowImpl )

END_MSG_MAP ()

These things are actually ProcessWindowMessage, they are macro.

#define begin_msg_map (theclass) /

PUBLIC: /

Bool ProcessWindowMessage (HWND HWND, UINT UMSG, WPARAM WPARAM, LPARAM LPARAM, LRESULT & LRESULT, DWORD DWMSGMAPID = 0) / {/

Bool bhandled = true; /

HWND; /

UMSG; /

WPARAM; /

LPARAM; /

LRESULT; /

Bhandled; /

Switch (dwmsgmapid) /

{/

Case 0:

#define message_range_handler (Msgfirst, MSGLAST, FUNC) /

IF (umsg> = msgfirst && umsg <= msglast) /

{/

Bhandled = true; /

Lresult = func (UMSG, WPARAM, LPARAM, BHANDLED); /

IF (bhandled) /

Return True; /

}

#define command_id_handler (ID, FUNC) /

IF (UMSG == WM_COMMAND && ID == Loword (WPARAM)) /

{/

Bhandled = true; /

Lresult = func (HiWord (WPARAM), LOWORD (WPARAM), (HWND) LPARAM, BHANDLED); /

IF (bhandled) /

Return True; /

}

#define chain_msg_map (thechainclass) /

{/

IF (ThechainClass :: ProcessWindowMessage (HWND, UMSG, WPARAM, LPARAM, LRESULT)) /

Return True; /

}

#DEFINE END_MSG_MAP () /

Break; /

DEFAULT: /

Atltrace2 (AtltraceWindowing, 0, _T ("INVALID Message Map ID), DWMSGMapID; /

Atlassert (false); /

Break; /

} /

Return False; /

}

At this point, everything understands. In fact, WTL is just an extension of the window part of the ATL, and most of the things analyzed here are ATL. And this part of the content is also described in detail in "ATL INTERNAL".

Welcome to my Personal Home http://www.noasia.net/taoen

转载请注明原文地址:https://www.9cbs.com/read-26598.html

New Post(0)