开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

查看: 1299|回复: 0
收起左侧

[源码分享] c++子类化源码, 支持类方法, 留帖记录

[复制链接]

结帖率:100% (9/9)
发表于 2020-2-28 18:21:04 | 显示全部楼层 |阅读模式   广西壮族自治区崇左市
  1. #pragma once

  2. // 命名空间内的不建议外部使用
  3. // 使用前需要 #include<windows.h>
  4. namespace __subclass
  5. {
  6.         typedef struct tagMAKEPROCDATA
  7.         {
  8.                 BYTE data[40];                // 为了通用, 指令都写到这个数组里, 也不需要1字节对齐
  9.         }MAKEPROCDATA, * LPMAKEPROCDATA;
  10. }
  11. typedef struct tagSUBCLASSSTRUCT : private __subclass::tagMAKEPROCDATA       
  12. {
  13.         // 私有继承
  14.         HWND hWnd;                                // 子类化的窗口句柄
  15.         WNDPROC oldProc;                // 旧窗口过程
  16.         void* param;                        // 用户数据
  17. }SUBCLASSSTRUCT, * LPSUBCLASSSTRUCT;
  18. namespace __subclass
  19. {
  20.         // 由于VirtualAlloc() 申请的字节数是一页, 一般一页是4096个字节
  21.         // 每次申请那么多, 只用几十个字节, 剩下的4000个字节都浪费了
  22.         // 所以做个简单的内存池
  23.         class simpleMempool
  24.         {
  25.         private:
  26.                 struct _list
  27.                 {
  28.                         struct _list* next;
  29.                 };
  30.                 _list* ptr;
  31.                 void* root;
  32.                 bool init()
  33.                 {
  34.                         if (ptr)return false;
  35.                         // 8k 个字节足够了, 最多支持 8192/sizeof(SUBCLASSSTRUCT) 次子类化操作, 在不释放的前提下
  36.                         size_t size = 0x2000;        // 4k对齐
  37. #ifdef _M_X64
  38.                         // x64需要自己指定申请的地址, 如果让系统自动分配, 有可能函数地址减去申请的地址会大于4个字节
  39.                         INT_PTR base = 0x100000000;
  40.                         for (INT_PTR i = 0; i < 50; i++)
  41.                         {
  42.                                 ptr = (_list*)VirtualAlloc((LPVOID)base, size,
  43.                                         MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  44.                                 if (ptr)break;
  45.                                 base += 0x100000000;
  46.                         }
  47. #else
  48.                         // x86没那么多讲究, 让系统自己分配地址
  49.                         ptr = (_list*)VirtualAlloc(0, size,
  50.                                 MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  51. #endif
  52.                         if (!ptr)return false;

  53.                         root = ptr;        // 首地址, 首节点
  54.                         LPBYTE p = (LPBYTE)ptr;
  55.                         size_t len = size / sizeof(SUBCLASSSTRUCT) - 1;
  56.                         for (size_t i = 0; i < len; i++)
  57.                         {
  58.                                 // 用一个单向链表记录可用内存地址
  59.                                 // 每个地址记录的大小为 SUBCLASSSTRUCT 结构大小
  60.                                 p += sizeof(SUBCLASSSTRUCT);
  61.                                 ptr->next = (_list*)p;
  62.                                 ptr = (_list*)p;        // 指向下一个节点
  63.                         }
  64.                         ptr->next = 0;
  65.                         ptr = (_list*)root;
  66.                         return true;
  67.                 }
  68.         public:
  69.                 simpleMempool() :ptr(0), root(0) { ; }
  70.                 ~simpleMempool() { VirtualFree(root, 0, MEM_RELEASE); ptr = 0; root = 0; }
  71.                
  72.                 // 申请失败则抛出int类型异常
  73.                 // 异常值 1=初始化失败, 2=空间不足,需要释放一些内存
  74.                 LPMAKEPROCDATA alloc()
  75.                 {
  76.                         size_t size = sizeof(SUBCLASSSTRUCT);
  77.                         if (!ptr)init();
  78.                         if (!ptr)throw int(1);
  79.                         void* p = ptr;                // 每次申请都从当前节点开始取, 释放则还原到首节点
  80.                         if (ptr->next == 0)throw int(2);        // 没有内存了, 抛出异常

  81.                         ptr = ptr->next;        // 当前节点指向下一块内存
  82.                         return (LPMAKEPROCDATA)p;
  83.                 }
  84.                 bool free(LPMAKEPROCDATA& p)
  85.                 {
  86.                         if (!ptr || !p)return false;
  87.                         // 释放就简单的加入链表中
  88.                         memset(p, 0, sizeof(SUBCLASSSTRUCT));
  89.                         ((_list*)p)->next = ptr;        // 放到链表头部
  90.                         ptr = (_list*)p;
  91.                         p = 0;
  92.                         return true;
  93.                 }
  94.         };
  95.         static simpleMempool mempool;
  96.         inline LPMAKEPROCDATA subclassAlloc()
  97.         {
  98.                 LPMAKEPROCDATA pData = 0;
  99.                 try
  100.                 {
  101.                         pData = mempool.alloc();
  102.                 }
  103.                 catch (int e)
  104.                 {
  105.                         switch (e)
  106.                         {
  107.                         case 1:
  108.                                 MessageBoxW(0, L"调用SubClassWindow() 失败\n失败原因为:VirtualAlloc() 申请内存失败", L"错误", MB_OK);
  109.                                 break;
  110.                         case 2:
  111.                                 MessageBoxW(0, L"调用SubClassWindow() 失败\n失败原因为:为SubClassWindow() 提供的内存不足", L"错误", MB_OK);
  112.                                 break;
  113.                         default:
  114.                                 MessageBoxW(0, L"调用SubClassWindow() 失败\n失败原因为:其他未知错误", L"错误", MB_OK);
  115.                                 break;
  116.                         }
  117.                         return 0;
  118.                 }
  119.                 return pData;
  120.         }
  121. }

  122. typedef LRESULT(__stdcall* SUBWNDPROC)(LPSUBCLASSSTRUCT, UINT, WPARAM, LPARAM);


  123. // 返回值强转成 WNDPROC 就可以在注册类时赋值, 窗口过程的第一个参数就是这个返回值
  124. // proc  = 子类化回调函数
  125. // param = 用户数据
  126. inline LPSUBCLASSSTRUCT MakeProc(SUBWNDPROC proc, void* param)
  127. {
  128.         __subclass::LPMAKEPROCDATA pData = __subclass::subclassAlloc();
  129.         if (!pData)return 0;
  130. #ifdef _M_X64
  131.         // 这里执行的指令有15个字节, 函数地址 - 指令地址 - 指令字节数
  132.         const INT_PTR pFun = (INT_PTR)proc - INT_PTR(pData) - 15;
  133.         const unsigned char bin[] = {
  134.                 0x48, 0xb9, 0,0,0,0,0,0,0,0,                // mov rcx, pData(+2)
  135.                 0xe9, 0,0,0,0                                                // jmp pFun(+11)
  136.         };
  137.         memcpy(pData->data, bin, sizeof(bin));
  138.         memcpy(pData->data + 2, &pData, 8);
  139.         memcpy(pData->data + 11, &pFun, 4);

  140. #else
  141.         // 这里执行的指令有13个字节
  142.         const int pFun = (int)proc - int(pData) - 13;
  143.         const unsigned char bin[] = {
  144.                 0xc7, 0x44, 0x24, 0x04,        0,0,0,0,        // mov [esp+4], pData(+4)
  145.                 0xe9, 0,0,0,0                                                // jmp pFun(+9)
  146.         };
  147.         memcpy(pData->data, bin, sizeof(bin));
  148.         memcpy(pData->data + 4, &pData, 4);
  149.         memcpy(pData->data + 9, &pFun, 4);

  150. #endif

  151.         FlushInstructionCache(GetCurrentProcess(), pData, sizeof(SUBCLASSSTRUCT));
  152.         LPSUBCLASSSTRUCT ptr = (LPSUBCLASSSTRUCT)pData;
  153.         ptr->param = param;
  154.         // 返回的这个回调函数可以用作子类化的回调函数
  155.         // 子类化第一个参数是 SUBCLASSSTRUCT 结构指针
  156.         return ptr;
  157. }

  158. // 返回值强转成 WNDPROC 就可以在注册类时赋值, 窗口过程的第一个参数就是这个返回值
  159. // pThis = 回调函数所在的类指针
  160. // proc  = 子类化回调函数, 可以用 类方法, &类名::方法名  来获取
  161. // param = 用户数据
  162. template<typename T>
  163. inline LPSUBCLASSSTRUCT MakeProc(T* pThis, LRESULT(__stdcall T::* fnCallback)(LPSUBCLASSSTRUCT, UINT, WPARAM, LPARAM), void* param)
  164. {
  165.         const INT_PTR proc = (INT_PTR) * ((void**)&fnCallback);
  166.         __subclass::LPMAKEPROCDATA pData = __subclass::subclassAlloc();
  167.         if (!pData || !proc)return 0;
  168. #ifdef _M_X64
  169.         // 这里执行的指令有34个字节
  170.         const INT_PTR pFun = proc - (INT_PTR)pData - 37;
  171.         const unsigned char bin[] = {
  172.                 0x41, 0x51,                                                        // push r9
  173.                 0x8f, 0x44, 0x24, 0x28,                                // pop [esp-28h]
  174.                 0x4d, 0x8b, 0xc8,                                        // mov r9, r8
  175.                 0x4c, 0x8b, 0xc2,                                        // mov r8, rdx
  176.                 0x48, 0xba,        0,0,0,0,0,0,0,0,                // mov rdx, pData(+14)
  177.                 0x48, 0xb9, 0,0,0,0,0,0,0,0,                // mov rcx, pThis(+24)
  178.                 0xe9, 0,0,0,0                                                // jmp pFun
  179.         };
  180.         memcpy(pData->data, bin, sizeof(bin));
  181.         memcpy(pData->data + 14, &pData, 8);
  182.         memcpy(pData->data + 24, &pThis, 8);
  183.         memcpy(pData->data + 33, &pFun, 4);

  184. #else
  185.         // 这里执行的指令有24个字节
  186.         const INT_PTR pFun = proc - (INT_PTR)pData - 24;
  187.         const unsigned char bin[] = {
  188.                 0xff, 0x34, 0x24,                                        // push esp
  189.                 0xc7, 0x44, 0x24, 0x04,        0,0,0,0,        // mov [esp+4], pThis(+7)
  190.                 0xc7, 0x44, 0x24, 0x08,        0,0,0,0,        // mov [esp+8], pData(+15)
  191.                 0xe9, 0,0,0,0                                                // jmp pFun(+20)
  192.         };
  193.         memcpy(pData->data, bin, sizeof(bin));
  194.         memcpy(pData->data + 7, &pThis, 4);
  195.         memcpy(pData->data + 15, &pData, 4);
  196.         memcpy(pData->data + 20, &pFun, 4);

  197. #endif
  198.         FlushInstructionCache(GetCurrentProcess(), pData, sizeof(SUBCLASSSTRUCT));
  199.         LPSUBCLASSSTRUCT ptr = (LPSUBCLASSSTRUCT)pData;
  200.         ptr->param = param;
  201.         // 返回的这个回调函数可以用作子类化的回调函数
  202.         // 子类化第一个参数是 SUBCLASSSTRUCT 结构指针
  203.         return ptr;
  204. }


  205. // 当不需要使用时必须要调用FreeSubClass()进行释放, 传入的数据为子类化的第一个参数
  206. // 此函数调用 SetWindowLongPtrW(hWnd, GWLP_WNDPROC, (WNDPROC)MakeProc()) 来进行子类化
  207. // 如果只需要获取子类化的函数, 请调用 MakeProc()
  208. // hWnd  = 要子类化的窗口句柄
  209. // proc  = 子类化回调函数
  210. // param = 用户数据
  211. inline bool __stdcall SubClassWindow(HWND hWnd, SUBWNDPROC proc, void* param)
  212. {
  213.         if (!IsWindow(hWnd))return false;
  214.         LPSUBCLASSSTRUCT ptr = MakeProc(proc, param);
  215.         if (!ptr)return false;
  216.         ptr->hWnd = hWnd;
  217.         ptr->oldProc = (WNDPROC)SetWindowLongPtrW(hWnd, GWLP_WNDPROC, (LONG_PTR)ptr);
  218.         return true;
  219. }

  220. // 当不需要使用时必须要调用FreeSubClass()进行释放, 传入的数据为子类化的第一个参数
  221. // 此函数调用 SetWindowLongPtrW(hWnd, GWLP_WNDPROC, (WNDPROC)MakeProc()) 来进行子类化
  222. // 如果只需要获取子类化的函数, 请调用 MakeProc()
  223. // hWnd  = 要子类化的窗口句柄
  224. // pThis = 回调函数所在的类指针
  225. // proc  = 子类化回调函数, 可以用 类方法, &类名::方法名  来获取
  226. // param = 用户数据
  227. template<typename T>
  228. inline bool __stdcall SubClassWindow( HWND hWnd, T* pThis, LRESULT(__stdcall T::* proc)(LPSUBCLASSSTRUCT, UINT, WPARAM, LPARAM), void* param)
  229. {
  230.         if (!IsWindow(hWnd)) return false;
  231.         LPSUBCLASSSTRUCT ptr = MakeProc(pThis, proc, param);
  232.         if (!ptr)return false;
  233.         ptr->hWnd = hWnd;
  234.         ptr->oldProc = (WNDPROC)SetWindowLongPtrW(hWnd, GWLP_WNDPROC, (LONG_PTR)ptr);
  235.         // 返回的这个回调函数可以用作子类化的回调函数
  236.         // 子类化第一个参数是 SUBCLASSSTRUCT 结构指针
  237.         return true;
  238. }


  239. // 释放申请的内存空间, 调用后会把传入参数置0, 参数是子类化里第一个参数的值
  240. // 调用此函数前必须先调用 SetWindowLongW(hWnd, GWLP_WNDPROC, oldProc) 还原子类化
  241. inline bool __stdcall FreeSubClass(__subclass::LPMAKEPROCDATA& proc)
  242. {
  243.         return __subclass::mempool.free(proc);
  244. }

复制代码


下面是调用例子

  1. class test
  2. {
  3. public:
  4.         LRESULT WINAPI testProc(LPSUBCLASSSTRUCT pData, UINT message, WPARAM wParam, LPARAM lParam)
  5.         {
  6.                 // 跟正常的子类化差不多, 只不过第一个参数不是窗口句柄, 而是一个结构指针
  7.                 return CallWindowProcW(pData->oldProc, pData->hWnd, message, wParam, lParam);
  8.         }
  9. };

  10. LRESULT CALLBACK testProc(LPSUBCLASSSTRUCT pData, UINT message, WPARAM wParam, LPARAM lParam)
  11. {
  12.         // 跟正常的子类化差不多, 只不过第一个参数不是窗口句柄, 而是一个结构指针
  13.         return CallWindowProcW(pData->oldProc, pData->hWnd, message, wParam, lParam);
  14. }

  15. void test1(HWND hWnd)
  16. {
  17.         test a;
  18.         SubClassWindow(hWnd, &a, &test::testProc, 0);
  19.         SubClassWindow(hWnd, testProc, 0);
  20. }
复制代码

subclass.zip (2.88 KB, 下载次数: 8)
您需要登录后才可以回帖 登录 | 注册

本版积分规则 致发广告者

发布主题 收藏帖子 返回列表

sitemap| 易语言源码| 易语言教程| 易语言论坛| 易语言模块| 手机版| 广告投放| 精易论坛
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
防范网络诈骗,远离网络犯罪 违法和不良信息举报电话0663-3422125,QQ: 793400750,邮箱:wp@125.la
Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备12094385号-1) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

快速回复 返回顶部 返回列表