VC版本的MakeObjectInstance把WNDPROC映射到類的成員函數

這段時間用VC封裝Windows類庫,沒有MakeObjectInstance處理窗口消息確實不爽,又不想使用MFC的消息映射,這玩意的效率和美觀只能呵呵。函數

至於MakeObjectInstance是什麼,Delphi轉過來的同窗必然很瞭解這個方便的功能,就是動態構造一個函數把普通函數轉到一個類的成員函數。spa

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);
}

用法相似的以下code

&nbsp;
 
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);

再例如ci

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));

 

 

http://www.raysoftware.cn/?p=552編譯器

相關文章
相關標籤/搜索