当前位置: 首页 > news >正文

17.ImGui-Hook消息循环

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!

本次游戏没法给

内容参考于:微尘网络安全

上一个内容:16.ImGui-绘制内部窗口

下图红框是ImGui里实现消息循环的代码,然后我们想得到游戏中的操作,比如改变窗口大小、键盘上按下了什么键,鼠标的操作等,就要得到游戏的消息循环,如果我们像下图一样创建一个消息循环,我们创建的消息循环只能得到,我们创建的窗口中的操作,没办法得到游戏中的操作,所以要获取一下游戏中的消息循环(获取的方式就是hook游戏的消息循环)

hook消息循环的代码,如下图红框,使用 SetWindowLongPtrA 函数,这是微软提供的它可以替换原本窗口的消息处理函数,也就是把原本的消息循环替换成我们的消息循环

然后找到ImGui的消息处理函数,如下图红框WndProc

把下图红框的代码复制到我们的代码中

如下图红框复制完后

然后ImGui的消息处理函数返回的是 DefWindowProcW 这个函数的结果

然后SetWindowLongPtr 微软官网说明要使用CallWindowProc函数,所以要改一下

然后改完之后的代码

效果图:我们的ImGui窗口就可以拖动、改变大小了,也就是能得到消息循环了

完整代码和说明

