简体   繁体   English

window 透明覆盖在几秒钟后停止工作

[英]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(&params, 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, &params, 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM