简体   繁体   English

从不知道 DPI 的应用程序获取屏幕截图(GetDC/BitBlt / GetDC(0) 的 DPI 中的错误会忽略 DPI 设置)

[英]Getting a screenshot from a DPI unaware app (a bug in DPI for GetDC/BitBlt / GetDC(0) ignores DPI settings)

Suppose I have a primary monitor, which starts at 0x0 and have the typical size of 1920x1080.假设我有一个主监视器,它从 0x0 开始,典型尺寸为 1920x1080。 My DPI for primary monitor is set to 125%, which results in DPI being 120.我的主显示器的 DPI 设置为 125%,这导致 DPI 为 120。

Now, I want to take a screenshot of my monitor.现在,我想为我的显示器截屏。

I will use the following code (error checking is removed for simplicity):我将使用以下代码(为简单起见,删除了错误检查):

DC = GetDC(0);
GetDeviceCaps(DC, LOGPIXELSX);  // returns 120
GetSystemMetrics(SM_CXSCREEN);  // returns 1920
W = GetDeviceCaps(DC, HORZRES); // returns 1920
H = GetDeviceCaps(DC, VERTRES); // returns 1080
Bmp = CreateCompatibleBitmap(DC, W, H);
BmpDC = CreateCompatibleDC(DC);
SelectObject(BmpDC, Bmp);
BitBlt(BmpDC, 0, 0, W, H, DC, 0, 0, SRCCOPY);

This code would run fine, if my application is DPI awared (for example, via a manifest).如果我的应用程序支持 DPI(例如,通过清单),则此代码可以正常运行。 W will be 1920, H will be 1080, and the resulting Bmp will hold a screenshot of my monitor. W将是 1920, H将是 1080,生成的Bmp将包含我的显示器的屏幕截图。

However, if my application is NOT DPI awared (for example, does not have a manifest), then all Windows functions will report scaled values, which results in:但是,如果我的应用程序不支持 DPI(例如,没有清单),那么所有 Windows 函数都将报告缩放值,这会导致:

LOGPIXELSX = 96;
SM_CXSCREEN = 1536;
HORZRES = 1536;
VERTRES = 864;

So far so good.到目前为止,一切都很好。

However, the code above will produce a clipped result: while the resulting image will have size of 1536x864, but it will contain only partial copy of the desktop (eg pixels are one-to-one, without scaling).然而,上面的代码会产生一个裁剪的结果:虽然结果图像的大小为 1536x864,但它只包含桌面的部分副本(例如,像素是一对一的,没有缩放)。

There is a clear difference in what functions returns for GetDC(0) (eg DPI is 96, width is 1536) vs what this DC is actually represents (DPI is 120, width is 1920). GetDC(0)的函数返回值(例如 DPI 为 96,宽度为 1536)与此 DC 实际表示的函数(DPI 为 120,宽度为 1920)之间存在明显差异。 In other words, the bitmap associated with the GetDC(0) is NOT scaled for the virtualized DPI.换句话说,与GetDC(0)关联的 bitmap 未针对虚拟化 DPI 进行缩放。 This behaviour looks clearly wrong to me.这种行为在我看来显然是错误的。

Question 1: Is this a bug?问题1:这是一个错误吗?

Question 2: How to make a screenshot of the monitor, which will work in non-DPI awared app?问题 2:如何制作监视器的屏幕截图,这将在非 DPI 感知应用程序中工作?

PS Making application DPI awared is not a solution to the question. PS 使应用程序知道 DPI 并不是问题的解决方案。

PPS I am using Windows 10.0.19044.1889. PPS 我正在使用 Windows 10.0.19044.1889。

PPPS I also tried to detect if application was scaled , but GetDPIForMonitor returns 96, GetScaleFactorForMonitor returns 100, GetMonitorInfo returns 1536x864. PPPS 我也尝试检测应用程序是否已缩放,但GetDPIForMonitor返回 96, GetScaleFactorForMonitor返回 100, GetMonitorInfo返回 1536x864。 In other words, I can not find how to detect the real 1920x1080 size of the desktop for using with BitBlt .换句话说,我找不到如何检测桌面的真实 1920x1080 尺寸以用于BitBlt

PPPPS There is a similar question here , but the "solution" is to "make process DPI awared", which is not what I am asking. PPPPS 这里有一个类似的问题,但“解决方案”是“让进程知道 DPI”,这不是我要问的。

It seems LogicalToPhysicalPointForPerMonitorDPI will do the conversion you need.似乎LogicalToPhysicalPointForPerMonitorDPI会进行您需要的转换。

#define NOMINMAX
#include <Windows.h>

#include <iostream>

int main() {
    auto hdc = ::GetDC(NULL);
    const auto dpi = ::GetDeviceCaps(hdc, LOGPIXELSX);
    const auto cx = ::GetDeviceCaps(hdc, HORZRES);
    const auto cy = ::GetDeviceCaps(hdc, VERTRES);
    auto corner = POINT{cx, cy};
    ::LogicalToPhysicalPointForPerMonitorDPI(NULL, &corner);
    ::ReleaseDC(NULL, hdc);
    std::cout << "DPI:              " << dpi << '\n'
              << "logical size:     " << cx << " x " << cy << '\n'
              << "physical size:    " << corner.x << " x " << corner.y
              << std::endl;
    return 0;
}

On my machine I get:在我的机器上,我得到:

DPI:              96
logical size:     2194 x 1234
physical size:    3840 x 2160

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM