使用Win32 API获取真实屏幕分辨率

3

我需要获取每个多屏幕的真实屏幕分辨率,这是由gpu实际输出的分辨率,而不受dpi设置影响。

我尝试了许多stackoverflow上的答案,但似乎都没有正确处理它。

-GetDpiForMonitor() 也无法返回正确的dpi。 它返回的 dpiXdpiY 甚至不匹配。

代码:

static int result = []() {SetProcessDpiAwareness(PROCESS_DPI_UNAWARE); return 1; }(); //nope

struct MonitorInfo
{
    MONITORINFO m_moniforInfo;
    UINT dpiX{};
    UINT dpiY{};
};

static std::vector<MonitorInfo> monitors;

BOOL EnumProc(HMONITOR monitor, HDC _, LPRECT __, LPARAM ___)
{
    MONITORINFO monitorInfo{ sizeof(MONITORINFO) };
    GetMonitorInfo(monitor, &monitorInfo);
    UINT dpix;
    UINT dpiy;
    GetDpiForMonitor(monitor, MDT_RAW_DPI, &dpix, &dpiy);
    monitors.push_back(MonitorInfo{ monitorInfo, dpix, dpiy});
    return true;
}

int main()
{
    SetProcessDpiAwareness(PROCESS_DPI_UNAWARE); //nope
    EnumDisplayMonitors(nullptr, nullptr, EnumProc, 0);
    for (auto const& monitor : monitors)
    {
        auto width = monitor.m_moniforInfo.rcMonitor.right - monitor.m_moniforInfo.rcMonitor.left;
        auto height = monitor.m_moniforInfo.rcMonitor.bottom - monitor.m_moniforInfo.rcMonitor.top;
        std::cout << width << " x " << height << '\t' << "dpix: " << monitor.dpiX << '\t' << monitor.dpiY << '\n';
    }
}

输出,注释在(括号中)

2560 x 1440     dpix: 108       109  (this is 3840*2160 with 150% scale)
2560 x 1440     dpix: 109       109  (this is 2560*2160 with 100% scale)
1920 x 1080     dpix: 163       106  (this is 3840*2160 with 200% scale)
1080 x 1920     dpix: 90        89   (this is 1080*1920 with 100% scale, shouldn't it be 96d?)
1920 x 1080     dpix: 163       106 (this is 3840*2160 with 200% scale too)

1
请确保您的应用程序清单已正确设置,否则您的调用可能无法返回实际结果,这是按设计来的。https://learn.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows - Simon Mourier
@SimonMourier 这是一个C++控制台应用程序。使用此https://learn.microsoft.com/en-us/windows/win32/hidpi/setting-the-default-dpi-awareness-for-a-process#setting-default-awareness-programmatically,我尝试在`main`的第一行设置api-awareness或者在全局范围内使用一些技巧进行设置。但是都没有起作用。 - sz ppeter
1
您所展示的代码并没有说明问题。如果您没有提供一个 [mcve],那么很难知道发生了什么。 - IInspectable
@IInspectable 好的,我添加了那两个尝试。 - sz ppeter
1
“PROCESS_DPI_UNAWARE” 是使用的完全错误的标志,因为它意味着系统会对您撒谎。 “PROCESS_PER_MONITOR_DPI_AWARE” 将为您提供所有监视器的真实尺寸。 - Jonathan Potter
3
这个问题的第一个评论直接链接到文档,详细解释了DPI感知如何工作以及不同的DPI感知模式意味着什么。任何人都会对你为什么决定向系统宣布你的应用程序不具备DPI感知能力,然后再问为什么系统将你的应用程序视为不具备DPI感知能力感到困惑。 - IInspectable
1个回答

2

以下答案已经使用SetProcessDPIAware()进行测试,无论是否使用该函数,都可以得到正确的值。

我在互联网上没有找到这个答案,所以很高兴与世界分享我的知识。

获取真实的显示器分辨率

void GetMonitorRealResolution(HMONITOR monitor, int* pixelsWidth, int* pixelsHeight)
{
    MONITORINFOEX info = { sizeof(MONITORINFOEX) };
    winrt::check_bool(GetMonitorInfo(monitor, &info));
    DEVMODE devmode = {};
    devmode.dmSize = sizeof(DEVMODE);
    winrt::check_bool(EnumDisplaySettings(info.szDevice, ENUM_CURRENT_SETTINGS, &devmode));
    *pixelsWidth = devmode.dmPelsWidth;
    *pixelsHeight = devmode.dmPelsHeight;
}

无论操作系统因进程的 DPI 感知而向您撒谎,它都会在任何情况下返回本机分辨率。

要获取虚拟分辨率和实际分辨率之间的缩放比例

float GetMonitorScalingRatio(HMONITOR monitor)
{
    MONITORINFOEX info = { sizeof(MONITORINFOEX) };
    winrt::check_bool(GetMonitorInfo(monitor, &info));
    DEVMODE devmode = {};
    devmode.dmSize = sizeof(DEVMODE);
    winrt::check_bool(EnumDisplaySettings(info.szDevice, ENUM_CURRENT_SETTINGS, &devmode));
    return (info.rcMonitor.right - info.rcMonitor.left) / static_cast<float>(devmode.dmPelsWidth);
}

这将为您提供给定监视器的实际分辨率相对于虚拟分辨率的比率。

如果主监视器的主要DPI为225%,第二个监视器为100%,并且您在第二个监视器上运行此函数,则会得到2.25。因为2.25 * 实际分辨率 = 监视器的虚拟分辨率

如果第二个监视器具有125%的缩放(而主监视器仍然是225%的缩放),则此函数将返回1.79999995,因为相对于225%的125%是此值(225/125 = 1.8),再次- 1.8 * 实际分辨率= 125%的虚拟分辨率`

获取实际DPI值(不相对于任何内容)

假设监视器A具有225%的DPI,监视器B具有125%的DPI,如上所述,如果您在第二个监视器上运行该函数,则不会获得1.25。您将像我说的那样获得1.8。

为了克服这一点,请使用此函数:

float GetRealDpiForMonitor(HMONITOR monitor)
{
    return GetDpiForSystem() / 96.0 / GetMonitorScalingRatio(monitor);
}

这个函数依赖于我之前编写的函数(函数GetMonitorScalingRatio,您需要复制它)

这将给出正确的值。


太棒了!这真的有效!EnumDisplaySettings确实提供了本机分辨率,这是我能够获得正确显示缩放的唯一方法(GetScaleFactorForMonitor会给出错误的结果)。 - lariona
你能帮忙用C语言编写这个吗? - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接