C++HOOKD3D IMGUI绘制教程
https://www.bilibili.com/video/BV1iL4y1z7Da
https://gitee.com/szkke/dx9-hk-im-gui
https://github.com/ocornut/imgui
https://github.com/microsoft/Detours
https://www.microsoft.com/zh-cn/download/details.aspx?id=6812
安装后例子在C:Program Files (x86)Microsoft DirectX SDK (June 2010)SamplesC++Direct3D11Bin\
教程是vs2019,我是vs2022
1、打开vs2022 -> 创建新项目->动态链接库(DLL)->项目名称:ImGuidx9Hook、不勾选将解决方案和项目放在同一目录
1、在pch.h同级的地方创建子目录ImGui
2、复制imgui最高级目录的cpp h 文件、复制 imguibackends中win32.h win32.cpp dx9.h dx9.cpp
3、右键项目-添加-新建筛选器-“ImGui”-拖入刚才复制的文件
4、pch.h中include 刚才加的文件 #include "ImGui/imconfig.h" 。。。
复制很多#include "ImGui" 在ImGui后加/就可以自动选择头文件添加
1、编译detours
vs2022- (上方菜单)工具 - 命令行 - 开发者命令提示
cd C:makerDoaEditordx9-hk-im-gui-stepDetourssrc
nmake
(生成文件为include中文件、lib 和obj文件夹)
2、配置项目
右键点击项目-属性
选所有配置-所有平台
2.1 设置include目录
vc++目录--包含目录--编辑-宏-“dx”选中$(DXSDK_DIR)-后面输入Include变为$(DXSDK_DIR)Include(下面可看具体目录)
再添加detours的include目录,直接在左边点三个点选择 C:makerDoaEditordx9-hk-im-gui-stepDetoursinclude目录
2.2 去掉预编译头 c/c++ -- 预编译头 -- 不使用预编译头
2.3 设置附加库
链接器--常规--附加库目录--编辑-宏-dx-选中$(DXSDK_DIR)后边输入Lib变为$(DXSDK_DIR)Lib
再添加detours的lib目录,直接在左边下一行点三个点选择 C:makerDoaEditordx9-hk-im-gui-stepDetourslib.X86目录
链接器--输入-附加依赖项-添加 d3d9.lib detours.lib (分两行写)
2.3 代码引入头文件
pch.h添加
DLL_PROCESS_ATTACH 下面创建线程,调用void InitD3d()
初始化代码参考:C:Program Files (x86)Microsoft DirectX SDK (June 2010)SamplesC++Direct3D
vs2022--工具--spy++--搜索--查找窗口--类名
FindWindowA(...)
点击代码 IDirect3DDevice9* 进入结构体定义 (虚表)
找到 Reset(改变窗口大小) EndScene 函数:获取函数行数(从最开始0,为16、42),复制函数声明用于自己的函数
typedef (第一个参数THIS改为LPDIRECT3DDEVICE9)和 声明变量
4.1 创建Helpers
项目右键-添加-类-Helpers
pch.h include "Helpers.h"
Helpers.h 声明两个公开静态方法
HookFunction()
UnHookFunction()
(右键-最上面-快捷-最上面-自动创建函数)
函数内容为使用Detours函数进行Hook
4.2 使用Helpers
获取虚表对象,获取虚表待hook函数
5.1 使用console 测试是否hook成功
InitD3d()里
//AllocConsole();
//freopen("CON", "w", stdout);
//SetConsoleTitleA("调试窗口");
使用printf("我进来了");
5.2 4996错误的忽略
项目-右键-属性-c/c++-高级-禁用特定警告-4996
ImWin.h
#pragma once
void LoadMyWin();
#include "pch.h"
#include "ImWin.h"
bool mainBeginSate = TRUE;
void LoadMyWin()
{
// 界面开始绘制
ImGui_ImplDX9_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
// 窗口大小
ImGui::SetNextWindowPos(ImVec2(50, 50), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(350, 450), ImGuiCond_FirstUseEver);
ImGui::Begin(u8"游戏辅助窗口", &mainBeginSate);
ImGui::End();
ImGui::EndFrame();
ImGui::Render();
ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
}
在pch.h中添加头文件,并复制字体baidu_font.hpp、风格sytpe.hpp文件到ImGui子目录下
风格文件sytpe.hpp有报错,注释掉
style->Colors[ImGuiCol_ModalWindowDarkening] = ImVec4(1.00f, 0.98f, 0.95f, 0.73f);
2.1 定义窗口消息函数和旧函数变量
typedef LRESULT(__stdcall* WndProc_t)(const HWND, UINT, WPARAM, LPARAM);
WndProc_t oWndProc;
2.2 窗口消息处理,获取窗口操作消息转嫁给IMGUI
extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT __stdcall hkWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) return true;
return CallWindowProc(oWndProc, hWnd, msg, wParam, lParam);
}
2.3 hkReset 编写
Helpers::UnHookFunction((PVOID*)(&oReset), hkReset);
ImGui_ImplDX9_InvalidateDeviceObjects();
HRESULT tmpReset = pd3dDevice->Reset(pPresentationParameters);
ImGui_ImplDX9_CreateDeviceObjects();
Helpers::HookFunction((PVOID*)(&oReset), hkReset);
return tmpReset;
2.4 hkEndScene 以及子函数MineImGuiInit 编写
void MineImGuiInit(IDirect3DDevice9* pd3dDevice)
{
// 检查IMGUI 版本
IMGUI_CHECKVERSION();
// 创建IMGUI上下文
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
// 关闭imgui 配置文件的显示
io.ConfigFlags = ImGuiConfigFlags_NoMouseCursorChange;
io.WantSaveIniSettings = false;
io.IniFilename = NULL;
//ImGuiStyle& style = ImGui::GetStyle();
//ImGui::StyleColorsDark();
ImGui::StyleColorsClassic();
//ImGui::StyleColorsLight();
//LoadMyStype();
//SetLayeredWindowAttributes();
// 设置字体---为了显示中文
ImFontConfig f_cfg;
f_cfg.FontDataOwnedByAtlas = false;
const ImFont* font = io.Fonts->AddFontFromMemoryTTF((void*)baidu_font_data, baidu_font_size, 17.0f, &f_cfg, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
ImGui_ImplWin32_Init(g_hWnd);
ImGui_ImplDX9_Init(pd3dDevice);
}
HRESULT __stdcall hkEndScene(IDirect3DDevice9* pd3dDevice)
{
Helpers::UnHookFunction((PVOID*)(&oEndScene), hkEndScene);
// IMGUI初始化
static bool firstCall = TRUE;
if (firstCall)
{
firstCall = !firstCall;
MineImGuiInit(pd3dDevice);
oWndProc = (WNDPROC)SetWindowLongPtr(g_hWnd, WNDPROC_INDEX, (LONG_PTR)hkWndProc);
}
LoadMyWin();
HRESULT result = pd3dDevice->EndScene();
Helpers::HookFunction((PVOID*)(&oEndScene), hkEndScene);
return result;
}
2.5 InitD3d()修改
...
// 开始hook对应的D3D9函数
dDeviceVT = (SELF_PTR*)*(SELF_PTR*)g_pd3dDevice;
oReset = (Reset_t)dDeviceVT[16];
oEndScene = (EndScene_t)dDeviceVT[42];
Helpers::HookFunction((PVOID*)(&oReset), hkReset);
Helpers::HookFunction((PVOID*)(&oEndScene), hkEndScene);
查看示例方法:
C:\maker\DoaEditor\dx9-hk-im-gui-step\imgui\examples\example_win32_directx9
1.1 位置
ImWin.cpp 开始添加变量 bool tab_bar_flags = TRUE;
在ImWin.cpp的LoadMyWin() 语句 ImGui::Begin(u8"游戏辅助窗口", &mainBeginSate);后添加
if (ImGui::BeginTabBar("tab1", tab_bar_flags))
{
}ImGui::EndTabBar();
1.2 文本控件以及换行
if (ImGui::BeginTabBar("tab1", tab_bar_flags))
{
ImGui::Text(u8"欢迎1");
ImGui::SameLine();
ImGui::Text(u8"欢迎2");
}ImGui::EndTabBar();
1.3 checkbox 以及button
开头定义 bool checkBoxFalgs_1 = FALSE;
if (ImGui::Button(u8"初始化")) {
checkBoxFalgs_1 = !checkBoxFalgs_1;
}
if (ImGui::Checkbox(u8"开启", &checkBoxFalgs_1)) {
//执行点击操作
}
if (checkBoxFalgs_1) {
ImGui::Text(u8"看看");
}
1.4 Text的字符串格式化能力测试
static int a = 5;
ImGui::Text(u8"初始化点击了:[ %d ] 次",a);
if (ImGui::Button(u8"初始化")) {
checkBoxFalgs_1 = !checkBoxFalgs_1;
a = a + 1;
}
if (ImGui::BeginTabItem(u8"特殊"))
{
if (ImGui::Button(u8"游戏初始化2"))
{
checkBoxFalgs_1 = !checkBoxFalgs_1;
}
if (ImGui::Checkbox(u8"开启飞天", &checkBoxFalgs_1))
{
// 点击事件执行
}
if (checkBoxFalgs_1)
{
ImGui::Text(u8"欢迎使用IMGUI 窗口3");
ImGui::SameLine();
ImGui::Text(u8"欢迎使用IMGUI 窗口4");
}
ImGui::EndTabItem();
}
ImGui::Columns(1);
ImGui::Separator();
ImGui::Text(u8"周围遍历的东西都在下面表格:");
ImGui::Columns(4, "mycolumns"); // 4-ways, with border
ImGui::Separator();
ImGui::Text("ID"); ImGui::NextColumn();//竖线
ImGui::Text("Name"); ImGui::NextColumn();
ImGui::Text("Path"); ImGui::NextColumn();
ImGui::Text("Hovered"); ImGui::NextColumn();
// 横线
ImGui::Separator();
const char* names[3] = { "One", "Two", "Three" };
const char* paths[3] = { "/path/one", "/path/two", "/path/three" };
static int selected = -1;
for (int i = 0; i < 3; i++)
{
char label[32];
sprintf(label, "%04d", i);
if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SpanAllColumns))
selected = i;
bool hovered = ImGui::IsItemHovered();
ImGui::NextColumn();
ImGui::Text(names[i]); ImGui::NextColumn();
ImGui::Text(paths[i]); ImGui::NextColumn();
ImGui::Text("%d", hovered); ImGui::NextColumn();
}
ImGui::Columns(1);
ImGui::Separator();
中文表格定义
头文件定义:
// 参数1:显示标题<类型:字符串>
// 参数2:选中的标识<类型:bool>
#define IM_选择框(显示标题,选中标识) ImGui::Checkbox(显示标题,选中标识)
#define IM_不换行 ImGui::SameLine();
#define IM_不换行EX(与最左边开始宽度,与前面的组件间隔) ImGui::SameLine(与最左边开始宽度,与前面的组件间隔);
#define IM_标签(标题内容) ImGui::Text(标题内容);
#define IM_按钮(标题内容) ImGui::Button(标题内容)
#define IM_颜色按钮(标题内容,颜色,是否可编辑) ImGui::ColorButton(标题内容,颜色,是否可编辑)
// 参数1:标题<类型:字符串>
// 参数2:选中编号<类型:int>
// 参数3:项目列表<类型:字符串>
#define IM_组合框(标题,选中编号指针,项目列表) ImGui::Combo(标题,选中编号指针,项目列表)
#define IM_进度条_FLOAT(标题,返回FLOAT指针,最小值,最大值,序列字符串,标志) ImGui::SliderFloat(标题,返回FLOAT指针,最小值,最大值,序列字符串,标志);
#define IM_进度条_INT(标题,返回INT指针,最小值,最大值,序列字符串,标志) ImGui::SliderInt(标题,返回INT指针,最小值,最大值,序列字符串,标志);
#define IM_树形开始(标题) if(ImGui::TreeNode(标题)){
#define IM_树形结束 ImGui::TreePop();}
#define IM_TAB父工具栏_开始(标题,打开标识) if(ImGui::BeginTabBar(标题, 打开标识)){
#define IM_TAB父工具栏_结束 }ImGui::EndTabBar();
#define IM_TAB子工具栏_开始(标题) if(ImGui::BeginTabItem(标题)){
#define IM_TAB子工具栏_结束 ImGui::EndTabItem();}
//ImGui::InputText("Completion", buf1, 64, ImGuiInputTextFlags_CallbackCompletion, Funcs::MyCallback);
//ImGui::InputText("password (clear)", password, IM_ARRAYSIZE(password));
//ImGui::InputTextWithHint("password (w/ hint)", "<password>", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password);
#define IM_编辑框(标题,内容指针,内存占用大小) ImGui::InputText(标题, 内容指针, 内存占用大小)
#define IM_编辑框_INT(标题,显示内容,内容指针,内存占用大小) ImGui::InputTextWithHint(标题,显示内容, 内容指针, 内存占用大小)
#define IM_表格_横线 ImGui::Separator();
#define IM_表格_竖线 ImGui::NextColumn();
#define IM_表格_开始(列数,表格标识) ImGui::Columns(列数, 表格标识);ImGui::Separator();
#define IM_表格_结束 ImGui::Columns(1);ImGui::Separator();
cpp文件调用中文表格
void loadTab1()
{
IM_TAB子工具栏_开始(u8"郎的诱惑")
IM_表格_横线
IM_标签(u8"表格大作战")
IM_表格_开始(6, "table1")
IM_标签(u8"序号") IM_表格_竖线
IM_标签(u8"姓名") IM_表格_竖线
IM_标签(u8"性别") IM_表格_竖线
IM_标签(u8"年龄") IM_表格_竖线
IM_标签(u8"班级") IM_表格_竖线
IM_标签(u8"操作") IM_表格_竖线
IM_表格_横线
const char* names[3] = { u8"张三", u8"李四", u8"老吴" };
const char* aYear[3] = { "19", "20", "45" };
const char* aClass[3] = { u8"1班", u8"2班", u8"3班" };
const char* Nums[3] = { u8"取岁1", u8"取岁2", u8"取岁3" };
for (int i = 0; i < 3; i++)
{
ImGui::Text("%02d", i); IM_表格_竖线
IM_标签(names[i]) IM_表格_竖线
IM_标签(u8"男") IM_表格_竖线
IM_标签(aYear[i]) IM_表格_竖线
IM_标签(aClass[i]) IM_表格_竖线
if (IM_按钮(Nums[i]))
MessageBoxA(0, (LPCSTR)(aYear[i]), 0, 0);
IM_表格_竖线
//IM_标签(Nums[i]) IM_表格_竖线
}
IM_表格_结束
IM_TAB子工具栏_结束
}
在dllmain
case DLL_PROCESS_DETACH:加入
unLoad();
其他case 加上break;
void unLoad()
{
Helpers::UnHookFunction((PVOID*)(&oReset), hkReset);
Helpers::UnHookFunction((PVOID*)(&oEndScene), hkEndScene);
//g_pd3dDevice->Release();
g_Direct3D9->Release();
}