Windows上的屏幕尺寸以英寸为单位。

4
我正在开发一款多平台游戏,可以在iOS和桌面电脑(Windows、Mac、Linux)上运行。我希望游戏能够根据屏幕的分辨率以英寸为单位调整某些UI元素的大小。我的想法是,如果一个按钮在任何界面中应该是大约1/2英寸宽,它将自动缩放到这个大小。
现在对于iOS设备,这个问题可以通过使用暴力技巧来解决。您可以查找设备类型并使用硬编码表来确定每个设备的屏幕尺寸(以英寸为单位)。这不是最优雅的解决方案,但已经足够了。
桌面电脑则比较棘手。我所希望的是,(某些?)显示器会向操作系统报告它们实际的屏幕尺寸(以英寸为单位)的机制存在。如果这种机制存在并且我可以以某种方式访问它,我至少可以得到一些显示器的好数据。但我从未在任何主要操作系统API中遇到过这样的概念。
在Win32中有一种方法可以查询屏幕尺寸吗?如果有,是否有显示器实际提供此信息?
(如果答案是否定的:天哪,这看起来非常有用!)

看起来最好按平台分开,因为每个平台都有不同的正确答案。你已经有了一个关于Windows的答案,所以我建议将这个问题设为关于Windows的问题,并另外发布一个关于OS X的问题。(我有一个答案给你,我会在那个问题上发布它。) - Peter Hosey
1
https://dev59.com/1Wcs5IYBdhLWcg3ww2xP - geowar
2个回答

3

3
避免使用GetDeviceCaps(VERTSIZE)GetDeviceCaps(HORZSIZE)有什么特殊原因吗?除非它们存在问题,否则它们似乎可以更直接地提供所需的信息。 - Jerry Coffin
1
@JerryCoffin - 显然在Windows 7上这些东西的可靠性存在一些问题。请参见http://social.msdn.microsoft.com/Forums/vstudio/en-US/6f06fa5e-1626-4668-b0ee-1f0d07e8d175/getdevicecapshorzsize-is-incorrect-in-windows-7。 - HerrJoebob
遗憾的是,根据我的卷尺测量,VERTSIZE和HORZSIZE产生了不正确的测量结果。我的小屏幕笔记本比我的大型台式机显示器多出了几毫米。那么,你如何才能准确地打印呢?有一本名为《小窗口》的书涵盖了这个主题。 - user13947194
@user13947194 很抱歉,看起来你错了。在依赖于逻辑像素之前,你可能已经错过了对_SetProcessDPIAware()_(我的答案第一行的文字)的调用。 - HerrJoebob
事实上我注意到了。我无法使用该函数,因为它在winxp上不可用。而在清单中使用DPI在win7上也无法运行。问题是,就我所知,DPI只是您设置的一个数字。随时用户可以选择更高或更低的dpi,这将导致Dpi感知应用程序必须绘制更大或更小的图像。与屏幕的实际大小无关。 - user13947194
我查看了SetMapMode(HDC, MM_LOMETRIC);,它承诺将逻辑单位映射到0.1毫米。但是我得到的结果非常不准确。幸运的是,唯一获得准确结果的方法是知道您的屏幕大小并将其除以屏幕分辨率。只有HwInfo应用程序可以从系统中获取屏幕大小的方法。 - user13947194

-1

这里有一个我在网址"https://ofekshilon.com/2011/11/13/reading-monitor-physical-dimensions-or-getting-the-edid-the-right-way/"上找到的方法。

作者说测量单位是毫米。

虽然不能给出精确的宽度和高度,但比HORSIZE和VERTSIZE更接近。我在两台不同的显示器上尝试了一下,得到了最大38厘米的差异(测量屏幕大小 - 计算屏幕大小)。

#include <SetupApi.h>
#pragma comment(lib, "setupapi.lib")

#define NAME_SIZE 128

const GUID GUID_CLASS_MONITOR = {0x4d36e96e, 0xe325, 0x11ce, 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18};

