Search code examples
c++gdi

How to draw an overlay in C++?


I'm loading my dll into a process and creating an overlay in a new thread.

My idea is to draw into that overlay from the main dll thread.

This is the maindll.cpp code:

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;
}

This is my CreateOverlayWindow() function which is probably wrong:

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;
}

And here is my Visuals() function which I've shortened to avoid useless information, as the coordinates I'm trying to draw are correct:

//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);

When I check the coordinates where it's trying to draw I see correct values, but I don't see anything drawn to the screen :( I don't know if it's important, but I have 3 screens.

How can I draw to my screen from my c++ dll?


Solution

  • I just had to change this line:

    SetLayeredWindowAttributes(hwndOverlay, RGB(0, 0, 0), 0, LWA_COLORKEY | LWA_ALPHA);
    

    To this:

    SetLayeredWindowAttributes(hwndOverlay, RGB(0, 0, 0), 0, LWA_COLORKEY);