How can I take a screenshot of the current screen using Win32?
Asked
Active
Viewed 7.9k times
57
-
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
5 Answers
67
// get the device context of the screen
HDC hScreenDC = CreateDC("DISPLAY", NULL, NULL, NULL);
// and a device context to put it in
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
int width = GetDeviceCaps(hScreenDC, HORZRES);
int height = GetDeviceCaps(hScreenDC, VERTRES);
// maybe worth checking these are positive values
HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);
// get a new bitmap
HBITMAP hOldBitmap = (HBITMAP) SelectObject(hMemoryDC, hBitmap);
BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
hBitmap = (HBITMAP) SelectObject(hMemoryDC, hOldBitmap);
// clean up
DeleteDC(hMemoryDC);
DeleteDC(hScreenDC);
// now your image is held in hBitmap. You can save it or do whatever with it
-
-
7
-
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
-
10This 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 - Reinstate Monica 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 - Reinstate Monica 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
30
- 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)
![](../../users/profiles/12597.webp)
Ian Boyd
- 220,884
- 228
- 805
- 1,125
![](../../users/profiles/179910.webp)
Jerry Coffin
- 437,173
- 71
- 570
- 1,035
-
2
-
What's the difference between DC compatible with screen vs compatible with the other compatible DC? – Ayxan Haqverdili 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
28
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);
}
![](../../users/profiles/645583.webp)
pcunite
- 1,147
- 13
- 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
7
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);
WriteFile(hFile, &bfHeader, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
WriteFile(hFile, &biHeader, sizeof(BITMAPINFOHEADER), &dwWritten, NULL);
WriteFile(hFile, bBits, cbBits, &dwWritten, NULL);
CloseHandle(hFile);
DeleteDC(hMemDC);
ReleaseDC(NULL, hDC);
DeleteObject(hBitmap);
return TRUE;
}
![](../../users/profiles/1773434.webp)
Govind Parmar
- 18,500
- 6
- 49
- 78
-
@MartinPrikryl I removed the manual allocation of bBits because at the time of posting I didn’t realize that CreateDIBSection allocates it for you – Govind Parmar Jun 22 '19 at 12:45
-
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
-
This code does not work correctly when there is screen to the left of main/primary screen. Saved bitmap is correct size but display on the left of main screen is not captured. We have black area on the right instead. – Wojciech Jakubas Oct 25 '19 at 17:20
-
To make it work just do this: Add these lines before BitBlt... code. `int x = GetSystemMetrics(SM_XVIRTUALSCREEN); int y = GetSystemMetrics(SM_YVIRTUALSCREEN);` And then replace BitBlt line with this one: `BitBlt(hMemDC, 0, 0, lWidth, lHeight, hDC, x, y, SRCCOPY);` – Wojciech Jakubas Oct 25 '19 at 17:31
-
-
@GovindParmar, and what if I wanted to create JPEG compressed file? I tried changing `biHeader.biCompression` to `BI_JPEG` but it fails. – Amogh Feb 15 '20 at 11:30
-
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
-
@Amogh Obviously changing the image format requires more than changing a simple value in a struct. You're still writing the bitmap file header and bits. This might be a valid topic for a new question. – Govind Parmar Nov 08 '20 at 20:45
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.