首页 > 其他分享 >如何在带有DX11绘图界面的软件上画imgui界面

如何在带有DX11绘图界面的软件上画imgui界面

时间:2024-07-20 17:08:35浏览次数:7  
标签:imgui 界面 reinterpret nullptr ImGui cast DX11 UINT sd

前言

处于某些研究目的,我们经常需要在dx11绘制的界面上绘制我们自己的操作菜单,以方便进行一些可视化操作;这里面imgui库因为其优越的可用性,健壮性和美观性,得到了很多人的青睐。那么我们应该如何在一个带有dx的软件界面上利用imgui绘制我们自己的界面呢?下面的代码就是为了解决这个问题的(采用dx11版本举例)。

代码样例

#include <windows.h>
#include <memory>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <thread>
#include <d3d11.h>
#include "Imgui/imgui.h"
#include "Imgui/imgui_impl_win32.h"
#include "Imgui/imgui_impl_dx11.h"

#ifdef _DEBUG
#define LOG(_text, ...) printf(_text "\n", __VA_ARGS__)
#else
#define LOG(_text, ...)
#endif

namespace
{
    /*
     * 0 ProcessAttach NoCall
     * 1 ProcessAttach Called
     */
    LONG volatile g_iDllMainProcessAttachFlag = 0;
    LONG volatile g_iDX11NeedInit = TRUE;

    BOOL g_bInitSuccess = FALSE;

    HWND g_GameHwnd = NULL;

    IDXGISwapChain* g_pHookedSwapChain = nullptr;
    IDXGISwapChain* g_pSwapChain = nullptr;

    ID3D11Device* g_pd3dDevice = nullptr;
    ID3D11DeviceContext* g_pd3dDeviceContext = nullptr;
    ID3D11RenderTargetView* g_mainRenderTargetView = nullptr;
}

void CleanupRenderTarget()
{
    if (g_mainRenderTargetView) { g_mainRenderTargetView->Release(); g_mainRenderTargetView = nullptr; }
}

void CleanupDeviceD3D()
{
    CleanupRenderTarget();
    if (g_pHookedSwapChain) { g_pHookedSwapChain->Release(); g_pHookedSwapChain = nullptr; }
    if (g_pSwapChain) { g_pSwapChain->Release(); g_pSwapChain = nullptr; }
    if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = nullptr; }
    if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = nullptr; }
}

void CreateRenderTarget()
{
    ID3D11Texture2D* pBackBuffer = nullptr;
    g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
    g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView);
    pBackBuffer->Release();
}

LRESULT CreateDeviceD3D(HWND hWnd)
{
    // Setup swap chain
    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;
    sd.BufferDesc.RefreshRate.Numerator = 60;
    sd.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.SampleDesc.Quality = 0;
    sd.Windowed = TRUE;
    sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

    UINT createDeviceFlags = 0;
    //createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
    D3D_FEATURE_LEVEL featureLevel;

    ID3D11Device* pd3dDevice = nullptr;
    ID3D11DeviceContext* pd3dDeviceContext = nullptr;

    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_pHookedSwapChain, &pd3dDevice, &featureLevel, &pd3dDeviceContext);
    if (res == DXGI_ERROR_UNSUPPORTED) // Try high-performance WARP software driver if hardware is not available.
        res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pHookedSwapChain, &pd3dDevice, &featureLevel, &pd3dDeviceContext);

    // We just need SwapChain, to get its virtual table and hook It!!
    if (pd3dDeviceContext) { pd3dDeviceContext->Release(); pd3dDeviceContext = nullptr; }
    if (pd3dDevice) { pd3dDevice->Release(); pd3dDevice = nullptr; }

    return res;
}

// Forward declare message handler from imgui_impl_win32.cpp
typedef LRESULT(WINAPI* typedef_WndProc)(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

typedef HRESULT(STDMETHODCALLTYPE* typedef_Present)(
    /* [in] */ IDXGISwapChain* This,
    /* [in] */ UINT SyncInterval,
    /* [in] */ UINT Flags);

typedef HRESULT(STDMETHODCALLTYPE* typedef_ResizeBuffers)(
    /* [in] */ IDXGISwapChain* This,
    /* [in] */ UINT BufferCount,
    /* [in] */ UINT Width,
    /* [in] */ UINT Height,
    /* [in] */ DXGI_FORMAT NewFormat,
    /* [in] */ UINT SwapChainFlags);

extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

static typedef_WndProc Real_WndProc = nullptr;
static constexpr typedef_WndProc Mine_WndProc = [](HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) -> LRESULT
    {
        LOG("Mine_WndProc Hooked!!");

        if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
            return S_FALSE;

        return Real_WndProc ? CallWindowProcW(Real_WndProc, hWnd, msg, wParam, lParam) : S_OK;
    };

static typedef_Present Real_Present = nullptr;
static constexpr typedef_Present Mine_Present =
[](IDXGISwapChain* This, UINT SyncInterval, UINT Flags) -> HRESULT {
    LOG("Mine_Present Hooked!!");

    if (InterlockedCompareExchange(&g_iDX11NeedInit, 0, 1)) {
        g_pSwapChain = This;
        g_pSwapChain->GetDevice(__uuidof(g_pd3dDevice), (void**)&g_pd3dDevice);
        g_pd3dDevice->GetImmediateContext(&g_pd3dDeviceContext);

        CreateRenderTarget();
        ImGui_ImplWin32_Init(g_GameHwnd);
        ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
    }

    // Start the Dear ImGui frame
    ImGui_ImplDX11_NewFrame();
    ImGui_ImplWin32_NewFrame();
    ImGui::NewFrame();

    ImGui::Begin("AHA");
    ImGui::Text("Gugu!!");
    ImGui::End();

    ImGui::Render();
    g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, nullptr);
    ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());

    return Real_Present(This, SyncInterval, Flags);
    };

