Sofia
Sofia

Reputation: 55

Taking a screenshot in Windows will output a black screen image

I'm trying to take a full page screenshot in windows. function works in first call but after second call won't work at all and it's just getting a black screen image with a stable size. when i use debugger the function works well without giving the black screen.

Here is the code:

void screenshot(std::string imageaPath)
{
    ULONG_PTR gdiplustoken;
    Gdiplus::GdiplusStartupInput gdistartupinput;
    Gdiplus::GdiplusStartupOutput gdistartupoutput;

    gdistartupinput.SuppressBackgroundThread = true;
    GdiplusStartup(&gdiplustoken, &gdistartupinput, &gdistartupoutput); //start GDI+

    HDC hScreenDC = GetDC(GetDesktopWindow());
    HDC hMemoryDC = CreateCompatibleDC(hScreenDC);

    int cx = GetSystemMetrics(SM_CXVIRTUALSCREEN);
    int cy = GetSystemMetrics(SM_CYVIRTUALSCREEN);
    int x = GetSystemMetrics(SM_XVIRTUALSCREEN); 
    int y = GetSystemMetrics(SM_YVIRTUALSCREEN);

    HBITMAP hbitmap = CreateCompatibleBitmap(hScreenDC, cx, cy);
    HBITMAP holdbitmap = static_cast<HBITMAP>(SelectObject(hMemoryDC, hbitmap));

    BitBlt(hMemoryDC, 0, 0, cx, cy, hScreenDC, x, y, SRCCOPY | CAPTUREBLT);
    hbitmap = static_cast<HBITMAP>(SelectObject(hMemoryDC, holdbitmap));    

    UINT num, size;

    Gdiplus::ImageCodecInfo* imagecodecinfo;
    Gdiplus::GetImageEncodersSize(&num, &size); // get count of codec

    imagecodecinfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
    GetImageEncoders(num, size, imagecodecinfo);//get codec

    CLSID clsidEncoder;

    for (int i = 0; i < num; i++)
    {
        if (wcscmp(imagecodecinfo[i].MimeType, L"image/jpeg") == 0)
            clsidEncoder = imagecodecinfo[i].Clsid; // get jpeg codec id
    }

    free(imagecodecinfo);
    Gdiplus::Bitmap* bm = new Gdiplus::Bitmap(hbitmap, NULL);
    std::wstring ws;
    ws.assign(imageaPath.begin(), imageaPath.end());//sring to wstring
    bm->Save(ws.c_str(), &clsidEncoder); //save in jpeg format
    SelectObject(hMemoryDC, holdbitmap);//Release Objects
    DeleteObject(hMemoryDC);
    DeleteObject(hbitmap);
    ReleaseDC(GetDesktopWindow(), hScreenDC);

    Gdiplus::GdiplusShutdown(gdiplustoken);
}

update:

Okay i find a way to take a screenshot without black screen image when i use system("pause"); to make program stop and when press enter to make program continue, it's working, I used c++ sleep methods but not works, any idea?

...
HBITMAP holdbitmap = static_cast<HBITMAP>(SelectObject(hMemoryDC, hbitmap));

system("pause");

BitBlt(hMemoryDC, 0, 0, cx, cy, hScreenDC, x, y, SRCCOPY | CAPTUREBLT);
...

sleep methods:

Sleep(1000);
std::this_thread::sleep_for(std::chrono::seconds(1));

update 2:

I was sending screenshot request using curl and I was testing in a server(rdp) and I was logged out when i was sending request, I think sleep mode in server is enabled and when I logged out the server will be sleep and it's like computer screen to go dark and that's why BitBlt() fails and GetLastError() will return 5 which means access denied

Upvotes: 1

Views: 952

Answers (2)

Grey
Grey

Reputation: 23

you can use PrintWindow to replace BitBlt api, the nFlags of PrintWindow should set to be PW_RENDERFULLCONTENT, make sure you called this on a windows 8.1 and later os computer, it may be works, just have a try.

Upvotes: -1

Ben Voigt
Ben Voigt

Reputation: 283921

The documentation for GdiplusShutdown says that

You must call GdiplusStartup before you create any GDI+ objects, and you must delete all of your GDI+ objects (or have them go out of scope) before you call GdiplusShutdown.

You are leaking bm = new Gdiplus::Bitmap(...) which is violating this rule.

Upvotes: 6

Related Questions