Overlay transparan jendela berhenti bekerja setelah beberapa detik
Saya mencoba membuat overlay transparan di atas beberapa jendela proses (saya ingin menggambar beberapa hal di overlay menggunakan Direct3D 9).
Overlay sedang dibuat dengan menggunakan program eksternal (bukan library yang diinjeksi .dll atau semacamnya).
Masalahnya adalah ketika saya meluncurkan program, hamparan tak terlihat muncul di jendela proses (dan bahkan menggambar beberapa teks di atasnya, sehingga WM_PAINT tampaknya berfungsi), tetapi dalam beberapa detik berikutnya kursor menjadi "jam pasir" bergaya (maaf, saya tidak tahu bagaimana lagi disebut xd) dan jika saya mengklik jendela, itu menunjukkan kesalahan "Aplikasi tidak merespons" dan menjadi putih solid.

Saya mengimpor kelas overlay ke file titik masuk dan inilah cara saya menjalankannya di fungsi utama (disederhanakan):
#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
Itu tidak bekerja dengan benar. Ada ide, apa yang saya lakukan salah?
Juga saya telah memperhatikan hal yang aneh, bahwa ketika saya mencoba untuk debug, itu memberi saya kesalahan "0xC0000005: Pelanggaran akses membaca lokasi 0xADE50000" pada titik di mana saya memanggil fungsi RegisterClassEx (overlay.hpp). Aneh sekali, meskipun saya menginisialisasi semua anggota struktur WNDCLASSEX. Juga cukup aneh bahwa proyek berhasil dibangun dan saya dapat menjalankan file .exe yang dibangun.
Jawaban
Alasan konflik akses adalah pengaturan hInstance
parameter dalam WNDCLASSEX
struktur Anda . Anda dapat mengaturnya ke GetModuleHandle(NULL)
;
Sama seperti kode:
wc.hInstance = GetModuleHandle(NULL);
Alasan mengapa aplikasi tidak merespons adalah Anda menyetel utas GUI di utas, dan menyetel loop nirkabel di utas utama, yang akan menyebabkan utas UI tidak merespons. Anda dapat merujuk ke utas ini .
Anda hanya perlu memanggil start
fungsi di utas utama, lalu membuat utas untuk menjalankan SendMessage
fungsi tersebut dapat menyelesaikan masalah ini:
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
Seperti yang dikatakan @David, Anda tidak boleh mengirim WM_PAINT
pesan, cukup panggil start
fungsinya secara langsung agar berfungsi dengan benar.