[英]A window transparent overlay stops working after a few seconds
I'm trying to create a transparent overlay over some process' window (I want to draw some stuff on the overlay using Direct3D 9).我正在尝试在某个进程的 window 上创建透明覆盖(我想使用 Direct3D 9 在覆盖上绘制一些东西)。
The overlay is being created by using an external program (not a.dll injected library or something).覆盖是使用外部程序创建的(不是 a.dll 注入库或其他东西)。
The problem is when I launch the program, an invisible overlay appears over the process window (and it even draws some text on it, so the WM_PAINT seems to be working), but in the next few seconds the cursor becomes a "sand clock" styled (sorry, I don't know how else is it called xd) and if I click on the window, it shows an error "The app is not responding" and becomes solid white.问题是当我启动程序时,进程 window 上出现了一个不可见的覆盖层(它甚至在上面绘制了一些文本,所以 WM_PAINT 似乎正在工作),但在接下来的几秒钟内,cursor 变成了“沙钟”样式(对不起,我不知道它怎么叫 xd),如果我点击 window,它会显示错误“应用程序没有响应”并变成纯白色。
I import the overlay class into the entry point file and here's how I run it in the main function (simplified):我将覆盖 class 导入入口点文件,这是我在主 function(简化)中运行它的方式:
#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:覆盖.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: 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:油漆.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
It just does not work correctly.它只是无法正常工作。 Any ideas, what am I doing wrong?
任何想法,我做错了什么?
Also I've noticed a strange thing, that when I try to debug, it gives me an error "0xC0000005: Access violation reading location 0xADE50000" at the point where I call the RegisterClassEx (overlay.hpp) function.我还注意到一件奇怪的事情,当我尝试调试时,在我调用 RegisterClassEx (overlay.hpp) function 时,它给了我一个错误“0xC0000005:访问冲突读取位置 0xADE50000”。 So strange, even though I initialised all the members of the WNDCLASSEX structure.
太奇怪了,即使我初始化了 WNDCLASSEX 结构的所有成员。 Also it's quite strange that the project builds successfully and I can run the builded.exe file.
项目构建成功并且我可以运行builded.exe文件也很奇怪。
The reason for the access conflict is the setting of the hInstance
parameter in your WNDCLASSEX
structure.访问冲突的原因是
WNDCLASSEX
结构中hInstance
参数的设置。 You can set it to GetModuleHandle(NULL)
;您可以将其设置为
GetModuleHandle(NULL)
;
Just like the code:就像代码一样:
wc.hInstance = GetModuleHandle(NULL);
The reason why the application is not responding is that you set the GUI thread in the thread, and set the wireless loop in the main thread, which will cause the UI thread to not respond.应用程序没有响应的原因是你在线程中设置了GUI线程,在主线程中设置了无线循环,会导致UI线程没有响应。 You can refer to this thread .
你可以参考这个线程。
You just need to call the start
function in the main thread, and then create a thread to execute the SendMessage
function can solve this problem:你只需要在主线程中调用
start
function,然后创建线程执行SendMessage
function就可以解决这个问题:
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;
}
Edit编辑
As @David said, You should not send the WM_PAINT
message, just call the start
function directly to work properly.正如@David所说,您不应该发送
WM_PAINT
消息,只需直接调用start
function 即可正常工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.