Create your own MFC: Thunk technology to achieve window packages

xiaoxiao2021-03-06  68

The MFC function is very powerful, and it is not meaningless to do itself, but it can learn a lot during this process. For example:

Window package, process the mapping method for processing from the global window message to the window object message processing:

Packaging the interface, usually a window, such as implementing a one-base window CMYWnd, you will definitely use the window process as a member function of this class, but you must register with WNDCLASS when you create a window using WinAPI. Member data lpfnwndproc requires WndProc's function pointer, the general idea is to pass the message processing function pointer of the window class, but the class member function is unless static, otherwise it cannot be converted to WndProc, and the overall message processing function cannot get the window object. Pointer. Here are some solutions: a solution is to use a window list, open a structure array, and put the window hWnd and the THIS pointer in an array when the window object is created. The global message processing function traverses an array, and use hwnd to find this. The pointer is then positioned to the message processing function inside the object. This method of finding an object increases with the increase of the number of windows. Another method is smart, and there is a member data in WNDCLASS. CbWndextra is generally unused. Use this, the registration class assigns a value, so that the system is created when the window will open up a memory and window binding according to this value. At this time, put the pointer of the created window class in the block, then take the pointer with getWindowlong (HWND, GWL_USERDATA) in a static window message loop function, Return (CMYWND *) -> WINDOWPROC (...), This will not use the traversal window. But in this way, there is a deadly weak point, and set to Windowlong (HWND, GWL_USERDATA, data) on the window, otherwise the program will crash. Fortunately, this function (specific these parameters) is a very low call chance, for the window, because the creation window is a CREATE function that calls the window class, does not need to be registered with the WNDCLASS class, and the setwindowlong function will not be called. However, after all, there is a lack of security, and when there are many window messages in a second, this lookup speed may not be fast enough. Another is a relatively perfect solution, called Thunk technology. Thunk is a set of dynamically generated ASM instructions that record the THIS pointer of the window class object, and this set of instructions can be used as a function, or can be used in a window process. Thunk first records the window object this pointer, then turn to the static stdproc callback function, and record the hWnd before turning, then replace the contents of HWnd in the stack to the THIS pointer, which can retrieve the object pointer from the HWND in stdproc, positioning Go to WindowProc. Let's take a look at the window process function definition: LResult WinApi WindowProc (HWND HWND, UINT UMSG, WPARAM WPARAM, LPARAM LPARAM) actually when our window CMYWND creates a window, the window handle can be saved and saved as a member data, so As soon as I get the first parameter hWnd is it, because you can get through this-> m_hwnd, we can do your hands here, HWnd is essentially a pointer, if this parameter replaces this parameter with the THIS pointer of the window class object, then Do we pass (CMYWND *) HWND-> WINDOWPROC to the window process inside the window class? But the window process is systematically called, how can I replace HWND? Let's first look at the stack when the system calls this function:

The stack when M_thunk is called: Ret HWND MSG WPARM LPARAM ------------------------------------------------------------------------------------------------------------------------------ ---- Stack top stack system puts the parameters from right to left, and finally returns the address stack. We only modify the stack when the system calls the window process, and the HWND parameter is replaced. At this time, Thunk technology is useful, we first define a structure: #pragma pack (push, 1) // This structure must be aligned in byte Struct thunk {byte call; int off version; wndproc prot; byte code [5] CMYWND * WINDOW; BYTE JMP; BYTE ECX;}; # pragma pack (POP) class definition: Class CMYWND {public: Bool Create (...); LRESULT WINAPI WINDOWPROC (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 you create a window, set the window process to this-> m_thunk, m_thunk type is WndProc, so it is completely legal, of course, this m_thunk has not been initialized, and must be initialized before the window is created:

