1

I tried to follow the example found: Capturing an image from a minimized window

My code:

#include <iostream>
#include <string>
#include <windows.h>
#include <gdiplus.h>

#pragma comment(lib, "gdiplus.lib")
#pragma warning(disable : 4996)

using namespace std;
using namespace Gdiplus;

int GetEncoderClsid(LPCWSTR format, CLSID* pClsid)
{
    unsigned int num = 0, size = 0;
    GetImageEncodersSize(&num, &size);
    if (size == 0) return -1;
    ImageCodecInfo* pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
    if (pImageCodecInfo == NULL) return -1;
    GetImageEncoders(num, size, pImageCodecInfo);

    for (unsigned int j = 0; j < num; ++j) {
        if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) {
            *pClsid = pImageCodecInfo[j].Clsid;
            free(pImageCodecInfo);
            return j;
        }
    }
    free(pImageCodecInfo);
    return -1;
}

int SaveScreenshot(string filename, ULONG uQuality, HWND hwnd) // by Napalm
{
    ULONG_PTR gdiplusToken;
    GdiplusStartupInput gdiplusStartupInput;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    HWND hMyWnd = hwnd;
    //HWND hMyWnd = GetDesktopWindow();

    RECT r;
    int w, h;
    HDC dc, hdcCapture;
    int nBPP, nCapture, iRes;
    LPBYTE lpCapture;
    CLSID imageCLSID;
    Bitmap* pScreenShot;

    // get the area of my application's window    
    GetWindowRect(hMyWnd, &r);
    dc = GetWindowDC(hMyWnd);   // GetDC(hMyWnd) ;
    w = r.right - r.left;
    h = r.bottom - r.top;
    nBPP = GetDeviceCaps(dc, BITSPIXEL);
    hdcCapture = CreateCompatibleDC(dc);

    // create the buffer for the screenshot
    BITMAPINFO bmiCapture = { sizeof(BITMAPINFOHEADER), w, -h, 1, nBPP, BI_RGB, 0, 0, 0, 0, 0, };

    // create a container and take the screenshot
    HBITMAP hbmCapture = CreateDIBSection(dc, &bmiCapture, DIB_PAL_COLORS, (LPVOID*)&lpCapture, NULL, 0);

    // failed to take it
    if (!hbmCapture) {
        DeleteDC(hdcCapture);
        DeleteDC(dc);
        GdiplusShutdown(gdiplusToken);
        printf("failed to take the screenshot. err: %d\n", GetLastError());
        return 0;
    }

    // copy the screenshot buffer
    nCapture = SaveDC(hdcCapture);
    SelectObject(hdcCapture, hbmCapture);
    BitBlt(hdcCapture, 0, 0, w, h, dc, 0, 0, SRCCOPY);
    RestoreDC(hdcCapture, nCapture);
    DeleteDC(hdcCapture);
    DeleteDC(dc);

    // save the buffer to a file  
    pScreenShot = new Bitmap(hbmCapture, (HPALETTE)NULL);
    EncoderParameters encoderParams;
    encoderParams.Count = 1;
    encoderParams.Parameter[0].NumberOfValues = 1;
    encoderParams.Parameter[0].Guid = EncoderQuality;
    encoderParams.Parameter[0].Type = EncoderParameterValueTypeLong;
    encoderParams.Parameter[0].Value = &uQuality;
    GetEncoderClsid(L"image/jpeg", &imageCLSID);

    wchar_t* lpszFilename = new wchar_t[filename.length() + 1];
    mbstowcs(lpszFilename, filename.c_str(), filename.length() + 1);

    iRes = (pScreenShot->Save(lpszFilename, &imageCLSID, &encoderParams) == Ok);
    delete pScreenShot;
    DeleteObject(hbmCapture);
    GdiplusShutdown(gdiplusToken);
    return iRes;
}

