The MFC function is already very powerful, and it may be boring to create an interface library by yourself, but you can learn a lot in this process. For example:
Window class encapsulation, from global window message processing to window object Message Processing ing method:
Encapsulate the interface. Generally, the interface is a window class. For example, to implement the most basic window class cmywnd, you must pass the window through
As a member function of this class, but when using winapi to create a window, you must register the wndclass class, which contains a function that requires wndproc for the member data lpfnwndproc.
Pointer. The general idea is to pass the pointer to the message processing function of the window class, but the class member function cannot be converted to wndproc unless it is static, the global message processing function cannot obtain a window.
Class Object Pointer. There are several solutions:
One solution is to use the window list to open a structure array. When creating a window object, the window hwnd and this pointers are put into the array, and the global message processing function traverses the array, find the this pointer using hwnd and locate the message processing function inside the object. The time for searching objects in this method increases with the increase in the number of windows.
The other method is more intelligent. In wndclass, cbwndextra contains a member data, which is generally not used. In this way, the member data is assigned a value when registering the class. In this way, the system creates a window.
A memory bound to the window will be opened based on this value. At this time, the pointer of the created window class will be placed in the memory, so that the static window message loop function can use
Getwindowlong (hwnd, gwl_userdata) retrieves this pointer, return
(Cmywnd *)-> windowproc (...), so that you do not need to traverse the window. But in this way, there is a fatal weakness, and the window cannot be called.
Setwindowlong (hwnd, gwl_userdata, data). Otherwise, the program will crash. Fortunately, this function (specific parameters) has a very low call probability.
In the window, because the create function of the window class is called during window creation, you do not need to manually register the wndclass class, so you will not call the setwindowlong function. However, security
Comprehensive, and when many window messages are processed within one second, the search speed may not be fast enough.
There is also a perfect solution called thunk technology. Thunk is a set of dynamically generated ASM commands, which record the this pointer of window class objects, and these commands can be used as functions, either as window procedures. Thunk first records the this pointer of the window object, and then redirects to the static stdproc callback function. It records hwnd first, and then replaces the hwnd content in the stack with the this pointer, in this way, you can retrieve the object pointer from hwnd in stdproc and locate windowproc.
Let's take a look at the window process function definition:
Lresult winapi windowproc (hwnd, uint
Umsg, wparam, lparam)
In fact, when the window class cmywnd creates a window, the window handle can be obtained and saved as the member data. In this way, the first parameter hwnd is optional because it can be accessed through
This-> m_hwnd, we can do it here. hwnd is actually a pointer. If we replace this parameter with the this pointer of the window class object, then, can we use (cmywnd *) hwnd-> windowproc to switch to the window process inside the window class? However, the window process is called by the system. How can we replace hwnd?
Let's take a look at the stack situation when the system calls this function:
Stack when the system calls m_thunk:
RET hwnd MSG wparam lparam
-------------------------------------------
Stack
Top stack bottom
The system presses the stack from right to left, and then the returned address presses the stack. We only need to modify the stack during the system call window and replace the hwnd parameter. Now thunk technology is useful. Let's first define a structure:
# Pragma pack (push, 1) // The structure must be byte aligned
Struct thunk {
Byte call;
Int offset;
Wndproc proc;
Byte Code [5];
Cmywnd * window;
Byte JMP;
Byte ECx;
};
# Pragma pack (POP)
Class Definition:
Class cmywnd
{
Public:
Bool create (...);
Lresult winapi windowproc (uint, wparam, lparam );
Static lresult
Winapi initproc (hwnd, uint, wparam, lparam );
Static lresult winapi
Stdproc (hwnd, uint, wparam, lparam );
Wndproc createthunk ();
Wndproc
Getthunk () {return m_thunk}
...
PRIVATE:
Wndproc
M_thunk;
}
When creating a window, set the window process to this-> m_thunk. The m_thunk type is wndproc, so it is completely legal. Of course, this
M_thunk has not been initialized. It must be initialized before the window is created:
Wndproc cmywnd: createthunk ()
{
Thunk * thunk = new thunk;
//////////////////////////////////////// ///////
//
Stack when the system calls m_thunk:
// RET hwnd MSG wparam lparam
//-------------------------------------------
//
Top stack bottom
//////////////////////////////////////// ///////
// Call offset
// Call code [0]. When the call is executed, the next command is pushed to the stack, that is, the proc is pressed to the stack.
Thunk-> call
= 0xe8; // call [rel] 32
Thunk-> offset =
(Size_t) & (Thunk *) 0)-> Code)-(size_t) & (Thunk *) 0)-> proc );
// Offset, skipping proc to code [0]
Thunk-> proc = cmywnd: stdproc; // static Window Process
// Pop
ECX and proc have been pushed to the stack, and proc to ECx are displayed.
Thunk-> code [0] = 0x59; // pop ECx
// Mov
Dword ptr [esp + 0x4], this
// The proc has been popped up. The top of the stack is the return address, followed by the hwnd.
// [Esp + 0x4]
That is, hwnd.
Thunk-> code [1] = 0xc7; // mov
Thunk-> code [2] =
0x44; // DWORD PTR
Thunk-> code [3] = 0x24; // disp8 [esp]
Thunk-> code [4]
= 0x04; // + 4
Thunk-> window = this;
// The column is stolen! Jump to proc
// JMP
[ECx]
Thunk-> JMP = 0xff; // JMP [R/m] 32
Thunk-> ECx =
0x21; // [ECx]
M_thunk = (wndproc) thunk;
Return
M_thunk;
}
In this way, although m_thunk is a structure, its data is a piece of executable code, and its type is wndproc, the system will faithfully call this code according to the window process rules, m_thunk replaces the this pointer recorded in the window field with the hwnd parameter in the stack, and jumps to the static stdproc:
//
The hwnd of this callback function has been replaced by the object pointer by m_thunk before being called.
Lresult winapi cmywnd: stdproc (hwnd
Hwnd, uint umsg, uint wparam, long lparam)
{
Cmywnd * w =
(Cmywnd *) hwnd;
Return w-> windowproc (umsg, wparam, lparam );
}
In this way, the window process is switched to the class member function windowproc. Of course, there is another problem, that is, the window handle hwnd has not been recorded yet, therefore, the initial window process should first locate the static initproc. When createwindow is used, assign the last parameter, that is, the initialization parameter to the this pointer:
Createmediawex (
Dwexstyle,
Szclass,
Sztitle,
Dwstyle,
X,
Y,
Width,
Height,
Hparentwnd,
Hmenu,
Hinst,
This // initialization parameter
);,
Retrieve the pointer from initproc:
Lresult winapi cmywnd: initproc (hwnd, uint umsg, uint wparam, long
Lparam)
{
If (umsg = wm_nccreate)
{
Cmywnd * w = NULL;
W
= (Cmywnd *) (lpcreatestruct) lparam)-> lpcreateparams;
If (W)
{
//
Record hwnd
W-> m_hwnd = hwnd;
// Change the window to m_thunk
Setwindowlong (hwnd, gwl_wndproc, (long) W-createthunk ());
Return
(* (Wndproc) (W-> getthunk () (hwnd, umsg, wparam, lparam );
}
}
Return
Defwindowproc (hwnd, umsg, wparam, lparam );
}
In this way, the success is achieved.
Window Process forwarding process:
Assume that the cmywnd class window object cmywnd has been created
* Window: After initialization, call window-> Create. In this case, the process function of the create window is static.
Cmywnd: initwndproc
Initwndproc |
Function: window-> Create Window Creation The object this pointer has been put into the window initialization parameter. In the wm_nccreate message in this process, the this pointer is obtained: cmywnd * w = (Cmywnd *) (lpcreatestruct) lparam)-> lpcreateparams; records hwnd: W-> m_hwnd = Hwnd, and then set the Window Process to w-> m_thunk (Thunk is a wndproc type member data, so you can set it) |
Else → |
Window-> m_thunk |
Function: Jump to static cmywnd: stdproc, Replace the system call parameter hwnd with the this pointer before this |
|
Else → |
Stdproc |
Function: Convert hwnd to window class pointer: Cmywnd * W = (cmywnd *) hwnd; Return w-> windowproc (umsg, wparam, lparam) |
|
|
Else → |
Window-> windowproc |
Function: implements the actual message processing. The window handle is saved in m_hwnd. |