zl程序教程

您现在的位置是:首页 >  后端

当前栏目

C/C++ 实现外部D3D屏幕绘制

C++ 实现 绘制 屏幕 外部 D3D
2023-06-13 09:16:22 时间

实现外部D3D屏幕绘制功能,例如将窗体附着到特定窗体之上,并在其上方绘制字体,方框等特殊线条,这类代码在透视辅助开发项目中用的最多,只不过如下提供的代码是外部绘制,只能应用到单机游戏内。

新建一个空项目,根据自己的D3D库路径导入D3D

新建一个预编译.h头文件:

#pragma once
 
#include <d3d9.h>
#include <d3dx9.h>
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#include <dwmapi.h>
#pragma comment(lib, "dwmapi.lib")
 
#include <iostream>
#include<Windows.h>
 
using namespace std;

D3D绘制.h

#pragma once
#include"预编译.h"
 
/*
D3D相关的一些东西
如果不想用static变量用全局变量的话,千万别再头文件声明,在cpp文件声明后
再在头文件extern 类型名 变量名
*/
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 辅助窗口句柄, GameHwnd;
static RECT 窗口矩形;
static int 窗口宽, 窗口高;

//注册窗口需要用到的窗口类
static WNDCLASSEX wClass;

//画矩形,文字之类的单独放在这个函数里
typedef void(*Draw)();
static Draw Render;

//窗口消息处理函数
LRESULT WinProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam);

bool 初始化D3D();
void 创建透明窗口(HWND 游戏窗口句柄, Draw 绘制函数);
void 窗口消息循环();
void 画线(D3DCOLOR Color, float X1, float Y1, float X2, float Y2, float Width);
void 绘制文字(float X, float Y, const char* Str, D3DCOLOR Color);
void 画框(float X, float Y, float W, float H, float Width, D3DCOLOR Color);
void 绘制开始();
void 绘制结束();

D3D绘制.cpp:

#include "D3D绘制.h"
 
bool 初始化D3D()
{
 	if ((g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL)
		return false;
 
	// 创建D3D设备
	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, 辅助窗口句柄, D3DCREATE_HARDWARE_VERTEXPROCESSING, &g_d3dpp, &g_pd3dDevice) < 0)
		return false;
 
	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;
}
 
void 创建透明窗口(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)
	{
		MessageBox(NULL, L"创建窗口出错!", L"提示!", 0);
		exit(1);
	}
 
	//创建窗口
	GetWindowRect(GameHwnd, &窗口矩形);
	窗口宽 = 窗口矩形.right - 窗口矩形.left;
	窗口高 = 窗口矩形.bottom - 窗口矩形.top;
	辅助窗口句柄 = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_LAYERED, L" ", L" ", WS_POPUP, 1, 1, 窗口宽, 窗口高, 0, 0, 0, 0);
 
	//显示窗口
	SetLayeredWindowAttributes(辅助窗口句柄, 0, RGB(0, 0, 0), LWA_COLORKEY);
	ShowWindow(辅助窗口句柄, SW_SHOW);
 
	初始化D3D();
}

