GetScaleFactorForMonitor winapi 是不是返回不正确的比例因子?

Posted

技术标签:

【中文标题】GetScaleFactorForMonitor winapi 是不是返回不正确的比例因子?【英文标题】:Is GetScaleFactorForMonitor winapi returning incorrect scaling factor?GetScaleFactorForMonitor winapi 是否返回不正确的比例因子? 【发布时间】:2020-12-20 21:00:55 【问题描述】:

如何获得正确的 DPI 缩放系数?我需要的是屏幕截图中显示的百分比,在本例中为 200%。下面的代码给出了 SCALE_180_PERCENT。我希望下面的代码应该返回 SCALE_200_PERCENT。系统为 Windows 10,Surface 2,显示分辨率 3000x2000,自定义缩放关闭,Windows 应用修复缩放开启。

#include <iostream>
#include <Windows.h>
#include <shellscalingapi.h>

#pragma comment(lib, "Shcore.lib")

int main()


    HWND hWnd = GetDesktopWindow();
    HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
    DEVICE_SCALE_FACTOR devScaleFactor;
    HRESULT res = GetScaleFactorForMonitor(hMonitor, &devScaleFactor);
    std::cout << "Device scale factor: " << devScaleFactor << std::endl;

    UINT dpi = GetDpiForWindow(hWnd);
    std::cout << "GetDpiForWindow DPI: " << dpi << std::endl;

    return 0;

【问题讨论】:

您没有处理任何错误。一旦你剥离了所有运行时动态链接,你就有足够的空间来实际处理(或至少观察)任何错误。 在 prod 代码中我非常喜欢 :) 这只是为了让代码 sn-p 更短 那么,取出运行时动态链接并节省大约 80% 的空间用于 真实 代码。 GetScaleFactorForMonitor 支持的最低客户端为 Windows 8.1 该应用支持 Win7。因此动态链接 - 剪切和粘贴。是的,Win10 的示例中不需要动态链接 我们不关心您的应用。我们关心您的问题。该问题不需要在已停产的系统上运行或复制。 【参考方案1】:

这篇文章已经很老了,但我遇到了这个问题,我想无论如何我都会分享我的解决方法。

POINT temp =  0,0 ;
HMONITOR primaryHandle = MonitorFromPoint(temp, MONITOR_DEFAULTTOPRIMARY);
UINT dpiX, dpiY;
HRESULT temp2 = GetDpiForMonitor(primaryHandle, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
double scalingFactor = dpiY / 96.0;

以上应该产生的 scalingFactor 等于您将比例设置为的百分比(在所问的问题中为 200%)。 GetDpiForMonitor 函数给出了 x 和 y DPI,快速测试它们是相同的,所以我只是使用 dpiY 作为我的最终答案,但是我不相信总是这样,你可能想单独找到它们。显示器在 100% 缩放时的 DPI 为 96,这就是为什么我们将生成的新 DPI 除以 96 以获得缩放因子,它需要为 96.0,否则 144/96 或任何会截断的值你 1(如果你更喜欢使用整数,你也可以在进行除法之前将它们都乘以 100)。

我还使用了不同的方法来查找监视器的句柄,这个具体的例子给出了主窗口的句柄。

至于实际遇到的问题(GetScaleFactorForMonitor 返回错误的比例因子),以下两篇文章包含更多信息。 Stack Overflow post for similar question but in c# Blog post touching on the return values you're receiving.

这两个链接的 TL;DR 是来自 windows 存储的应用程序具有 100、140 和 180 的缩放值,所以最好的猜测是对 GetScaleFactorForMonitor 的函数调用实际上是从与之相关的东西中提取的,而不是根据在显示设置中选择的比例和布局选项进行适当的缩放。这似乎是合理的,因为当我摆弄我的缩放设置时,GetScaleFactorForMonitor 提供给我的只有这三个值。

【讨论】:

【参考方案2】:

原来这取决于调用进程的dpi意识。 如果调用应用程序已将其 dpi 感知设置为 [DPI_AWARENESS_UNAWARE][1]GetScaleFactorForMonitor() 将返回正确的结果。

如果设置了其他任何一个(DPI_AWARENESS_SYSTEM_AWAREDPI_AWARENESS_PER_MONITOR_AWARE),则结果不正确。

另一个问题是,GetDpiForMonitorGetDpiForWindow 只有在调用进程具有 DPI_AWARENESS_PER_MONITOR_AWARE 时才能正确工作。 看: https://docs.microsoft.com/en-us/windows/win32/api/shellscalingapi/nf-shellscalingapi-getdpiformonitor

如果您将程序定义为在 Windows 10 或更高版本上运行,则可以通过编程方式确定感知。

#if WINVER >= 0x0A00
    DPI_AWARENESS_CONTEXT oldContext = GetThreadDpiAwarenessContext();
    DPI_AWARENESS oldDpiAwareness = GetAwarenessFromDpiAwarenessContext(oldContext);
#endif 

如果没有,似乎无法为您正在运行的显示器获得正确的 DPI 设置(或缩放系数)。

【讨论】:

以上是关于GetScaleFactorForMonitor winapi 是不是返回不正确的比例因子?的主要内容,如果未能解决你的问题,请参考以下文章