C++ x86 x64 类内类外子类化

留帖记录学习过程

要看懂源码需要了解一些汇编知识
不需要知道原理的, 直接按下面的调用例子来使用

subclass.h


#pragma once

// 命名空间内的不建议外部使用
// 使用前需要 #include<windows.h>
namespace __subclass
{
   
	typedef struct tagMAKEPROCDATA
	{
   
		BYTE data[80];		// 为了通用, 指令都写到这个数组里, 也不需要1字节对齐
	}MAKEPROCDATA, * LPMAKEPROCDATA;
}
typedef struct tagSUBCLASSSTRUCT : private __subclass::tagMAKEPROCDATA	
{
   
	// 私有继承
	HWND hWnd;				// 子类化的窗口句柄
	WNDPROC oldProc;		// 旧窗口过程
	void* param;			// 用户数据
}SUBCLASSSTRUCT, * LPSUBCLASSSTRUCT;

namespace __subclass
{
   
	// 由于VirtualAlloc() 申请的字节数是一页, 一般一页是4096个字节
	// 每次申请那么多, 只用几十个字节, 剩下的4000个字节都浪费了
	// 所以做个简单的内存池
	class simpleMempool
	{
   
	private:
		struct _list
		{
   
			struct _list* next;
		};
		_list* ptr;
		void* root;
		bool init()
		{
   
			if (ptr)return false;
			// 8k 个字节足够了, 最多支持 8192/sizeof(SUBCLASSSTRUCT) 次子类化操作, 在不释放的前提下
			size_t size = 0x2000;	// 4k对齐
#ifdef _M_X64
			// x64需要自己指定申请的地址, 如果让系统自动分配, 有可能函数地址减去申请的地址会大于4个字节
			INT_PTR base = 0x100000000;
			for (INT_PTR i = 0; i < 50; i++)
			{
   
				ptr = (_list*)VirtualAlloc((LPVOID)base, size,
					MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
				if (ptr)break;
				base += 0x100000000;
			}
#else
			// x86没那么多讲究, 让系统自己分配地址
			ptr = (_list*)VirtualAlloc(0, size,
				MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
#endif
			if (!ptr)return false;

			root = ptr;	// 首地址, 首节点
			LPBYTE p = (LPBYTE)ptr;
			size_t len = size / sizeof(SUBCLASSSTRUCT) - 1;
			for (size_t i = 0; i < len; i++)
			{
   
				// 用一个单向链表记录可用内存地址
				// 每个地址记录的大小为 SUBCLASSSTRUCT 结构大小
				p += sizeof(SUBCLASSSTRUCT);
				ptr->next = (_list*)p;
				ptr = (_list*)p;	// 指向下一个节点
			}
			ptr->next = 0;
			ptr = (_list*)root;
			return true;
		}
	public:
		simpleMempool() :ptr(0), root(0) {
    ; }
		~simpleMempool() {
    VirtualFree(root, 0, MEM_RELEASE); ptr = 0; root = 0; }
		
		// 申请失败则抛出int类型异常
		// 异常值 1=初始化失败, 2=空间不足,需要释放一些内存
		LPMAKEPROCDATA alloc()
		{
   
			size_t size = sizeof(SUBCLASSSTRUCT);
			if (!ptr)init();
			if (!ptr)throw int(1);
			void* p = ptr;		// 每次申请都从当前节点开始取, 释放则还原到首节点
			if (ptr->next == 0)throw int(2);	// 没有内存了, 抛出异常

			ptr = ptr->next;	// 当前节点指向下一块内存
			return (LPMAKEPROCDATA)p;
		}
		bool free(LPMAKEPROCDATA& p)
		{
   
			if (!ptr || !p)return false;
			// 释放就简单的加入链表中
			memset
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值