3

Is it possible to get the real screen size in cm not in pixels ? ie i need to know the size of the sreen not its resolution.

I need this if it's possible in a windows application.

Hicham
  • 983
  • 6
  • 17
  • 1
    yes, it's possible ;) – Xaruth Nov 05 '14 at 13:38
  • @Xaruth i think your'e wrong: http://stackoverflow.com/questions/6363752/acquire-monitor-physical-dimension – No Idea For Name Nov 05 '14 at 13:39
  • See http://ofekshilon.com/2011/11/13/reading-monitor-physical-dimensions-or-getting-the-edid-the-right-way/ Not sure this is accurate or correct though since @NoIdeaForName posted a link to a question, where if you follow the link to the duplicate it refers to the answer says no, can't be done. – Lasse V. Karlsen Nov 05 '14 at 13:44
  • @NoIdeaForName no, I think I'm right ;) – Xaruth Nov 05 '14 at 13:54
  • @Xaruth after looking more to it, i think you are partially right :) and i also think this question should get more pluses cus' it should be good for some other programers – No Idea For Name Nov 05 '14 at 14:02
  • I updated my answer to be more complete (NativeMethods for having connected monitors, get ratio and diagonal, ...) – Xaruth Nov 05 '14 at 14:11

2 Answers2

5

All information about screen (from manufacturer) is in registry HKLM\SYSTEM\CurrentControlSet\Enum\DISPLAY. The size of the screen is encoded, and hard to find, but it's possible.

