Windows 截图简单实现 – Windows API

最近更新于 2024-05-05 14:18

Table of Contents

环境

Windows 11 专业工作站版 22H2
Visual Studio 2022 专业版

使用 Unicode 字符集,C17 标准编译,64 位编译

代码

/**
* @file screenshot.c
* @author IYATT-yx
* @brief Windows 截图
* @version 0.1
* @data 2023.2.1
* @details 
*   使用:
*               运行后,鼠标左键按下然后拖动选择截图范围,放开鼠标截图到剪切板
*               按 Esc 退出截图界面
* 
*   开发环境:   Windows 11 专业工作站版 22H2
*               Visual Studio 2022 专业版
*               使用 Unicode 字符集
*               C17 标准,64 位编译
*/
#include <Windows.h>

int g_screen_width, g_screen_height; // 保存屏幕分辨率
HDC g_memory_DC; // 用于存放图像
RECT g_rect; // 矩形区域,用于存放选择框的位置信息

/**
* @brief 捕捉整个桌面的图像
*/
void capture_desktop()
{
    HDC display_DC = CreateDC(L"DISPLAY", NULL, NULL, NULL); // 为主显示设备创建设备描述表
    g_screen_width = GetDeviceCaps(display_DC, HORZRES); // 获取屏幕宽度
    g_screen_height = GetDeviceCaps(display_DC, VERTRES); // 获取屏幕高度
    g_memory_DC = CreateCompatibleDC(display_DC);  // 创建显示设备的内存 DC
    HBITMAP hCanvas = CreateCompatibleBitmap(display_DC, g_screen_width, g_screen_height); // 创建与显示设备关联的位图用作画布
    SelectObject(g_memory_DC, hCanvas); // 将放入内存 DC 的内存中
    BitBlt(g_memory_DC, 0, 0, g_screen_width, g_screen_height, display_DC, 0, 0, SRCCOPY); // 将屏幕显示的内容拷贝到内存 DC 中
}

/**
 * @brief 将数据拷贝到剪切板
*/
void copy_bitmap_to_clipboard()
{
    HDC display_DC = CreateDC(L"DISPLAY", NULL, NULL, NULL);
    int width = g_rect.right - g_rect.left;
    int height = g_rect.bottom - g_rect.top;
    HDC memory_DC = CreateCompatibleDC(display_DC);
    HBITMAP hCanvas = CreateCompatibleBitmap(display_DC, width, height);
    HBITMAP hOldMap = SelectObject(memory_DC, hCanvas);
    BitBlt(memory_DC, 0, 0, width, height, display_DC, g_rect.left, g_rect.top, SRCCOPY);
    HBITMAP hNewMap = SelectObject(memory_DC, hOldMap);

    OpenClipboard(NULL); // 打开剪切板
    EmptyClipboard(); // 清空剪切板
    SetClipboardData(CF_BITMAP, hNewMap); // 将位图数据拷贝到剪切板
    CloseClipboard(); // 关闭剪切板
}

LRESULT WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_CREATE: // 窗口创建前
        {
            capture_desktop();
            break;
        }
        case WM_LBUTTONDOWN: // 鼠标左键按下
        {
            POINT pt;
            GetCursorPos(&pt); // 获取鼠标光标的位置
            g_rect.left = pt.x;
            g_rect.top = pt.y;
            g_rect.right = pt.x;
            g_rect.bottom = pt.y;
            InvalidateRgn(hWnd, 0, TRUE); // 有更新内容就发送绘图消息
            break;
        }
        case WM_LBUTTONUP: // 鼠标左键放开
        {
            POINT pt;
            GetCursorPos(&pt);
            g_rect.right = pt.x;
            g_rect.bottom = pt.y;
            InvalidateRgn(hWnd, 0, TRUE);
            copy_bitmap_to_clipboard();
            break;
        }
        case WM_PAINT: // 绘图
        {
            LOGPEN pen;
            pen.lopnColor = RGB(255, 0, 0); // 截图选择框的颜色 - 红
            pen.lopnStyle = PS_SOLID; // 截图选择框的线型 - 实线
            pen.lopnWidth = (POINT){2, 2}; // 截图框的线宽
            HPEN hPen = CreatePenIndirect(&pen); // 画笔,用于绘制图
            LOGBRUSH brush;
            brush.lbStyle = BS_NULL;
            HBRUSH hBrush = CreateBrushIndirect(&brush); // 画刷,同于填充图形内部

            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps); // 开始绘图
            HGDIOBJ hOldBrush = SelectObject(hdc, hBrush); // 切换画刷
            HGDIOBJ hOldPen = SelectObject(hdc, hPen); // 切换画笔
            BitBlt(hdc, 0, 0, g_screen_width, g_screen_height, g_memory_DC, 0, 0, SRCCOPY); // 从内存 DC 中复制捕捉的图像数据到窗口中
            Rectangle(hdc, g_rect.left, g_rect.top, g_rect.right, g_rect.bottom); // 绘制图像选择框
            SelectObject(hdc, hOldBrush); // 换回默认画刷
            SelectObject(hdc, hOldPen);  // 换回默认画笔
            EndPaint(hWnd, &ps); // 结束绘图
            break;
        }
        case WM_CLOSE: // 点击关闭按钮
        {
            DestroyWindow(hWnd);
            break;
        }
        case WM_KEYDOWN: // 按 Esc 退出
        {
            if (wParam == 27)
            {
                PostMessage(hWnd, WM_CLOSE, 0, 0); // 发送退出信号
            }
            break;
        }
        case WM_DESTROY: // 窗口销毁后
        {
            PostQuitMessage(0); // 退出消息循环
            break;
        }
        default:
        {
            return DefWindowProc(hWnd, msg, wParam, lParam);
        }
    }
    return 0;
}

int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPreHinstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
    // 注册窗口类
    PCWSTR szClassName = L"Desktop";
    WNDCLASSEX wc = {0};
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.lpfnWndProc = WndProc;
    wc.lpszClassName = szClassName; 
    wc.hCursor = LoadCursor(NULL, IDC_CROSS); // 鼠标光标设置为十字键头

    if (!RegisterClassEx(&wc))
    {
        MessageBox(NULL, L"注册窗口失败!", L"错误", MB_OK);
        return 1;
    }

    // 创建窗口 - WS_POPUP 弹出窗口,没有标题栏
    HWND hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, szClassName, L"截图", WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
    if (!hWnd)
    {
        MessageBox(NULL, L"创建窗口失败", L"错误", MB_OK);
        return 1;
    }

    // 显示窗口
    ShowWindow(hWnd, SW_MAXIMIZE); // 设置最大化显示
    UpdateWindow(hWnd);

    // 消息循环
    MSG msg = {0};
    while (GetMessage(&msg, NULL, 0, 0))
    {
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

按下左键拉动选择截图范围,放开左键拷贝到剪切板
file

Windows 截图简单实现 – Windows API
Scroll to top