static typedef_ResizeBuffers Real_ResizeBuffers = nullptr;
static constexpr typedef_ResizeBuffers Mine_ResizeBuffers =
[](IDXGISwapChain* This,
    UINT BufferCount,
    UINT Width,
    UINT Height,
    DXGI_FORMAT NewFormat,
    UINT SwapChainFlags) -> HRESULT {
        LOG("Mine_ResizeBuffers Hooked!!");

        ImGui_ImplDX11_Shutdown();
        ImGui_ImplWin32_Shutdown();
        CleanupDeviceD3D();
        InterlockedCompareExchange(&g_iDX11NeedInit, 1, 0);

        return Real_ResizeBuffers(This, BufferCount, Width, Height, NewFormat, SwapChainFlags);
    };

BOOL SetupHook() {
    Real_WndProc = reinterpret_cast<decltype(Real_WndProc)>(SetWindowLongPtrW(g_GameHwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(Mine_WndProc)));

    DWORD dwOldProt = PAGE_EXECUTE_READ;
    if (!VirtualProtect(*reinterpret_cast<uintptr_t**>(g_pHookedSwapChain), 1, PAGE_EXECUTE_READWRITE, &dwOldProt)) {
        return FALSE;
    }

    Real_Present = static_cast<decltype(Real_Present)>(InterlockedExchangePointer(reinterpret_cast<volatile PVOID*>(*reinterpret_cast<uintptr_t**>(g_pHookedSwapChain) + 8), Mine_Present));
    Real_ResizeBuffers = static_cast<decltype(Real_ResizeBuffers)>(InterlockedExchangePointer(reinterpret_cast<volatile PVOID*>(*reinterpret_cast<uintptr_t**>(g_pHookedSwapChain) + 13), Mine_ResizeBuffers));
    VirtualProtect(*reinterpret_cast<uintptr_t**>(g_pHookedSwapChain), 1, dwOldProt, &dwOldProt);

    return TRUE;
}

void RemoveHook() {
    DWORD dwOldProt = PAGE_EXECUTE_READ;

    if (!VirtualProtect(*reinterpret_cast<uintptr_t**>(g_pHookedSwapChain), 1, PAGE_EXECUTE_READWRITE, &dwOldProt)) {
        return;
    }

    InterlockedCompareExchangePointer(reinterpret_cast<volatile PVOID*>(*reinterpret_cast<uintptr_t**>(g_pHookedSwapChain) + 8), Real_Present, Mine_Present);
    InterlockedCompareExchangePointer(reinterpret_cast<volatile PVOID*>(*reinterpret_cast<uintptr_t**>(g_pHookedSwapChain) + 13), Real_ResizeBuffers, Mine_ResizeBuffers);
    VirtualProtect(*reinterpret_cast<uintptr_t**>(g_pHookedSwapChain), 1, dwOldProt, &dwOldProt);

    if (Real_WndProc) {
        SetWindowLongPtrW(g_GameHwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(Real_WndProc));
    }
}

void LibraryHandler_ProcAttach(HANDLE hModule) {
    LOG("Attach Begin!!");

    // 这里要修改为你需要绘制的父窗口的类名,可通过spy++获取
    g_GameHwnd = FindWindowW(L"ImGui Example", NULL);

    LRESULT res = CreateDeviceD3D(g_GameHwnd);
    if (res != S_OK) {
        LOG("CreateDeviceD3D Failed, res=0x%X", static_cast<unsigned int>(res));
        CleanupDeviceD3D();
        return;
    }

    // Setup Dear ImGui context
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();

    //// Setup Dear ImGui style
    ImGui::StyleColorsDark();
    //ImGui::StyleColorsLight();

    // Setup Platform/Renderer Backends
    if (!SetupHook()) {
        LOG("Setup Hook Failed");
    }

    LOG("Attach Init Success");
    g_bInitSuccess = TRUE;
}

void LibraryHandler_ProcDetach(HANDLE hModule) {
    LOG("Detach Begin!!");

    if (!g_bInitSuccess) {
        return;
    }

    RemoveHook();

    ImGui_ImplDX11_Shutdown();
    ImGui_ImplWin32_Shutdown();
    ImGui::DestroyContext();

    CleanupDeviceD3D();
}

