如何用MinHook正确Hook D3D12的CopyTextureRegion方法
代码失效原因
ID3D12GraphicsCommandList::CopyTextureRegion是COM接口的成员方法,不会以独立命名符号的形式从d3d12.dll导出,用GetProcAddress按函数名查询必然返回空地址。这类虚函数走接口虚表分发逻辑,和你之前成功Hook的D3D12CreateDevice这类全局导出函数的寻址逻辑完全不同。
正确Hook实现方案
用MinHook Hook COM成员方法的核心是直接修改虚表中对应位置的函数地址,操作流程如下:
- 获取一个有效的
ID3D12GraphicsCommandList实例指针:既可以用渲染流程中正常创建的CommandList对象,也可以临时创建临时CommandList对象取虚表地址(同版本接口的所有实例共享同一张虚函数表,临时对象用完释放即可)。 - 从实例头部取出虚表指针,定位
CopyTextureRegion方法在虚表中的对应偏移位置,用MinHook对该地址创建Hook即可。
参考实现代码:
// 原函数指针定义,签名、调用约定必须和原方法完全匹配 typedef HRESULT(WINAPI* CopyTextureRegion_Original)( ID3D12GraphicsCommandList* pThis, const D3D12_TEXTURE_COPY_LOCATION* pDst, UINT DstX, UINT DstY, UINT DstZ, const D3D12_TEXTURE_COPY_LOCATION* pSrc, const D3D12_BOX* pSrcBox ); CopyTextureRegion_Original oCopyTextureRegion = nullptr; // 自定义Hook函数 HRESULT WINAPI HK_CopyTextureRegion( ID3D12GraphicsCommandList* pThis, const D3D12_TEXTURE_COPY_LOCATION* pDst, UINT DstX, UINT DstY, UINT DstZ, const D3D12_TEXTURE_COPY_LOCATION* pSrc, const D3D12_BOX* pSrcBox) { // 自定义Hook逻辑写在这里 return oCopyTextureRegion(pThis, pDst, DstX, DstY, DstZ, pSrc, pSrcBox); } // Hook初始化逻辑 void InitCopyTextureRegionHook() { // 假设你已经通过正常流程拿到了有效的D3D12设备指针pDevice ID3D12CommandAllocator* pTempAllocator = nullptr; ID3D12GraphicsCommandList* pTempCmdList = nullptr; // 创建临时资源用于取虚表 pDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&pTempAllocator)); pDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, pTempAllocator, nullptr, IID_PPV_ARGS(&pTempCmdList)); // 取虚表指针:COM对象首地址存储的就是虚表指针 void** pVTable = *reinterpret_cast<void***>(pTempCmdList); // 基类ID3D12GraphicsCommandList中CopyTextureRegion的固定虚表索引,若调试异常可自行核对虚表偏移 constexpr int VTABLE_INDEX_CopyTextureRegion = 16; void* pTargetFunc = pVTable[VTABLE_INDEX_CopyTextureRegion]; // 创建并启用Hook MH_CreateHook(pTargetFunc, &HK_CopyTextureRegion, reinterpret_cast<void**>(&oCopyTextureRegion)); MH_EnableHook(pTargetFunc); // 释放临时资源 pTempCmdList->Close(); pTempCmdList->Release(); pTempAllocator->Release(); }
注意事项
- 虚表索引从0开始计数,需要把所有父接口(IUnknown、ID3D12Object、ID3D12DeviceChild、ID3D12CommandList)的方法全部计入偏移。COM接口二进制规范要求已发布的基类接口不能修改方法顺序,正常情况下上述索引在所有Win10/Win11版本中通用,如果调试发现调用异常,可以在调试器中直接查看虚表内存,匹配函数签名确认正确位置。
- Hook函数的返回值、参数列表、调用约定必须和原方法完全一致,x86下COM方法默认使用
__stdcall调用约定,x64下统一使用微软x64快速调用约定,签名不匹配会直接触发程序崩溃。 - 同版本接口的所有实例共享同一张虚表,只需要修改一次虚表对应位置的函数地址,所有该接口实例调用
CopyTextureRegion时都会走到Hook逻辑中,不需要给每个实例单独部署Hook。
内容的提问来源于stack exchange,提问作者Heinz Josef
相关产品推荐
相关产品推荐

