13.2 外部DirectX绘制实现

外部,directx,绘制,实现 · 浏览次数 : 5

小编点评

**屏幕绘制功能代码** ```cpp void GlobalDrawFunction() { // 开始绘制 g_pd3dDevice->Clear(0, 0, D3DCLEAR_TARGET, 0, 1.0f, 0); g_pd3dDevice->BeginScene(); // 绘制文本字符串 DrawTextString(100, 200, "Hello LyShark", D3DCOLOR_ARGB(56, 0, 52, 0)); DrawTextString(200, 300, "PowerBy LyShark", D3DCOLOR_ARGB(255, 0, 255, 0)); // 屏幕画线 DrawLine(110, 300, 20, 30, D3DCOLOR_ARGB(56, 0, 52, 0)); DrawBox(40, 40, 50, 100, 1, D3DCOLOR_ARGB(255, 0, 52, 0)); DrawBox(140, 38, 50, 90, 1, D3DCOLOR_ARGB(255, 0, 255, 0)); // 结束绘制 g_pd3dDevice->EndScene(); g_pd3dDevice->Present(0, 0, 0, 0); } ``` **代码说明** * `GlobalDrawFunction` 函数负责绘制屏幕上的各种图形和文本。 * `g_pd3dDevice` 是 D3DX 设备的句柄。 * `Clear()` 方法清理屏幕上的所有绘制对象。 * `BeginScene()` 方法开始绘制图形。 * `DrawTextString()` 方法绘制文本字符串。 * `DrawLine()` 方法绘制直线。 * `DrawBox()` 方法绘制矩形。 * `EndScene()` 方法结束绘制图形。 * `Present()` 方法绘制当前图形。 **运行代码** 打开代码,并运行它。观察屏幕上的各种图形和文本。

正文

在前一节中我们简单介绍了D3D绘制窗体所具备的基本要素,本节将继续探索外部绘制技术的实现细节,并以此实现一些简单的图形绘制功能,首先外部绘制的核心原理是通过动态创建一个新的窗口并设置该窗口属性为透明无边框状态,通过消息循环机制实现对父窗口的动态跟随附着功能,当读者需要绘制新的图形时只需要绘制在透明窗体之上即可实现动态显示的效果。

13.2.1 必要参数定义

首先第一步定义所需要的关键变量如下,代码中包含了DirectX 9DWM的必要库,代码初始化了一些Direct3D 9的变量和指针,包括Direct3D 9设备、呈现参数、Direct3D 线条对象和 Direct3D 字体对象。代码还定义了一个窗口类和一个用于渲染矩形的全局函数指针。

#include <d3dx9.h>
#include <dwmapi.h>

#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#pragma comment(lib, "dwmapi.lib")

static MARGINS Margin;
static LPDIRECT3D9 g_pD3D = NULL;
static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
static D3DPRESENT_PARAMETERS g_d3dpp = {};
static ID3DXLine* pLine = 0;
static ID3DXFont* Font;

// 存储全局窗口信息
static HWND AuxiliaryWindowHandle, GameHwnd;
static RECT WindowRectangle;
static int WindowWidth, WindowHeight;

// 注册窗口类
static WNDCLASSEX wClass;

// 绘制矩形全局指针
typedef VOID(*Draw)();
static Draw Render;

13.2.2 初始化绘图引擎

初始化绘图引擎InitD3D函数,函数通过Direct3DCreate9创建Direct3D对象,并用g_pD3D指针指向它,并将绘制结构体g_d3dpp中一些参数初始化,例如启用窗口模式、交换方式等等。通过CreateDevice方法创建Direct3D绘图设备,通过D3DXCreateLine方法创建Direct3D线条对象,以便绘制直线段。最后调用D3DXCreateFontW来创建Direct3D字体对象,使得程序可以在绘图中使用特定的字体呈现文字。

// 初始化绘制引擎
BOOL InitD3D()
{
    // 初始化绘制引擎
    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;

    // 创建D3D绘制设备
    if (g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, AuxiliaryWindowHandle, D3DCREATE_HARDWARE_VERTEXPROCESSING, &g_d3dpp, &g_pd3dDevice) < 0)
    {
        return FALSE;
    }

    // 创建D3D线条
    if (pLine == NULL)
    {
        D3DXCreateLine(g_pd3dDevice, &pLine);
    }

    // 创建D3D字体
    D3DXCreateFontW(g_pd3dDevice, 16, 0, FW_DONTCARE, D3DX_DEFAULT, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, L"Vernada", &Font);

    return TRUE;
}

