c++ hookd3d imgui 一步步操作(艾鱼)

一、项目源地址

C++HOOKD3D IMGUI绘制教程
https://www.bilibili.com/video/BV1iL4y1z7Da
https://gitee.com/szkke/dx9-hk-im-gui

二、开发环境配置

1、IMGUI 、detours下载

https://github.com/ocornut/imgui
https://github.com/microsoft/Detours

2、DXSDK_Jun10 安装

https://www.microsoft.com/zh-cn/download/details.aspx?id=6812
安装后例子在C:Program Files (x86)Microsoft DirectX SDK (June 2010)SamplesC++Direct3D11Bin\

3、vs2019、或 2022 安装

教程是vs2019,我是vs2022

4、基础dll项目创建

1、打开vs2022 -> 创建新项目->动态链接库(DLL)->项目名称:ImGuidx9Hook、不勾选将解决方案和项目放在同一目录

5、导入ImGui

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后加/就可以自动选择头文件添加

6、编译和引用detours、dx

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添加

include <d3d9.h>

include "detours.h"

三、开始写代码,获取dx函数

1、初始化D3d

DLL_PROCESS_ATTACH 下面创建线程,调用void InitD3d()
初始化代码参考:C:Program Files (x86)Microsoft DirectX SDK (June 2010)SamplesC++Direct3D

2、获取待注入exe类名并获取hwnd句柄

vs2022--工具--spy++--搜索--查找窗口--类名
FindWindowA(...)

3、找到待注入函数

点击代码 IDirect3DDevice9* 进入结构体定义 (虚表)
找到 Reset(改变窗口大小) EndScene 函数:获取函数行数(从最开始0,为16、42),复制函数声明用于自己的函数
typedef (第一个参数THIS改为LPDIRECT3DDEVICE9)和 声明变量

4、hook函数

4.1 创建Helpers
项目右键-添加-类-Helpers
pch.h include "Helpers.h"
Helpers.h 声明两个公开静态方法

include "pch.h"

include "Helpers.h" 这两个include 次序不能变

HookFunction()
UnHookFunction()
(右键-最上面-快捷-最上面-自动创建函数)
函数内容为使用Detours函数进行Hook
4.2 使用Helpers
获取虚表对象,获取虚表待hook函数

5、 构建自己的d3d函数

5.1 使用console 测试是否hook成功
InitD3d()里
//AllocConsole();
//freopen("CON", "w", stdout);
//SetConsoleTitleA("调试窗口");
使用printf("我进来了");

5.2 4996错误的忽略
项目-右键-属性-c/c++-高级-禁用特定警告-4996

四、创建一个可以点击的基本imgui窗口

1、创建ImWin类(函数)

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子目录下

include "ImWin.h"

include "ImGui/baidu_font.hpp"

include "ImGui/sytpe.hpp"

风格文件sytpe.hpp有报错,注释掉
style->Colors[ImGuiCol_ModalWindowDarkening] = ImVec4(1.00f, 0.98f, 0.95f, 0.73f);

2、dllmain.cpp中各项功能增加

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);

五、ImGui控件的应用

查看示例方法:

C:\maker\DoaEditor\dx9-hk-im-gui-step\imgui\examples\example_win32_directx9

1、基本控件测试

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;
}

2 标签页的编写BeginTabItem,可独立到函数

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();
}

3 表格

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();
}
发表新评论