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

VC版本的MakeObjectInstance把WNDPROC映射到类的成员函数的更多相关文章

  1. VC.VS版本&VC版本&OpenCV版本

    1.VS版本 与 VC版本 的对应关系,以及opencv 对 VC版本 的支持情况 - 魔法学徒 - CSDN博客.html(https://blog.csdn.net/yefcion/article ...

  2. 直接调用类成员函数地址(用汇编取类成员函数的地址,各VS版本还有所不同)

    在C++中,成员函数的指针是个比较特殊的东西.对普通的函数指针来说,可以视为一个地址,在需要的时候可以任意转换并直接调用.但对成员函数来说,常规类型转换是通不过编译的,调用的时候也必须采用特殊的语法. ...

  3. VS2013 VC++的.cpp文件调用CUDA的.cu文件中的函数

    CUDA 8.0在函数的调用中方便的让人感动.以下是从网上学到的VC++的.cpp文件调用CUDA的.cu文件中的函数方法,和一般的VC++函数调用的方法基本没差别. 使用的CUDA版本为CUDA 8 ...

  4. MyBatis-Spring 学习笔记一 SqlSessionFactoryBean以及映射器类

    MyBatis-Spring 是一个用来整合 MyBatis 和 Spring 框架的小类库,通过这个jar包可以将 MyBatis 代码地整合到 Spring 中. 使用这个类库中的类, Sprin ...

  5. Hibernate框架学习之注解映射实体类

         前面的相关文章中,我们已经介绍了使用XML配置文件映射实体类及其各种类型的属性的相关知识.然而不论是时代的潮流还是臃肿繁杂的配置代码告诉我们,注解配置才是更人性化的设计,于是学习了基本的映射 ...

  6. MyBatis Spring整合配置映射接口类与映射xml文件

    本文转自http://blog.csdn.net/zht666/article/details/38706083 Spring整合MyBatis使用到了mybatis-spring,在配置mybati ...

  7. vc MFC 通过IDispatch调用默认成员函数

    CComPtr<IDispatch> spDisp(IDispatch *); if(!spDisp) return; DISPPARAMS dispParam={0}; //没有参数 V ...

  8. SMACH(五)----用户数据UserData类和重映射Remapper类的原理和例子

    用户数据UserData类和重映射Remapper类包含在smach中的user_data.py文件中实现,该博文主要介绍其原理和例子 UserData主要用于状态之间的数据传递,包括数据的输入inp ...

  9. c++类成员函数重载常量与非常量版本时避免代码重复的一种方法

    c++有时候需要为类的某个成员函数重载常量与非常量的版本,定义常量版本是为了保证该函数可作用于常量类对象上,并防止函数改动对象内容.但有时两个版本的函数仅仅是在返回的类型不同,而在返回前做了大量相同的 ...

随机推荐

  1. 晨曦之光 linux Crontab 使用(转)

    cron用法说明 cron的用法老是记不住,索性写下来备忘.下文内容大部分是根据<Cron Help Guide>翻译而来,有些部分是自己加上的. 全文如下: cron来源于希腊单词chr ...

  2. PHP面向对象之旅:模板模式(转)

    抽象类的应用就是典型的模版模式 抽象类的应用就是典型的模版模式,先声明一个不能被实例化的模版,在子类中去依照模版实现具体的应用. 我们写这样一个应用: 银行计算利息,都是利率乘以本金和存款时间,但各种 ...

  3. Android制作粒子爆炸特效

    简介 最近在闲逛的时候,发现了一款粒子爆炸特效的控件,觉得比较有意思,效果也不错. 但是代码不好扩展,也就是说如果要提供不同的爆炸效果,需要修改的地方比较多.于是我对源代码进行了一些重构,将爆炸流程和 ...

  4. Winform 程序中dll程序集嵌入exe可执行文件

    关于这方面,Google一下有很多方法,参考: http://blog.csdn.net/astonqa/article/details/7300856 但按照以上的方法我并没有成功,于是继续找到了一 ...

  5. 织梦dede编辑器ckeditor如何添加中文字体不乱码

    dedecms内容编辑器ckeditor没有中文字体,找了很多教程都是千篇一律,而且都是错的,终于找到了一篇,结合自己的实际操作,来教您如何添加中文字体,并且解决乱码问题.   工具/原料 dedec ...

  6. CSS3 变形小结

    为原始大小 b:纵向扭曲,0为不变 c :横向扭曲,0不变 d:垂直伸缩量,1为原始大小 e:水平偏移量,0为初始位置 f  :垂直偏移向,0是初始位置 Ø原点 transform-origin() ...

  7. <html:text> Id属性

    可能 会遇到这样 的问题,需要通过document.getElementById得到<html:text>标签的id, 可是据说ie设置property后id就是一样的了,不过具体没有去测 ...

  8. 判断浏览器是否支持FileReader

    1.js代码: //判断浏览器是否支持FileReader if (typeof FileReader == "undefined") { document.write(" ...

  9. 不用Google Adsense的84个赚钱方法

    这是一个关于网络广告商和网络销售的汇总列表,可以用来为您的网站或博客赚点钱.广告商都是英文的,加入广告请确认其是否支持中国地区支持,不支持的话就不必加入了. Chitika : 购物中心旗帜广告. ( ...

  10. javascript的选项卡

    主要用的索引值 首先 写三个按钮 <input type="button" > <input type="button" > <i ...