如何检测当前屏幕分辨率?

49

如何在Winapi中(用C或C ++)检测当前屏幕分辨率?

一些背景:

我想要启动一个新的OpenGL全屏窗口,但希望它打开时与桌面已设置的水平和垂直尺寸相同。(现在每个人都使用LCD屏幕,我认为这是获取屏幕本地分辨率的最佳方法。)

虽然知道桌面色彩深度也是不错的奖励,但我并不迫切需要知道。


12
请确保你的代码能够以合理的方式处理多个显示器。 - In silico
@In silico,什么是明智的方式?在主显示器上启动游戏(这是一个小游戏)是合理的吗? - Prof. Falken
3
默认使用主监视器,但允许用户在游戏设置屏幕中设置特定的监视器。或者也可以支持类似“mygame.exe /Monitor1”这样的命令。如果您还支持窗口模式,请使用“MonitorFromWindow”函数确定用户何时切换回全屏模式需要还原到哪个监视器。 - Anders
1
@Amigable Clark Kant:Anders 已经准确地指出了对于你的小游戏来说什么是“合理”的。当我说“合理”时,我真正意思是“适合你的应用程序的行为”。在计算机游戏的情况下,即使在多显示器环境下,我也应该能够正确配置监视器设置。 - In silico
@In silico。但是你到底想要什么?这将是一个小巧的2D街机风格游戏。如果你告诉我对这款应用的期望,我可以在完成后向你发送第一个版本。 :) - Prof. Falken
1
@Amigable Clark Kant:安德斯在上面的评论中所描述的基本上就是我对你的应用程序的期望。 :-) - In silico
8个回答

86
  • 主显示器尺寸: GetSystemMetrics 函数的SM_CXSCREEN / SM_CYSCREEN参数(也可使用GetDeviceCaps函数)
  • 所有显示器尺寸(合并后): GetSystemMetrics函数的SM_CX/YVIRTUALSCREEN参数
  • 主显示器工作区尺寸(不包括任务栏和其他停靠栏): SystemParametersInfo 函数的SPI_GETWORKAREA参数
  • 指定显示器尺寸(含工作区和“屏幕”): GetMonitorInfo 函数

编辑说明: 需要注意的是,显示器的左上角坐标不一定是0x0,因此仅知道显示器尺寸不能确定窗口位置。您可以使用MonitorFromWindow函数查找您的窗口所在的显示器,然后调用GetMonitorInfo函数。

如果您想采用低级方式或更改分辨率,可以使用EnumDisplayDevices、EnumDisplaySettings和ChangeDisplaySettings函数(这是获取刷新率的唯一方法,但GetDeviceCaps函数可以告诉您颜色深度)。


2
我刚刚测试了一下,在多显示器设置中GetDeviceCaps确实可以工作,VERT/HORZRES和VREFRESH似乎也可以工作,但是我只能让CreateDC接受“\.\DISPLAY2”样式的字符串,而不能接受“\.\DISPLAY2\Monitor0”,所以如果一个适配器有多个监视器,我不确定该怎么办,所以如果你从EnumDisplayDevices得到了监视器列表,请坚持使用EnumDisplaySettings... - Anders
1
可能不会,我在谈论Windows,希望OpenGL可以将其抽象化,您只需告诉它使用哪个显示器,它就会处理其余的事情。 - Anders
1
@CoryTrese:要定位一个窗口,你应该使用GetMonitorInfo。如果你正在寻找低级信息,你也可以尝试使用ENUM_CURRENT_SETTINGS来调用EnumDisplaySettings函数... - Anders
@Anders:ENUM_CURRENT_SETTINGS在后来的Windows版本中肯定是开启的。您认为在答案中值得更详细地描述吗? - Laurie Stearn
1
@bobobobo DPI?Win10有GetSystemMetricsForDpi。另外,请确保您的应用程序具有DPI感知功能。 - Anders
显示剩余6条评论

12
当系统使用DPI虚拟化(Vista及以上版本)时,使用GetSystemMetrics或GetWindowRect将失败以获取真实的屏幕分辨率(您将获得虚拟分辨率),除非您创建了DPI感知应用程序。因此,在这里最好的选择(简单且向后兼容)是使用EnumDisplaySettings与ENUM_CURRENT_SETTINGS。

8

以下是使用GetSystemMetrics函数的参数:

  • SM_CXSCREEN < width:主显示器屏幕的宽度,以像素为单位。该值可以通过以下方式获取:GetDeviceCaps( hdcPrimaryMonitor, HORZRES)
  • SM_CYSCREEN < height:主显示器屏幕的高度,以像素为单位。

简而言之,这些参数可以帮助您获得主显示器屏幕的宽度和高度。


请注意,GetSystemMetrics 仅用于向后兼容以支持 Windows 98 之前(和多监视器)的版本。您应该使用 SM_CXVIRTUALSCREEN/SM_CYVIRTUALSCREEN 来获取用户的整个屏幕。 - Ian Boyd

4

这不是和 https://dev59.com/RG445IYBdhLWcg3w7uc0#4631313 的答案完全相同吗? - Prof. Falken
4
GetSystemMetrics 只提供关于主(第一个)显示器的信息,参见 https://msdn.microsoft.com/en-us/library/windows/desktop/dd162729(v=vs.85).aspx。 - Sandburg

3

SystemParametersInfo在你只想要“工作区域”= 屏幕上未被系统任务栏或应用程序桌面工具栏遮挡的部分时非常有用 :) - jave.web

3

MFC示例-使用GetSystemMetrics、EnumDisplayMonitors和GetMonitorInfo实现多显示器支持

请点击链接查看示例源代码:使用源代码枚举显示器


2

获取真实的显示器分辨率

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,需要复制)。
这将给你正确的值。

2

大约一周前删除,然后于3-4-13进行了编辑。

以下是一个适用于用户决定以较低分辨率运行桌面(不好的主意)或某些情况下人们决定购买他们的图形控制器无法充分利用的显示器的情况的良好解决方案:

// get actual size of desktop
RECT actualDesktop;
GetWindowRect(GetDesktopWindow(), &actualDesktop);

1
GetWindowRect(GetDesktopWindow) 是完全错误的。它会故意返回主监视器的矩形。 "由 GetWindowRect 或 GetClientRect 返回的桌面窗口的矩形始终等于主监视器的矩形,以便与现有应用程序兼容。" - Ian Boyd

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