void 窗口消息循环()
{
	while (1)
	{
		//使辅助窗口一直盖在游戏窗口上
		if (GameHwnd)
		{
			GetWindowRect(GameHwnd, &窗口矩形);
			窗口宽 = 窗口矩形.right - 窗口矩形.left;
			窗口高 = 窗口矩形.bottom - 窗口矩形.top;
			DWORD dwStyle = GetWindowLong(GameHwnd, GWL_STYLE);
			if (dwStyle & WS_BORDER)
			{
				窗口矩形.top += 23;
				窗口高 -= 23;
			}
			MoveWindow(辅助窗口句柄, 窗口矩形.left, 窗口矩形.top, 窗口宽, 窗口高, 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(辅助窗口句柄);
 
	::UnregisterClass(wClass.lpszClassName, wClass.hInstance);
}

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 画线(D3DCOLOR Color, float X1, float Y1, float X2, float Y2, float Width)
{
	D3DXVECTOR2 Vertex[2] = { {X1,Y1},{X2,Y2} };
	pLine->SetWidth(Width);
	pLine->Draw(Vertex, 2, Color);
}
 
void 绘制文字(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 画框(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);
}
 
void 绘制开始()
{
	g_pd3dDevice->Clear(0, 0, D3DCLEAR_TARGET, 0, 1.0f, 0);
	g_pd3dDevice->BeginScene();
}
 
void 绘制结束()
{
	g_pd3dDevice->EndScene();
	g_pd3dDevice->Present(0, 0, 0, 0);
}

调用例子

#include"D3D绘制.h"
 
int 线粗 = 2;
D3DCOLOR 红色 = D3DCOLOR_ARGB(255, 255, 255, 255);
 
void 绘制()
{
	绘制开始();
	画线(D3DCOLOR_ARGB(255, 0, 0, 255), 20, 20, 66, 66,线粗);
	画框(100, 100, 100, 100, 线粗, D3DCOLOR_ARGB(255, 255, 255, 0));
	绘制文字(200, 200, "吾无法无天", D3DCOLOR_ARGB(255, 255, 0, 255));
	绘制结束();
}
 
HWND 游戏窗口 = (HWND)0x50A00;
void 开始()
{
	创建透明窗口(游戏窗口, 绘制);
	窗口消息循环();
}
 
int main()
{
	CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)开始, NULL, 0, NULL);
 
	while (1)
	{
		cout << "输入233关闭:" << endl;
		int a=0;
		cin >> a;
		if (a == 233)
		{
			return 0;
		}
	}
 
	return 0;
}

精简一下这段代码,将其压缩成一个文件。

#include <d3d9.h>
#include <d3dx9.h>
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#include <dwmapi.h>
#pragma comment(lib, "dwmapi.lib")

#include <iostream>
#include<Windows.h>

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 辅助窗口句柄, GameHwnd;
static RECT 窗口矩形;
static int 窗口宽, 窗口高;

//注册窗口需要用到的窗口类
static WNDCLASSEX wClass;

//画矩形,文字之类的单独放在这个函数里
typedef void(*Draw)();
static Draw Render;

//窗口消息处理函数
LRESULT WinProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam);

bool 初始化D3D();

void 创建透明窗口(HWND 游戏窗口句柄, Draw 绘制函数);

void 窗口消息循环();

void 画线(D3DCOLOR Color, float X1, float Y1, float X2, float Y2, float Width);

void 绘制文字(float X, float Y, const char* Str, D3DCOLOR Color);

void 画框(float X, float Y, float W, float H, float Width, D3DCOLOR Color);

void 绘制开始();

void 绘制结束();

bool 初始化D3D()
{
	/*
	D3D这玩意比较复杂,如果单纯是想搞点辅助什么的,复制粘贴我的足够了,
	如果想深入学习,可能得另找资料了,下面的这些基本是固定的,想知道是用来干啥的
	可以自行百度,我这个人比较懒。。。
	*/

	if ((g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL)
		return false;

	// 创建D3D设备
	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, 辅助窗口句柄, D3DCREATE_HARDWARE_VERTEXPROCESSING, &g_d3dpp, &g_pd3dDevice) < 0)
		return false;

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

void 创建透明窗口(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)
	{
		MessageBox(NULL, L"创建窗口出错!", L"提示!", 0);
		exit(1);
	}

	//创建窗口
	GetWindowRect(GameHwnd, &窗口矩形);
	窗口宽 = 窗口矩形.right - 窗口矩形.left;
	窗口高 = 窗口矩形.bottom - 窗口矩形.top;
	辅助窗口句柄 = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_LAYERED, L" ", L" ", WS_POPUP, 1, 1, 窗口宽, 窗口高, 0, 0, 0, 0);

	//显示窗口
	SetLayeredWindowAttributes(辅助窗口句柄, 0, RGB(0, 0, 0), LWA_COLORKEY);
	ShowWindow(辅助窗口句柄, SW_SHOW);

	初始化D3D();
}

