Une superposition transparente de fenêtre cesse de fonctionner après quelques secondes
J'essaye de créer une superposition transparente sur une fenêtre de processus (je veux dessiner des trucs sur la superposition en utilisant Direct3D 9).
La superposition est créée en utilisant un programme externe (pas une bibliothèque injectée .dll ou quelque chose).
Le problème est que lorsque je lance le programme, une superposition invisible apparaît sur la fenêtre de processus (et elle dessine même du texte dessus, donc le WM_PAINT semble fonctionner), mais dans les prochaines secondes, le curseur devient une "horloge de sable" stylé (désolé, je ne sais pas comment cela s'appelle xd) et si je clique sur la fenêtre, il affiche une erreur "L'application ne répond pas" et devient blanc fixe.

J'importe la classe de superposition dans le fichier de point d'entrée et voici comment je l'exécute dans la fonction principale (simplifiée):
#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
Cela ne fonctionne tout simplement pas correctement. Des idées, qu'est-ce que je fais de mal?
J'ai également remarqué une chose étrange, que lorsque j'essaie de déboguer, cela me donne une erreur "0xC0000005: violation d'accès à l'emplacement de lecture 0xADE50000" au moment où j'appelle la fonction RegisterClassEx (overlay.hpp). Tellement étrange, même si j'ai initialisé tous les membres de la structure WNDCLASSEX. De plus, il est assez étrange que le projet se construit avec succès et que je puisse exécuter le fichier .exe généré.
Réponses
La raison du conflit d'accès est le réglage du hInstance
paramètre dans votre WNDCLASSEX
structure. Vous pouvez le définir sur GetModuleHandle(NULL)
;
Tout comme le code:
wc.hInstance = GetModuleHandle(NULL);
La raison pour laquelle l'application ne répond pas est que vous définissez le thread GUI dans le thread et que vous définissez la boucle sans fil dans le thread principal, ce qui entraînera le thread d'interface utilisateur pour ne pas répondre. Vous pouvez vous référer à ce fil .
Il vous suffit d'appeler la start
fonction dans le thread principal, puis de créer un thread pour exécuter la SendMessage
fonction peut résoudre ce problème:
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;
}
Éditer
Comme @David l'a dit, vous ne devriez pas envoyer le WM_PAINT
message, appelez simplement la start
fonction directement pour fonctionner correctement.