Click here to Skip to main content
14,390,052 members

Screenshot using the Magnification library

Rate this:
4.78 (5 votes)
Please Sign up or sign in to vote.
4.78 (5 votes)
14 Aug 2013CPOL
A simple way to take a screenshot using the Magnification library

Introduction

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:

  1. Use MagInitialize() function to initialize the magnification library.  
  2. if (!MagInitialize())
    { 
    	return FALSE;
    } 
  3. 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.  
  4. // Get screen resolution
    RECT rect;
    HWND hDesktop = ::GetDesktopWindow();
    ::GetWindowRect(hDesktop, &rect);
    
    // Set window position
    SetWindowPos(NULL, 0, 0, rect.right, rect.bottom, SWP_HIDEWINDOW);
    
    // Set window layered attribute
    SetWindowLong(this->GetSafeHwnd(), GWL_EXSTYLE, 
    	GetWindowLong(this->GetSafeHwnd(), GWL_EXSTYLE) | WS_EX_LAYERED);
    SetLayeredWindowAttributes(0, 255, LWA_ALPHA);  
  5. 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 MS_SHOWMAGNIFIEDCURSOR enabled. 
  6. hwndMag = CreateWindow(WC_MAGNIFIER, TEXT("MagnifierWindow"), 
    		WS_CHILD /*| MS_SHOWMAGNIFIEDCURSOR */| WS_VISIBLE,
    		0, 0, m_ScreenX, m_ScreenY, 
    		hostDlg->GetSafeHwnd(), NULL, hInstance, NULL ); 
  7. 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. 
  8. // Setup the filter list to exclude the main window
    pFilterList = new HWND[1];
    pFilterList[0] = this->GetSafeHwnd();
    
    if (!MagSetWindowFilterList(hwndMag, MW_FILTERMODE_EXCLUDE, 1, pFilterList))
    {
    	return;
    }    
  9. 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. 
  10. // Get the screen rectangle
    RECT sourceRect;
    sourceRect.top = 0;
    sourceRect.left = 0;
    sourceRect.right = m_ScreenX;
    sourceRect.bottom = m_ScreenY;
    if (!MagSetWindowSource(hwndMag, sourceRect))
    {
    	return;
    }

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.  

  1. Call the MagSetImageScalingCallback() function before performing the screenshot to set the callback function. 
  2. // Set the callback function
    if (!MagSetImageScalingCallback(hwndMag, (MagImageScalingCallback)MagImageScaling))
    { 
    	return FALSE;
    }  
  3. The callback function has the following syntax: 
  4. BOOL MagImageScaling(HWND hwnd, void *srcdata, MAGIMAGEHEADER srcheader, 
    					 void *destdata, MAGIMAGEHEADER destheader, 
    					 RECT unclipped, RECT clipped, HRGN dirty)

The 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 BITMAPFILEHEADER.  

The BITMAPINFOHEADER can be set as below: 

BITMAPINFOHEADER bmif;
// Setup the bitmap info header
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; 

The BITMAPFILEHEADER can be set as below:

// Setup the bitmap file header
BITMAPFILEHEADER bmfh;
LONG offBits = sizeof(BITMAPFILEHEADER) + bmif.biSize;
bmfh.bfType = 0x4d42; // "BM"
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];
BYTE* pStart;
BYTE* pEnd;
LONG lineStart = 0;
LONG lineEnd = bmif.biHeight - 1;
while (lineStart < lineEnd)
{
	// Get the address of the swap line
	pStart = pData + (lineStart * lineSize);
	pEnd = pData + (lineEnd * lineSize);
	// Swap the top with the bottom
	memcpy(pLineData, pStart, lineSize);
	memcpy(pStart, pEnd, lineSize);
	memcpy(pEnd, pLineData, lineSize);
	// Adjust the line index
	lineStart++;
	lineEnd--;
} 
delete pLineData;  

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. 

// File open
CFile pFile;
if(!pFile.Open((LPCTSTR)fileName, CFile::modeCreate | CFile::modeWrite))
{
	return;
}
//Write data to file
pFile.Write(&bmfh, sizeof(BITMAPFILEHEADER)); // bitmap file header
pFile.Write(&bmif, sizeof(BITMAPINFOHEADER)); // bitmap info header
pFile.Write(pData, bmif.biSizeImage); // converted bitmap data
// File close
pFile.Close(); 

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.

Conclusion 

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

thanh_bkhn
Team Leader Rikkeisoft Co.,Ltd
Vietnam Vietnam
We provide software outsourcing service including mobile application, mobile game, web development, business system...

Comments and Discussions

 
QuestionIt fails in Windows7 64bit Pin
Member 1234141613-Nov-18 2:03
MemberMember 1234141613-Nov-18 2:03 
QuestionScreenshot using the Magnification library Pin
Member 1383034116-May-18 6:49
MemberMember 1383034116-May-18 6:49 
AnswerRe: Screenshot using the Magnification library Pin
Member 1234141613-Nov-18 20:15
MemberMember 1234141613-Nov-18 20:15 
BugMemory Leak Pin
joseph shen11-Mar-17 3:18
Memberjoseph shen11-Mar-17 3:18 
in the callback function,
BYTE* pLineData = new BYTE[lineSize];
but free as
delete pLineData;  

should be
delete[] pLineData;  

GeneralRe: Memory Leak Pin
thanh_bkhn15-Mar-17 0:04
professionalthanh_bkhn15-Mar-17 0:04 
QuestionIs it works on windows 32 bits ? Pin
CAF22-Nov-13 1:19
MemberCAF22-Nov-13 1:19 
AnswerRe: Is it works on windows 32 bits ? Pin
CAF22-Nov-13 4:13
MemberCAF22-Nov-13 4:13 
GeneralRe: Is it works on windows 32 bits ? Pin
CAF22-Nov-13 4:19
MemberCAF22-Nov-13 4:19 
GeneralRe: Is it works on windows 32 bits ? Pin
thanh_bkhn22-Nov-13 5:32
professionalthanh_bkhn22-Nov-13 5:32 
GeneralRe: Is it works on windows 32 bits ? Pin
CAF13-Dec-13 1:25
MemberCAF13-Dec-13 1:25 
GeneralRe: Is it works on windows 32 bits ? Pin
thanh_bkhn13-Dec-13 1:40
professionalthanh_bkhn13-Dec-13 1:40 
Questionfill rect with random colors Pin
Member 1001007321-Aug-13 19:18
MemberMember 1001007321-Aug-13 19:18 
AnswerRe: fill rect with random colors Pin
thanh_bkhn22-Aug-13 0:24
professionalthanh_bkhn22-Aug-13 0:24 
QuestionI want to know How to capture the Screen of montor-2 Pin
YoungBokKim30-Jul-13 22:47
MemberYoungBokKim30-Jul-13 22:47 
AnswerRe: I want to know How to capture the Screen of montor-2 Pin
thanh_bkhn2-Aug-13 16:10
professionalthanh_bkhn2-Aug-13 16:10 
GeneralRe: I want to know How to capture the Screen of montor-2 Pin
YoungBokKim6-Aug-13 15:52
MemberYoungBokKim6-Aug-13 15:52 
AnswerRe: I want to know How to capture the Screen of montor-2 Pin
YoungBokKim20-Aug-13 20:17
MemberYoungBokKim20-Aug-13 20:17 
GeneralRe: I want to know How to capture the Screen of montor-2 Pin
thanh_bkhn20-Aug-13 20:28
professionalthanh_bkhn20-Aug-13 20:28 
GeneralRe: I want to know How to capture the Screen of montor-2 Pin
Member 1007102725-Nov-13 19:33
MemberMember 1007102725-Nov-13 19:33 
GeneralThank you!! Pin
Member 85937618-Jul-13 16:30
MemberMember 85937618-Jul-13 16:30 
GeneralRe: Thank you!! Pin
thanh_bkhn8-Jul-13 16:38
professionalthanh_bkhn8-Jul-13 16:38 
QuestionMy Vote: 5 Pin
Roger6517-Jun-13 2:14
MemberRoger6517-Jun-13 2:14 
AnswerRe: My Vote: 5 Pin
thanh_bkhn17-Jun-13 5:56
professionalthanh_bkhn17-Jun-13 5:56 
GeneralRe: My Vote: 5 Pin
Roger6517-Jun-13 6:14
MemberRoger6517-Jun-13 6:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Article
Posted 16 Jun 2013

Stats

37.9K views
1.5K downloads
27 bookmarked