How to implement passthrough screenshot using C++ in Windows 7 and above?

RL Chen 250 Reputation points
2023-09-21T06:55:35.8866667+00:00

I need to implement a screenshot function in C++ or COM to realize the following requirement: I can intercept the image of the desktop and penetrate a specified number of HWND windows and take a screenshot (meaning that the intercepted image will not be covered by these windows).

One idea was proposed: get a list of windows, then get the image of each window separately (make sure it can't be covered by the upper windows) and exclude certain specified windows and overlay them. Is this idea feasible? Or is there a better solution?

Another idea has been proposed: hide a window and take a screenshot of it, then show it again without updating the state of the window, so that the window is always visible to the user without flickering. How can this idea be realized?

Developer technologies C++
{count} votes

Accepted answer
  1. Minxin Yu 13,501 Reputation points Microsoft External Staff
    2023-09-22T08:12:04.73+00:00

    Hi, @RL Chen

    Here is an example to passthrough screen specific HWNDs based on MSDN sample.
    I tested "NotePad" . HWND hwndNotepad = FindWindow(L"NotePad", NULL);
    Create a C++ desktop app. Replace the code below.

    User's image

    Code:

    //Ensure that the following definition is in effect before winuser.h is included.
    #ifndef _WIN32_WINNT
    #define _WIN32_WINNT 0x0501    
    #endif
    
    #include <windows.h>
    #include <wincodec.h>
    #include <magnification.h>
    
    #include <fstream>
    
    #include"resource.h"
    
    #pragma comment(lib, "Magnification.lib")
    #pragma comment(lib, "Gdiplus.lib")
    //For simplicity, the sample uses a constant magnification factor.
    
    
    
    // Global variables and strings.
    HINSTANCE           hInst;
    const TCHAR         WindowClassName[] = TEXT("MagnifierWindow");
    const TCHAR         WindowTitle[] = TEXT("Screen Magnifier Sample");
    const UINT          timerInterval = 16; // close to the refresh rate @60hz
    HWND                hwndMag;
    HWND                hwndHost;
    RECT                magWindowRect;
    RECT                hostWindowRect;
    
    
    // Forward declarations.
    LRESULT CALLBACK HostWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    ATOM                RegisterHostWindowClass(HINSTANCE hInstance);
    BOOL                SetupMagnifier(HINSTANCE hinst);
    LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
    void CALLBACK       UpdateMagWindow(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
    void                GoFullScreen();
    
    BOOL                isFullScreen = FALSE;
    int CaptureAnImage(HWND hWnd);
    BOOL MagImageScaling(HWND hwnd, void* srcdata, MAGIMAGEHEADER srcheader,
        void* destdata, MAGIMAGEHEADER destheader,
        RECT unclipped, RECT clipped, HRGN dirty)
    {
    
        BITMAPINFOHEADER bmif;
        BITMAPFILEHEADER bmfh;
    
        bmif.biSize = sizeof(BITMAPINFOHEADER);
        bmif.biHeight = srcheader.height;
        bmif.biWidth = srcheader.width;
        bmif.biSizeImage = bmif.biWidth * bmif.biHeight * 4;
        bmif.biPlanes = 1;
        bmif.biBitCount = (WORD)(bmif.biSizeImage / bmif.biHeight / bmif.biWidth * 8);
        bmif.biCompression = BI_RGB;
    
        LONG offBits = sizeof(BITMAPFILEHEADER) + bmif.biSize;
        bmfh.bfType = 0x4d42; // "BM"
        bmfh.bfOffBits = offBits;
        bmfh.bfSize = offBits + bmif.biSizeImage;
        bmfh.bfReserved1 = 0;
        bmfh.bfReserved2 = 0;
    
    
        LPBYTE pData = (BYTE*)new BYTE[bmif.biSizeImage];
        memcpy(pData, (LPBYTE)srcdata + srcheader.offset, bmif.biSizeImage);
        LONG nLineSize = bmif.biWidth * bmif.biBitCount / 8;
        BYTE* pLineData = new BYTE[nLineSize];
        LONG nLineStartIndex = 0;
        LONG nLineEndIndex = bmif.biHeight - 1;
        while (nLineStartIndex < nLineEndIndex)
        {
            BYTE* pStart = pData + (nLineStartIndex * nLineSize);
            BYTE* pEnd = pData + (nLineEndIndex * nLineSize);
            memcpy(pLineData, pStart, nLineSize);
            memcpy(pStart, pEnd, nLineSize);
            memcpy(pEnd, pLineData, nLineSize);
            nLineStartIndex++;
            nLineEndIndex--;
        }
        delete[] pLineData;
        pLineData = nullptr;
        std::ofstream ofs(L"screenshot.bmp", std::fstream::out | std::fstream::binary);
        if (ofs.is_open())
        {
            ofs.write((const char*)&bmfh, sizeof(BITMAPFILEHEADER));
            ofs.write((const char*)&bmif, sizeof(BITMAPINFOHEADER));
            ofs.write((const char*)pData, bmif.biSizeImage);
            ofs.close();
        }
    
        delete[] pData;
        pData = nullptr;
        //MessageBox(hwndHost,L"saved",L"",NULL);
        PostQuitMessage(0);
        return 1;
    }
    
    
    
    
    //
    // FUNCTION: WinMain()
    //
    // PURPOSE: Entry point for the application.
    //
    int APIENTRY WinMain(_In_ HINSTANCE hInstance,
        _In_opt_ HINSTANCE /*hPrevInstance*/,
        _In_ LPSTR     /*lpCmdLine*/,
        _In_ int       nCmdShow)
    {
        if (FALSE == MagInitialize())
        {
            return 0;
        }
        hwndHost = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, HostWndProc);
        SetWindowLong(hwndHost, GWL_EXSTYLE, WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT);
        int xSpan = GetSystemMetrics(SM_CXSCREEN);
        int ySpan = GetSystemMetrics(SM_CYSCREEN);
        int xBorder = GetSystemMetrics(SM_CXFRAME);
        int yCaption = GetSystemMetrics(SM_CYCAPTION);
        int yBorder = GetSystemMetrics(SM_CYFRAME);
    
        // Calculate the window origin and span for full-screen mode.
        int xOrigin = -xBorder;
        int yOrigin = -yBorder - yCaption;
        xSpan += 2 * xBorder;
        ySpan += 2 * yBorder + yCaption;
    
        SetWindowPos(hwndHost, HWND_TOPMOST, xOrigin, yOrigin, xSpan, ySpan,
            SWP_SHOWWINDOW | SWP_NOZORDER | SWP_NOACTIVATE);
        if (FALSE == SetupMagnifier(hInstance))
        {
            return 0;
        }
    
    
        ShowWindow(hwndHost, nCmdShow);
    
    
        // Create a timer to update the control.
        UINT_PTR timerId = SetTimer(hwndHost, 0, timerInterval, UpdateMagWindow);
    
        // Main message loop.
        MSG msg;
        while (GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        // Shut down.
        KillTimer(NULL, timerId);
        MagUninitialize();
        return (int)msg.wParam;
    }
    
    //
    // FUNCTION: HostWndProc()
    //
    // PURPOSE: Window procedure for the window that hosts the magnifier control.
    //
    LRESULT CALLBACK HostWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch (message)
        {
    
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
    
        case WM_SIZE:
            if (hwndMag != NULL)
            {
                GetClientRect(hWnd, &magWindowRect);
    
                int xSpan = GetSystemMetrics(SM_CXSCREEN);
                int ySpan = GetSystemMetrics(SM_CYSCREEN);
    
    
                // Calculate the size of system elements.
                int xBorder = GetSystemMetrics(SM_CXFRAME);
               
                int yCaption = GetSystemMetrics(SM_CYCAPTION);
                int yBorder = GetSystemMetrics(SM_CYFRAME);
    
                // Calculate the window origin and span for full-screen mode.
                int xOrigin = -xBorder;
                int yOrigin = -yBorder - yCaption;
                xSpan += 2 * xBorder;
                ySpan += 2 * yBorder + yCaption;
                // Resize the control to fill the window.
                SetWindowPos(hwndMag, NULL,
                    magWindowRect.left, magWindowRect.top, magWindowRect.right+ yCaption, magWindowRect.bottom + 2 * yBorder + 2 * yCaption, 0);
            }
            break;
    
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        return 0;
    }
    
    //
    //  FUNCTION: RegisterHostWindowClass()
    //
    //  PURPOSE: Registers the window class for the window that contains the magnification control.
    //
    ATOM RegisterHostWindowClass(HINSTANCE hInstance)
    {
        WNDCLASSEX wcex = {};
    
        wcex.cbSize = sizeof(WNDCLASSEX);
        wcex.style = CS_HREDRAW | CS_VREDRAW;
        wcex.lpfnWndProc = HostWndProc;
        wcex.hInstance = hInstance;
        wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
        wcex.hbrBackground = (HBRUSH)(1 + COLOR_BTNFACE);
        wcex.lpszClassName = WindowClassName;
    
        return RegisterClassEx(&wcex);
    }
    
    //
    // FUNCTION: SetupMagnifier
    //
    // PURPOSE: Creates the windows and initializes magnification.
    //
    BOOL SetupMagnifier(HINSTANCE hinst)
    {
        // Set bounds of host window according to screen size.
        hostWindowRect.top = 0;
        hostWindowRect.bottom = GetSystemMetrics(SM_CYSCREEN);  // top quarter of screen
        hostWindowRect.left = 0;
        hostWindowRect.right = GetSystemMetrics(SM_CXSCREEN);
    
        // Create the host window.
        RegisterHostWindowClass(hinst);
    
        if (!hwndHost)
        {
            return FALSE;
        }
    
        // Make the window opaque.
        SetLayeredWindowAttributes(hwndHost, 0, 255, LWA_ALPHA);
    
        // Create a magnifier control that fills the client area.
        GetClientRect(hwndHost, &magWindowRect);
        hwndMag = CreateWindow(WC_MAGNIFIER, TEXT("MagnifierWindow"),
            WS_CHILD | MS_SHOWMAGNIFIEDCURSOR | WS_VISIBLE,
            magWindowRect.left, magWindowRect.top, magWindowRect.right, magWindowRect.bottom, hwndHost, NULL, hInst, NULL);
        GoFullScreen();
        if (!hwndMag)
        {
            return FALSE;
        }
        HWND hwndNotepad = FindWindow(L"NotePad", NULL);
    
    
        HWND hwndArray[] = { hwndNotepad };
        int Size = 1;//size of Array
    
        MagSetWindowFilterList(hwndMag, MW_FILTERMODE_EXCLUDE, Size, hwndArray);
    
    
    
    
    
    
        if (!MagSetImageScalingCallback(hwndMag, (MagImageScalingCallback)MagImageScaling))
        {
            return FALSE;
        }
    
    
        return 1;
    }
    
    
    //
    // FUNCTION: UpdateMagWindow()
    //
    // PURPOSE: Sets the source rectangle and updates the window. Called by a timer.
    //
    void CALLBACK UpdateMagWindow(HWND /*hwnd*/, UINT /*uMsg*/, UINT_PTR /*idEvent*/, DWORD /*dwTime*/)
    {
    
        //GetCursorPos(&mousePoint);
        int screenWidth = GetSystemMetrics(SM_CXSCREEN);
        int screenHeight = GetSystemMetrics(SM_CYSCREEN);
    
        POINT centerPoint;
        centerPoint.x = screenWidth / 2;
        centerPoint.y = screenHeight / 2;
        int width = (int)((magWindowRect.right - magWindowRect.left));
        int height = (int)((magWindowRect.bottom - magWindowRect.top));
        RECT sourceRect;
        sourceRect.left = centerPoint.x - width / 2;
        sourceRect.top = centerPoint.y - height / 2;
    
        // Don't scroll outside desktop area.
        if (sourceRect.left < 0)
        {
            sourceRect.left = 0;
        }
        if (sourceRect.left > GetSystemMetrics(SM_CXSCREEN) - width)
        {
            sourceRect.left = GetSystemMetrics(SM_CXSCREEN) - width;
        }
        sourceRect.right = sourceRect.left + width;
    
        if (sourceRect.top < 0)
        {
            sourceRect.top = 0;
        }
        if (sourceRect.top > GetSystemMetrics(SM_CYSCREEN) - height)
        {
            sourceRect.top = GetSystemMetrics(SM_CYSCREEN) - height;
        }
        sourceRect.bottom = sourceRect.top + height;
    
        // Set the source rectangle for the magnifier control.
        MagSetWindowSource(hwndMag, sourceRect);
    
        // Reclaim topmost status, to prevent unmagnified menus from remaining in view. 
        SetWindowPos(hwndHost, HWND_TOPMOST, 0, 0, 0, 0,
            SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
    
        // Force redraw.
        InvalidateRect(hwndMag, NULL, TRUE);
    }
    
    
    //
    // FUNCTION: GoFullScreen()
    //
    // PURPOSE: Makes the host window full-screen by placing non-client elements outside the display.
    //
    void GoFullScreen()
    {
        isFullScreen = TRUE;
        // The window must be styled as layered for proper rendering. 
        // It is styled as transparent so that it does not capture mouse clicks.
        SetWindowLong(hwndHost, GWL_EXSTYLE, WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT);
        // Give the window a system menu so it can be closed on the taskbar.
        SetWindowLong(hwndHost, GWL_STYLE, WS_CAPTION | WS_SYSMENU);
    
        // Calculate the span of the display area.
        HDC hDC = GetDC(NULL);
        int xSpan = GetSystemMetrics(SM_CXSCREEN);
        int ySpan = GetSystemMetrics(SM_CYSCREEN);
        ReleaseDC(NULL, hDC);
    
        // Calculate the size of system elements.
        int xBorder = GetSystemMetrics(SM_CXFRAME);
        int yCaption = GetSystemMetrics(SM_CYCAPTION);
        int yBorder = GetSystemMetrics(SM_CYFRAME);
    
        // Calculate the window origin and span for full-screen mode.
        int xOrigin = -xBorder;
        int yOrigin = -yBorder - yCaption;
        xSpan += 2 * xBorder;
        ySpan += 2 * yBorder + yCaption;
    
        SetWindowPos(hwndHost, HWND_TOPMOST, xOrigin, yOrigin, xSpan, ySpan,
            SWP_NOZORDER | SWP_NOACTIVATE);
    }
    

    Best regards,

    Minxin Yu


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


2 additional answers

Sort by: Most helpful
  1. Castorix31 90,521 Reputation points
    2023-09-21T07:33:11.0566667+00:00

    If you want to capture an image and exclude some windows, it can be done with

    Magnification API

    and

    MagSetWindowFilterList


  2. Guido Franzke 2,191 Reputation points
    2023-09-21T07:42:11.5066667+00:00

    Hello,

    here is a way how you can do it:

    int CaptureAnImage(LPCTSTR szFileName, HWND hWnd)
    

    Regards, Guido


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.