// 包含核心头文件:定义DirectX、Windows API、ImGui等基础功能
#include "main.h"// 1. 获取游戏窗口句柄(定位目标窗口)
// 原理:Unreal引擎游戏的窗口类名固定为"UnrealWindow"(引擎官方定义)
// 作用:后续所有UI绘制和消息处理都绑定到这个窗口
HWND hWnd = FindWindowA("UnrealWindow", NULL);// 2. DirectX11核心组件(全局变量)
// 为什么用static?确保变量在DLL生命周期内唯一,避免重复创建
static ID3D11Device* g_pd3dDevice = nullptr;               // 绘图设备:创建ImGui所需的渲染资源
static ID3D11DeviceContext* g_pd3dDeviceContext = nullptr; // 设备上下文:执行实际的绘制命令
static IDXGISwapChain* g_pSwapChain = nullptr;             // 临时交换链:仅用于获取Present函数的位置
static ID3D11RenderTargetView* g_mainRenderTargetView = nullptr; // 渲染目标:绑定游戏后台缓冲区(UI绘制的目标)
DWORD64* VirtaulTable; // 虚函数表指针:存储IDXGISwapChain的全局共享函数表// 3. 定义Present函数类型(遵循DXGI标准)
// 为什么这样定义?Present是交换链用于显示画面的核心函数,必须严格匹配其参数格式
// 参数说明:
// - This:调用该函数的交换链对象(游戏自身的交换链)
// - SyncInterval:垂直同步参数(0=关闭,1=开启)
// - Flags:显示控制标志(通常为0)
typedef HRESULT(STDMETHODCALLTYPE* Present)(IDXGISwapChain* This,UINT SyncInterval,UINT Flags
);
Present MyPresent; // 保存游戏原始的Present函数地址(Hook后需调用以显示画面)// 4. 最终Hook函数(每帧执行,绘制ImGui UI)
// 作用:替换游戏的Present函数,在显示画面时叠加ImGui界面
HRESULT VtPresent(IDXGISwapChain* This,UINT SyncInterval,UINT Flags
) {// 初始化ImGui新帧(每帧必须调用,重置UI状态)// 为什么要分三步?ImGui需要先初始化后端(DX11/Win32),再初始化核心逻辑ImGui_ImplDX11_NewFrame();  // DX11后端初始化ImGui_ImplWin32_NewFrame(); // Win32后端初始化(处理输入)ImGui::NewFrame();          // ImGui核心初始化// 绘制ImGui窗口(示例:简单的Hello窗口)ImGui::Begin("Hello, world!");  // 开始创建窗口ImGui::End();                   // 结束窗口创建(必须与Begin配对)// 渲染ImGui界面(输出到游戏画面)ImGui::Render();// 绑定渲染目标:告诉GPU绘制到游戏的后台缓冲区// 为什么用g_mainRenderTargetView?它绑定了游戏的后台缓冲区,确保UI能叠加显示g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, nullptr);// 执行绘制命令:将ImGui的UI数据画到屏幕上ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());// 调用游戏原始的Present函数(必须调用,否则游戏画面无法显示)return MyPresent(This, SyncInterval, Flags);
}// 5. 窗口消息处理相关(让ImGui能响应鼠标/键盘)
// 声明ImGui的消息处理函数(来自ImGui库,负责处理UI交互)
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
WNDPROC myWNDPROC; // 保存游戏原始的窗口消息处理函数// 自定义窗口消息处理函数(Hook游戏的消息处理)
// 作用:先让ImGui处理UI相关消息,再将剩余消息传给游戏
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{// 优先让ImGui处理消息(如UI拖动、按钮点击)// 为什么先处理?避免游戏误响应UI操作(如点击UI时游戏以为点击了游戏内物体)if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))return true;// 未被ImGui处理的消息,传给游戏原始函数(确保游戏正常响应操作)return ::CallWindowProc(myWNDPROC, hWnd, msg, wParam, lParam);
}// 6. 初始化Hook函数(只执行一次,准备ImGui环境)
// 作用:第一次调用Present时执行,完成初始化后切换到VtPresent
HRESULT Init(IDXGISwapChain* This,UINT SyncInterval,UINT Flags
) {// Hook窗口消息处理函数(让ImGui能接收输入)// 为什么在这里Hook?初始化阶段只执行一次,确保UI显示前就具备交互能力myWNDPROC = (WNDPROC)SetWindowLongPtrA(hWnd, GWLP_WNDPROC, (LONG_PTR)WndProc);// 获取游戏的DirectX设备和上下文(关键步骤)// 为什么从This获取?This是游戏自己的交换链,其绑定的设备与游戏环境完全兼容// 自己创建设备可能导致UI无法显示(设备不匹配)This->GetDevice(_uuidof(g_pd3dDevice), (void**)&g_pd3dDevice);g_pd3dDevice->GetImmediateContext((ID3D11DeviceContext**)&g_pd3dDeviceContext);// 创建ImGui的渲染目标(绑定游戏后台缓冲区)// 为什么要绑定?ImGui必须画在游戏的缓冲区上才能与游戏画面叠加ID3D11Texture2D* pBackBuffer;This->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); // 获取游戏的后台缓冲区g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView);pBackBuffer->Release(); // 释放临时指针(避免内存泄漏)// 初始化ImGui(创建上下文、绑定后端)ImGui::CreateContext();               // 创建ImGui核心上下文ImGui::StyleColorsDark();             // 设置暗色主题ImGui_ImplWin32_Init(hWnd);           // 绑定Win32后端(处理输入)ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); // 绑定DX11后端(绘制UI)printf("HOOK成功:初始化完成\n"); // 调试日志// 切换Hook目标:从Init换成VtPresent(初始化完成,后续每帧执行VtPresent)VirtaulTable[8] = (DWORD64)VtPresent;// 调用原始Present函数(显示当前帧画面,避免黑屏)return MyPresent(This, SyncInterval, Flags);
}// 7. 线程函数(核心:创建临时交换链,安装初始Hook)
// 为什么用线程?避免阻塞游戏主线程,确保注入后游戏能正常运行
DWORD Go(LPVOID lpThreadParameter) {// 配置临时交换链参数(创建符合游戏窗口特性的交换链)DXGI_SWAP_CHAIN_DESC sd;ZeroMemory(&sd, sizeof(sd)); // 清空参数(避免随机值导致创建失败)sd.BufferCount = 2;          // 双缓冲区(标准配置,避免画面闪烁)sd.BufferDesc.Width = 0;     // 宽度自动匹配游戏窗口sd.BufferDesc.Height = 0;    // 高度自动匹配游戏窗口sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // 32位RGBA(游戏通用格式)sd.BufferDesc.RefreshRate.Numerator = 60; // 刷新率60Hzsd.BufferDesc.RefreshRate.Denominator = 1;sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; // 允许窗口/全屏切换sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;  // 用于显示画面sd.OutputWindow = hWnd;      // 绑定游戏窗口sd.SampleDesc.Count = 1;     // 无抗锯齿(性能优先)sd.Windowed = TRUE;          // 窗口模式(兼容性更好)sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; // 交换后丢弃后台数据(性能最优)// 创建临时DirectX设备和交换链(用于获取虚函数表)UINT createDeviceFlags = 0;D3D_FEATURE_LEVEL featureLevel;const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0 };HRESULT res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags,featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain,&g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);// 硬件加速失败时,尝试软件渲染(兼容性兜底)if (res == DXGI_ERROR_UNSUPPORTED)res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, createDeviceFlags,featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain,&g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);if (res != S_OK) return false; // 创建失败则退出// 获取IDXGISwapChain的全局虚函数表(关键!所有交换链共用此表)VirtaulTable = *(DWORD64**)g_pSwapChain;// 保存原始Present函数地址(虚表第8位,微软标准规定)MyPresent = (Present)VirtaulTable[8];// 修改虚表内存保护(默认只读,需改为可写才能替换函数)DWORD oldProtect;VirtualProtect(VirtaulTable, 1, PAGE_EXECUTE_READWRITE, &oldProtect);// 安装初始Hook:将Present替换为Init(第一次调用时执行初始化)VirtaulTable[8] = (DWORD64)Init;return 0;
}