int main() {
    HWND hWnd;
    hWnd = FindWindowA(NULL, "txt.txt - Bloco de Notas");

    WINDOWPLACEMENT wp = { 0 };
    wp.length = sizeof(WINDOWPLACEMENT);
    GetWindowPlacement(hWnd, &wp);

    ANIMATIONINFO ai = { 0 };
    bool restoreAnimated = false;

    if (wp.showCmd == SW_SHOWMINIMIZED)
    {
        ai.cbSize = sizeof(ANIMATIONINFO);
        SystemParametersInfo(SPI_GETANIMATION, sizeof(ANIMATIONINFO), &ai, 0);

        if (ai.iMinAnimate != 0)
        {
            ai.iMinAnimate = 0;
            SystemParametersInfo(SPI_SETANIMATION, sizeof(ANIMATIONINFO), &ai, 0);
            restoreAnimated = true;
        }

        // optionally move the window off-screen, or
        // apply alpha using SetLayeredWindowAttributes()...

        ShowWindow(hWnd, SW_SHOWNOACTIVATE);
    }
    
    // capture as needed ...
    string path = "C:\\Users\\CAIO\\Desktop\\screenshot.jpg";
    ULONG quality = 100;
    SaveScreenshot(path, quality, hWnd);

    if (wp.showCmd == SW_SHOWMINIMIZED)
    {
        SetWindowPlacement(hWnd, &wp);

        // optionally remove alpha using SetLayeredWindowAttributes()...

        if (restoreAnimated)
        {
            ai.iMinAnimate = 1;
            SystemParametersInfo(SPI_SETANIMATION, sizeof(ANIMATIONINFO), &ai, 0);
        }
    }

    return 0;
}

In their code there's restoreAnimation = true; and if (restoreAnimation) did they mean restoreAnimated?

With the code above, it still shows the window for a second, also the captured image is all black (maybe captured while minimized) or is not captured properly.

https://youtu.be/8b1wXxtaXsY?t=9 Seconds 8 and 9 from the video, you can see the window is shown on the screen. Suggestions?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197

1 Answers1

1

did they mean restoreAnimated?

I think yes.

This code from @Remy only disables the animation effects when minimizing and restoring, the window will still be displayed for a short time when restoring. As the answer pointed out, you can use SetLayeredWindowAttributes to make the window nearly completely transparent (It should be noted that using SetLayeredWindowAttributes needs to ensure that hwnd has WS_EX_LAYERED style.) and then you will not see the window, but the operating system will.

In addition, the problem of incomplete window capture is because the window is not drawn completely, call UpdateWindow after ShowWindow works for me, it will send a WM_PAINT message to the window.

bool restoreAnimated = false;
BYTE Alph = 0;
LONG_PTR exstyle = 0;
if (wp.showCmd == SW_SHOWMINIMIZED)
{
    ai.cbSize = sizeof(ANIMATIONINFO);
    SystemParametersInfo(SPI_GETANIMATION, sizeof(ANIMATIONINFO), &ai, 0);

    if (ai.iMinAnimate != 0)
    {
        ai.iMinAnimate = 0;
        SystemParametersInfo(SPI_SETANIMATION, sizeof(ANIMATIONINFO), &ai, 0);
        restoreAnimated = true;
    }

    // optionally move the window off-screen, or
    // apply alpha using SetLayeredWindowAttributes()...
    exstyle = GetWindowLongPtr(hWnd, GWL_EXSTYLE);
    SetWindowLongPtr(hWnd, GWL_EXSTYLE, exstyle | WS_EX_LAYERED);
    DWORD flag;
    BOOL ret = GetLayeredWindowAttributes(hWnd, 0, &Alph, 0);
    SetLayeredWindowAttributes(hWnd, 0, 1, LWA_ALPHA);

    ShowWindow(hWnd, SW_SHOWNOACTIVATE);
    UpdateWindow(hWnd);
}

// capture as needed ...
string path = "C:\\Users\\name\\Desktop\\screenshot.jpg";
ULONG quality = 100;
SaveScreenshot(path, quality, hWnd);

if (wp.showCmd == SW_SHOWMINIMIZED)
{
    SetWindowPlacement(hWnd, &wp);

    // optionally remove alpha using SetLayeredWindowAttributes()...
    SetLayeredWindowAttributes(hWnd, 0, Alph, LWA_ALPHA);
    SetWindowLongPtr(hWnd, GWL_EXSTYLE, exstyle);

    if (restoreAnimated)
    {
        ai.iMinAnimate = 1;
        SystemParametersInfo(SPI_SETANIMATION, sizeof(ANIMATIONINFO), &ai, 0);
    }
    
}
Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
Drake Wu
  • 6,927
  • 1
  • 7
  • 30
  • Thank for your help, the window I'm trying to capture did not get transparent with `SetLayeredWindowAttributes(hWnd, NULL, Alph, LWA_ALPHA);`, what else I could try? and can someone explain why my question got closed? "This question needs to be more focused." what are not clear in my question? there have even a video..... –  Nov 23 '20 at 19:53
  • Have you added the `WS_EX_LAYERED` style to the hWnd? The sample works for me on win10 with a notepad window. Did you get any error when calling `SetLayeredWindowAttributes` or any other functions? – Drake Wu Nov 24 '20 at 01:18