13.2.3 初始化消息循环

封装实现CreateTransparentWindow函数,该函数用于创建一个透明窗口来显示Direct3D渲染的图形和文本,函数接受两个参数,游戏窗口句柄和绘制函数,其中游戏窗口句柄表示将要在其上绘制图形和文本的窗口句柄,而绘制函数则是指向绘制矩形的全局指针。函数WindowMessageLoop则用于等待消息循环,在该循环内我们通过不间断调用GetWindowRect获取父窗口大小变化或移动位置变化,并通过MoveWindow动态调整,该流程可实现动态跟随窗体移动。

// 窗口消息处理函数
LRESULT WinProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
    switch (Message)
    {
    case WM_PAINT:
        if (g_pd3dDevice)Render();
        break;

    case WM_CREATE:
        DwmExtendFrameIntoClientArea(hWnd, &Margin);
        break;

    case WM_DESTROY:
    {
        g_pD3D->Release();
        g_pd3dDevice->Release();
        exit(1);
        return 0;
    }
    default:
        return DefWindowProc(hWnd, Message, wParam, lParam);
        break;
    }
    return 0;
}

// 创建透明窗口过程
VOID CreateTransparentWindow(HWND 游戏窗口句柄, Draw 绘制函数)
{
    if (绘制函数 == NULL || 游戏窗口句柄 == 0)
    {
        return;
    }

    GameHwnd = 游戏窗口句柄;
    Render = 绘制函数;

    // 初始化窗口类
    wClass.cbClsExtra = NULL;
    wClass.cbSize = sizeof(WNDCLASSEX);
    wClass.cbWndExtra = NULL;
    wClass.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0, 0, 0));
    wClass.hCursor = LoadCursor(0, IDC_ARROW);
    wClass.hIcon = LoadIcon(0, IDI_APPLICATION);
    wClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
    wClass.hInstance = GetModuleHandle(NULL);
    wClass.lpfnWndProc = (WNDPROC)WinProc;
    wClass.lpszClassName = L" ";
    wClass.lpszMenuName = L" ";
    wClass.style = CS_VREDRAW | CS_HREDRAW;

    // 注册窗口
    if (RegisterClassEx(&wClass) == 0)
    {
        exit(1);
    }

    //创建窗口
    GetWindowRect(GameHwnd, &WindowRectangle);
    WindowWidth = WindowRectangle.right - WindowRectangle.left;
    WindowHeight = WindowRectangle.bottom - WindowRectangle.top;
    AuxiliaryWindowHandle = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_LAYERED, L" ", L" ", WS_POPUP, 1, 1, WindowWidth, WindowHeight, 0, 0, 0, 0);

    //显示窗口
    SetLayeredWindowAttributes(AuxiliaryWindowHandle, 0, RGB(0, 0, 0), LWA_COLORKEY);
    ShowWindow(AuxiliaryWindowHandle, SW_SHOW);

    // 初始化D3D引擎
    InitD3D();
}

// 设置窗体消息循环
VOID WindowMessageLoop()
{
    while (1)
    {
        // 动态附着
        if (GameHwnd)
        {
            // 得到窗口大小
            GetWindowRect(GameHwnd, &WindowRectangle);
            WindowWidth = 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(AuxiliaryWindowHandle, WindowRectangle.left, WindowRectangle.top, WindowWidth, WindowHeight, true);
        }

        // 处理窗口消息
        MSG Message;
        ZeroMemory(&Message, sizeof(Message));
        if (PeekMessage(&Message, 0, 0, 0, PM_REMOVE))
        {
            DispatchMessage(&Message);
            TranslateMessage(&Message);
        }

        Sleep(1);
    }

    if (g_pd3dDevice)
    {
        g_pd3dDevice->Release();
        g_pd3dDevice = NULL;
    }

    if (g_pD3D)
    {
        g_pD3D->Release();
        g_pD3D = NULL;
    }

    CloseWindow(AuxiliaryWindowHandle);

    UnregisterClass(wClass.lpszClassName, wClass.hInstance);
}

13.2.4 封装实现绘图函数

DrawLine,用于绘制线条该函数接受四个参数,分别为线段的起始坐标X1Y1,线段的终止坐标X2Y2,以及颜色Color。该函数使用D3DXVECTOR2结构体初始化两个点型变量Vertex,然后调用pLineSetWidth方法设置绘制线段的宽度为 1,最后调用Draw方法在屏幕上绘制出一条线段。

DrawTextString,用于绘制文本该函数接受四个参数,分别为文本字符串的起始坐标XY,需要显示的文本字符串Str,以及文本颜色Color。该函数首先使用Font对象的DrawTextA方法来测量文本字符串的大小,并将其存储在一个RECT结构体变量Rect中,然后再次使用Font对象的DrawTextA方法来将字符串绘制在屏幕上。

DrawBox,用于绘制矩形该函数接受五个参数,分别为矩形的左上角坐标XY,矩形的宽度W和高度H,以及矩形线条的宽度Width,以及颜色C。该函数使用D3DXVECTOR2结构体初始化5个点型变量Vertex,依次为左上角、右上角、右下角、左下角和左上角。然后调用pLine对象的SetWidth方法,设置绘制线段的宽度为Width,最后调用Draw方法在屏幕上绘制出整个矩形。

// 屏幕画线
VOID DrawLine(float X1, float Y1, float X2, float Y2, D3DCOLOR Color)
{
    D3DXVECTOR2 Vertex[2] = { { X1, Y1 }, { X2, Y2 } };
    pLine->SetWidth(1);
    pLine->Draw(Vertex, 2, Color);
}

// 绘制文本字符串
VOID DrawTextString(float X, float Y, const char* Str, D3DCOLOR Color)
{
    RECT Rect = { (LONG)X, (LONG)Y };
    Font->DrawTextA(NULL, Str, -1, &Rect, DT_CALCRECT, Color);
    Font->DrawTextA(NULL, Str, -1, &Rect, DT_LEFT, Color);
}

// 屏幕画方框
VOID DrawBox(float X, float Y, float W, float H, float Width, D3DCOLOR Color)
{
    D3DXVECTOR2 Vertex[5] = { { X, Y }, { X + W, Y }, { X + W, Y + H }, { X, Y + H }, { X, Y } };
    pLine->SetWidth(Width);
    pLine->Draw(Vertex, 5, Color);
}

13.2.5 实现屏幕绘制功能

在最后读者可将上述功能整合在一起,实现动态绘制功能,首先我们需要得到所需绘制进程的窗口句柄,在VS中的工具类自带了一个Spy++读者可使用该工具得到指定窗体的句柄信息,如下图所示;

当得到句柄后则可填充之如下所示GameHandle变量内,当我们需要绘制图形时只需要在GlobalDrawFunction函数内部编写流程即可,该函数内通过BeginScene设置开始绘制,在绘制代码区读者可自行使用上述封装函数实现自定义绘制,当绘制结束后需要通过EndScene结束本次绘制操作,完整绘制代码如下图所示;

// 调用全局绘制
VOID GlobalDrawFunction()
{
    // 开始绘制
    g_pd3dDevice->Clear(0, 0, D3DCLEAR_TARGET, 0, 1.0f, 0);
    g_pd3dDevice->BeginScene();

    // 绘制文本字符串
    DrawTextString(100, 200, "Hello LyShark", D3DCOLOR_ARGB(56, 0, 52, 0));
    DrawTextString(200, 300, "PowerBy LyShark", D3DCOLOR_ARGB(255, 0, 255, 0));

    // 屏幕画线
    DrawLine(110, 300, 20,  30, D3DCOLOR_ARGB(56, 0, 52, 0));

    // 屏幕画方框
    DrawBox(40, 40, 50, 100, 1, D3DCOLOR_ARGB(255, 0, 52, 0));
    DrawBox(140, 38, 50, 90, 1, D3DCOLOR_ARGB(255, 0, 255, 0));


    // 结束绘制
    g_pd3dDevice->EndScene();
    g_pd3dDevice->Present(0, 0, 0, 0);
}

