Windows API 读取屏幕图像(截图)

获取屏幕尺寸

1GetSystemMetrics(SM_CXSCREEN);  // 获取主显示器的逻辑宽度
2GetSystemMetrics(SM_CYSCREEN);  // 获取主显示器的逻辑高度

1GetSystemMetrics(SM_CXVIRTUALSCREEN);   // 获取虚拟屏幕的逻辑宽度
2GetSystemMetrics(SM_CXVIRTUALSCREEN);   // 获取虚拟屏幕的逻辑高度

如果开启了屏幕缩放,则可以调用以下代码忽略屏幕缩放:

1// 忽略缩放
2SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);

读取屏幕的图像

1// 获取屏幕设备上下文
2HDC hScreenDC = GetDC(NULL);
3
4// 创建兼容指定设备的内存设备上下文
5HDC hMemDC = CreateCompatibleDC(hScreenDC);
6
7// 创建兼容指定设备上下文的位图
8HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);
9
10// 绑定内存设备上下文和位图
11SelectObject(hMemDC, hBitmap);
12
13// 将屏幕图像读取到内存设备上下文
14BitBlt(hMemDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY | CAPTUREBLT);

从图像中读取像素

1// 创建位图的 Info Header
2BITMAPINFOHEADER bi = {0};
3bi.biSize = sizeof(BITMAPINFOHEADER);
4bi.biWidth = width;
5bi.biHeight = -height;      // 屏幕原点在左上角,位图原点在左下角
6bi.biPlanes = 1;
7bi.biBitCount = 24;         // 使用 24 位颜色,注意:颜色顺序是 BGR
8bi.biCompression = BI_RGB;
9
10// 读取像素
11int lineBytes = ((bi.biWidth * bi.biBitCount + 31) / 32) * 4;   // 四字节对齐
12int imageSize = lineBytes * height;
13BYTE* pixels = malloc(imageSize);
14GetDIBits(hMemDC, hBitmap, 0, height, pixels, (BITMAPINFO*)&bi, DIB_RGB_COLORS);

注意:BMP 的像素每行都按 4 字节对齐,并且颜色顺序是 BGR。

完整示例

1#include <stdio.h>
2#include <Windows.h>
3
4// 使用 MinGW 时需要添加 -lgdi32 选项
5#pragma comment(lib, "gbi32.lib")
6
7int main()
8{
9    // 获取虚拟屏幕尺寸
10    int width  = GetSystemMetrics(SM_CXVIRTUALSCREEN);
11    int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
12
13    // 获取屏幕设备上下文
14    HDC hScreenDC = GetDC(NULL);
15
16    // 创建兼容指定设备的内存设备上下文
17    HDC hMemDC = CreateCompatibleDC(hScreenDC);
18
19    // 创建兼容指定设备上下文的位图
20    HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);
21
22    // 绑定内存设备上下文和位图
23    SelectObject(hMemDC, hBitmap);
24
25    // 将屏幕图像读取到内存设备上下文
26    BitBlt(hMemDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY | CAPTUREBLT);
27
28    // 创建位图的 Info Header
29    BITMAPINFOHEADER bi = {0};
30    bi.biSize = sizeof(BITMAPINFOHEADER);
31    bi.biWidth = width;
32    bi.biHeight = -height;      // 屏幕原点在左上角,位图原点在左下角
33    bi.biPlanes = 1;
34    bi.biBitCount = 24;         // 使用 24 位颜色,注意:颜色顺序是 BGR
35    bi.biCompression = BI_RGB;
36
37    // 读取像素
38    int lineBytes = ((bi.biWidth * bi.biBitCount + 31) / 32) * 4;   // 四字节对齐
39    int imageSize = lineBytes * height;
40    BYTE* pixels = malloc(imageSize);
41    GetDIBits(hMemDC, hBitmap, 0, height, pixels, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
42
43    // 创建位图的 File Header
44    BITMAPFILEHEADER bf = {0};
45    bf.bfType = 0x4D42; // 'BM'
46    bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
47    bf.bfSize = bf.bfOffBits + imageSize;
48
49    // 写入文件
50    FILE* fp = fopen("save.bmp", "wb");
51    fwrite(&bf, sizeof(bf), 1, fp);
52    fwrite(&bi, sizeof(bi), 1, fp);
53    fwrite(pixels, imageSize, 1, fp);
54    fclose(fp);
55
56    // 释放资源
57    free(pixels);
58    DeleteObject(hBitmap);
59    DeleteDC(hMemDC);
60    ReleaseDC(NULL, hScreenDC);
61    return 0;
62}