C++ DLL注入进程后Overlay绘制无显示问题排查与解决
三屏环境下DLL注入Overlay绘制不显示的解决方法
问题描述
将DLL注入目标进程后,在新线程创建Overlay窗口,主线程负责绘制内容。经检测绘制坐标值正确,但三屏显示环境下屏幕始终看不到绘制内容。
相关代码
maindll.cpp
uintptr_t mainModule = NULL; bool visuals = false; HWND hwndOverlay; void DoMagic() { uintptr_t LocalPlayer = *(uintptr_t*)(mainModule + Offsets::dwLocalPlayerPawn); if (LocalPlayer == 0) return; if (GetAsyncKeyState(VK_F1) & 1) { visuals = !visuals; } if (hwndOverlay) { HDC hdcOverlay = GetDC(hwndOverlay); if (visuals) Visuals(mainModule, LocalPlayer, hdcOverlay); } Sleep(1); } DWORD WINAPI OverlayThread(LPVOID lpParam) { HMODULE hModule = (HMODULE)lpParam; hwndOverlay = CreateOverlayWindow(hModule); if (!hwndOverlay) { MessageBox(NULL, L"Error creating overlay window", L"Error", MB_ICONERROR); return 1; } MSG msg; while (GetMessage(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } DWORD WINAPI MainFunc(HMODULE hModule) { AllocConsole(); FILE* f; freopen_s(&f, "CONOUT$", "w", stdout); CloseHandle(CreateThread(nullptr, 0, OverlayThread, hModule, 0, nullptr)); mainModule = (uintptr_t)GetModuleHandle(L"client.dll"); if (hModule) { while (!GetAsyncKeyState(VK_END) & 1) DoMagic(); } else { std::cout << "Main module not found, press ENTER to exit..." << std::endl; getchar(); } fclose(f); FreeConsole(); FreeLibraryAndExitThread(hModule, 0); return 0; }
CreateOverlayWindow()函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, uMsg, wParam, lParam); } HWND CreateOverlayWindow(HINSTANCE hInstance) { const wchar_t CLASS_NAME[] = L"OverlayClass"; WNDCLASSEX wc = {}; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.hCursor = LoadCursor(nullptr, IDC_ARROW); wc.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0, 0, 0)); wc.lpszClassName = CLASS_NAME; RegisterClassEx(&wc); HWND hwndOverlay = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_LAYERED, CLASS_NAME, L"Overlay", WS_POPUP, 0, 0, 1920, 1080, nullptr, nullptr, hInstance, nullptr); SetLayeredWindowAttributes(hwndOverlay, RGB(0, 0, 0), 0, LWA_COLORKEY | LWA_ALPHA); ShowWindow(hwndOverlay, SW_SHOW); UpdateWindow(hwndOverlay); return hwndOverlay; }
Visuals()核心代码
//Sets the Brush Color HBRUSH Brush = (playerTeam == localPlayerTeam) ? hBrushGreen : hBrushRed; COLORREF TextCOLOR = (playerTeam == localPlayerTeam) ? TextColorGreen : TextColorRed; //Draws the box DrawBorderBox(playerFeetInScreen.x + center, playerFeetInScreen.y, width, head - extra, 1, hdcOverlay, Brush); //Draw our heath by converting a int to a char char healthChar[255]; sprintf_s(healthChar, sizeof(healthChar), "%i", playerHealth); DrawString(playerFeetInScreen.x, playerFeetInScreen.y, TextCOLOR, healthChar, Font, hdcOverlay);
解决步骤
1. 适配三屏环境的窗口位置与尺寸
当前Overlay固定创建在(0,0)位置且尺寸为1920x1080,三屏环境下大概率不在目标游戏所在屏幕,导致无法看到。需动态获取目标屏幕的区域:
HWND CreateOverlayWindow(HINSTANCE hInstance) { // ... 窗口类注册代码不变 ... // 获取目标游戏窗口(替换为实际游戏窗口标题或通过进程获取主窗口) HWND gameHwnd = FindWindow(nullptr, L"目标游戏窗口标题"); RECT gameRect; GetWindowRect(gameHwnd, &gameRect); // 创建覆盖游戏窗口的Overlay HWND hwndOverlay = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_LAYERED, CLASS_NAME, L"Overlay", WS_POPUP, gameRect.left, gameRect.top, gameRect.right - gameRect.left, gameRect.bottom - gameRect.top, nullptr, nullptr, hInstance, nullptr); // ... 后续属性设置代码不变 ... }
如果游戏是全屏模式,也可以直接获取当前显示器的分辨率:
int screenWidth = GetSystemMetrics(SM_CXSCREEN); int screenHeight = GetSystemMetrics(SM_CYSCREEN);
2. 修复GDI资源泄漏问题
DoMagic中调用GetDC后未释放,会导致GDI资源耗尽,最终无法绘制。必须添加ReleaseDC:
void DoMagic() { // ... 其他代码不变 ... if (hwndOverlay) { HDC hdcOverlay = GetDC(hwndOverlay); if (visuals) Visuals(mainModule, LocalPlayer, hdcOverlay); ReleaseDC(hwndOverlay, hdcOverlay); // 释放设备上下文 } // ... 其他代码不变 ... }
3. 调整Overlay窗口透明度设置
当前SetLayeredWindowAttributes中LWA_ALPHA设为0会让整个窗口完全透明,导致绘制内容无法显示。仅保留颜色键透明即可:
SetLayeredWindowAttributes(hwndOverlay, RGB(0, 0, 0), 255, LWA_COLORKEY);
4. 确保窗口就绪后再绘制
添加窗口就绪标志,避免主线程在Overlay窗口未完全创建时就尝试绘制:
// 全局变量 bool overlayReady = false; DWORD WINAPI OverlayThread(LPVOID lpParam) { HMODULE hModule = (HMODULE)lpParam; hwndOverlay = CreateOverlayWindow(hModule); if (!hwndOverlay) { MessageBox(NULL, L"Error creating overlay window", L"Error", MB_ICONERROR); return 1; } overlayReady = true; // 标记窗口就绪 // ... 消息循环代码不变 ... overlayReady = false; return 0; } // DoMagic中修改判断逻辑 if (hwndOverlay && overlayReady) { HDC hdcOverlay = GetDC(hwndOverlay); if (visuals) Visuals(mainModule, LocalPlayer, hdcOverlay); ReleaseDC(hwndOverlay, hdcOverlay); }
5. 校验绘制函数实现
确保DrawBorderBox和DrawString的实现正确,比如DrawBorderBox需正确使用传入的画刷:
void DrawBorderBox(int x, int y, int w, int h, int thickness, HDC hdc, HBRUSH brush) { RECT rect = {x, y, x + thickness, y + h}; FillRect(hdc, &rect, brush); rect = {x + w - thickness, y, x + w, y + h}; FillRect(hdc, &rect, brush); rect = {x, y, x + w, y + thickness}; FillRect(hdc, &rect, brush); rect = {x, y + h - thickness, x + w, y + h}; FillRect(hdc, &rect, brush); }
DrawString需确保字体句柄有效,并正确设置文本颜色:
void DrawString(int x, int y, COLORREF color, const char* text, HFONT font, HDC hdc) { SetTextColor(hdc, color); SetBkMode(hdc, TRANSPARENT); // 背景透明 HFONT oldFont = (HFONT)SelectObject(hdc, font); TextOutA(hdc, x, y, text, strlen(text)); SelectObject(hdc, oldFont); }
内容的提问来源于stack exchange,提问作者kuhi
相关产品推荐
相关产品推荐