BOOL WINAPI DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpvReserved) {
    static FILE* g_stream = nullptr;

    switch (dwReason) {
    case DLL_PROCESS_ATTACH: {
        if (0 == InterlockedCompareExchange(&g_iDllMainProcessAttachFlag, 1, 0)) {
#ifdef _DEBUG
            AllocConsole();
            freopen_s(&g_stream, "CONOUT$", "w+", stdout);
#endif
            auto threadObj = std::thread([hModule]() -> void { LibraryHandler_ProcAttach(hModule); });
            threadObj.detach();
        }
        break;
    }
    case DLL_PROCESS_DETACH: {
        LibraryHandler_ProcDetach(hModule);
#ifdef _DEBUG
        fflush(stdout);
        fclose(g_stream);
#endif
        break;
    }
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    }

    return TRUE;
}

标签:imgui,界面,reinterpret,nullptr,ImGui,cast,DX11,UINT,sd
From: https://www.cnblogs.com/PeterZ1997/p/18313415

相关文章

  • openwrt之luci界面开发------问题解析
    查阅视频:我取不来名字的https://space.bilibili.com/320467466在openwrt的luci界面开发中,用到的这个E()函数,其功能是在网页界面创建各种各种视图效果,如按钮,文字等等。其原函数:functionE(){    returnL.dom.create.apply(L.dom,arguments)}这个E()函数定义实......
  • Windows图形界面(GUI)-DLG-C/C++ - 工具栏(ToolBar)
    公开视频-> 链接点击跳转公开课程博客首页-> ​​​​​​链接点击跳转博客主页目录工具栏(ToolBar)创建工具栏-CreateWindowEx初始工具栏-TB_BUTTONSTRUCTSIZE工具栏图标-TBADDBITMAP-TB_ADDBITMAP工具栏按钮-TB_ADDBUTTONS示例代码工具栏(ToolBar)......
  • Windows图形界面(GUI)-DLG-C/C++ - 滑动条(Trackbar)
    公开视频-> 链接点击跳转公开课程博客首页-> ​​​​​​链接点击跳转博客主页目录滑动条(Trackbar)使用场景初始控件控件消息示例代码滑动条(Trackbar)使用场景音量控制亮度调节视频播放进度控制任何需要用户在特定范围内选择值的场景初始控件TBM_......
  • Intellij IDE界面提示安装SDK
    好久没打开IntellijIDE,今天打开后发现打开一个项目包后只剩下一个尝试通过项目结构去处理,重新选择SDK版本,方式不行最后解决方案:删除掉Day15.iml,重新加载项目,软件会自动索引所安装的SDK版本......
  • 【Python】使用PySide6 + Qt Designer创建简易用户界面(含用户交互)
    【Python】使用PySide6+QtDesigner创建简易用户界面(含用户交互)文章目录【Python】使用PySide6+QtDesigner创建简易用户界面(含用户交互)相关代码运行环境操作过程1.PySide6和QtDesigner的安装2.创建外部工具PyUIC和QtDesigner3.QtDesigner的简单使用说明4.完整代......
  • 基于PySide6与requests的多功能B站小帮手软件GUI界面设计并打包为exe文件
    小生今日闲来无事,学习了PySide6,并基于PySide6为之前写过的爬虫程序设计了GUI界面,和ffmpeg一起打包成一个exe文件,做成一个面向大众群体的软件。该软件目前仍在持续更新中,目前是0.6.0版本喵。先放一张软件GUI成品图喵:话不多说,我们直接讲解喵~0.导入库下面是本程序所有......
  • 界面控件DevExpress Blazor UI v24.1 - 发布全新TreeList组件
    DevExpress BlazorUI组件使用了C#为BlazorServer和BlazorWebAssembly创建高影响力的用户体验,这个UI自建库提供了一套全面的原生BlazorUI组件(包括PivotGrid、调度程序、图表、数据编辑器和报表等)。DevExpress Blazor控件目前已经升级到v24.1版本了,此版本发布了全新的TreeLi......
  • 适用任意复杂转移函数及方程式的 matlab 二维曲线作图与客制化界面
    優點曲線可自行定義粗細,顏色,精細度......等等字可自行定義字體,顏色,大小,方向,位置......等等可自行定義虛線與字來標註曲線上的某一點此方法可延伸至matlab三維曲線或曲面作圖,亦或是同樣概念,但是改用其他程式諸如python來實現前情提要matlab現有表達轉移函數,......
  • python tkinter 界面设计(1)
    pythonGUI设计tkinter模块tkinter是一个开发源码的图形接口开发工具,目前已经已经一直到python内建的模块。下面从窗体开始慢慢开始整理,图1,查看tkinter版本,8.5以后得版本功能比较健全。图2,创建窗体。 图3-图5,是对窗体的属性设置。  有需要了解更多内容的小伙伴,可......
  • C语言超市管理系统UI界面
    以下是部分源码,需要源码的+qq:2758566124 #include<easyx.h>#include<stdio.h>#include<stdlib.h>#definewidth1280#defineheight840#definefont_w35//字体宽度#definefont_h90//字体高度typedefstructnode{ charname[100];//名字 charnumb......