window 透明覆盖几秒钟后停止工作
A window transparent overlay stops working after a few seconds
我正在尝试在某些进程上创建透明覆盖层'window(我想使用 Direct3D 9 在覆盖层上绘制一些东西)。
覆盖是使用外部程序创建的(不是 .dll 注入库或其他东西)。
问题是当我启动程序时,一个不可见的覆盖层出现在进程 window 上(它甚至在上面绘制了一些文本,所以 WM_PAINT 似乎可以正常工作),但是在接下来的几秒钟内,光标变成了“沙钟”样式(抱歉,我不知道它还叫什么 xd),如果我单击 window,它会显示错误“该应用程序不是响应”并变为纯白色。
我将叠加 class 导入入口点文件,这是我在主函数中 运行 的方式(简化):
#include <iostream>
#include "memory.hpp"
#include "overlay.hpp"
int main() {
Memory mem;
Overlay ol(&mem);
HANDLE overlay = CreateThread(NULL, NULL, &ol.static_start, (void*)&ol, NULL, NULL);
while (1) {
SendMessage(ol.hwnd, WM_PAINT, NULL, NULL);
Sleep(2);
}
return EXIT_SUCCESS;
}
overlay.hpp:
#ifndef OVERLAY_HPP
#define OVERLAY_HPP
#pragma once
#include <Windows.h>
#include <d3d9.h>
#include "paint.hpp" // has a class with methods to draw on overlay using d3dx9
#include "memory.hpp" // has a tHWND variable - handle to target window
#include <iostream>
#include <dwmapi.h>
#pragma comment(lib, "dwmapi.lib")
Paint paint;
class Overlay {
private:
WCHAR _title[20] = L"anoverlay"; // overlay window title
// HINSTANCE hwnd_inst;
Memory* mem;
RECT rect; // coordinates of target window
// registers window class
ATOM _register_сlass() {
WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = NULL;
wc.cbWndExtra = NULL;
wc.hInstance = reinterpret_cast<HINSTANCE>(GetWindowLongW(mem->tHWND, GWL_HINSTANCE)); // hwnd_inst;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hbrBackground = WHITE_BRUSH;
wc.lpszMenuName = NULL;
wc.lpszClassName = _title;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);;
return RegisterClassEx(&wc);
}
// initialise overlay instance
bool _init_instance(int width, int height) {
hwnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_LAYERED, _title, _title, WS_POPUP, rect.left, rect.top, width, height, NULL, NULL, NULL, NULL);
if (!hwnd) return false;
SetLayeredWindowAttributes(hwnd, 0, 1.0f, LWA_ALPHA);
SetLayeredWindowAttributes(hwnd, 0, RGB(0, 0, 0), LWA_COLORKEY);
MARGINS _margins = { -1, -1, -1, -1 };
DwmExtendFrameIntoClientArea(hwnd, &_margins);
ShowWindow(hwnd, SW_SHOW);
return true;
}
public:
HWND hwnd; // an HWND to the overlay window
Overlay(Memory* mem) {
this->mem = mem;
if (!init()) std::cout << "The overlay window was not created" << std::endl;
}
~Overlay() {
DestroyWindow(hwnd);
}
bool init() {
if (!mem->tHWND) return false;
if (!GetWindowRect(mem->tHWND, &rect)) return false;
_register_сlass();
if (!_init_instance(rect.right - rect.left, rect.bottom - rect.top)) return false;
paint = Paint(hwnd, mem->tHWND, rect.right - rect.left, rect.bottom - rect.top);
return true;
}
DWORD APIENTRY start() {
MSG msg;
while (true) {
if (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
GetWindowRect(mem->tHWND, &rect);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
MoveWindow(hwnd, rect.left, rect.top, width, height, true);
}
Sleep(1);
}
return (int)msg.wParam;
}
static DWORD WINAPI static_start(void* param) {
Overlay* ol = (Overlay*)param;
return ol->start();
}
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_PAINT:
paint.render();
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
};
#endif
memory.hpp:
#ifndef MEMORY_HPP
#define MEMORY_HPP
#pragma once
#pragma warning(disable: 6276)
#include <Windows.h>
#include <TlHelp32.h>
const wchar_t* TARGET = L"Telegram.exe"; // you may put any program here, like Notepad or Calculator
LPCSTR WINDOW_NAME = "Telegram";
class Memory {
public:
DWORD tPID;
HANDLE tProcess;
HWND tHWND;
Memory() {
this->tPID = NULL;
this->tProcess = NULL;
if (!this->handle_process(TARGET)) return;
this->tHWND = FindWindowA(0, WINDOW_NAME);
}
~Memory() {
CloseHandle(tProcess);
}
HANDLE handle_process(const wchar_t* processName) {
HANDLE handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
PROCESSENTRY32 entry;
entry.dwSize = sizeof(entry);
do {
if (!wcscmp(entry.szExeFile, processName)) {
tPID = entry.th32ProcessID;
CloseHandle(handle);
tProcess = OpenProcess(PROCESS_ALL_ACCESS, false, tPID);
return tProcess;
}
} while (Process32Next(handle, &entry));
CloseHandle(handle);
return NULL;
}
};
#endif
paint.hpp:
#ifndef PAINT_HPP
#define PAINT_HPP
#pragma once
#pragma warning(disable: 26495)
#include <Windows.h>
#include <d3d9.h>
#include <d3dx9.h> // Project -> [project name] Properties -> VC++ Directories: Add $(DXSDK_DIR)Include into Include Directories and $(DXSDK_DIR)Lib\x86 into Library directories
// Make sure you have Direct3D 9 SDK installed - https://www.microsoft.com/en-gb/download/details.aspx?id=6812
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#pragma comment(lib, "legacy_stdio_definitions.lib")
class Paint {
private:
IDirect3D9Ex* object = NULL; // used to create device
IDirect3DDevice9Ex* device = NULL; // contains functions like begin and end scene
D3DPRESENT_PARAMETERS params;
ID3DXFont* d3d_font = 0; // font used when displaying text
HWND t_hwnd; // target process window
int width; // target process width
int height;
int d3d9init(HWND hwnd) {
if (FAILED(Direct3DCreate9Ex(D3D_SDK_VERSION, &object))) {
DestroyWindow(hwnd);
}
ZeroMemory(¶ms, sizeof(params));
params.BackBufferWidth = width;
params.BackBufferHeight = height;
params.Windowed = TRUE;
params.SwapEffect = D3DSWAPEFFECT_DISCARD;
params.hDeviceWindow = hwnd;
params.MultiSampleQuality = D3DMULTISAMPLE_NONE;
params.BackBufferFormat = D3DFMT_A8R8G8B8;
params.EnableAutoDepthStencil = TRUE;
params.AutoDepthStencilFormat = D3DFMT_D16;
HRESULT res = object->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, ¶ms, 0, &device);
if (FAILED(res)) DestroyWindow(hwnd);
D3DXCreateFont(device, 50, 0, FW_BOLD, 1, false, DEFAULT_CHARSET, OUT_DEVICE_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH, L"Consolas", &d3d_font);
return 0;
}
void draw_text(char* text, int x, int y, int a, int r, int g, int b) {
RECT rect;
rect.left = x;
rect.top = y;
d3d_font->DrawTextA(0, text, strlen(text), &rect, DT_NOCLIP, D3DCOLOR_ARGB(a, r, g, b));
}
public:
Paint() {
this->device = nullptr;
this->object = nullptr;
}
~Paint() {
if (object != NULL) object->Release();
if (device != NULL) device->Release();
}
Paint(HWND hwnd, HWND t_hwnd, int width, int height) {
this->t_hwnd = t_hwnd;
this->width = width;
this->height = height;
d3d9init(hwnd);
}
int render() {
if (device == nullptr) return 1;
device->Clear(0, 0, D3DCLEAR_TARGET, 0, 1.0f, 0);
device->BeginScene();
if (t_hwnd == GetForegroundWindow()) {
draw_text((char*)"Test message", 15, 15, 255, 150, 150, 150);
}
device->EndScene();
device->PresentEx(0, 0, 0, 0, 0);
return 0;
}
};
#endif
只是不能正常工作。任何想法,我做错了什么?
我还注意到一件奇怪的事情,当我尝试调试时,它在我调用 RegisterClassEx (overlay.hpp) 函数时出现错误“0xC0000005:访问冲突读取位置 0xADE50000”。好奇怪,虽然我初始化了WNDCLASSEX结构的所有成员。另外很奇怪,项目构建成功,我可以 运行 构建的 .exe 文件。
访问冲突的原因是 WNDCLASSEX
结构中 hInstance
参数的设置。您可以将其设置为 GetModuleHandle(NULL)
;
就像代码一样:
wc.hInstance = GetModuleHandle(NULL);
应用没有响应的原因是你在线程中设置了GUI线程,在主线程中设置了无线循环,会导致UI线程无反应。可以参考这个thread.
只需要在主线程中调用start
函数,然后创建线程执行SendMessage
函数就可以解决这个问题:
void fun(const Overlay&ol)
{
while (1) {
SendMessage(ol.hwnd, WM_PAINT, NULL, NULL);
}
}
int main() {
Memory mem;
Overlay ol(&mem);
std::thread t(fun, ol);
ol.static_start(&ol);
//HANDLE overlay = CreateThread(NULL, NULL, &ol.static_start, (void*)&ol, NULL, NULL);
t.join();
return EXIT_SUCCESS;
}
编辑
正如@David所说,您不应该发送WM_PAINT
消息,直接调用start
函数即可正常工作。
我正在尝试在某些进程上创建透明覆盖层'window(我想使用 Direct3D 9 在覆盖层上绘制一些东西)。
覆盖是使用外部程序创建的(不是 .dll 注入库或其他东西)。
问题是当我启动程序时,一个不可见的覆盖层出现在进程 window 上(它甚至在上面绘制了一些文本,所以 WM_PAINT 似乎可以正常工作),但是在接下来的几秒钟内,光标变成了“沙钟”样式(抱歉,我不知道它还叫什么 xd),如果我单击 window,它会显示错误“该应用程序不是响应”并变为纯白色。
我将叠加 class 导入入口点文件,这是我在主函数中 运行 的方式(简化):
#include <iostream>
#include "memory.hpp"
#include "overlay.hpp"
int main() {
Memory mem;
Overlay ol(&mem);
HANDLE overlay = CreateThread(NULL, NULL, &ol.static_start, (void*)&ol, NULL, NULL);
while (1) {
SendMessage(ol.hwnd, WM_PAINT, NULL, NULL);
Sleep(2);
}
return EXIT_SUCCESS;
}
overlay.hpp:
#ifndef OVERLAY_HPP
#define OVERLAY_HPP
#pragma once
#include <Windows.h>
#include <d3d9.h>
#include "paint.hpp" // has a class with methods to draw on overlay using d3dx9
#include "memory.hpp" // has a tHWND variable - handle to target window
#include <iostream>
#include <dwmapi.h>
#pragma comment(lib, "dwmapi.lib")
Paint paint;
class Overlay {
private:
WCHAR _title[20] = L"anoverlay"; // overlay window title
// HINSTANCE hwnd_inst;
Memory* mem;
RECT rect; // coordinates of target window
// registers window class
ATOM _register_сlass() {
WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = NULL;
wc.cbWndExtra = NULL;
wc.hInstance = reinterpret_cast<HINSTANCE>(GetWindowLongW(mem->tHWND, GWL_HINSTANCE)); // hwnd_inst;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hbrBackground = WHITE_BRUSH;
wc.lpszMenuName = NULL;
wc.lpszClassName = _title;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);;
return RegisterClassEx(&wc);
}
// initialise overlay instance
bool _init_instance(int width, int height) {
hwnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_LAYERED, _title, _title, WS_POPUP, rect.left, rect.top, width, height, NULL, NULL, NULL, NULL);
if (!hwnd) return false;
SetLayeredWindowAttributes(hwnd, 0, 1.0f, LWA_ALPHA);
SetLayeredWindowAttributes(hwnd, 0, RGB(0, 0, 0), LWA_COLORKEY);
MARGINS _margins = { -1, -1, -1, -1 };
DwmExtendFrameIntoClientArea(hwnd, &_margins);
ShowWindow(hwnd, SW_SHOW);
return true;
}
public:
HWND hwnd; // an HWND to the overlay window
Overlay(Memory* mem) {
this->mem = mem;
if (!init()) std::cout << "The overlay window was not created" << std::endl;
}
~Overlay() {
DestroyWindow(hwnd);
}
bool init() {
if (!mem->tHWND) return false;
if (!GetWindowRect(mem->tHWND, &rect)) return false;
_register_сlass();
if (!_init_instance(rect.right - rect.left, rect.bottom - rect.top)) return false;
paint = Paint(hwnd, mem->tHWND, rect.right - rect.left, rect.bottom - rect.top);
return true;
}
DWORD APIENTRY start() {
MSG msg;
while (true) {
if (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
GetWindowRect(mem->tHWND, &rect);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
MoveWindow(hwnd, rect.left, rect.top, width, height, true);
}
Sleep(1);
}
return (int)msg.wParam;
}
static DWORD WINAPI static_start(void* param) {
Overlay* ol = (Overlay*)param;
return ol->start();
}
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_PAINT:
paint.render();
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
};
#endif
memory.hpp:
#ifndef MEMORY_HPP
#define MEMORY_HPP
#pragma once
#pragma warning(disable: 6276)
#include <Windows.h>
#include <TlHelp32.h>
const wchar_t* TARGET = L"Telegram.exe"; // you may put any program here, like Notepad or Calculator
LPCSTR WINDOW_NAME = "Telegram";
class Memory {
public:
DWORD tPID;
HANDLE tProcess;
HWND tHWND;
Memory() {
this->tPID = NULL;
this->tProcess = NULL;
if (!this->handle_process(TARGET)) return;
this->tHWND = FindWindowA(0, WINDOW_NAME);
}
~Memory() {
CloseHandle(tProcess);
}
HANDLE handle_process(const wchar_t* processName) {
HANDLE handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
PROCESSENTRY32 entry;
entry.dwSize = sizeof(entry);
do {
if (!wcscmp(entry.szExeFile, processName)) {
tPID = entry.th32ProcessID;
CloseHandle(handle);
tProcess = OpenProcess(PROCESS_ALL_ACCESS, false, tPID);
return tProcess;
}
} while (Process32Next(handle, &entry));
CloseHandle(handle);
return NULL;
}
};
#endif
paint.hpp:
#ifndef PAINT_HPP
#define PAINT_HPP
#pragma once
#pragma warning(disable: 26495)
#include <Windows.h>
#include <d3d9.h>
#include <d3dx9.h> // Project -> [project name] Properties -> VC++ Directories: Add $(DXSDK_DIR)Include into Include Directories and $(DXSDK_DIR)Lib\x86 into Library directories
// Make sure you have Direct3D 9 SDK installed - https://www.microsoft.com/en-gb/download/details.aspx?id=6812
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#pragma comment(lib, "legacy_stdio_definitions.lib")
class Paint {
private:
IDirect3D9Ex* object = NULL; // used to create device
IDirect3DDevice9Ex* device = NULL; // contains functions like begin and end scene
D3DPRESENT_PARAMETERS params;
ID3DXFont* d3d_font = 0; // font used when displaying text
HWND t_hwnd; // target process window
int width; // target process width
int height;
int d3d9init(HWND hwnd) {
if (FAILED(Direct3DCreate9Ex(D3D_SDK_VERSION, &object))) {
DestroyWindow(hwnd);
}
ZeroMemory(¶ms, sizeof(params));
params.BackBufferWidth = width;
params.BackBufferHeight = height;
params.Windowed = TRUE;
params.SwapEffect = D3DSWAPEFFECT_DISCARD;
params.hDeviceWindow = hwnd;
params.MultiSampleQuality = D3DMULTISAMPLE_NONE;
params.BackBufferFormat = D3DFMT_A8R8G8B8;
params.EnableAutoDepthStencil = TRUE;
params.AutoDepthStencilFormat = D3DFMT_D16;
HRESULT res = object->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, ¶ms, 0, &device);
if (FAILED(res)) DestroyWindow(hwnd);
D3DXCreateFont(device, 50, 0, FW_BOLD, 1, false, DEFAULT_CHARSET, OUT_DEVICE_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH, L"Consolas", &d3d_font);
return 0;
}
void draw_text(char* text, int x, int y, int a, int r, int g, int b) {
RECT rect;
rect.left = x;
rect.top = y;
d3d_font->DrawTextA(0, text, strlen(text), &rect, DT_NOCLIP, D3DCOLOR_ARGB(a, r, g, b));
}
public:
Paint() {
this->device = nullptr;
this->object = nullptr;
}
~Paint() {
if (object != NULL) object->Release();
if (device != NULL) device->Release();
}
Paint(HWND hwnd, HWND t_hwnd, int width, int height) {
this->t_hwnd = t_hwnd;
this->width = width;
this->height = height;
d3d9init(hwnd);
}
int render() {
if (device == nullptr) return 1;
device->Clear(0, 0, D3DCLEAR_TARGET, 0, 1.0f, 0);
device->BeginScene();
if (t_hwnd == GetForegroundWindow()) {
draw_text((char*)"Test message", 15, 15, 255, 150, 150, 150);
}
device->EndScene();
device->PresentEx(0, 0, 0, 0, 0);
return 0;
}
};
#endif
只是不能正常工作。任何想法,我做错了什么?
我还注意到一件奇怪的事情,当我尝试调试时,它在我调用 RegisterClassEx (overlay.hpp) 函数时出现错误“0xC0000005:访问冲突读取位置 0xADE50000”。好奇怪,虽然我初始化了WNDCLASSEX结构的所有成员。另外很奇怪,项目构建成功,我可以 运行 构建的 .exe 文件。
访问冲突的原因是 WNDCLASSEX
结构中 hInstance
参数的设置。您可以将其设置为 GetModuleHandle(NULL)
;
就像代码一样:
wc.hInstance = GetModuleHandle(NULL);
应用没有响应的原因是你在线程中设置了GUI线程,在主线程中设置了无线循环,会导致UI线程无反应。可以参考这个thread.
只需要在主线程中调用start
函数,然后创建线程执行SendMessage
函数就可以解决这个问题:
void fun(const Overlay&ol)
{
while (1) {
SendMessage(ol.hwnd, WM_PAINT, NULL, NULL);
}
}
int main() {
Memory mem;
Overlay ol(&mem);
std::thread t(fun, ol);
ol.static_start(&ol);
//HANDLE overlay = CreateThread(NULL, NULL, &ol.static_start, (void*)&ol, NULL, NULL);
t.join();
return EXIT_SUCCESS;
}
编辑
正如@David所说,您不应该发送WM_PAINT
消息,直接调用start
函数即可正常工作。