You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++截图程序仅捕获屏幕左上角,如何修改实现全屏截图?

全屏截图仅捕获左上角区域的解决方法

我编写了一个C++程序用于捕获屏幕截图并保存为PNG格式,其他功能正常,但只能捕获屏幕的左上角区域。尝试用GetDpiForSystem()做DPI感知后问题依旧,请问如何修改代码实现全屏截图?

截图

初始代码

#define _CRT_SECURE_NO_WARNINGS

#include <windows.h>
#include <objidl.h>
#include <stdio.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")

int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) {
    UINT num = 0;
    UINT size = 0;
    Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;

    Gdiplus::GetImageEncodersSize(&num, &size);
    if (size == 0) {
        return -1;
    }

    pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
    if (pImageCodecInfo == NULL) {
        return -1;
    }

    GetImageEncoders(num, size, pImageCodecInfo);

    for (UINT j = 0; j < num; ++j) {
        if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) {
            *pClsid = pImageCodecInfo[j].Clsid;
            free(pImageCodecInfo);
            return j;
        }
    }

    free(pImageCodecInfo);
    return -1;
}

void TakeScreenshot(const wchar_t* file_name) {
    // 获取整个桌面的尺寸
    int width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
    int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);

    // 创建用于存储截图的位图
    HDC screen_dc = GetDC(NULL);
    HDC mem_dc = CreateCompatibleDC(screen_dc);
    HBITMAP bitmap = CreateCompatibleBitmap(screen_dc, width, height);
    HGDIOBJ old_bitmap = SelectObject(mem_dc, bitmap);

    // 将屏幕内容复制到位图
    BitBlt(mem_dc, 0, 0, width, height, screen_dc, 0, 0, SRCCOPY);

    // 将位图保存为文件
    Gdiplus::Bitmap image(bitmap, NULL);
    CLSID png_clsid;
    GetEncoderClsid(L"image/png", &png_clsid);
    image.Save((WCHAR*)file_name, &png_clsid, NULL);

    // 清理资源
    SelectObject(mem_dc, old_bitmap);
    DeleteObject(bitmap);
    DeleteDC(mem_dc);
    ReleaseDC(NULL, screen_dc);
}

int main()
{
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    TakeScreenshot(L"test.png");

    GdiplusShutdown(gdiplusToken);
}

更新后的尝试代码

#define _CRT_SECURE_NO_WARNINGS

#include <windows.h>
#include <objidl.h>
#include <stdio.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")

int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
    UINT num = 0;
    UINT size = 0;
    Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;

    Gdiplus::GetImageEncodersSize(&num, &size);
    if (size == 0) {
        printf("Error: GetImageEncodersSize returned size 0\n");
        return -1;
    }

    pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
    if (pImageCodecInfo == NULL) {
        printf("Error: malloc failed to allocate memory for ImageCodecInfo\n");
        return -1;
    }

    if (GetImageEncoders(num, size, pImageCodecInfo) != Ok) {
        printf("Error: GetImageEncoders failed\n");
        free(pImageCodecInfo);
        return -1;
    }

    for (UINT j = 0; j < num; ++j) {
        if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) {
            *pClsid = pImageCodecInfo[j].Clsid;
            free(pImageCodecInfo);
            return j;
        }
    }

    free(pImageCodecInfo);
    return -1;
}

void TakeScreenshot(const wchar_t* file_name)
{
    // 获取整个桌面的尺寸
    int dpi_x = GetDpiForSystem();
    int dpi_y = GetDpiForSystem();
    int width = GetSystemMetricsForDpi(SM_CXSCREEN, dpi_x);
    int height = GetSystemMetricsForDpi(SM_CYSCREEN, dpi_y);

    if (width == 0 || height == 0) {
        printf("Error: GetSystemMetrics returned invalid screen dimensions\n");
        return;
    }

    printf("Width: %d\n", width);
    printf("Height: %d\n", height);

    // 创建用于存储截图的位图
    HDC screen_dc = GetDC(NULL);
    if (screen_dc == NULL) {
        printf("Error: GetDC failed to get a handle to the screen device context\n");
        return;
    }

    HDC mem_dc = CreateCompatibleDC(screen_dc);
    if (mem_dc == NULL) {
        printf("Error: CreateCompatibleDC failed to create a compatible device context\n");
        ReleaseDC(NULL, screen_dc);
        return;
    }

    // 创建适配DPI的位图
    HBITMAP bitmap = CreateBitmap(width, height, 1, GetDeviceCaps(screen_dc, BITSPIXEL), NULL);
    if (bitmap == NULL) {
        printf("Error: CreateCompatibleBitmap failed to create a compatible bitmap\n");
        DeleteDC(mem_dc);
        ReleaseDC(NULL, screen_dc);
        return;
    }

    HGDIOBJ old_bitmap = SelectObject(mem_dc, bitmap);

    // 设置内存DC的DPI匹配系统DPI
    SetGraphicsMode(mem_dc, GM_ADVANCED);
    XFORM xform;
    xform.eM11 = (FLOAT)dpi_x / 96;
    xform.eM12 = xform.eM21 = xform.eM22 = 0;
    xform.eDx = xform.eDy = 0;
    SetWorldTransform(mem_dc, &xform);

    // 将屏幕内容复制到位图
    if (BitBlt(mem_dc, 0, 0, width, height, screen_dc, 0, 0, SRCCOPY) == 0) {
        printf("Error:BitBlt failed to copy screen contents to bitmap\n");
        return;
    }

    // 将位图保存为文件
    Gdiplus::Bitmap image(bitmap, NULL);
    if (image.GetLastStatus() != Ok) {
        printf("Error: Bitmap constructor failed to create a Bitmap object\n");
        return;
    }

    CLSID png_clsid;
    int r = GetEncoderClsid(L"image/png", &png_clsid);
    if (r == -1)
    {
        printf("Error: unable to find image encoder for MIME type 'image/png'\n");
        return;
    }

    if (image.Save(file_name, &png_clsid, NULL) != Ok) {
        printf("Error: Bitmap::Save failed to save image\n");
        return;
    }

    // 清理资源
    SelectObject(mem_dc, old_bitmap);
    DeleteObject(bitmap);
    DeleteDC(mem_dc);
    ReleaseDC(NULL, screen_dc);
}

