How can I take a screenshot of the current screen using Win32?
How can I take a screenshot in a windows application?
Asked Answered
HDC hScreenDC = GetDC(nullptr); // CreateDC("DISPLAY",nullptr,nullptr,nullptr);
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
int width = GetDeviceCaps(hScreenDC,HORZRES);
int height = GetDeviceCaps(hScreenDC,VERTRES);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC,width,height);
HBITMAP hOldBitmap = static_cast<HBITMAP>(SelectObject(hMemoryDC,hBitmap));
BitBlt(hMemoryDC,0,0,width,height,hScreenDC,0,0,SRCCOPY);
hBitmap = static_cast<HBITMAP>(SelectObject(hMemoryDC,hOldBitmap));
DeleteDC(hMemoryDC);
DeleteDC(hScreenDC);
This works on all nt based windows from Windows NT4 to Windows 7. –
Apiary
Why are you using CreateDC and not just GetDC(NULL)? –
Charil
Honestly I haven't looked at it for a while, this is code from quite a way back which I have been using in an application. It works in everything so I have never gone back to it! If GetDC would be better, I can ammend the answer. –
Apiary
This isn't a good example at all for someone coming into this stuff. The variable aren't declared, the functions don't match the supposed data types, and there are simple syntax errors like missing semicolons. I cannot fix this as I am trying to learn this myself, but this really needs to be updated. –
Dowry
@Dowry I've attempted to fix the lack of variable declarations and missing semicolon (edit awaiting peer review). Which are the non-matching data types? –
Llamas
You might also want
CAPTUREBLT
in the BitBlt ROP parameter. It's necessary when capturing a window, but maybe not when capturing the entire screen. –
Koreykorff @Apiary for some reason, this gives me black screen for some games, like League of legends. Are there any workarounds? –
Renayrenckens
@TomášZato - I am afraid I don't know, I haven't tried, or had to do anything like that for many years. My guess would be that it is rendered into a 3d context outside the device context, but that is just a guess –
Apiary
@Apiary Any idea what to google for? I'm really lost with this, I don't even know where to start... –
Renayrenckens
@TomášZato - I really don't know, not something I have tried. I am not familiar with the game but have you tried something like this: https://mcmap.net/q/323445/-opengl-game-screen-capture –
Apiary
What is
width
and height
? I guess it's x
and y
? –
Kimber On win 10 I have a memory leak with this example. The example of pcunite works fine. –
Burne
Windows documentation for DeleteDC says that you shall call ReleaseDC instead of DeleteDC for a device context handle obtained by GetDC. (learn.microsoft.com/en-us/windows/win32/api/wingdi/…) –
Blood
- Use
GetDC(NULL);
to get a DC for the entire screen. - Use
CreateCompatibleDC
to create a DC compatible with the screen DC. - Use
CreateCompatibleBitmap
to create a bitmap compatible with the screen DC to hold the result. - Use
SelectObject
to select the compatible bitmap into the compatible DC. - Use
BitBlt
to copy from the screen DC to the compatible DC. - Use
SelectObject
to deselect the compatible bitmap from the compatible DC. - Use
DeleteDC
to delete the compatible DC.
When you create the compatible bitmap, you want it compatible with the screen DC, not the compatible DC.
For example:
HDC dcScreen = GetDC(0);
HDC dcTarget = CreateCompatibleDC(dcScreen);
HBITMAP bmpTarget = CreateCompatibleBitmap(dcScreen);
HGDIOBJ oldBmp = SelectObject(dcTarget, bmpTarget);
BitBlt(dcTarget, 0, 0, cx, cy, dcDesktop, x, y, SRCCOPY | CAPTUREBLT);
SelectObject(dcTarget, oldBmp);
DeleteDC(dcTarget);
ReleaseDC(dcScreen);
The other important part is to get the size, and location, of the entire virtual screen:
int x = GetSystemMetrics(SM_XVIRTUALSCREEN); //left (e.g. -1024)
int y = GetSystemMetrics(SM_YVIRTUALSCREEN); //top (e.g. -34)
int cx = GetSystemMetrics(SM_CXVIRTUALSCREEN); //entire width (e.g. 2704)
int cy = GetSystemMetrics(SM_CYVIRTUALSCREEN); //entire height (e.g. 1050)
What about dual display systems? Shot of both screens? –
Oenomel
What's the difference between DC compatible with screen vs compatible with the other compatible DC? –
Baloney
@Ayxan It's about the compatible bitmap, not the compatible DC. When you create a compatible DC, it contains the most "economical" bitmap possible--a 1x1 monochrome bitmap. If you create a bitmap compatible with that DC, you get a monochrome bitmap. –
Pennsylvanian
void GetScreenShot(void)
{
int x1, y1, x2, y2, w, h;
// get screen dimensions
x1 = GetSystemMetrics(SM_XVIRTUALSCREEN);
y1 = GetSystemMetrics(SM_YVIRTUALSCREEN);
x2 = GetSystemMetrics(SM_CXVIRTUALSCREEN);
y2 = GetSystemMetrics(SM_CYVIRTUALSCREEN);
w = x2 - x1;
h = y2 - y1;
// copy screen to bitmap
HDC hScreen = GetDC(NULL);
HDC hDC = CreateCompatibleDC(hScreen);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, w, h);
HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
BOOL bRet = BitBlt(hDC, 0, 0, w, h, hScreen, x1, y1, SRCCOPY);
// save bitmap to clipboard
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBitmap);
CloseClipboard();
// clean up
SelectObject(hDC, old_obj);
DeleteDC(hDC);
ReleaseDC(NULL, hScreen);
DeleteObject(hBitmap);
}
This example will not work correctly if there is a display above or to the left of the primary display, such that x1!=0 || x2!=0. This is because SM_CXVIRTUALSCREEN and SM_CYVIRTUALSCREEN are the size, not the bottom-right extents. Should just set w,h from them directly. See MSDN –
Nuthouse
Full code for saving a raw 24-bit bitmap of all monitors at the current window station using Windows API:
BOOL WINAPI SaveBitmap(WCHAR *wPath)
{
BITMAPFILEHEADER bfHeader;
BITMAPINFOHEADER biHeader;
BITMAPINFO bInfo;
HGDIOBJ hTempBitmap;
HBITMAP hBitmap;
BITMAP bAllDesktops;
HDC hDC, hMemDC;
LONG lWidth, lHeight;
BYTE *bBits = NULL;
HANDLE hHeap = GetProcessHeap();
DWORD cbBits, dwWritten = 0;
HANDLE hFile;
INT x = GetSystemMetrics(SM_XVIRTUALSCREEN);
INT y = GetSystemMetrics(SM_YVIRTUALSCREEN);
ZeroMemory(&bfHeader, sizeof(BITMAPFILEHEADER));
ZeroMemory(&biHeader, sizeof(BITMAPINFOHEADER));
ZeroMemory(&bInfo, sizeof(BITMAPINFO));
ZeroMemory(&bAllDesktops, sizeof(BITMAP));
hDC = GetDC(NULL);
hTempBitmap = GetCurrentObject(hDC, OBJ_BITMAP);
GetObjectW(hTempBitmap, sizeof(BITMAP), &bAllDesktops);
lWidth = bAllDesktops.bmWidth;
lHeight = bAllDesktops.bmHeight;
DeleteObject(hTempBitmap);
bfHeader.bfType = (WORD)('B' | ('M' << 8));
bfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
biHeader.biSize = sizeof(BITMAPINFOHEADER);
biHeader.biBitCount = 24;
biHeader.biCompression = BI_RGB;
biHeader.biPlanes = 1;
biHeader.biWidth = lWidth;
biHeader.biHeight = lHeight;
bInfo.bmiHeader = biHeader;
cbBits = (((24 * lWidth + 31)&~31) / 8) * lHeight;
hMemDC = CreateCompatibleDC(hDC);
hBitmap = CreateDIBSection(hDC, &bInfo, DIB_RGB_COLORS, (VOID **)&bBits, NULL, 0);
SelectObject(hMemDC, hBitmap);
BitBlt(hMemDC, 0, 0, lWidth, lHeight, hDC, x, y, SRCCOPY);
hFile = CreateFileW(wPath, GENERIC_WRITE | GENERIC_READ, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if(INVALID_HANDLE_VALUE == hFile)
{
DeleteDC(hMemDC);
ReleaseDC(NULL, hDC);
DeleteObject(hBitmap);
return FALSE;
}
WriteFile(hFile, &bfHeader, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
WriteFile(hFile, &biHeader, sizeof(BITMAPINFOHEADER), &dwWritten, NULL);
WriteFile(hFile, bBits, cbBits, &dwWritten, NULL);
FlushFileBuffers(hFile);
CloseHandle(hFile);
DeleteDC(hMemDC);
ReleaseDC(NULL, hDC);
DeleteObject(hBitmap);
return TRUE;
}
OK, I see! Though it probably does not allocate anything (were it, you would have to free it). –
Wenwenceslaus
Can I ask why we can't just call GetSystemMetrics(SM_CXVIRTUALSCREEN) and GetSystemMetrics(SM_CYVIRTUALSCREEN) to get lWidth and lHeight? @GovindParmar –
Pony
There is a MSDN sample, Capturing an Image, for capturing an arbitrary HWND to a DC (you could try passing the output from GetDesktopWindow to this). But how well this will work under the new desktop compositor on Vista/Windows 7, I don't know.
© 2022 - 2024 — McMap. All rights reserved.
Prt-scr
on your keyboard ;) – Iyar