// Assumes hDevRegKey is valid
bool GetMonitorSizeFromEDID(const HKEY hDevRegKey, short& WidthMm, short& HeightMm)
{
    DWORD dwType, AcutalValueNameLength = NAME_SIZE;
    TCHAR valueName[NAME_SIZE];

    BYTE EDIDdata[1024];
    DWORD edidsize=sizeof(EDIDdata);

    for (LONG i = 0, retValue = ERROR_SUCCESS; retValue != ERROR_NO_MORE_ITEMS; ++i)
    {
        retValue = RegEnumValueA ( hDevRegKey, i, &valueName[0],
            &AcutalValueNameLength, NULL, &dwType,
            EDIDdata, // buffer
            &edidsize); // buffer size

        if (retValue != ERROR_SUCCESS || 0 != strcmp(valueName,"EDID"))
            continue;

        WidthMm  = ((EDIDdata[68] & 0xF0) << 4) + EDIDdata[66];
        HeightMm = ((EDIDdata[68] & 0x0F) << 8) + EDIDdata[67];
        return true; // valid EDID found
    }

    return false; // EDID not found
}

// strange! Authour requires TargetDevID argument but does not use it
bool GetSizeForDevID(const char *TargetDevID, short& WidthMm, short& HeightMm)
{
    HDEVINFO devInfo = SetupDiGetClassDevsExA(
                &GUID_CLASS_MONITOR, //class GUID
                NULL, //enumerator
                NULL, //HWND
                DIGCF_PRESENT, // Flags //DIGCF_ALLCLASSES|
                NULL, // device info, create a new one.
                NULL, // machine name, local machine
                NULL);// reserved

    if (NULL == devInfo)  return false;

    bool bRes = false;
    for (ULONG i=0; ERROR_NO_MORE_ITEMS != GetLastError(); ++i)
    {
        SP_DEVINFO_DATA devInfoData;
        memset(&devInfoData,0,sizeof(devInfoData));
        devInfoData.cbSize = sizeof(devInfoData);

        if (SetupDiEnumDeviceInfo(devInfo,i,&devInfoData))
        {
            HKEY hDevRegKey = SetupDiOpenDevRegKey(devInfo,&devInfoData,DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
            if(!hDevRegKey || (hDevRegKey == INVALID_HANDLE_VALUE)) continue;

            bRes = GetMonitorSizeFromEDID(hDevRegKey, WidthMm, HeightMm);
            RegCloseKey(hDevRegKey);
        }
    }

    SetupDiDestroyDeviceInfoList(devInfo);
    return bRes;
}

int main(int argc, CHAR* argv[])
{
    short WidthMm, HeightMm;

    DISPLAY_DEVICE dd;
    dd.cb = sizeof(dd);

    DWORD dev = 0; // device index
    int id = 1; // monitor number, as used by Display Properties > Settings

    char DeviceID[1024];
    bool bFoundDevice = false;

    while (EnumDisplayDevices(0, dev, &dd, 0) && !bFoundDevice)
    {
        DISPLAY_DEVICE ddMon = {sizeof(ddMon)};
        DWORD devMon = 0;

        while (EnumDisplayDevices(dd.DeviceName, devMon, &ddMon, 0) && !bFoundDevice)
        {
            if (ddMon.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP &&
                !(ddMon.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
            {
                sprintf(DeviceID,"%s", ddMon.DeviceID+8);

                for(auto it=DeviceID; *it; ++it)
                if(*it == '\\') { *it = 0; break; }
             
                bFoundDevice = GetSizeForDevID(DeviceID, WidthMm, HeightMm);
            }
            devMon++;

            ZeroMemory(&ddMon, sizeof(ddMon));
            ddMon.cb = sizeof(ddMon);
        }

        ZeroMemory(&dd, sizeof(dd));
        dd.cb = sizeof(dd);
        dev++;
    }
    
    return 0;
}

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