zoukankan      html  css  js  c++  java
  • VC版本的MakeObjectInstance把WNDPROC映射到类的成员函数

    这段时间用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);
    }

    用法类似的如下

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

    再例如

    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

  • 相关阅读:
    53. Maximum Subarray
    64. Minimum Path Sum
    28. Implement strStr()
    26. Remove Duplicates from Sorted Array
    21. Merge Two Sorted Lists
    14. Longest Common Prefix
    7. Reverse Integer
    412. Fizz Buzz
    linux_修改域名(centos)
    linux_redis常用数据类型操作
  • 原文地址:https://www.cnblogs.com/findumars/p/5509809.html
Copyright © 2011-2022 走看看