• 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

  • 相关阅读:
    ORM查询相关的操作
    分享一些珍藏和网上搜集的一些接码平台
    DRF: serializers ModelSerializer的序列化中model在有外键的情况下显示name代替显示id的几种方式
    Django Rest framework中序列化A表时怎么获取B表的数据
    10步入门Django Rest framework后端接口框架
    Django Rest framework后端接口框架,常用的子类视图
    redis学习(九)——数据持久化
    Java8之lambda表达式
    Java多线程(九)—— interrupt()和线程终止方式
    redis学习(八)——redis应用场景
  • 原文地址:https://www.cnblogs.com/findumars/p/5833672.html
Copyright © 2020-2023  润新知