An increasing number of developers work using several monitors simultaneously. But a multi-monitor system entails additional overhead when taking screenshots.
There are many tools for taking single- and multi-monitor screenshots on Windows systems. However, if you need to implement this feature in your application, here is how you can do this.
In this article, we discuss how to create a screenshot with two or more monitors in order to generate a single image and how to splice screenshots from several displays into one virtual screen-size bitmap using the Windows Graphics Device Interface functions, which are a group of WinAPI functions for working with graphics.
We’ll start with describing the process of taking screenshots of all monitors in multi-monitor mode and locating them in one bitmap programmatically, keeping the arrangement of monitors on the virtual screen. For these purposes, we’ll use the Windows GDI functions. But first, let’s explore the nature of virtual screens.
A virtual screen is a bounding rectangle that contains the images of several monitors. When you work in multi-monitor mode, the desktop covers the virtual screen instead of a single monitor.
The following figure shows a possible arrangement of three monitors into a single virtual screen:
The primary monitor (Monitor 1) contains the origin (0,0). If the primary monitor is not in the upper left corner of the virtual screen, parts of the virtual screen will have negative coordinates. As the monitor arrangement is set by the user, developers should make their applications able to work with negative coordinates.
Now let’s see how you can efficiently take a screenshot of the primary monitor with the help of Windows GDI functions.
Taking a screenshot of the primary monitor is a well-known task that has a rather simple implementation. But let’s take a look at it anyway, since we’ll need to do this later when we take screenshots of all monitors.
To capture the primary monitor, we execute the Windows GDI function CaptureDesktop:
This function assumes we already have a handle to monitor the device context (DC) — CDCGuard &desktopGuard in the parameters list. To get the device context for the entire screen, we use the GetDC function:
Other handles (captureGuard and bmpGuard) are out parameters. We’ll use them for creating the bitmap file with the BITMAPFILEHEADER property.
CDCGuard is a class-wrapper that deletes the handle to the device context in its destructor:
CBitmapGuard is also a class wrapper and has a similar implementation, but it deletes the HBITMAP object in its destructor:
The final function for capturing a screenshot of the primary monitor looks like this:
Now that you know how to take a screenshot of just one monitor, let’s take a look at how to take a screenshot with two monitors.
To make a single screen capture with dual monitors, we have to get the DC from each monitor on the virtual screen, then capture its contents. To do this, take the following steps:
- Enumerate monitors using the EnumDisplayMonitors function.
- Take a screenshot of each enumerated monitor using the CaptureDesktop function.
- Splice the screenshots of all monitors into a single virtual screen-sized GDI bitmap.
The declaration of the EnumDisplayMonitors Windows GDI function is the following:
In the code above, LPARAM dwData is a pointer to the class encapsulating the list of pairs of handles to the monitors’ display contexts and corresponding coordinates (the RECT structure):
The EnumDisplayMonitors function is called in its constructor. The CDisplayHandlesPool class provides the EnumDisplayMonitors method with the context for the discovered data storage with the help of HDCPoolType for the purposes of safety and convenience:
In the code above,
MonitorEnumProc is a callback function:
Now, all we need to do is merge the captures of all monitors into a single virtual screen-sized bitmap. To create the Windows GDI bitmap, the SpliceImages function follows the same algorithm as the CaptureDesktop function. Then we have to copy data from the defined DC to the same DC of the virtual screen using the BitBlt function.
An example arrangement of monitors on a virtual screen is presented below. Monitor 2 has a negative top coordinate value. By calculating the y coordinate shift, we can locate the captures of the monitors, keeping the locations of windows and desktops on the bitmap.
Possible arrangement of displays:
As a result, we get the following screenshot:
In this article, we showed how to take multi-monitor screenshots with WinAPI GDI functions. We also provided a detailed template code here that you can use for taking multi-monitor screenshots in your own solution.
At Apriorit, we have experienced teams of software developers that are ready to help you build a product in C, C++, and other programming languages. Contact us to bring your ideas to life.