ImGUI 1.87 绘制D3D外部菜单

ImGUI 它是与平台无关的C++轻量级跨平台图形界面库,没有任何第三方依赖 , 可以将ImGUI的源码直接加到项目中使用,该框架通常会配合特定的D3Dx9等图形开发工具包一起使用,ImGUI常用来实现进程内的菜单功能,而有些辅助开发作者也会使用该框架开发菜单页面,总体来说这是一个很不错的绘图库,如下将公开新版ImGUI如何实现绘制外部菜单的功能 。
ImGUI官方下载地址:https://github.com/ocornut/imgui/releases
在使用ImGUI页面之前需要先来实现一个简单的附着功能,即如何将一个窗体附着到另一个窗体之上,其实代码很简单,如下所示当用户输入进程PID时 , 会自动跟随窗体并附着在窗体顶部 。
#include <Windows.h>#include <iostream>struct handle_data{ unsigned long process_id; HWND best_handle;};// By: LySharkBOOL IsMainWindow(HWND handle){ return GetWindow(handle, GW_OWNER) == (HWND)0 && IsWindowVisible(handle);}BOOL CALLBACK EnumWindowsCallback(HWND handle, LPARAM lParam){ // By: LyShark handle_data& data = https://www.huyubaike.com/biancheng/*(handle_data*)lParam; unsigned long process_id = 0; GetWindowThreadProcessId(handle, &process_id); if (data.process_id != process_id || !IsMainWindow(handle)) {return TRUE; } data.best_handle = handle; return FALSE;}// By: LySharkHWND FindMainWindow(unsigned long process_id){ handle_data data; data.process_id = process_id; data.best_handle = 0; EnumWindows(EnumWindowsCallback, (LPARAM)&data); return data.best_handle;}int main(int argc, char* argv[]){ DWORD pid = 28396; std::cout <<"输入进程PID: " << std::endl; std::cin >> pid; // 获取屏幕宽和高 int iWidth = ::GetSystemMetrics(SM_CXSCREEN); int iHeight = ::GetSystemMetrics(SM_CYSCREEN); // 根据PID寻找游戏窗口 HWND hwnd = FindMainWindow(pid); while (1) {SetTimer(hwnd, 1, 150, NULL);// 实现透明必须设置WS_EX_LAYERED标志LONG lWinStyleEx = GetWindowLong(hwnd, GWL_EXSTYLE);lWinStyleEx = lWinStyleEx | WS_EX_LAYERED;SetWindowLong(hwnd, GWL_EXSTYLE, lWinStyleEx);SetLayeredWindowAttributes(hwnd, 0, RGB(40, 40, 40), LWA_ALPHA);// 去掉标题栏及边框LONG_PTR Style = GetWindowLongPtr(hwnd, GWL_STYLE);Style = Style & ~WS_CAPTION & ~WS_SYSMENU & ~WS_SIZEBOX;SetWindowLongPtr(hwnd, GWL_STYLE, Style);// 至顶层窗口 最大化SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, iWidth, iHeight, SWP_SHOWWINDOW);// 设置窗体可穿透鼠标SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_TRANSPARENT | WS_EX_LAYERED);// 绘图HDC hdc = ::GetDC(hwnd);HDC mdc = ::CreateCompatibleDC(hdc);// 创建画笔HPEN hpen = CreatePen(PS_SOLID, 10, RGB(0, 255, 0));// DC 选择画笔SelectObject(hdc, hpen);// (画笔)从初始点移动到 50,50MoveToEx(hdc, 100, 100, NULL);// (画笔)从初始点画线到 100,100LineTo(hdc, 1000, 1000);RECT rect = {0};rect.bottom = 10;rect.left = 20;rect.right = 20;rect.top = 15;DrawText(hdc, L"hello lyshark.com", strlen("hello lyshark.com"), &rect, DT_CALCRECT | DT_CENTER | DT_SINGLELINE); } return 0;}绘制效果图:

ImGUI 1.87 绘制D3D外部菜单

文章插图
接着我们使用Imgui绘制一个动态菜单,首先下载imgui并打开项目中的examples目录,找到example_win32_directx9打开后自己配置好dx9SDK开发工具包 。
ImGUI 1.87 绘制D3D外部菜单