int main(int argc, char *argv[])
{
    // 获取窗口句柄
    HWND GameHandle = (HWND)0x00506E6;

    while (1)
    {
        // 循环绘制
        CreateTransparentWindow(GameHandle, GlobalDrawFunction);
        WindowMessageLoop();
    }

    return 0;
}

运行上述代码片段,此时读者可看到如下图所示的输出信息,其中包括两个矩形,两个文本字符串,以及一条直线;

注释:仅用于技术研究学习,请勿用于其他用途。
本文作者: 王瑞
本文链接: https://www.lyshark.com/post/990795ac.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

与13.2 外部DirectX绘制实现相似的内容:

13.2 外部DirectX绘制实现

在前一节中我们简单介绍了D3D绘制窗体所具备的基本要素,本节将继续探索外部绘制技术的实现细节,并以此实现一些简单的图形绘制功能,首先外部绘制的核心原理是通过动态创建一个新的窗口并设置该窗口属性为透明无边框状态,通过消息循环机制实现对父窗口的动态跟随附着功能,当读者需要绘制新的图形时只需要绘制在透明窗体之上即可实现动态显示的效果。

将外部jar打入本地maven仓库

1.将jar包放入某不含中文的路径下 ,例如:E:\file\zip4j-1.3.2.jar 2.在命令行输入操作命令 mvn install:install-file -DgroupId=zip4j -DartifactId=zip4j -Dversion=1.3.2 -Dpackaging=ja

【转帖】JVM 内存模型与垃圾回收

文章目录 1. JVM内存模型1.1. 程序计数器 (线程私有)1.2. Java 虚拟机栈 (线程私有)1.3. 本地方法栈 (线程私有)1.4. Java 堆 (线程共享)1.5. 方法区 (线程共享)1.6. 运行时常量池 (线程共享)1.7. 直接内存 (堆外内存) 2. 垃圾查找算法2.1

[转帖]sar -Linux 上全面的系统性能分析工具(2)

https://zhuanlan.zhihu.com/p/554619823 sar -b[ <时间间隔> [ <次数> ] ] 示例: sar -b 1 3 Linux 2.6.32-696.13.2.el6.x86_64 (upfor163) 2018年04月25日 _x86_64_ (2 CP

Blazor前后端框架Known-V1.2.13

# V1.2.13 Known是基于C#和Blazor开发的前后端分离快速开发框架,开箱即用,跨平台,一处代码,多处运行。 - Gitee: [https://gitee.com/known/Known](https://gitee.com/known/Known) - Github:[https:

2.13 PE结构:实现PE代码段加密

代码加密功能的实现原理,首先通过创建一个新的`.hack`区段,并对该区段进行初始化,接着我们向此区段内写入一段具有动态解密功能的`ShellCode`汇编指令集,并将程序入口地址修正为`ShellCode`地址位置处,当解密功能被运行后则可释放加密的`.text`节,此时再通过一个`JMP`指令跳转到原始`OEP`位置,则可继续执行解密后的区段。

kafka的学习之二_kafka的压测与GUI管理

# kafka的学习之二_kafka的压测与GUI管理 ## 第一部分创建topic ``` cd /root/kafka_2.13-3.5.0 bin/kafka-topics.sh --create --bootstrap-server 10.110.139.184:9093 --command

[转帖]JMeter设置Http代理对web或者app进行录制

https://www.cnblogs.com/jingdenghuakai/p/11125846.html 一、录制web 1、首先保证JMeter的安装环境都正确。启动JMeter:在安装路径的bin目录下双击jmeter.bat (例如:D:\apache-jmeter-2.13\bin) ​

【译】Apache Kafka 快速入门

编译自官方文档。 第 1 步:获取 Kafka 下载最新版本(当前为 v3.3.1)的 Kafka 并解压: $ tar -xzf kafka_2.13-3.3.1.tgz $ cd kafka_2.13-3.3.1 第 2 步:启动 Kafka 环境 注意:本地环境必须安装了 Java 8+。 A

Maven依赖管理

本文主要记录Maven依赖管理中关于依赖传递和依赖范围的知识 Maven项目示例 创建3个maven项目,分配依赖log4j 1.2.12, 1.2.13, 1.2.14版本。 com.leo project1