Reputation: 13047
I am trying to find a way to store data pertaining to a specific monitor. Using System.Windows.Forms.Screen
, the DeviceName
property only contains a string like \\.\DISPLAY1
, which is based only on the index of the monitor. I need a unique ID for that monitor, not its order in the display setup.
I need to link this monitor ID to its working area.
I am not tied to using System.Windows.Forms.Screen
if that is not necessary.
Upvotes: 1
Views: 1632
Reputation: 3678
I took a slightly different approach from hofmeister; I'm using P/Invoke to get the display name of the Form's monitor using MonitorFromWindow + GetMonitorInfo (this can be turned into a handy extension method), and then use EnumDisplayDevices to get a list of displays to see which one that is, followed by emitting the PnP device ID:
public partial class Form1 : Form
{
[DllImport("user32.dll")]
private static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern bool GetMonitorInfo(IntPtr hMonitor, ref MonitorInfoEx lpmi);
// size of a device name string
private const int CCHDEVICENAME = 32;
/// <summary>
/// The MONITORINFOEX structure contains information about a display monitor.
/// The GetMonitorInfo function stores information into a MONITORINFOEX structure or a MONITORINFO structure.
/// The MONITORINFOEX structure is a superset of the MONITORINFO structure. The MONITORINFOEX structure adds a string member to contain a name
/// for the display monitor.
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct MonitorInfoEx
{
/// <summary>
/// The size, in bytes, of the structure. Set this member to sizeof(MONITORINFOEX) (72) before calling the GetMonitorInfo function.
/// Doing so lets the function determine the type of structure you are passing to it.
/// </summary>
public int Size;
/// <summary>
/// A RECT structure that specifies the display monitor rectangle, expressed in virtual-screen coordinates.
/// Note that if the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.
/// </summary>
public RectStruct Monitor;
/// <summary>
/// A RECT structure that specifies the work area rectangle of the display monitor that can be used by applications,
/// expressed in virtual-screen coordinates. Windows uses this rectangle to maximize an application on the monitor.
/// The rest of the area in rcMonitor contains system windows such as the task bar and side bars.
/// Note that if the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.
/// </summary>
public RectStruct WorkArea;
/// <summary>
/// The attributes of the display monitor.
///
/// This member can be the following value:
/// 1 : MONITORINFOF_PRIMARY
/// </summary>
public uint Flags;
/// <summary>
/// A string that specifies the device name of the monitor being used. Most applications have no use for a display monitor name,
/// and so can save some bytes by using a MONITORINFO structure.
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
public string DeviceName;
public void Init()
{
this.Size = 40 + 2 * CCHDEVICENAME;
this.DeviceName = string.Empty;
}
}
/// <summary>
/// The RECT structure defines the coordinates of the upper-left and lower-right corners of a rectangle.
/// </summary>
/// <see cref="http://msdn.microsoft.com/en-us/library/dd162897%28VS.85%29.aspx"/>
/// <remarks>
/// By convention, the right and bottom edges of the rectangle are normally considered exclusive.
/// In other words, the pixel whose coordinates are ( right, bottom ) lies immediately outside of the the rectangle.
/// For example, when RECT is passed to the FillRect function, the rectangle is filled up to, but not including,
/// the right column and bottom row of pixels. This structure is identical to the RECTL structure.
/// </remarks>
[StructLayout(LayoutKind.Sequential)]
public struct RectStruct
{
/// <summary>
/// The x-coordinate of the upper-left corner of the rectangle.
/// </summary>
public int Left;
/// <summary>
/// The y-coordinate of the upper-left corner of the rectangle.
/// </summary>
public int Top;
/// <summary>
/// The x-coordinate of the lower-right corner of the rectangle.
/// </summary>
public int Right;
/// <summary>
/// The y-coordinate of the lower-right corner of the rectangle.
/// </summary>
public int Bottom;
}
[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
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
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;
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, uint dwFlags);
public Form1()
{
InitializeComponent();
String thisFormsMonitor = null;
IntPtr hMon = MonitorFromWindow(this.Handle, 0);
MonitorInfoEx monInfo = new MonitorInfoEx();
monInfo.Size = 104;
if (GetMonitorInfo(hMon, ref monInfo))
{
thisFormsMonitor = monInfo.DeviceName;
}
DISPLAY_DEVICE displayDevice = new DISPLAY_DEVICE();
displayDevice.cb = Marshal.SizeOf(displayDevice);
uint deviceIndex = 0;
while (EnumDisplayDevices(null, deviceIndex, ref displayDevice, 0))
{
if (displayDevice.DeviceName == thisFormsMonitor) System.Diagnostics.Debug.WriteLine(displayDevice.DeviceID);
deviceIndex++;
}
this.Text = System.Windows.Forms.Screen.PrimaryScreen.DeviceName;
}
}
Upvotes: 2
Reputation: 3416
I had similar requirements. I am parsing the EDIDs
in the Windows Registry to get the monitor specific id and couple of other information. The EDID
information are stored in following key:
"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\DISPLAY\{DevideId}\Device Parameters"
I use an additional WMI query to get the {DeviceId}
. The EDID contains the monitor model as well as the serial number and the screen size. Have a look at the EDID data format as well. I hope that will help you.
Here is my code snippet to parse the EDID information:
var bytes = (byte[]) key.GetValue("EDID");
if (bytes == null) return;
/* Read model number */
var buffer = new byte[4];
for (var i = 54; i < 109; i += 18)
{
var sb = new StringBuilder();
Buffer.BlockCopy(bytes, i, buffer, 0, 4);
Array.Reverse(buffer);
if (BitConverter.ToInt32(buffer, 0).Equals(0xFF))
{
for (var j = i + 5; (bytes[j] != 10) && (j < i + 18); j++)
{
sb.Append((char) bytes[j]);
}
this.ModelNo = sb.ToString();
sb.Clear();
}
if (BitConverter.ToInt32(buffer, 0).Equals(0xFC))
{
for (var j = i + 5; (bytes[j] != 10) && (j < i + 18); j++)
{
sb.Append((char)bytes[j]);
}
this.Model = sb.ToString();
sb.Clear();
}
}
if (string.IsNullOrEmpty(this.ModelNo)) this.ModelNo = string.Empty;
if (string.IsNullOrEmpty(this.Model)) this.Model = string.Empty;
/* Read serial number */
buffer = new byte[4];
Buffer.BlockCopy(bytes, 12, buffer, 0, 4);
//this.SerialNo = BitConverter.ToString(buffer);
this.SerialNo = string.Concat(buffer.Select(b => b.ToString("X2")));
/* Read screen size */
var x = (int) bytes[21];
var y = (int) bytes[22];
this.Size = Math.Round(Math.Sqrt(x * x + y * y) / 2.54).ToString();
Upvotes: 2