void 窗口消息循环()
{
	while (1)
	{
		//使辅助窗口一直盖在游戏窗口上
		if (GameHwnd)
		{
			GetWindowRect(GameHwnd, &窗口矩形);
			窗口宽 = 窗口矩形.right - 窗口矩形.left;
			窗口高 = 窗口矩形.bottom - 窗口矩形.top;
			DWORD dwStyle = GetWindowLong(GameHwnd, GWL_STYLE);
			if (dwStyle & WS_BORDER)
			{
				窗口矩形.top += 23;
				窗口高 -= 23;
			}
			MoveWindow(辅助窗口句柄, 窗口矩形.left, 窗口矩形.top, 窗口宽, 窗口高, 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(辅助窗口句柄);

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

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 line(float X1, float Y1, float X2, float Y2)
{
	D3DXVECTOR2 Vertex[2] = { { X1, Y1 }, { X2, Y2 } };
	pLine->SetWidth(1);
	pLine->Draw(Vertex, 2, D3DCOLOR_ARGB(12, 255, 255, 25));
}

void 绘制文字(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 画框(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);
}

void DrawBox(LPDIRECT3DDEVICE9 pDevice, int x, int y, int w, int h, D3DCOLOR Color)
{
	struct Vertex
	{
		float x, y, z, ht;
		DWORD Color;
	};

	Vertex V[8] = { 0 };

	V[0].Color = V[1].Color = V[2].Color = V[3].Color = Color;
	V[0].z = V[1].z = V[2].z = V[3].z = 0.0f;
	V[0].ht = V[1].ht = V[2].ht = V[3].ht = 0.0f;
	V[0].x = V[1].x = (float)x;
	V[0].y = V[2].y = (float)(y + h);
	V[1].y = V[3].y = (float)y;
	V[2].x = V[3].x = (float)(x + w);

	pDevice->SetTexture(0, NULL);

	// 设置缓冲区格式
	pDevice->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1);

	// 画两个三角形组成实体矩形
	pDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, V, sizeof(Vertex));
}

void draw_line(int x, int y, int w, int h, int color)
{
	D3DRECT rect{ x + 0, y + 0, x + w, y + h };
	g_pd3dDevice->Clear(2, &rect, D3DCLEAR_TARGET, color, 0.0f, 0);
}

void fk(int zb, int db, int kd, int gd, int xc)
{
	draw_line(zb + 1, db + gd - xc, kd - 2, xc, D3DCOLOR_ARGB(255, 255, 255, 255));
	draw_line(zb, db + 1, xc, gd - 2, D3DCOLOR_ARGB(255, 255, 255, 255));
	draw_line(zb + 1, db, kd - 2, xc, D3DCOLOR_ARGB(255, 255, 255, 255));
	draw_line(zb + kd - xc + 1, db + 1, xc, gd - 2, D3DCOLOR_ARGB(255, 255, 255, 255));
}

void box(float X, float Y, float 宽度, float 高度, float Width, D3DCOLOR Color)
{
	D3DXVECTOR2 Vertex[5] = { { X, Y }, { X + 宽度, Y }, { X + 宽度, Y + 高度 }, { X, Y + 高度 }, { X, Y } };
	pLine->SetWidth(Width);
	pLine->Draw(Vertex, 5, Color);
}

void 绘制开始()
{
	g_pd3dDevice->Clear(0, 0, D3DCLEAR_TARGET, 0, 1.0f, 0);
	g_pd3dDevice->BeginScene();
}

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

int 线粗 = 2;
D3DCOLOR 红色 = D3DCOLOR_ARGB(255, 255, 255, 255);

void 绘制()
{
	绘制开始();

	// 高度
	draw_line(110, 300, 1, (200 / 2) - 30, D3DCOLOR_ARGB(56, 0, 52, 0));
	draw_line(110, 300 + (200 / 2 + 30), 1, (200 / 2) - 30, D3DCOLOR_ARGB(56, 0, 100, 0));

	draw_line(200, 300, 1, (200 / 2) - 30, D3DCOLOR_ARGB(56, 0, 52, 0));
	draw_line(200, 300 + (200 / 2 + 30), 1, (200 / 2) - 30, D3DCOLOR_ARGB(56, 0, 100, 0));

	/*
	// 宽度
	draw_line(110, 300, (200 / 2) - 20, 1, D3DCOLOR_ARGB(56, 0, 52, 0));
	draw_line(110 + (200 / 2 + 20), 300, (200 / 2) - 20, 1, D3DCOLOR_ARGB(56, 0, 52, 0));
	*/
	绘制结束();
}

HWND 游戏窗口 = (HWND)0x002F02D4;

void 开始()
{
	创建透明窗口(游戏窗口, 绘制);
	窗口消息循环();
}

int main()
{
	CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)开始, NULL, 0, NULL);
	while (1)
	{
		std::cout << "输入233关闭:" << std::endl;
		int a = 0;
		std::cin >> a;
		if (a == 233)
		{
			return 0;
		}
	}

	return 0;
}