We may know there are many methods to take a screenshot of the desktop. But sometime we may want to capture the desktop without some specified windows. If these windows are layered, we may use the BitBlt function without the
CAPTUREBLT flag when running on Windows XP. But nowadays we are running into Windows 7 or Windows 8, in which situation the
BitBlt function cannot perform this filter anymore. Therefore, we may want to exclude more non-layered windows. So we have to find a new method to do this.
In this sample, I will introduce a method to take a screenshot with the Magnification library. This library is available from Windows Vista so we cannot use it on Windows XP. But with many user switching from XP to a newer version of Windows, this will not be a huge problem.
The sample program contains only a Capture button. When the user clicks, it will take a screenshot and open a dialog for the user to specify a file name and save the captured image to bitmap file.
Using the Magnification library
The complete documentation of this library is available on MSDN.
According to the documentation, the screen capture process can be simply done by the following steps:
MagInitialize() function to initialize the magnification library.
- Create the host dialog with layered attribute, set it to full screen and invisible. We use MFC to create a dialog and use it as the host dialog. Because we use it to store the captured image,
but don't want to show it to the user, so we hide it from being displayed.
HWND hDesktop = ::GetDesktopWindow();
SetWindowPos(NULL, 0, 0, rect.right, rect.bottom, SWP_HIDEWINDOW);
GetWindowLong(this->GetSafeHwnd(), GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(0, 255, LWA_ALPHA);
- Create the magnification window as the child window of the host. Note
that the window class is
MagnifierWindow. If you want the mouse cursor to be captured, create the magnification window with
hwndMag = CreateWindow(WC_MAGNIFIER, TEXT("MagnifierWindow"),
WS_CHILD | WS_VISIBLE,
0, 0, m_ScreenX, m_ScreenY,
hostDlg->GetSafeHwnd(), NULL, hInstance, NULL );
- Call the
MagSetWindowFilterList() function to exclude specified windows from being captured. Because of this powerful function, this is the main reason we want to use magnification library. In this program, we filter the main dialog, but we may filter as much as we want.
pFilterList = new HWND;
pFilterList = this->GetSafeHwnd();
if (!MagSetWindowFilterList(hwndMag, MW_FILTERMODE_EXCLUDE, 1, pFilterList))
- Whenever the
MagSetWindowSource() function is called, the entire desktop is captured into the magnification window. In the above code, we set the host window invisible, but if we show the host window, we will see the desktop's image in it.
sourceRect.top = 0;
sourceRect.left = 0;
sourceRect.right = m_ScreenX;
sourceRect.bottom = m_ScreenY;
if (!MagSetWindowSource(hwndMag, sourceRect))
Saving the data
Normally, we could save the content of a window into a file, or copy its content into memory by using the BitBlt function. The main problem while saving the captured data by the magnification library is that we cannot access the bitmap of the host window or the magnification window with the BitBlt function as usual. So we use a work around by using the
MagSetImageScalingCallback() function, which is described as below.
- Call the
MagSetImageScalingCallback() function before performing the screenshot to set the callback function.
if (!MagSetImageScalingCallback(hwndMag, (MagImageScalingCallback)MagImageScaling))
- The callback function has the following syntax:
BOOL MagImageScaling(HWND hwnd, void *srcdata, MAGIMAGEHEADER srcheader,
void *destdata, MAGIMAGEHEADER destheader,
RECT unclipped, RECT clipped, HRGN dirty)
srcdata parameter is the pointer to the bitmap source, and the
srcheader contains the information of the captured image, so we can use these two parameters to create the
BITMAPINFOHEADER and the
BITMAPINFOHEADER can be set as below:
bmif.biSize = sizeof(BITMAPINFOHEADER);
bmif.biHeight = srcheader.height;
bmif.biWidth = srcheader.width;
bmif.biSizeImage = srcheader.cbSize;
bmif.biPlanes = 1;
bmif.biBitCount = (WORD)(bmif.biSizeImage / bmif.biHeight / bmif.biWidth * 8);
bmif.biCompression = BI_RGB;
BITMAPFILEHEADER can be set as below:
LONG offBits = sizeof(BITMAPFILEHEADER) + bmif.biSize;
bmfh.bfType = 0x4d42;
bmfh.bfOffBits = offBits;
bmfh.bfSize = offBits + bmif.biSizeImage;
bmfh.bfReserved1 = 0;
bmfh.bfReserved2 = 0;
With the bitmap pixels, note that the captured data has reversed order with the normal bitmap data, so we have to convert it to the correct order before saving, otherwise the bitmap will be horizontally flipped.
LONG lineSize = bmif.biWidth * bmif.biBitCount / 8;
BYTE* pLineData = new BYTE[lineSize];
LONG lineStart = 0;
LONG lineEnd = bmif.biHeight - 1;
while (lineStart < lineEnd)
pStart = pData + (lineStart * lineSize);
pEnd = pData + (lineEnd * lineSize);
memcpy(pLineData, pStart, lineSize);
memcpy(pStart, pEnd, lineSize);
memcpy(pEnd, pLineData, lineSize);
The last thing is save all of above to a file. This sample only support BMP files, but we could to save it into other formats by using compression libraries.
if(!pFile.Open((LPCTSTR)fileName, CFile::modeCreate | CFile::modeWrite))
Using the sample source code
This sample source code is written with Visual Studio 2012. Because used functions in the magnification library are generally available from Windows Vista, so this code should work with Visual Studio 2008 or above.
This is a simple way to take a screenshot using the powerful Magnification library. However there is a problem that is need to be solved. The
MagImageScalingCallback() function is deprecated, and could be removed from newer versions of Windows. So we need to find another way to access the data. Currently I haven't not found any solution yet. If anyone could find anyway, please let me know.