使用放大库进行屏幕截图






4.85/5 (8投票s)
使用放大库进行屏幕截图的简单方法
引言
我们可能知道有很多方法可以截取桌面屏幕。 但有时我们可能想捕获桌面,而不需要一些指定的窗口。 如果这些窗口是分层的,我们可以在 Windows XP 上运行时使用不带 CAPTUREBLT
标志的 BitBlt 函数。 但现在我们正在运行 Windows 7 或 Windows 8,在这种情况下,BitBlt
函数无法再执行此过滤。 因此,我们可能希望排除更多非分层窗口。 所以我们必须找到一种新的方法来做到这一点。
在这个例子中,我将介绍一种使用 Magnification 库进行截图的方法。 这个库从 Windows Vista 开始可用,所以我们不能在 Windows XP 上使用它。 但是,随着许多用户从 XP 切换到更新版本的 Windows,这不会是一个大问题。
示例程序只包含一个 Capture 按钮。 当用户点击时,它将截取屏幕截图并打开一个对话框,供用户指定文件名并将捕获的图像保存为位图文件。
使用 Magnification 库
该库的完整文档可在 MSDN 上找到。
根据文档,屏幕截图过程可以通过以下步骤简单地完成:
- 使用
MagInitialize()
函数初始化 magnification 库。 - 创建带有分层属性的宿主对话框,将其设置为全屏且不可见。 我们使用 MFC 创建一个对话框并将其用作宿主对话框。 因为我们使用它来存储捕获的图像,但不想将其显示给用户,所以我们将其隐藏而不显示。
- 将 magnification 窗口创建为宿主的子窗口。 请注意,窗口类是
MagnifierWindow
。 如果您希望捕获鼠标光标,请创建启用MS_SHOWMAGNIFIEDCURSOR
的 magnification 窗口。 - 调用
MagSetWindowFilterList()
函数以排除要捕获的指定窗口。 由于这个强大的功能,这是我们想要使用 magnification 库的主要原因。 在这个程序中,我们过滤了主对话框,但我们可以根据需要过滤任意数量的对话框。 - 每当调用
MagSetWindowSource()
函数时,整个桌面都会被捕获到 magnification 窗口中。 在上面的代码中,我们将宿主窗口设置为不可见,但如果我们显示宿主窗口,我们将看到桌面图像在其中。
if (!MagInitialize())
{
return FALSE;
}
// 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);
hwndMag = CreateWindow(WC_MAGNIFIER, TEXT("MagnifierWindow"),
WS_CHILD /*| MS_SHOWMAGNIFIEDCURSOR */| WS_VISIBLE,
0, 0, m_ScreenX, m_ScreenY,
hostDlg->GetSafeHwnd(), NULL, hInstance, NULL );
// 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;
}
// 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;
}
保存数据
通常,我们可以将窗口的内容保存到文件中,或者使用 BitBlt 函数将其内容复制到内存中。 通过 magnification 库保存捕获的数据的主要问题是,我们无法像往常一样使用 BitBlt 函数访问宿主窗口或 magnification 窗口的位图。 所以我们使用了一个解决方法,即使用 MagSetImageScalingCallback()
函数,如下所述。
- 在执行屏幕截图之前调用
MagSetImageScalingCallback()
函数来设置回调函数。 - 回调函数具有以下语法:
// Set the callback function
if (!MagSetImageScalingCallback(hwndMag, (MagImageScalingCallback)MagImageScaling))
{
return FALSE;
}
BOOL MagImageScaling(HWND hwnd, void *srcdata, MAGIMAGEHEADER srcheader,
void *destdata, MAGIMAGEHEADER destheader,
RECT unclipped, RECT clipped, HRGN dirty)
srcdata
参数是指向位图源的指针,srcheader
包含捕获图像的信息,所以我们可以使用这两个参数来创建 BITMAPINFOHEADER
和 BITMAPFILEHEADER
。
BITMAPINFOHEADER
可以如下设置:
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;
BITMAPFILEHEADER
可以如下设置:
// 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;
对于位图像素,请注意捕获的数据与普通位图数据的顺序相反,因此我们必须在保存之前将其转换为正确的顺序,否则位图将被水平翻转。
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;
最后一件事是将以上所有内容保存到文件中。 此示例仅支持 BMP 文件,但我们可以使用压缩库将其保存为其他格式。
// 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();
使用示例源代码
这个示例源代码是用 Visual Studio 2012 编写的。 因为 magnification 库中使用的函数通常从 Windows Vista 开始可用,所以这段代码应该可以在 Visual Studio 2008 或更高版本中使用。
结论
这是使用强大的 Magnification 库进行屏幕截图的一个简单方法。 但是,有一个问题需要解决。 MagImageScalingCallback()
函数已被弃用,可能会从较新版本的 Windows 中删除。 所以我们需要找到另一种访问数据的方式。 目前我还没有找到任何解决方案。 如果有人能找到任何方法,请告诉我。