IMGUI

imgui开发记录

环境配置

imgui库下载

detours库下载

imgui的使用需要提前安装dx库:安装执行文件:DXSDK_jun10.exe,安装好后文件夹名称:Microsoft DirectX SDK(June 2010)

imgui环境配置

配置DX库

配置-VC++目录-包含目录加入DX安装路径根目录的include文件夹,$(DXSDK_DIR)是表示DX安装目录的宏

image-20220123125402231

配置-链接器-常规-附加库目录加入DX安装路径根目录Lib文件夹,$(PlatformTarget)是表示当前项目是x86或x64,因为Lib文件夹下分x86和x64文件夹要针对性选择库文件,因此使用该宏

image-20220123130226095

配置-链接器-输入-附加依赖项中添加库的名字

image-20220123131449030

项目中导入头文件#include <d3d9.h>,根据引入的d3d版本来导入头文件

配置imgui

下图可以取到对应版本的DX实例:用于实验imgui

image-20220123115314047

新建项目后,将imgui-master文件夹中的所有.h和.cpp文件拖拽到项目文件夹中(可以新建一个文件夹存放),然后还要将backends文件夹中的选对应的文件(根据需要DX9还是DX10还是DX11还是DX12还是别的版本对应选下面的imgui文件)也挪到项目下

image-20220123121501036

同时由于我们是在windows下使用的,因此,backends下的如下文件也要添到新项目下:

image-20220123122127035

项目中添加头文件:

image-20220123123606448

detours环境配置

detours库到github下载下来的是源文件,因此我们需要把他编译出来

在powershell中cd到Detours-master文件夹下的src文件夹中,输入nmake,编译。

image-20220123124013459image-20220123124033809

编译后在当前目录下生成obj.x86文件夹

我们需要的是Detours-master下的include中的头文件和Detours-master下的lib.X86文件夹中的库(64位的话需要lib.X64文件夹中的库)

image-20220123124734694image-20220123124745480

将上述内容放入项目中。也可以直接在包含目录和附加库目录和附加依赖项直接添加。

最后导入头文件#include "Detours.h"

detours使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void vtHook(PVOID* oldFunc,PVOID newFunc)
{
//虚表hook流程
DetourTransactionBegin();//开始hook
DetourUpdateThread(GetCurrentThread());//当前线程
DetourAttach(oldFunc,newFunc);//虚表hook老函数位置(二级指针)改成新函数
DetourTransactionCommit();//hook提交
}
void vtUnhook(PVOID* oldFunc,PVOID newFunc)
{
//虚表unhook流程
DetourTransactionBegin();//开始hook
DetourUpdateThread(GetCurrentThread());//当前线程
DetourDetach(oldFunc,newFunc);//卸除hook
DetourTransactionCommit();//hook提交
}

D3D初始化

安装目录中直接提供了D3D的初始化代码,封装在一个类似initD3D的函数中

上图后面的数字表示在虚表中的序号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
D3DPRESENT_PARAMETERS g_present;
IDirect3D9* g_Direct3D9 = NULL;
IDirect3DDevice9* g_pd3dDevice =NULL;
HWND g_hWnd = NULL;
//虚表地址
DWORD* dDeviceVT =NULL;//写64位imgui唯一的区别就是这个类型要改成64位的!!!!!!!
//下面是需要hook的三个D3D函数
typedef HRESULT(WINAPI* Reset_t)(LPDIRECT3DDEVICE9,D3DPRESENT_PARAMETERS*)//16 改变窗口大小(窗口重置)的函数
typedef long(__stdcall* EndScene_t)(LPDIRECT3DDEVICE9)//42 d3d结束绘制函数
typedef LRESULT(__stdcall* WndProc_t)(const HWND,UINT,WPARAM,LPARAM);
Reset_t oReset;
EndScene_t oEndScene;
WndProc_t oWndProc;

//初始化imgui函数
void myImGuiInit(LPDIRECT3DDEVICE9 pd3dDevice)
{
//检查IMGUI版本
IMGUI_CHECKVERSION();
//创建imgui上下文
ImGui::CreateContext();
ImGuiIo& io =ImGui::GetIO();
//关闭imgui配置文件的显示
io.ConfigFlags = ImGuiConfigFlags_NoMouseCursorChange;
io.WantSaveIniSettings=false;
io.IniFilename=NULL;
//设置imgui的风格
//Imgui::StyleColorsDark();//深黑主题
//Imgui::StyleColorsLight();//白色主题
ImGui::StyleColorsClassic();//经典主题
//imgui不设置字体的话中文显示为问号
ImFFontConfig f_cfg;
f_cfg.FontDataOwnedByAtlas = false;
//字体自己选择
const ImFont* font = io.Fonts->AddFontFromMemoryTTF((void*)baidu_font_data,baidu_font_size,14.0f,&f_cfg,io.Fonts->GetGlyphRangesChineseSimplifi)//这里不全,参考源代码!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//imgui真正的初始化
ImGui_ImplWin32_Init(g_hWnd);
ImGui_ImplDX9_Init(pd3dDevice);
}

bool mainBeginSate=TRUE;//窗口打开标识
bool checkBoxFlags_1=FALSE;//checkBox选中标识

void LoadMyWin()
{
//界面开始绘制
ImGui_ImplDX9_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
//窗口大小
ImGui::SetNextWindowPos(ImVec2(50,50),ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(350,450));
ImGui::Begin(u8"测试",&mainBeginSate);//设置窗口标题
//begin与end之间进行窗口控件绘制====================
if(ImGui::BeginTabBar("tab1",tab_bar_flags))//第一个参数是id,第二个参数是打开关闭的标识,配合下面的EndTabBar使用构成一个tab,tab中包含的东西就写到他们之间。
{
ImGui::Text(u8"测试文本");//显示静态文本
ImGui::SameLine();//将上下两个控件放到同一行
ImGui::Text(u8"欢迎使用imgui");//显示静态文本(TEXT支持%d等printf的使用方法)
if(ImGui::CheckBox(u8"打开功能",&checkBoxFlags_1))//checkBox控件,checkBoxFlags_1用于接受选中状态
{
//按下上面的checkBox就执行这里的代码
if(checkBoxFlags_1)
{
//...勾选的事件处理
}
else
{
//...取消勾选的事件处理
}
}
if(checkBoxFlags_1)//通过是否选中checkBOx来插入新控件
{
ImGui::Text(u8"选中了");//显示静态文本
}
if(ImGui::Button(u8"按钮文本"))//按钮控件
{
//。。。处理按钮按下事件的代码
}
ImGui::BeginTabItem(u8"测试item1");//tabItem标签项控件
ImGui::EndTabItem();
ImGui::BeginTabItem(u8"测试item2");//tabItem标签项控件
ImGui::EndTabItem();
ImGui::BeginTabItem(u8"测试item3");//tabItem标签项控件
ImGui::EndTabItem();

ImGui::EndTabBar();
}
//=================================================
ImGui::End();
ImGui::EndFrame();
ImGui::Render();//绘制
ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
}

//窗口消息处理,获取窗口操作消息转嫁给IMGUI
extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam);

//用于hook的3个新函数
LRESULT __stdcall newWndProc(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);//运行原函数
}

HRESULT __stdcall newResult(LPDIRECT3DDEVICE9 pd3dDevice,D3DPRESENT_PARAMETERS* pPresentationParameters)
{
vtUnhook((PVOID*)(&oReset),newReset);//先解除hook方便调用原函数

ImGui_ImplDX9_InvalidateDeviceObjects();
HRESULT tmpReset=pd3dDevice->Reset(pPresentationParameters);//运行原函数
ImGui_ImplDX9_CreateDeviceObjects();

vtHook((PVOID*)(&oReset),newReset);//再恢复hook
return tmpReset;
}

HRESULT __stdcall newEndScene(LPDIRECT3DDEVICE9 pd3dDevice)
{
vtUnhook((PVOID*)(&oEndScene),newEndScene);//先解除hook方便调用原函数
//IMGUI第一次运行的时候初始化
static bool firstCall=TRUE;
if(firstCall)
{
firstCall=!firstCall;
//开始初始化imgui
myImGuiInit(pd3dDevice);
//通过SetWindowLongPtr API函数hook窗口处理函数,SetWindowLong函数修改窗口处理过程
oWndProc = (WNDPROC)SetWindowLongPtr(g_hWnd,GWL_WNDPROC,(LONG_PTR)newWndProc);
}
//窗口初始化
LoadMyWin();
//运行未hook的原函数
HRESULT result=pd3dDevice->EndScene();
vtHook((PVOID*)(&oEndScene),newEndScene);//再恢复hook
return result;
}

void InitD3d()
{
//创建一个控制台用于显示调试信息
//AllocConsole();
//freopen("CON","w",stdout);//会产生4996错误,配置-C/C++-高级-禁用特定警告 中加入4996即可解决
//SetConsoleTitleA("调试窗口");//设置调试窗口名
g_hWnd=FindWindowA("类名","窗口标题");//寻找目标窗口句柄
//创建D3D设备对象
g_Direct3D9 = Direct3DCreate9(D3D_SDK_VERSION);
//初始化D3DPRESENT_PARAMETERS结构体
memset(&g_present,0,sizeof(g_present));
g_present.Windowed=TRUE;
g_present.SwapEffect=D3DSWAPEFFECT_DISCARD;
g_present.BackBufferFormat = D3DFMT_UNKNOWN;
g_present.EnableAutoDepthStencil = TRUE;
g_present.AutoDepthStencilFormat = D3DFMT_D16;
//创建d3d9设备标识
HRESULT result = g_Direct3D9->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
g_hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&g_present,
&g_pd3dDevice);
//开始hook对应的d3d9函数
//获取虚表地址
dDeviceVT=(DWORD*)*(DWORD*)g_pd3dDevice;
oReset=(Reset_t)dDeviceVT[16];//保存虚表中序号为16的函数原本的地址
oEndScene=(EndScene_t)dDeviceVT[42];//保存虚表中序号为42的函数原本的地址
//开始hook,利用下一标题的detours库使用方法
vtHook((PVOID*)(&oReset),newReset);
vtHook((PVOID*)(&oEndScene),newEndScene);
}

更多控件写法参考下图案例(下图路径为官方提供的实例)

image-20220125124453251