int main()
{
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    TakeScreenshot(L"test.png");

    GdiplusShutdown(gdiplusToken);
    return 0;
}

解决方法

问题根源是程序未正确声明DPI感知,仅调用GetDpiForSystem()不足以让系统正确识别程序的DPI适配能力,导致获取的屏幕尺寸和实际绘制不匹配。

步骤1:添加DPI感知清单

在程序中嵌入DPI感知清单,告诉Windows程序支持高DPI。最简单的方式是在代码中添加编译指令:

#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#pragma comment(linker, "/manifest:\"<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'><application xmlns='urn:schemas-microsoft-com:asm.v3'><windowsSettings><dpiAware xmlns='http://schemas.microsoft.com/SMI/2005/WindowsSettings'>true/PM</dpiAware></windowsSettings></application></assembly>\"")

或者创建独立的app.manifest文件并添加到项目中,内容如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <application xmlns="urn:schemas-microsoft-com:asm.v3">
        <windowsSettings>
            <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
        </windowsSettings>
    </application>
</assembly>

步骤2:修正屏幕尺寸获取与绘制逻辑

移除更新代码中不必要的SetWorldTransform操作,因为声明DPI感知后,系统会自动处理坐标转换。修改TakeScreenshot函数:

void TakeScreenshot(const wchar_t* file_name)
{
    // 获取真实全屏尺寸(多显示器用SM_CXVIRTUALSCREEN/SM_CYVIRTUALSCREEN)
    int width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
    int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
    // 单显示器也可以用SM_CXSCREEN/SM_CYSCREEN

    if (width == 0 || height == 0) {
        printf("Error: GetSystemMetrics returned invalid screen dimensions\n");
        return;
    }

    printf("Width: %d\n", width);
    printf("Height: %d\n", height);

    HDC screen_dc = GetDC(NULL);
    if (screen_dc == NULL) {
        printf("Error: GetDC failed\n");
        return;
    }

    HDC mem_dc = CreateCompatibleDC(screen_dc);
    if (mem_dc == NULL) {
        printf("Error: CreateCompatibleDC failed\n");
        ReleaseDC(NULL, screen_dc);
        return;
    }

    // 使用CreateCompatibleBitmap保证和屏幕DC格式一致
    HBITMAP bitmap = CreateCompatibleBitmap(screen_dc, width, height);
    if (bitmap == NULL) {
        printf("Error: CreateCompatibleBitmap failed\n");
        DeleteDC(mem_dc);
        ReleaseDC(NULL, screen_dc);
        return;
    }

    HGDIOBJ old_bitmap = SelectObject(mem_dc, bitmap);

    // 直接BitBlt,无需额外坐标转换
    if (!BitBlt(mem_dc, 0, 0, width, height, screen_dc, 0, 0, SRCCOPY)) {
        printf("Error: BitBlt failed\n");
        SelectObject(mem_dc, old_bitmap);
        DeleteObject(bitmap);
        DeleteDC(mem_dc);
        ReleaseDC(NULL, screen_dc);
        return;
    }

    Gdiplus::Bitmap image(bitmap, NULL);
    if (image.GetLastStatus() != Ok) {
        printf("Error: Bitmap constructor failed\n");
        SelectObject(mem_dc, old_bitmap);
        DeleteObject(bitmap);
        DeleteDC(mem_dc);
        ReleaseDC(NULL, screen_dc);
        return;
    }

    CLSID png_clsid;
    int r = GetEncoderClsid(L"image/png", &png_clsid);
    if (r == -1)
    {
        printf("Error: unable to find PNG encoder\n");
        SelectObject(mem_dc, old_bitmap);
        DeleteObject(bitmap);
        DeleteDC(mem_dc);
        ReleaseDC(NULL, screen_dc);
        return;
    }

    if (image.Save(file_name, &png_clsid, NULL) != Ok) {
        printf("Error: Save failed\n");
    }

    // 资源清理
    SelectObject(mem_dc, old_bitmap);
    DeleteObject(bitmap);
    DeleteDC(mem_dc);
    ReleaseDC(NULL, screen_dc);
}

步骤3:验证效果

编译运行修改后的程序,此时程序会正确识别系统DPI,获取真实的全屏尺寸并完整捕获屏幕内容。


内容的提问来源于stack exchange,提问作者ErlingHaaland

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 05:16:38