这段时间用VC封装Windows类库,没有MakeObjectInstance处理窗口消息确实不爽,又不想使用MFC的消息映射,这玩意的效率和美观只能呵呵。
至于MakeObjectInstance是什么,Delphi转过来的同学必然很了解这个方便的功能,就是动态构造一个函数把普通函数转到一个类的成员函数。
VC X86实现起来没问题,但是X64实现起来的麻烦在于不能内嵌汇编了,X64必须结合ASM文件编译的obj(这一点还是感激Delphi的编译器,X86和X64都可以内联汇编)。
我的实现方案是通过构造一段ShellCode来达到目的。
SIZE_T PageSize = 4096; template <typename T>//产生一个代理函数 WNDPROC MakeObjectInstance(LPVOID AObject, T AMethod) { union { T MethodAddr;//成员函数指针 LPVOID NomralAddr;//正常指针 }ut;//因为VC不允许成员函数指针转换到普通指针。只能变通的通过union来实现 const unsigned char BlockCode[] = { #ifdef _WIN64 0x55,//{ push rbp } 0x48, 0x83, 0xEC, 0x40,//{ sub rsp,0x40 } 0x48, 0x8B, 0xEC,//{ mov rbp,rsp } 0x48, 0x89, 0x4D, 0x50,//{ mov[rbp + 0x50],rcx } 0x89, 0x55, 0x58,//{ mov[rbp + 0x58],edx } 0x4C, 0x89, 0x45, 0x60,//{ mov[rbp + 0x60],r8 } 0x4C, 0x89, 0x4D, 0x68,//{ mov[rbp + 0x68],r9 } 0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,//{ mov rcx,AObject } 0x48, 0x8B, 0x55, 0x50,//{ mov rdx,[rbp + 0x50] } 0x44, 0x8B, 0x45, 0x58,//{ mov r8,[rbp + 0x58] } 0x4C, 0x8B, 0x4D, 0x60,//{ mov r9,[rbp + 0x60] } 0x48, 0x8B, 0x45, 0x68,//{ mov rax,[rbp + 0x68] } 0x48, 0x89, 0x44, 0x24, 0x20,//{ mov[rsp + 0x20],rax } 0x49, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,//{ mov r11, AMethod } 0x49, 0xFF, 0xD3,//{ call r11 } 0x48, 0x8D, 0x65, 0x40,//{ lea rsp,[rbp + 0x40] } 0x5D,//{ pop rbp } 0xC3//{ ret } #else 0x58,//{ pop eax } 0x68, 0x00, 0x00, 0x00, 0x00,//{ push AObject } 0x50,//{ push eax } 0xB8, 0x00, 0x00, 0x00, 0x00,//{ mov eax, AMethod } 0xFF, 0xE0//{ jmp eax } #endif // endif }; size_t CodeBytes = sizeof(BlockCode); LPVOID Block = VirtualAlloc(nullptr, PageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); memcpy(Block, BlockCode, CodeBytes); unsigned char * bBlock = (unsigned char *)Block; ut.MethodAddr = AMethod; #ifdef _WIN64 *PLONG64(&bBlock[25])= LONG64(AObject); *PLONG64(&bBlock[0x38]) = LONG64(ut.NomralAddr); #else *PLONG32(&bBlock[2]) = LONG32(AObject); *PLONG32(&bBlock[8]) = LONG32(ut.NomralAddr); #endif return (WNDPROC)Block; } //释放代理函数 void FreeObjectInstance(WNDPROC wndProc) { VirtualFree(wndProc, PageSize, MEM_RELEASE); }
用法类似的如下
class MyClass{ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); } MyClass c; WNDCLASSEXW wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = MakeObjectInstance(&c, &MyClass::WndProc);//使用MyClass::WndProc作为创消息处理函数 wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WNDPROCTEST)); wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WNDPROCTEST); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); RegisterClassExW(&wcex);
再例如
class MyClass{ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); } MyClass c; SetWindowLongPtr(hWnd, GWL_WNDPROC, (LONG_PTR)MakeObjectInstance(&c, &MyClass::WndProc));