How can I take a screenshot of the current screen using Win32?
Asked
Active
Viewed 1e+01k times
62
-
2Various methods for capturing the screen http://www.codeproject.com/Articles/5051/Various-methods-for-capturing-the-screen – hB0 Jul 06 '12 at 11:44
-
1Here's my compilable gist: https://gist.github.com/rdp/9821698 – rogerdpack Mar 27 '14 at 23:45
-
Press `Prt-scr` on your keyboard ;) – Jesper Juhl Apr 17 '23 at 12:00
5 Answers
72
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);
-
-
10
-
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. – Woody Jul 21 '10 at 08:04
-
12This 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. – ozdrgnaDiies Jan 03 '13 at 07:21
-
2@ozdrgnaDiies I've attempted to fix the lack of variable declarations and missing semicolon (edit awaiting peer review). Which are the non-matching data types? – JBentley Mar 21 '13 at 21:01
-
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. – Adrian McCarthy Jan 10 '14 at 22:53
-
@Woody for some reason, this gives me black screen for some games, like League of legends. Are there any workarounds? – Tomáš Zato Mar 23 '15 at 20:57
-
@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 – Woody Mar 26 '15 at 10:03
-
@Woody Any idea what to google for? I'm really lost with this, I don't even know where to start... – Tomáš Zato Mar 26 '15 at 11:21
-
1@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: http://stackoverflow.com/questions/23898877/opengl-game-screen-capture – Woody Mar 26 '15 at 12:27
-
-
On win 10 I have a memory leak with this example. The example of pcunite works fine. – Ilyssis Feb 16 '17 at 20:34
-
Windows documentation for DeleteDC says that you shall call ReleaseDC instead of DeleteDC for a device context handle obtained by GetDC. (https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-deletedc) – Holger Strauss Mar 23 '23 at 11:08
34
- 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)

Ian Boyd
- 246,734
- 253
- 869
- 1,219

Jerry Coffin
- 476,176
- 80
- 629
- 1,111
-
3
-
What's the difference between DC compatible with screen vs compatible with the other compatible DC? – Aykhan Hagverdili May 21 '20 at 16:43
-
1@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. – Jerry Coffin May 21 '20 at 16:56
29
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);
}

pcunite
- 1,197
- 15
- 23
-
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](https://msdn.microsoft.com/en-us/library/windows/desktop/ms724385(v=vs.85).aspx) – Kevin May 13 '18 at 04:05
8
Full code for saving a raw 24-bit lossless 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;
}

Govind Parmar
- 20,656
- 7
- 53
- 85
-
OK, I see! Though it probably does not allocate anything (were it, you would have to free it). – Martin Prikryl Jun 22 '19 at 13:59
-
Can I ask why we can't just call GetSystemMetrics(SM_CXVIRTUALSCREEN) and GetSystemMetrics(SM_CYVIRTUALSCREEN) to get lWidth and lHeight? @GovindParmar – Rick May 02 '20 at 15:41
-
1
5
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.