繁体   English   中英

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

[英]A window transparent overlay stops working after a few seconds

我正在尝试在某个进程的 window 上创建透明覆盖(我想使用 Direct3D 9 在覆盖上绘制一些东西)。

覆盖是使用外部程序创建的(不是 a.dll 注入库或其他东西)。

问题是当我启动程序时,进程 window 上出现了一个不可见的覆盖层(它甚至在上面绘制了一些文本,所以 WM_PAINT 似乎正在工作),但在接下来的几秒钟内,cursor 变成了“沙钟”样式(对不起,我不知道它怎么叫 xd),如果我点击 window,它会显示错误“应用程序没有响应”并变成纯白色。“应用程序没有响应”消息

我将覆盖 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;
}

覆盖.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

油漆.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

它只是无法正常工作。 任何想法,我做错了什么?
我还注意到一件奇怪的事情,当我尝试调试时,在我调用 RegisterClassEx (overlay.hpp) function 时,它给了我一个错误“0xC0000005:访问冲突读取位置 0xADE50000”。 太奇怪了,即使我初始化了 WNDCLASSEX 结构的所有成员。 项目构建成功并且我可以运行builded.exe文件也很奇怪。

访问冲突的原因是WNDCLASSEX结构中hInstance参数的设置。 您可以将其设置为GetModuleHandle(NULL)

就像代码一样:

wc.hInstance = GetModuleHandle(NULL);

应用程序没有响应的原因是你在线程中设置了GUI线程,在主线程中设置了无线循环,会导致UI线程没有响应。 你可以参考这个线程

你只需要在主线程中调用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;
}

编辑

正如@David所说,您不应该发送WM_PAINT消息,只需直接调用start function 即可正常工作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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