一、前言
窗口本质上就是高速刷新的、由大量像素点组成的图片,所谓“截图”,实际上就是将某一时刻的窗口上的像素数据保存为文件的过程。
图片格式有很多,并且大都很复杂,但我们也无需过于担忧,因为这些常用的格式、都已经有别人写好的现成的库可以让我们使用了。
比如在vs中,就有官方提供的方法可以让我们非常轻易的达到这一目的。
二、使用方法
vs提供了一个atlimage.h
头文件,其内存在一个图像类CImage
。
只要将一个窗口的设备上下文(Device context
,简称DC
)复制到该类,就能将该窗口的图像直接保存为图片,使用起来非常的简单。
主要用到的类和函数:
GetDeviceCaps(m_hDc, BITSPIXEL); //获取窗口DC像素的大小
GetDeviceCaps(m_hDc, HORZRES); //获取窗口DC宽度
GetDeviceCaps(m_hDc, VERTRES); //获取窗口DC高度
GetDpiForWindow(m_hWnd); //获取窗口单位英寸像素个数,一般电脑像素过大,windows为正常显示图标,会放大该数值,所以需要依靠该数值调整DC大小,否则截图会出现大小不适配的问题
CImage image;//用于图片操作的类
image.Create(m_width, m_hight, m_bitOfPix);//为该类创建与原窗口一样大小的DC
BitBlt(m_hImgDc, 0, 0, m_width, m_hight, m_hDc, 0, 0, SRCCOPY); //将窗口DC图像复制到image
m_image.Save(name, Gdiplus::ImageFormatBMP); //将图像数据保存为对应文件
注意这些函数用到的参数,一般都涉及到两个概念:
- 窗口句柄(HWND)
- 设备上下文(Device context)
窗口句柄是用来标识一个窗口的,比如我们的操作系统桌面其实也是一个窗口,微信软件界面同样是一个窗口。
在上一章我们创建一个窗口时,相应的api的返回值也是一个窗口句柄,它的作用就是用来标识该窗口的。
有了窗口句柄,我们才能更进一步去操作这个窗口,比如通过GetDC
函数拿到该窗口的设备上下文(简称DC)。
所谓设备上下文,更直白点来说就是拿到该窗口所有相关数据信息的标识,比如我们想要截图,就得拿到该窗口的所有像素数据,而像素数据就被包含在了设备上下文中。
有了窗口的DC,那么我们就可以将该窗口当前所有的像素给拷贝出来,将其存放到文件中,也就是实现了所谓的“截图”。
三、完整代码
#include<string>
#include<atlimage.h>
using namespace std;
//name:保存的文件名
//hWnd:要截图的窗口句柄,NULL表示对桌面截图
bool SavePic(wstring name,HWND hWnd) {
HDC hDc=NULL;
hWnd = (hWnd == NULL) ? GetDesktopWindow() : hWnd; //判断窗口句柄是否为NULL,如果为NULL则默认获取桌面DC
hDc = GetDC(hWnd); //获取DC
if (hDc == NULL) return false;
int bitOfPix = GetDeviceCaps(hDc, BITSPIXEL); //获取DC像素的大小
int width = GetDeviceCaps(hDc, HORZRES); //获取DC宽度
int hight = GetDeviceCaps(hDc, VERTRES); //获取DC高度
UINT dpi = GetDpiForWindow(hWnd); //获取dpi
float fold; //根据dpi计算放大倍数
switch (dpi) {
case 96:
fold = 1;
break;
case 120:
fold = 1.25;
break;
case 144:
fold = 1.5;
break;
case 192:
fold = 2;
break;
case 216:
fold = 2.25;
break;
default:
fold = 1;
break;
}
width *= fold; //复原宽度
hight *= fold; //复原高度
CImage image;
image.Create(width, hight, bitOfPix); //为图像类创建与窗口DC相同大小的DC
BitBlt(image.GetDC(), 0, 0, width, hight, hDc, 0, 0, SRCCOPY); //将窗口DC图像复制到image
image.Save(name.data(), Gdiplus::ImageFormatPNG); //保存为png格式图片文件
image.ReleaseDC(); //释放DC
ReleaseDC(hWnd, hDc); //释放DC资源
}
int main() {
SavePic(L"1.png",NULL);//对桌面截图,保存为1.png文件
}
上面的过程总的来说都比较简单,只要你有C/C++基础,我相信你应该很容易看懂。
无非就是用到了几个相关的win api而已,如果不理解的可以直接浏览器中搜索。
主要需要注意的是这个dpi
(Dots per inch),也就是“每英寸像素点”,默认情况下为96
。
但由于当下很多了电脑屏幕的像素都非常大,如果按照这种原有的方式进行展示,就会导致应用图标、字体都非常的小。
所以操作系统就会提供缩放功能:
一旦调整了缩放,比如程序图标放大了,实际的效果就是单位尺寸内存的像素更多了,所以根据这个值,我们就能计算出当前系统的缩放比例,从而复原图片应该有的大小去存放像素。
不恢复的话,就会导致你的截图会比原本窗口大小更小。
其支持保存的图片格式参数有如下内容:
Gdiplus::ImageFormatUndefined
Gdiplus::ImageFormatMemoryBMP
Gdiplus::ImageFormatBMP
Gdiplus::ImageFormatEMF
Gdiplus::ImageFormatWMF
Gdiplus::ImageFormatJPEG
Gdiplus::ImageFormatPNG
Gdiplus::ImageFormatGIF
Gdiplus::ImageFormatTIFF
Gdiplus::ImageFormatEXIF
Gdiplus::ImageFormatIcon
Gdiplus::ImageFormatHEIF
Gdiplus::ImageFormatWEBP
可根据参数后缀确定保存文件类型,但并不一定所有格式都是可用的,但基本的jpeg
、bmp
、jpeg
格式应该都是没问题的。
运行后,可在当前程序目录查看图片。