文章插图
代码直接调用,并附加到Counter-Strike Source游戏窗体之上即可,这段代码也很简单 。
【ImGUI 1.87 绘制D3D外部菜单】#include "imgui.h"#include "imgui_impl_dx9.h"#include "imgui_impl_win32.h"#include <d3d9.h>#include <tchar.h>#include <iostream>#pragma execution_character_set("utf-8")// 全局变量// lyshark.comstatic HWND hwnd;static HWND GameHwnd;static RECT WindowRectangle;static int WindowWide, WindowHeight;static LPDIRECT3D9 g_pD3D = NULL;static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;static D3DPRESENT_PARAMETERS g_d3dpp = {};// 单选框设置状态bool show_another_window = false;// imgui 回调函数extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);// By: LySharkbool CreateDeviceD3D(HWND hWnd){ if ((g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL) {return false; } ZeroMemory(&g_d3dpp, sizeof(g_d3dpp)); g_d3dpp.Windowed = TRUE; g_d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; g_d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; g_d3dpp.EnableAutoDepthStencil = TRUE; g_d3dpp.AutoDepthStencilFormat = D3DFMT_D16; g_d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; if (g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &g_d3dpp, &g_pd3dDevice) < 0) {return false; } return true;}void CleanupDeviceD3D(){ if (g_pd3dDevice) {g_pd3dDevice->Release();g_pd3dDevice = NULL; } if (g_pD3D) {g_pD3D->Release();g_pD3D = NULL; }}void ResetDevice(){ ImGui_ImplDX9_InvalidateDeviceObjects(); HRESULT hr = g_pd3dDevice->Reset(&g_d3dpp); if (hr == D3DERR_INVALIDCALL) {IM_ASSERT(0); } ImGui_ImplDX9_CreateDeviceObjects();}LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){ if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) {return true; } switch (msg) { case WM_SIZE:if (g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED){g_d3dpp.BackBufferWidth = LOWORD(lParam);g_d3dpp.BackBufferHeight = HIWORD(lParam);ResetDevice();}return 0; case WM_SYSCOMMAND:if ((wParam & 0xfff0) == SC_KEYMENU){return 0;}break; case WM_DESTROY:PostQuitMessage(0);return 0; } return DefWindowProc(hWnd, msg, wParam, lParam);}// 绘制主方法// www.cnblogs.com/lysharkvoid DrawImGUI(){ // 启动IMGUI自绘 ImGui_ImplDX9_NewFrame(); ImGui_ImplWin32_NewFrame(); ImGui::NewFrame(); static float f = 0.0f; static int counter = 0; static char sz[256] = { 0 }; ImGui::Begin("LyShark 辅助GUI主菜单"); ImGui::Text("这是一段测试字符串"); ImGui::Checkbox("弹出子窗口", &show_another_window); ImGui::SliderFloat("浮点条", &f, 0.0f, 1.0f); ImGui::InputText("输入内容", sz, 256, 0, 0, 0); if (ImGui::Button("点我触发"))counter++; ImGui::SameLine(); ImGui::Text("触发次数 = %d", counter); ImGui::Text("当前FPS: %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); ImGui::End(); if (show_another_window) {ImGui::Begin("我是子窗体", &show_another_window);ImGui::Text(" 您好,LyShark !");if (ImGui::Button("关闭窗体"))show_another_window = false;ImGui::End(); } ImGui::EndFrame();}// 自身窗口循环事件void WindowMessageLoop(){ bool done = false; while (!done) {// 每次都将窗体置顶并跟随游戏窗体移动GetWindowRect(GameHwnd, &WindowRectangle);WindowWide = (WindowRectangle.right) - (WindowRectangle.left);WindowHeight = (WindowRectangle.bottom) - (WindowRectangle.top);DWORD dwStyle = GetWindowLong(GameHwnd, GWL_STYLE);if (dwStyle & WS_BORDER){WindowRectangle.top += 23;WindowHeight -= 23;}// 跟随窗口移动MoveWindow(hwnd, WindowRectangle.left + 8, WindowRectangle.top + 8, WindowWide - 11, WindowHeight - 11, true);// 开始消息循环MSG msg;while (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);if (msg.message == WM_QUIT){done = true;}}if (done){break;}// 开始绘制DrawImGUI();g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE);g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 1.0f, 0);if (g_pd3dDevice->BeginScene() >= 0){ImGui::Render();ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());g_pd3dDevice->EndScene();}HRESULT result = g_pd3dDevice->Present(NULL, NULL, NULL, NULL);if (result == D3DERR_DEVICELOST && g_pd3dDevice->TestCooperativeLevel() == D3DERR_DEVICENOTRESET){ResetDevice();} }}int main(int argc, char *argv[]){ // 注册窗体类 WNDCLASSEX wc; // 附加到指定窗体上 wc.cbClsExtra = NULL; wc.cbSize = sizeof(WNDCLASSEX); wc.cbWndExtra = NULL; wc.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0, 0, 0)); wc.hCursor = LoadCursor(0, IDC_ARROW); wc.hIcon = LoadIcon(0, IDI_APPLICATION); wc.hIconSm = LoadIcon(0, IDI_APPLICATION); wc.hInstance = GetModuleHandle(NULL); wc.lpfnWndProc = (WNDPROC)WndProc; wc.lpszClassName = L" "; wc.lpszMenuName = L" "; wc.style = CS_VREDRAW | CS_HREDRAW; RegisterClassEx(&wc); // 得到窗口句柄 GameHwnd = FindWindowA(NULL, "Counter-Strike Source"); GetWindowRect(GameHwnd, &WindowRectangle); WindowWide = WindowRectangle.right - WindowRectangle.left; WindowHeight = WindowRectangle.bottom - WindowRectangle.top; // 创建窗体 hwnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TOOLWINDOW, L" ", L" ", WS_POPUP, 1, 1, WindowWide, WindowHeight, 0, 0, wc.hInstance, 0); // 显示窗口 SetLayeredWindowAttributes(hwnd, 0, RGB(0, 0, 0), LWA_ALPHA); SetLayeredWindowAttributes(hwnd, 0, RGB(0, 0, 0), LWA_COLORKEY); ShowWindow(hwnd, SW_SHOW); // 初始化D3D if (!CreateDeviceD3D(hwnd)) {CleanupDeviceD3D();UnregisterClass(wc.lpszClassName, wc.hInstance);return 0; } // 更新窗体 UpdateWindow(hwnd); // 初始化ImGUI ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.Fonts->AddFontFromFileTTF("c:/windows/fonts/simhei.ttf", 13.0f, NULL, io.Fonts->GetGlyphRangesChineseSimplifiedCommon()); ImGui::StyleColorsDark(); ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX9_Init(g_pd3dDevice); // 开始执行绘制循环事件 WindowMessageLoop(); ImGui_ImplDX9_Shutdown(); ImGui_ImplWin32_Shutdown(); ImGui::DestroyContext(); CleanupDeviceD3D(); DestroyWindow(hwnd); UnregisterClass(wc.lpszClassName, wc.hInstance); return 0;}

推荐阅读