img

http://www.dtcms.com/a/390895.html

相关文章:

  • 《Skinned Mesh Renderer与LOD系统蒙皮变形异常全解析》
  • 免费插件分享 |Pro Scene Manager
  • Elasticsearch 的 ES|QL 编辑器体验 vs. OpenSearch 的 PPL 事件分析器
  • Unity核心概念⑪:光
  • C 语言运算符优先级(超详细)
  • Ingress使用示例
  • HarmonyOS开源项目分享:识笺——高效学习的卡片应用
  • 揭秘提示词攻击:AI时代的安全新战场
  • vscode安装go插件问题
  • 创作一个简单的编程语言3 加上VLLM后端
  • C语言入门指南:内存操作函数详解
  • React 列表渲染 列表排序 条件渲染 数据渲染 响应式处理
  • 从安卓手机切换到iPhone:好处、缺点及4种方法
  • C++ 篇 类和对象(1)万能工具怎么用?
  • Ansible-copy模块
  • SAPO去中心化训练:多节点协作让LLM训练效率提升94%
  • Stm32 IAP 升级
  • 5G标准学习笔记17------ MDT(Minimization of Drive Tests)路测最小化
  • [Dify] 构建“流程型表单问答”系统:逐步提问逻辑实现
  • 从RAW到JPG到BMP:工业视觉图像格式怎么选?
  • Linux系统Rsync+sersync 实现数据同步
  • 【13/20】缓存与性能优化:Redis 在 Express 中的整合,实现用户数据缓存
  • 如何防止电脑长时间运行过热?定时关机是第一道防线
  • 开源监控利器Prometheus+Grafana在银河麒麟操作系统的落地实践
  • 小程序移动端设计UI(一)预约小程序——东方仙盟练气期
  • Android13 命令启用WLAN详细日志分析
  • 临床AI产品化全流程研究:环境聆听、在环校验与可追溯系统的多技术融合实践(中)
  • 深度解读昇腾CANN动态Shape图调度加速技术
  • linux系统使用ImageMagick注意,只能使用convert命令
  • [Windows] 搜狗拼音一键净化