Search code examples
c++visual-c++drawinggdi+gdi

How to draw over second monitor with GDIPLUS


I'm want to draw some text and lines over the desktop.

I'm using gdiplus.h for print texts with the function DrawString.

But its only print text on a primary screen monitor.

If has in presentation mode, with 2 monitors I need to print text in a second monitor.

#define _WIN32_WINNT 0x500
#include <windows.h>
#include <gdiplus.h>

using namespace Gdiplus;

#pragma comment (lib,"Gdiplus.lib")

int main() {

    HWND desktop = GetDesktopWindow();
    HDC hdc = GetWindowDC(desktop); 

    ULONG_PTR m_gdiplusToken;
    // Initialize GDI+
    GdiplusStartupInput gdiplusStartupInput;
    GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);

    while (true)
    {

        HPEN newpen;
        LPPOINT point = NULL;
        newpen = CreatePen(PS_COSMETIC, 20, RGB(255, 0, 0));
        SelectObject(hdc, newpen);
        MoveToEx(hdc, 1500, 500, point);
        LineTo(hdc, 1600, 550 ); 
        //this block works for draw line in second monitor

        TextOut(hdc, 1500, 300, TEXT("Text of text out"), 17); // this works too


    //But if I'm use gdiplus only print things on the primary screen
    Gdiplus::Graphics g(hdc);

    Pen      pen(Gdiplus::Color(0, 0, 255), 2);

    g.DrawLine(&pen, 1500, 0, 1700, 600);// dont work

    g.DrawLine(&pen, 0, 0, 1200, 600);//  work, but is in the primary screen

    FontFamily  fontFamily(L"Times New Roman");
    Font        font(&fontFamily, 24, FontStyleRegular, UnitPixel);
    SolidBrush  brush(Color(255, 0, 0, 255));

    g.DrawString(TEXT("test of GDI+"), 13, &font, PointF(1600.0f, 300.0f), &brush); // dont work

    g.DrawString(TEXT("test of GDI+"), 13, &font, PointF(500.0f, 300.0f), &brush); // work, but is in the primary screen
    }

    Gdiplus::GdiplusShutdown(m_gdiplusToken);

    return 0;
}

Solution

  • Yes, this is surprisingly difficult. The help for GetWindowDC does warn us it only applies to the primary display monitor (which is clearly only half true, because the regular GDI bits in your example do work):

    To get the device context for other display monitors, use the EnumDisplayMonitors and CreateDC functions.

    That's not much of a hint, but I suppose something along the lines of this working example (based on your code) is intended:

    #define _WIN32_WINNT 0x500
    #include <windows.h>
    #include <gdiplus.h>
    
    using namespace Gdiplus;
    
    #pragma comment (lib,"Gdiplus.lib")
    
    BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
    {
        MONITORINFOEXW info;
        info.cbSize = sizeof(info);
        ::GetMonitorInfoW(hMonitor, &info);
    
        HDC hdc = ::CreateDCW(NULL, info.szDevice, NULL, NULL);
    
        {
            Gdiplus::Graphics graphics(hdc);
    
            FontFamily  fontFamily(L"Times New Roman");
            Font        font(&fontFamily, 24, FontStyleRegular, UnitPixel);
            SolidBrush  brush(Color(255, 0, 0, 255));
    
            graphics.DrawString(
                        info.szDevice,
                        wcslen(info.szDevice),
                        &font,
                        PointF(0.0f, 0.0f),
                        &brush);
        }
    
        ::DeleteDC(hdc);
    
        return TRUE;
    }
    
    int CALLBACK WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
    {
        ULONG_PTR m_gdiplusToken;
        GdiplusStartupInput gdiplusStartupInput;
        Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
    
        while (true)
        {
            ::EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, NULL);
        }
    
        Gdiplus::GdiplusShutdown(m_gdiplusToken);
    
        return 0;
    }