Implementing SetupAPI in C# (WinForms) to read physical screen size

444 views Asked by At

I'm trying to read the physical size of my two monitor, for learning purpouses...

I'm doing this because I need to know exactly the distance that the mouse travels along the screen (in centimeters).

I have tried the option of the DPI, but scren always returns 96 DPI (if the scale is 100%, I had read the following)

But this is not real, because of this:

My first monitor has:

93 DPI

By dividing, 600 px / 6,417323 inches we get = 93,4 ... dpi

Aprox, 93,4 DPI.

And my second monitor:

84,6 DPI

84,6 DPI, 12 less units than the average monitor, I suppose.

So, we can't take this, because this is very unnacurate. And I want to be precise.


I tried by getting the real centimeters (reading this) and using PInvoke for Interop calls.

But as @Andreas Rejbrand has said, this returns a big value (1600 x 900 mm, a screen of 1,6 meters by 90 centimeters, that's not logic, my biggest screen has 52 centimeters by 29 centimeters, dividing 1600/900 returns almost the same as 52 by 30, that is 1,7777 but is this useful for me???)

PD: Asking to the user is not an option because they can lie to me, I don't was such things.


Another option was searching in the WinAPI for solutions, and I had found the following: (I think I will be downvoted for this xD, but I want to explain eveything I done)

Under the last answer I shared I found the following:

https://stackoverflow.com/a/3858175/3286975

This is edited by someone, I click the revisions and the user shared the following:

How to obtain the correct physical size of the monitor?

In a comment we can see the following link:

https://ofekshilon.com/2014/06/19/reading-specific-monitor-dimensions/

Well, we can find the following code:

#include <atlstr.h>
#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 = RegEnumValue ( hDevRegKey, i, &valueName[0],
            &AcutalValueNameLength, NULL, &dwType,
            EDIDdata, // buffer
            &edidsize); // buffer size

        if (retValue != ERROR_SUCCESS || 0 != _tcscmp(valueName,_T("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 } bool GetSizeForDevID(const CString& TargetDevID, short& WidthMm, short& HeightMm) {   HDEVINFO devInfo = SetupDiGetClassDevsEx(       &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 _tmain(int argc, _TCHAR* 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

    CString DeviceID;
    bool bFoundDevice = false;
    while (EnumDisplayDevices(0, dev, &dd, 0) && !bFoundDevice)
    {
        DISPLAY_DEVICE ddMon;
        ZeroMemory(&ddMon, sizeof(ddMon));
        ddMon.cb = sizeof(ddMon);
        DWORD devMon = 0;

        while (EnumDisplayDevices(dd.DeviceName, devMon, &ddMon, 0) && !bFoundDevice)
        {
            if (ddMon.StateFlags & DISPLAY_DEVICE_ACTIVE &&
                !(ddMon.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
            {
                DeviceID.Format (L"%s", ddMon.DeviceID);
                DeviceID = DeviceID.Mid (8, DeviceID.Find (L"\\", 9) - 8);

                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;
}

I have been searching in PInvoke this, and examples, but I'm very lost, because I don't know if we can translate it all to C#, or is only a C++ example that can't be translated. I suppose that it can be translated, but I don't know how, I'm very newbie at interoperability calls.

PD: I have found this: http://www.programering.com/a/MDO2YjMwATc.html but I don't know yet which methods I have to use for translating the code into C# and the parameters I have to pass.

So, if any one can teach me how to figure this out, If you want to give some codes for me, that's good, but I think that good explanation is enough. i don't know.

And if there are better options to do this, please, tell me.

Because, my final solution will be applying this (centimeters of screen) to this: https://stackoverflow.com/a/422333/3286975

Also, I'm seeing that DPI are different from PPI, I'm maybe I'm searching for PPI.

Thanks in advance!

0

There are 0 answers