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

非DPI感知应用截图异常:GetDC/BitBlt的DPI Bug及解决方法

问题解答

问题1:这是否为Bug?

这不是Bug,是Windows的DPI虚拟化特性。对于未声明DPI感知的应用,系统会自动启用该机制,将应用的坐标和分辨率请求进行缩放转换,让应用误以为运行在96DPI(100%缩放比例)的环境中,以此保证老旧应用的界面不会因为高DPI缩放而出现布局错乱。你看到的缩放后的值,正是这个机制的正常表现。

问题2:如何在非DPI感知应用中实现完整截图?

需要绕过DPI虚拟化获取真实桌面分辨率,再基于真实尺寸创建位图并完成截图,以下是两种可行方案:

方案1:通过枚举显示器获取真实分辨率(支持多显示器)

通过EnumDisplayMonitors枚举显示器,在回调中获取主显示器的真实像素尺寸,再创建对应大小的位图完成截图:

#include <Windows.h>

// 显示器枚举回调函数,获取主显示器真实尺寸
BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
    MONITORINFOEX monitorInfo = {0};
    monitorInfo.cbSize = sizeof(MONITORINFOEX);
    GetMonitorInfoW(hMonitor, &monitorInfo);
    *(RECT*)dwData = monitorInfo.rcMonitor;
    return FALSE; // 仅获取主显示器,如需多显示器可返回TRUE继续枚举
}

// 获取真实桌面矩形
RECT GetRealDesktopRect() {
    RECT realRect = {0};
    EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&realRect);
    return realRect;
}

// 完整截图函数
void CaptureFullDesktop() {
    RECT realRect = GetRealDesktopRect();
    int realWidth = realRect.right - realRect.left;
    int realHeight = realRect.bottom - realRect.top;

    // 创建真实显示器DC
    HDC hScreenDC = CreateDCW(L"DISPLAY", NULL, NULL, NULL);
    HDC hMemDC = CreateCompatibleDC(hScreenDC);
    HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, realWidth, realHeight);
    HGDIOBJ hOldBitmap = SelectObject(hMemDC, hBitmap);

    // 执行截图
    BitBlt(hMemDC, 0, 0, realWidth, realHeight, hScreenDC, realRect.left, realRect.top, SRCCOPY);

    // 此处可添加位图保存逻辑(如写入BMP文件)
    // ...

    // 释放资源
    SelectObject(hMemDC, hOldBitmap);
    DeleteObject(hBitmap);
    DeleteDC(hMemDC);
    DeleteDC(hScreenDC);
}

方案2:通过系统DPI计算真实分辨率(单显示器场景)

先获取系统真实DPI,再结合虚拟化后的分辨率计算出真实尺寸:

#include <Windows.h>

void CaptureFullDesktopSingleMonitor() {
    UINT systemDpi = GetDpiForSystem();
    // 获取虚拟化后的分辨率
    int virtualWidth = GetSystemMetrics(SM_CXSCREEN);
    int virtualHeight = GetSystemMetrics(SM_CYSCREEN);
    // 计算真实分辨率
    int realWidth = MulDiv(virtualWidth, systemDpi, 96);
    int realHeight = MulDiv(virtualHeight, systemDpi, 96);

    HDC hScreenDC = CreateDCW(L"DISPLAY", NULL, NULL, NULL);
    HDC hMemDC = CreateCompatibleDC(hScreenDC);
    HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, realWidth, realHeight);
    HGDIOBJ hOldBitmap = SelectObject(hMemDC, hBitmap);

    BitBlt(hMemDC, 0, 0, realWidth, realHeight, hScreenDC, 0, 0, SRCCOPY);

    // 保存位图逻辑
    // ...

    // 释放资源
    SelectObject(hMemDC, hOldBitmap);
    DeleteObject(hBitmap);
    DeleteDC(hMemDC);
    DeleteDC(hScreenDC);
}

注:方案1兼容性更好,支持多显示器场景;方案2仅适用于单显示器环境,需确保系统DPI与主显示器DPI一致。

内容的提问来源于stack exchange,提问作者Александр Кулагин

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 20:57:24