4

I found this code on the web to print the screen (take a screenshot), but I don't know how to modify it to save the results to a PNG file.

I can save a bitmap to the clipboard but I need save to PNG file now.

  • Is it possible to extract a bitmap from the clipboard and save it as a PNG file?
  • Can this be done differently?
  • If so, how?

My code so far is:

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

 using namespace std;
 using namespace Gdiplus;
 using namespace Gdiplus::DllExports;
 using std::runtime_error;

void screenshot(POINT a, POINT b)
{

    HDC     hScreen = GetDC(NULL);
    HDC     hDc     = CreateCompatibleDC(hScreen);
    HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, abs(b.x-a.x), abs(b.y-a.y));
    HGDIOBJ old_obj = SelectObject(hDc, hBitmap);
    BOOL    bRet    =  BitBlt(hDc, 0, 0, abs(b.x-a.y), abs(b.y-a.y), hScreen, a.x, a.y, SRCCOPY);

    OpenClipboard(NULL);
    EmptyClipboard();
    SetClipboardData(CF_BITMAP, hBitmap);
    CloseClipboard();


    SelectObject(hDc, old_obj);
    DeleteDC(hDc);
    ReleaseDC(NULL, hScreen);
    DeleteObject(hBitmap);
}

int main()
{
    POINT a,b;
    a.x=386;
    a.y=749;

    b.x=686;
    b.y=1049;

    screenshot(a,b);
}

Link - https://causeyourestuck.io/2016/01/12/screenshot-c-win32-api/ Author Omar AFLAK

F.Fipoo
  • 111
  • 1
  • 4
  • 12
  • ... You can stop repeating that you want to save things to a PNG file and want help with that. You must've said that ten times now, and saying it again won't make your question any more precise! – Marcus Müller Sep 09 '17 at 15:23
  • I reopened this question because it is *much* improved over its original version, but you still really need to provide [attribution](https://stackoverflow.com/help/referencing) for the source of the code you found "on the web". A link to the website and the author's name are a minimum requirement. – Cody Gray - on strike Sep 09 '17 at 15:25
  • 2
    As far as the answer to your question, you can find it [here](https://stackoverflow.com/questions/39551863/creating-gdi-bitmaps-in-memory-and-then-saving-as-png), although that question isn't really a duplicate of this one. GDI+ can write out directly to a PNG file if you provide it with the appropriate encoder class ID. See also: https://stackoverflow.com/questions/5345803/does-gdi-have-standard-image-encoder-clsids – Cody Gray - on strike Sep 09 '17 at 15:27

1 Answers1

6

First, include the library for Gdiplus. In Visual Studio you can use the #pragam keyword or add the Gdiplus.lib in project settings.

Next, initialize Gdiplus with Gdiplus::GdiplusStartup

Find the encoder CLSID as noted in earlier.

In your code you have abs(b.x-a.y), presumably that's supposed to be abs(b.x-a.x). This logic may not work if a is supposed to be the starting point. Just make sure POINT b is greater than POINT a. Or read carefully the documentation of BitBlt

#include <Windows.h>
#include "gdiplus.h"

//Visual Studio shortcut for adding library:
#pragma comment(lib, "Gdiplus.lib")

int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
    UINT  num = 0;          // number of image encoders
    UINT  size = 0;         // size of the image encoder array in bytes

    Gdiplus::GetImageEncodersSize(&num, &size);
    if(size == 0)
        return -1;  // Failure

    Gdiplus::ImageCodecInfo* pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
    if(pImageCodecInfo == NULL)
        return -1;  // Failure

    GetImageEncoders(num, size, pImageCodecInfo);

    for(UINT j = 0; j < num; ++j)
    {
        if(wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
        {
            *pClsid = pImageCodecInfo[j].Clsid;
            free(pImageCodecInfo);
            return j;  // Success
        }
    }

    free(pImageCodecInfo);
    return -1;  // Failure
}

void screenshot(POINT a, POINT b)
{
    int w = b.x - a.x;
    int h = b.y - a.y;

    if(w <= 0) return;
    if(h <= 0) return;

    HDC     hScreen = GetDC(HWND_DESKTOP);
    HDC     hDc = CreateCompatibleDC(hScreen);
    HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, w, h);
    HGDIOBJ old_obj = SelectObject(hDc, hBitmap);
    BitBlt(hDc, 0, 0, w, h, hScreen, a.x, a.y, SRCCOPY);

    Gdiplus::Bitmap bitmap(hBitmap, NULL);
    CLSID clsid;

    GetEncoderClsid(L"image/png", &clsid);

    //GDI+ expects Unicode filenames
    bitmap.Save(L"c:\\test\\test.png", &clsid);

    SelectObject(hDc, old_obj);
    DeleteDC(hDc);
    ReleaseDC(HWND_DESKTOP, hScreen);
    DeleteObject(hBitmap);
}

int main()
{
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    RECT      rc;
    GetClientRect(GetDesktopWindow(), &rc);
    POINT a{ 0, 0 };
    POINT b{ 100, 100 };

    screenshot(a, b);

    Gdiplus::GdiplusShutdown(gdiplusToken);

    return 0;
}
Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
  • 1
    throws me c2731 'winmain' function can not be overloaded. How to fix it? – F.Fipoo Sep 10 '17 at 11:38
  • 1
    You showed `main` entry point in your code earlier. If you mix `main` with `wWinMain` or `WinMain` you get an error. GDI+ functions need Unicode input/output for text transfer. – Barmak Shemirani Sep 10 '17 at 15:25
  • @F.Fipoo as implied by Barmak Shemirani, you need to go to your project settings if using Visual Studio and change `General->Project Defaults->Character set` to `Use Unicode Character Set` and that should un-break compilation. It defaults to `Use Multi-Byte Character Set` – kayleeFrye_onDeck Jun 20 '18 at 04:03
  • @BarmakShemirani may I make a suggestion to improve this answer? Using something like `int wmain(int argc, wchar_t * argv[])` is probably going to cause less confusion, as well as mentioning within the answer that the character set should be set to Unicode. – kayleeFrye_onDeck Jun 20 '18 at 04:10
  • 1
    @kayleeFrye_onDeck I just changed to `main` since `wmain` is Visual Studio specific. – Barmak Shemirani Jun 20 '18 at 07:37
  • Works fine but note: The `C:\test` folder has to exist – BullyWiiPlaza Apr 18 '19 at 22:15
  • 2
    `bitmap.Save(L"c:\\test\\test.png", &clsid, NULL);` for mingw. – Aikon Mogwai May 08 '21 at 07:42