How to capture the screen of a window that contains transparency?

Jack 21 Reputation points
2022-01-20T04:31:04.493+00:00

Trying to find a way to capture a window that contains transparency that also works when the window is covered by another.

I tried the function [PrintWindow][1] but it doesn't store the transparency information in the hdc.
If possible using gdi+.

Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,411 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,516 questions
{count} votes

4 answers

Sort by: Most helpful
  1. Junjie Zhu - MSFT 14,136 Reputation points Microsoft Vendor
    2022-01-27T07:54:08.103+00:00

    Hello,
    Welcome to Microsoft Q&A!

    I used GetSystemMetrics(SM_CYCAPTION) to get the title bar height and got a full screenshot. I made these changes in the following code.(Attached is the complete code)
    based on code sample: https://learn.microsoft.com/en-us/windows/win32/gdi/capturing-an-image#code-example
    In the screenshot, the transparent area of the original window turned green area.
    Find this FindWindow(NULL, L" untitled-notepad "); Then change the name of the window you want to capture.

    168961-gdi-capture.txt
    Thank you.


    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 people found this answer helpful.
    0 comments No comments

  2. Jack 21 Reputation points
    2022-01-21T17:55:02.093+00:00

    @Junjie Zhu - MSFT In the saved image its missing the window titlebar, also how i could capture the window when its covered by another?

    int CaptureAnImage(HWND hWnd)  
    {  
     HDC hdcScreen;  
     HDC hdcWindow;  
     HDC hdcMemDC = NULL;  
     HBITMAP hbmScreen = NULL;  
     BITMAP bmpScreen;  
     DWORD dwBytesWritten = 0;  
     DWORD dwSizeofDIB = 0;  
     HANDLE hFile = NULL;  
     char* lpbitmap = NULL;  
     HANDLE hDIB = NULL;  
     DWORD dwBmpSize = 0;  
      
     // Retrieve the handle to a display device context for the client   
     // area of the window.   
     hdcScreen = GetDC(hWnd); // GetDC(NULL);  
     hdcWindow = GetDC(hWnd);  
      
     // Create a compatible DC, which is used in a BitBlt from the window DC.  
     hdcMemDC = CreateCompatibleDC(hdcWindow);  
      
     if (!hdcMemDC)  
     {  
     MessageBox(hWnd, L"CreateCompatibleDC has failed", L"Failed", MB_OK);  
     goto done;  
     }  
      
     // Get the client area for size calculation.  
     RECT rcClient;  
     GetClientRect(hWnd, &rcClient);  
      
     // This is the best stretch mode.  
     SetStretchBltMode(hdcWindow, HALFTONE);  
      
     // The source DC is the entire screen, and the destination DC is the current window (HWND).  
     if (!StretchBlt(hdcWindow,  
     0, 0,  
     rcClient.right, rcClient.bottom,  
     hdcScreen,  
     0, 0,  
     rcClient.right, //GetSystemMetrics(SM_CXSCREEN),  
     rcClient.bottom, //GetSystemMetrics(SM_CYSCREEN),  
     SRCCOPY | CAPTUREBLT))  
     {  
     MessageBox(hWnd, L"StretchBlt has failed", L"Failed", MB_OK);  
     goto done;  
     }  
      
     // Create a compatible bitmap from the Window DC.  
     hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);  
      
     if (!hbmScreen)  
     {  
     MessageBox(hWnd, L"CreateCompatibleBitmap Failed", L"Failed", MB_OK);  
     goto done;  
     }  
      
     // Select the compatible bitmap into the compatible memory DC.  
     SelectObject(hdcMemDC, hbmScreen);  
      
     // Bit block transfer into our compatible memory DC.  
     if (!BitBlt(hdcMemDC,  
     0, 0,  
     rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,  
     hdcWindow,  
     0, 0,  
     SRCCOPY | CAPTUREBLT))  
     {  
     MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);  
     goto done;  
     }  
      
     // Get the BITMAP from the HBITMAP.  
     GetObject(hbmScreen, sizeof(BITMAP), &bmpScreen);  
      
     BITMAPFILEHEADER   bmfHeader;  
     BITMAPINFOHEADER   bi;  
      
     bi.biSize = sizeof(BITMAPINFOHEADER);  
     bi.biWidth = bmpScreen.bmWidth;  
     bi.biHeight = bmpScreen.bmHeight;  
     bi.biPlanes = 1;  
     bi.biBitCount = 32;  
     bi.biCompression = BI_RGB;  
     bi.biSizeImage = 0;  
     bi.biXPelsPerMeter = 0;  
     bi.biYPelsPerMeter = 0;  
     bi.biClrUsed = 0;  
     bi.biClrImportant = 0;  
      
     dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;  
      
     // Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that   
     // call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc   
     // have greater overhead than HeapAlloc.  
     hDIB = GlobalAlloc(GHND, dwBmpSize);  
     lpbitmap = (char*)GlobalLock(hDIB);  
      
     // Gets the "bits" from the bitmap, and copies them into a buffer   
     // that's pointed to by lpbitmap.  
     GetDIBits(hdcWindow, hbmScreen, 0,  
     (UINT)bmpScreen.bmHeight,  
     lpbitmap,  
     (BITMAPINFO*)&bi, DIB_RGB_COLORS);  
      
     // A file is created, this is where we will save the screen capture.  
     hFile = CreateFile(L"captureqwsx.png",  
     GENERIC_WRITE,  
     0,  
     NULL,  
     CREATE_ALWAYS,  
     FILE_ATTRIBUTE_NORMAL, NULL);  
      
     // Add the size of the headers to the size of the bitmap to get the total file size.  
     dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);  
      
     // Offset to where the actual bitmap bits start.  
     bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);  
      
     // Size of the file.  
     bmfHeader.bfSize = dwSizeofDIB;  
      
     // bfType must always be BM for Bitmaps.  
     bmfHeader.bfType = 0x4D42; // BM.  
      
     WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);  
     WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);  
     WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);  
      
     // Unlock and Free the DIB from the heap.  
     GlobalUnlock(hDIB);  
     GlobalFree(hDIB);  
      
     // Close the handle for the file that was created.  
     CloseHandle(hFile);  
      
     // Clean up.  
    done:  
     DeleteObject(hbmScreen);  
     DeleteObject(hdcMemDC);  
     ReleaseDC(NULL, hdcScreen);  
     ReleaseDC(hWnd, hdcWindow);  
      
     return 0;  
    }  
      
      
              
              
    int _tmain(int argc, _TCHAR* argv[])  
    {  
                  
        HWND hWnd = FindWindow(NULL, L"Untitled - Notepad");  
                  
                  
        DWORD exstyle = GetWindowLong(hWnd, GWL_EXSTYLE);  
        SetWindowLong(hWnd, GWL_EXSTYLE, exstyle | WS_EX_LAYERED);  
        SetLayeredWindowAttributes(hWnd, 0, 100, LWA_ALPHA);  
                  
        CaptureAnImage(hWnd);  
                  
    }  
    

  3. Castorix31 81,446 Reputation points
    2022-01-22T09:38:33.77+00:00

    I tested with DwmRegisterThumbnail and it detects transparent regions
    For example, the third PictureBox in this Form with 3 PictureBoxes is transparent, then I set background to light green and the Picture Box rectangle is drawn with transparency in light green
    (then I used D3DXSaveSurfaceToFileInMemory to get the HBITMAP)

    167336-dwmregisterthumbnail.jpg


  4. Junjie Zhu - MSFT 14,136 Reputation points Microsoft Vendor
    2022-01-26T03:08:13.867+00:00

    I used GetSystemMetrics(SM_CYCAPTION) to get the title bar height and got a full screenshot. I made these changes in the following code.
    based on code sample: https://learn.microsoft.com/en-us/windows/win32/gdi/capturing-an-image#code-example
    In the screenshot, the transparent area of the original window turned green area.

    int CaptureAnImage(HWND hWnd)  
    {  
    HDC hdcScreen;  
    HDC hdcWindow;  
    HDC hdcMemDC = NULL;  
    HBITMAP hbmScreen = NULL;  
    BITMAP bmpScreen;  
    DWORD dwBytesWritten = 0;  
    DWORD dwSizeofDIB = 0;  
    HANDLE hFile = NULL;  
    char* lpbitmap = NULL;  
    HANDLE hDIB = NULL;  
    DWORD dwBmpSize = 0;  
      
    // Retrieve the handle to a display device context for the client   
    // area of the window.   
    HWND mhWnd = FindWindow(NULL, L"GifCam 1000x639");  
    hdcScreen = GetDC(mhWnd);  
    hdcWindow = GetDC(hWnd);  
      
    // Create a compatible DC, which is used in a BitBlt from the window DC.  
    hdcMemDC = CreateCompatibleDC(hdcWindow);  
      
    if (!hdcMemDC)  
    {  
    MessageBox(hWnd, L"CreateCompatibleDC has failed", L"Failed", MB_OK);  
    goto done;  
    }  
      
    // Get the client area for size calculation.  
    RECT rcClient;  
    GetClientRect(hWnd, &rcClient);  
    RECT rcSrcWnd;  
    GetClientRect(mhWnd, &rcSrcWnd);  
    // This is the best stretch mode.  
    SetStretchBltMode(hdcWindow, HALFTONE);  
      
    // The source DC is the entire screen, and the destination DC is the current window (HWND).  
    if (!StretchBlt(hdcWindow,  
    rcClient.left, rcClient.top,  
    rcClient.right, rcClient.bottom,  
    hdcScreen,  
    0, 0-2 * GetSystemMetrics(SM_CYCAPTION),  
    rcSrcWnd.right- rcSrcWnd.left, rcSrcWnd.bottom- rcSrcWnd.top,  
    /*GetSystemMetrics(SM_CXSCREEN),  
    GetSystemMetrics(SM_CYSCREEN),*/  
    SRCCOPY))  
    {  
    MessageBox(hWnd, L"StretchBlt has failed", L"Failed", MB_OK);  
    goto done;  
    }  
      
    // Create a compatible bitmap from the Window DC.  
    hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);  
      
    if (!hbmScreen)  
    {  
    MessageBox(hWnd, L"CreateCompatibleBitmap Failed", L"Failed", MB_OK);  
    goto done;  
    }  
      
    // Select the compatible bitmap into the compatible memory DC.  
    SelectObject(hdcMemDC, hbmScreen);  
      
    // Bit block transfer into our compatible memory DC.  
    if (!BitBlt(hdcMemDC,  
    0, 0,  
    rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,  
    hdcWindow,  
    0, 0,  
    SRCCOPY))  
    {  
    MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);  
    goto done;  
    }  
      
    // Get the BITMAP from the HBITMAP.  
    GetObject(hbmScreen, sizeof(BITMAP), &bmpScreen);  
      
    BITMAPFILEHEADER   bmfHeader;  
    BITMAPINFOHEADER   bi;  
      
    bi.biSize = sizeof(BITMAPINFOHEADER);  
    bi.biWidth = bmpScreen.bmWidth;  
    bi.biHeight = bmpScreen.bmHeight;  
    bi.biPlanes = 1;  
    bi.biBitCount = 32;  
    bi.biCompression = BI_RGB;  
    bi.biSizeImage = 0;  
    bi.biXPelsPerMeter = 0;  
    bi.biYPelsPerMeter = 0;  
    bi.biClrUsed = 0;  
    bi.biClrImportant = 0;  
      
    dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;  
      
    // Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that   
    // call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc   
    // have greater overhead than HeapAlloc.  
    hDIB = GlobalAlloc(GHND, dwBmpSize);  
    lpbitmap = (char*)GlobalLock(hDIB);  
      
    // Gets the "bits" from the bitmap, and copies them into a buffer   
    // that's pointed to by lpbitmap.  
    GetDIBits(hdcWindow, hbmScreen, 0,  
    (UINT)bmpScreen.bmHeight,  
    lpbitmap,  
    (BITMAPINFO*)& bi, DIB_RGB_COLORS);  
      
    // A file is created, this is where we will save the screen capture.  
    hFile = CreateFile(L"captureqwsx.bmp",  
    GENERIC_WRITE,  
    0,  
    NULL,  
    CREATE_ALWAYS,  
    FILE_ATTRIBUTE_NORMAL, NULL);  
      
    // Add the size of the headers to the size of the bitmap to get the total file size.  
    dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);  
      
    // Offset to where the actual bitmap bits start.  
    bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);  
      
    // Size of the file.  
    bmfHeader.bfSize = dwSizeofDIB;  
      
    // bfType must always be BM for Bitmaps.  
    bmfHeader.bfType = 0x4D42; // BM.  
      
    WriteFile(hFile, (LPSTR)& bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);  
    WriteFile(hFile, (LPSTR)& bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);  
    WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);  
      
    // Unlock and Free the DIB from the heap.  
    GlobalUnlock(hDIB);  
    GlobalFree(hDIB);  
      
    // Close the handle for the file that was created.  
    CloseHandle(hFile);  
      
    // Clean up.  
    done:  
    DeleteObject(hbmScreen);  
    DeleteObject(hdcMemDC);  
    ReleaseDC(NULL, hdcScreen);  
    ReleaseDC(hWnd, hdcWindow);  
      
    return 0;  
    }