For more information, search on the web for : EDID ("Extended display identification data") ( http://en.wikipedia.org/wiki/Extended_display_identification_data , the bytes for the size are #21 and #22)

Here the code I use to have the size (and more information, but I cleaned the code to have only the size) :

// Open the Display Reg-Key
RegistryKey displayRegistry = Registry.LocalMachine;
Boolean isFailed = false;
try
{
    displayRegistry = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Enum\DISPLAY");
}
catch
{
    isFailed = true;
}

if (!isFailed & (displayRegistry != null))
{
    // Get all MonitorIDss
    foreach (String monitorID in displayRegistry.GetSubKeyNames())
    {
        if (monitorID == name)
        {
            RegistryKey monitorIDRegistry = displayRegistry.OpenSubKey(monitorID);

            if (monitorIDRegistry != null)
            {
                // Get all Plug&Play ID's
                foreach (String subname in monitorIDRegistry.GetSubKeyNames())
                {
                    RegistryKey pnpID = monitorIDRegistry.OpenSubKey(subname);
                    if (pnpID != null)
                    {
                        String[] subkeys = pnpID.GetSubKeyNames();

                        // Check if Monitor is active
                        if (subkeys.Contains("Control"))
                        {
                            if (subkeys.Contains("Device Parameters"))
                            {
                                RegistryKey devParam = pnpID.OpenSubKey("Device Parameters");

                                Int16 sizeH = 0;
                                Int16 sizeV = 0;

                                // Get the EDID code
                                byte[] edidObj = devParam.GetValue("EDID", null) as byte[];
                                if (edidObj != null)
                                {
                                    sizeH = Convert.ToInt16(Encoding.Default.GetString(edidObj, 0x15, 1)[0]);
                                    sizeV = Convert.ToInt16(Encoding.Default.GetString(edidObj, 0x16, 1)[0]);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

The size is in cm (it's integer only).

You can have with this physical ratio and diagonal :

private static String GetRatio(Double MaxSizeH, Double MaxSizeV)
{
    if (MaxSizeV == 0)
    {
        return "undefined";
    }

    Double ratio = MaxSizeH / MaxSizeV;

    String strRatio = "4/3";
    Double ecartRatio = Math.Abs(ratio - (4 / (Double)3));

    if (Math.Abs(ratio - (16 / (Double)10)) < ecartRatio)
    {
        ecartRatio = Math.Abs(ratio - (16 / (Double)10));
        strRatio = "16/10";
    }

    if (Math.Abs(ratio - (16 / (Double)9)) < ecartRatio)
    {
        ecartRatio = Math.Abs(ratio - (16 / (Double)9));
        strRatio = "16/9";
    }

    return strRatio;
}

// diagonal in inch
private static Double GetDiagonale(Double MaxSizeH, Double MaxSizeV)
{
    return 0.3937 * Math.Sqrt(MaxSizeH * MaxSizeH + MaxSizeV * MaxSizeV);
}

Edit: How to have monitor name (all connected monitors) :

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DISPLAY_DEVICE
{
    [MarshalAs(UnmanagedType.U4)]
    public int cb;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string DeviceName;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
    public string DeviceString;

    [MarshalAs(UnmanagedType.U4)]
    public DisplayDeviceStateFlags StateFlags;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
    public string DeviceID;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
    public string DeviceKey;
}

[Flags]
public enum DisplayDeviceStateFlags : int
{
    /// <summary>The device is part of the desktop.</summary>
    AttachedToDesktop = 0x1,

    MultiDriver = 0x2,

    /// <summary>The device is part of the desktop.</summary>
    PrimaryDevice = 0x4,

    /// <summary>Represents a pseudo device used to mirror application drawing for remoting or other purposes.</summary>
    MirroringDriver = 0x8,

    /// <summary>The device is VGA compatible.</summary>
    VGACompatible = 0x10,

    /// <summary>The device is removable; it cannot be the primary display.</summary>
    Removable = 0x20,

    /// <summary>The device has more display modes than its output devices support.</summary>
    ModesPruned = 0x8000000,

    Remote = 0x4000000,
    Disconnect = 0x2000000
}

[DllImport("User32.dll")]
public static extern int EnumDisplayDevices(string lpDevice, int iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, int dwFlags);


private static List<NativeMethods.DISPLAY_DEVICE> GetAllDevice()
{
    List<NativeMethods.DISPLAY_DEVICE> devices = new List<NativeMethods.DISPLAY_DEVICE>();
    bool error = false;
    for (int devId = 0; !error; devId++)
    {
        try
        {
            NativeMethods.DISPLAY_DEVICE device = new NativeMethods.DISPLAY_DEVICE();
            device.cb = Marshal.SizeOf(typeof(NativeMethods.DISPLAY_DEVICE));
            error = NativeMethods.EnumDisplayDevices(null, devId, ref device, 0) == 0;
            if (String.IsNullOrWhiteSpace(device.DeviceID) == false)
            {
                devices.Add(device);
            }
        }
        catch (Exception)
        {
            error = true;
        }
    }

    return devices;
}

and to finish :

List<NativeMethods.DISPLAY_DEVICE> devices = GetAllDevice();

foreach (NativeMethods.DISPLAY_DEVICE device in devices)
{
    NativeMethods.DISPLAY_DEVICE monitor = new NativeMethods.DISPLAY_DEVICE();
    monitor.cb = Marshal.SizeOf(typeof(NativeMethods.DISPLAY_DEVICE));
    NativeMethods.EnumDisplayDevices(device.DeviceName, 0, ref monitor, 0);
    String monitorname = monitor.DeviceID.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries).Skip(1).FirstOrDefault();

    GetMonitorDetail(monitorname);
}
Xaruth
  • 4,034
  • 3
  • 19
  • 26
  • it is proper solution (devcaps returns wrong values), one more question: how to get monitor name from .net Graphics properly? – user861768 Sep 16 '19 at 15:26
3

i found this

public class NativeMethods
{
  [DllImport("gdi32.dll", EntryPoint = "CreateDC", CharSet = CharSet.Auto, SetLastError = true)]
  private static extern IntPtr CreateDC(string lpszDriver, string lpszDeviceName, string lpszOutput, IntPtr devMode);

  [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
  static extern bool DeleteDC(IntPtr hdc);

  [DllImport("gdi32.dll", SetLastError = true)]
  private static extern Int32 GetDeviceCaps(IntPtr hdc, Int32 capindex);
  private const int LOGPIXELSX = 88;

  private static int _dpi = -1;
  public static int DPI
  {
   get
   {
    if (_dpi != -1)
     return _dpi;

    _dpi = 96;
    try
    {
     IntPtr hdc = CreateDC("DISPLAY", null, null, IntPtr.Zero);
     if (hdc != IntPtr.Zero)
     {
      _dpi = GetDeviceCaps(hdc, LOGPIXELSX);
      if (_dpi == 0)
       _dpi = 96;
      DeleteDC(hdc);
     }
    }
    catch (Exception)
    {
    }

    return _dpi;
   }
  }
}

that uses PInvoke to get the DPI in other question about the subject and i post it here because i only saw links.

i will mention this is not a common thing and i don't know why you will need that. this sounds like an A-B problem, so i'll say you need to be absolutly sure you need the DPI before using that

look at Lasse V. Karlsen comment please. this might not be accurate, so take that with care

No Idea For Name
  • 11,411
  • 10
  • 42
  • 70
  • Looks promising, but not sure how correct/accurate this. My BENQ BL2400 screen, reports a DPI of 96, and is full HD, but this equates to 22.94 inches, not 24 as it is supposed to. – Lasse V. Karlsen Nov 05 '14 at 13:47
  • @LasseV.Karlsen i looked up for ppl using this code, some said it worked well, and some said it didn't work at all. haven't seen "not accurate" comment, but i'll post it in the answer – No Idea For Name Nov 05 '14 at 13:49