ATL Package for Window Message Processing Functions
Two puzzles in the package window in this section, where the first question is to resolve the corresponding function in the instance of the message of the window function to the class of the HWnd.
Let's take a look at it, what is the way ATL uses it.
We know that each Windows window class has a window function.
LRESULT WNDPROC (HWND HWND, UINT UMSG, WPARAM WPARAM, LPARAM LPARAM);
In the class CWindowImplbaset, two types of static member functions are defined.
Template
Class ATL_NO_VTABLE CWINDOWIMPLBASET: PUBLIC CWINDOWIMPLROOT
{
PUBLIC:
...
Static Lresult Callback StartWindowProc (HWND HWND, UINT UMSG, WPARAM WPARAM,
Lparam lparam;
Static Lresult Callback WindowProc (HWND HWND, UINT UMSG, WPARAM WPARAM,
Lparam lparam;
...
}
They are all window functions. The reason is defined as a static member function because each class must have only one window function, and the declaration of the window function must be like this.
During the message processing logic described earlier, we know how to make the virtual function ProcessWindowsMessage (HWND HWND, UINT UMSG, WPARAM WPARAM, LPARAM LPARAM, LRESULT & LRESULT, DWORD DWMSGMAPID).
The current task is how to pass the message to an instance (window) in the window function (window) processWindowsMessage (). This is a problem. The window function is a static member function of the class, so it is not like other member functions of the class, and there is no THIS pointer in the parameter.
Note that this problem is because ProcessWindowsMessage () is a virtual function. The reason why the virtual function is to take into account the derived and polymorphism of the class. If you don't need to achieve the derivation and polymorphism of the window class, it does not exist this problem.
The solution that is usually thought is to find a pointer to the instance of the class corresponding to the class based on the HWND parameter of the window function. Then, the message logic processing function ProcessWindowsMessage () of the instance is called by this pointer.
This requires storage of a global array, stores a pointer to the instance of the class in this array one by one of the pointers of the instance of the class.
ATL solutions to this problem are very clever. This method does not store these correspondence, but allows the window function to receive a C class pointer as a parameter to replace HWND as a parameter.
Specific steps are as follows:
• Specify a start window function when registering a window class.
· When you create a window class, you are temporarily saved somewhere.
· Windows calls the start window function when you create this class. Its role is to create a series of binary code (THUNK). These codes replace the HWND parameters of the window function with the physical address of the THIS pointer, and then jump to the actual window function. This is achieved by changing the stack.
· Then use these code as the window function of the window. In this way, the parameters are converted each time the window function is called.
· In the actual window function, only the parameter CAST needs to be the window pointer type.
Take a detailed look at the ATL package code.
1. Specify a start window function when registering a window class.
In SuperClass, when we analyze the window registration, the specified window function is startWindowProc (). 2. When you create a window class, you temporarily save the THIS pointer to somewhere.
Template
HWND CWINDOWIMPLBASET
LPCTSTR SZWINDOWNAME,
DWORD DWSTYLE, DWORD DWEXSTYLE, UINT NID, Atom Atom, LPVOID LPCREATEPARAM
{
Atlassert (m_hwnd == null);
IF (atom == 0)
Return NULL;
_Module.addcreateWnddata (& m_thunk.cd, this);
IF (NID == 0 && (DWStyle & WS_CHILD))
NID = (uint).
HWND HWND = :: CreateWindowex (dwexstyle, (lpctstr) makelong (atom, 0), Szwindowname,
DWStyle, Rcpos.Left, Rcpos.top, Rcpos.right - rcpos.Left,
Rcpos.bottom - rcpos.top, hwndparent, (HMENU) NID,
_Module.getModuleInstance (), LPCReateParam;
Atlassert (m_hwnd == hwnd);
Return hwnd;
}
This function is used to create a window. It did two things. The first is to pass _Module.addcreateWnddata (& m_thunk.cd, this); statement saves the THIS pointer to a place of _Module.
The second thing is to create a Windows window.
3. A wonderful binary code
Let's take a look at a key binary code. Its role is to replace the HWND parameters passing to the actual window function.
ATL defines a structure to represent this code:
#pragma Pack (Push, 1)
Struct _WndProcthunk
{
DWORD M_MOV; // MOV DWORD PTR [ESP 0x4], PTHIS (ESP 0x4 IS HWND)
DWORD M_THIS; //
BYTE M_JMP; // JMP WNDPROC
DWORD M_RELPROC; // Relative JMP
}
#pragma pack (POP)
#pragma pack (push, 1) means telling the compiler that each field is tight in memory. Because it is stored, the machine instruction is stored.
This code contains two machine instructions:
Mov DWORD PTR [ESP 4], PTHIS
JMP WNDPROC
The MOV command turns the HWND parameter (ESP 0x4) in the stack into an instance pointer PTHIS. The JMP instruction completes a relative jump to the actual window function WNDPROC. Note that the HWND parameter in the stack has become PTHIS, that is, the HWND parameter obtained by WinProc is actually PTHIS.
The most critical problem above is to calculate the relative offset of JMP WndProc.
Let's take a look at how the ATL initializes this structure.
Class CWndPROCTHUNK
{
PUBLIC:
union
{
_Atlcreatewnddata cd;
_Wndprocthunk thunk;};
Void Init (WndProc Proc, Void * Pthis)
{
Thunk.m_mov = 0x042444c7; // C7 44 24 0C
Thunk.m_this = (dword) PTHIS;
Thunk.m_jmp = 0xE9;
Thunk.m_relproc = (int) proc - ((int) this sizeof (_wndprocthunk));
// Write Block from data cache and
// Flush from instruction cache
FlushinstructionCache (GetCurrentProcess (), & Thunk, Sizeof (Thunk);
}
}
ATL packs a class and defines an init () member function to set the initial value. In statement thunk.m_relproc = (int) proc - ((int) this sizeof (_wndprocthun)); used to set the relative address of the jump instruction (int) proc - ((int) THIS SIZEOF (_WndProCThunk)) .
The above figure is an instance (object) memory image map of the window class, which describes each pointer and their relationship. It is easy to calculate the relative address is (int) proc - ((int) this sizeof (_WndProctHunk)).
4. STARTWINDOWPROC () role
Template
LResult Callback CWindowImplbaset
UINT UMSG, WPARAM WPARAM, LPARAM LPARAM)
{
CWindowImplbaset Twintraits> *) _ 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); } This function has four things: First, call the _Module.extractCreateWndData () statement, get the THIS pointer from the place where the THIS pointer is saved. The second is to call m_thunk.init (pthis-> getwindowproc (), pthis) statement initializes the Thunk code. The third is to set the Thunk code to the window function of the window class. WndProc Pproc = (WndProc) & (PTHIS-> m_thunk.thunk); WndProc PoldProc = (WndProc) :: SetWindowlong (hwnd, gwl_wndproc, (long) pproc In this way, the future message processing is first called the THUNK code. It changes the hwnd parameter to the PTHIS pointer, then jumps to the actual window function WindowProc (). The fourth is to call the above window function after completing the above work. Since STARTWINDOWPROC () is called by Windows when the window is created. After completing the above tasks, it should continue to complete the tasks that Windows requirements are completed. Therefore, the actual window function is simply invoked here. 5. WINDOWPROC () window function Below is the definition of this function: Template LResult Callback CWindowImplbaset UINT UMSG, WPARAM WPARAM, LPARAM LPARAM) { CWindowImplbaset Twintraits> *) 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; } First, the function puts the HWND parameter Cast to a class of instance pointer PTHIS. Then call PTHIS-> ProcessWindowMessage (PTHIS-> M_HWND, UMSG, WPARAM, LPARAM, LRES, 0); statement, that is, call message logic processing, handle the specific message processing transaction to ProcessWindowMessage (). Next, if ProcessWindowMessage () does not process any messages, the default message processing is called. Note that WM_NCDESTROY is processed here. This is related to subclass, and finally restores the previous window function before Subclass. WTL to Subclass package The principle of Subclass is said before, and look at how to encapsulate. Template Bool CWindowImplbaset { Atlassert (m_hwnd == null); Atlassert (:: iswindow (hwnd)); M_thunk.init (getWindowProc (), this) WndProc pproc = (wndproc) & (m_thunk.thunk); WndProc PfnWndProc = (WndProc) :: setWindowlong (hwnd, gwl_wndproc, (Long) PPROC); IF (PfnWndProc == Null) Return False; m_pfnsuperwindowproc = pfnwndproc; m_hwnd = hwnd; Return True; } Nothing to say, its job is to initialize a THUNK code and then replace the original window function.