Ways to capture the screen

Capturing the screen (or put another way, creating an in-memory copy of the image currently being displayed on the screen, for re-display, printing, or later saving) is a task with many different solutions.  I'll try to enumerate the possibilities below.  This isn't a perfect or complete list, but it covers the best supported ways, and lists some of their drawbacks.

1 - Use the Desktop Duplication API.  This is a very powerful and full-featured API, which provides access to every frame of desktop update, BUT, it is not available prior to Windows 8.

One drawback is that most full-screen exclusive mode DirectX or OpenGL applications will not be able to be captured with Desktop Duplication.  Exclusive mode really means that the ‘Windowed’ field of D3DPRESENT_PARAMETERS is set to false.  Some apps are not in true full-screen mode and are windowed, but with the window set to the size of the desktop.  These can still be captured.

To make matters a little more confusing, newer DirectX (11.1 and later) exclusive mode apps can be captured with the Desktop Duplication API, unless they "opt-out" and specifically disallow it (in which case they won't be capture-able by Desktop Duplication, just like a pre-11.1 DirectX app).

2 - The old standby, GDI, can still capture the screen.  Typically, BitBlt is used.  This technique is not perfect, and there are some things, such as multimedia output, that won't get captured.

BitBlt can also can be slow, although you can get much better performance by making sure that the bitmap (that you are capturing into) is the same resolution as the screen.  To assure this, you could use CreateCompatibleBitmap (for a device-dependent bitmap), or CreateDIBSection (for a device-independent bitmap).  If you use CreateDIBSection, just make sure the bit-depth and layout matches the screen's.  This allows BitBlt to avoid any color conversion, which can drastically slow everything down.  Here's what such code might look like from a high level...

// Pseudo-code...

HDC hDCScreen = GetDC(NULL);

HDC hDCMem = CreateCompatibleDC(hDCScreen);

HBITMAP hBitmap = CreateCompatibleBitmap(hDCScreen, screenwidth, screenheight);

SelectObject(hDCMem, hBitmap);

BitBlt(hDCMem, 0, 0, screenwidth, screenheight, 0, 0, SRCCOPY);

 

3 - Another option is to use Direct3D.  Note that some have reported that this can actually be slower than BitBlt, but performance of any technique will depend a lot on the hardware and graphics settings (regardless of the method used).  To do this with Direct3D, you could use GetRenderTargetData to copy the rendering surface (retrieved with GetRenderTarget) to an off-screen surface (which can be created with CreateOffscreenPlainSurface).  After copying the surface data this way, you can save the contents of the surface to a file using D3DXSaveSurfaceToFile.

 

4 - Yet another possibility is to use Windows Media technologies to do the capture (specifically, Expression Encoder will do this if you just need another application).  To do it programmatically yourself (using Windows Media), you can use the Windows Media Video 9 Screen codec.

 

5 - Finally, for pre-Windows 8 systems, you could develop a Mirror Driver to do this (Windows 8 uses another driver model, which is also described under the preceding link).

 

Some of the above techniques (particularly #2 and #3) can also be used to capture only a single window.  Note that iif you use BitBlt to do this, you could "or in" the CAPTUREBLT flag if you wanted to capture windows that are layered on top of your window .  The PrintWindow API is another good option for capturing only a single window (not listed above since it can't capture the entire screen).