WndProc CMYWND :: Createthunk () {thunk * thunk = new thunk; /// // The stack when the system calls m_thunk: // Ret HWND MSG WPARAM LPARAM / / ------------ ----------------------------- / / Stack top ///

// Call Offset // Call Code [0], the CALL is executed, the next instruction is stack, that is, put the proc stack thunk-> call = 0xe8; // call [rel] 32 tunk-> offset = (size_t) & ((THUNK *) 0) -> CODE) - (SIZE_T) & ((Thunk *) 0) -> proc); // Offset, Skip Proc to Code [0] thunk-> proc = CMYWND :: stdproc; // Static window process // POP ECX, PROC has been placed, pop-up PROC to ECX THUNK-> CODE [0] = 0x59; // Pop ECX // Mov DWORD PTR [ESP 0x4], this // Proc has popped up, the top of the stack is the address, which is hwnd. // [ESP 0x4] 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; // steal the beam change column success! Jump to Proc // JMP [ECX] thunk-> jmp = 0xff; // jmp [r / m] 32 thunk-> ECX = 0x21; // [ECX] m_thunk = (wndproc) thunk; return m_thunk;} Although M_thunk is a structure, its data is an executable code, and its type is WndProc, and the system will call this code faithfully by the window process rule, and m_thunk replaces the this pointer recorded in the Window field. Replace the stack The hWnd parameters, then jump to the static stdproc: // This callback function has been replaced by m_thunk to object pointer LRESULT WINAPI CMYWND :: STDPROC (HWND HWND, UINT UMSG, UINT WPARAM, Long LPARAM) {CMYWND * w = (cmywnd *) hWnd; return, wparam, lparam;} This will turn the window process to the class member function WindowProc, of course, there is still a problem, that is, the window handle HWND has not been able to record Therefore, the start of the window process should first be positioned to static initproc, and will give the last parameter when CreateWindow, that is, the initialization parameter assigns the value of this pointer: CreateWindowex (DwexStyle, Szclass, Sztitle, DWStyle, X, Y, Width, Height, HparentWnd , HMENU, HINST, THIS / / Initialization parameters;

Remove the pointer in Initproc:

LRESULT WINAPI CMyWnd :: InitProc (HWND 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 process to m_thunk setwindowlong (hwnd, gwl_wndproc, (long) W-Createthunk ()); return (* (w-> getthunk) ())) (HWND, UMSG, WPARAM, LPARAM);}} Return DEFWINDOWPROC (HWND, UMSG, WPARAM, LPARAM);} This is greatly made. Window Process Forward Process: Assuming that the window object of the CMYWND class has been established CMYWND * Window, then call Window-> Create after initialization, at this time, Create's window has a function of static CMYWnd :: InitWndProc

InitWndProc implementation: Window-> CREATE Create a window When you put an object this pointer into a window initialization parameter, take the THIS pointer in the WM_NCCREATE message of this process: CMYWND * W = (CMYWND *) ((lpcreateStruct) LPARAM) -> lpcreateParams; Record hWnd: w-> m_hwnd = hWnd, then set the window process for W-> m_thunk (thunk is a WndProc type member data, so you can set) └ → window-> m_thunk implementation function: Jump to static CMYWND :: STDPROC, before this replacement system call parameter hWnd is this pointer └ → stdproc implementation function: convert HWND to window pointer: CMYWND * W = (CMYWND *) hWnd; Return W-> WINDOWPROC (UMSG, WPARAM , lParam) └ → Window-> WINDOWPROC implementation: Perform the actual message processing, the window handle is saved in m_hwnd

Exterior: Thunk's assembly code is written in the annotation, and the compilation of this compilation can have a lot of effort. At that time, there was no suitable tool. There was only one "8086/8088 assembly language program design", according to Appendix. The instruction code summary table is converted into machine code data, and there is no concept of EAX, ECX, ESP, etc., and can only connotation of guessing debugging, illegally operating N (n> 10) returned to those data, at the time It's really a breather: TNND, finally got it! :-)

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

New Post(0)