将 System.Windows.Form.Screen 链接到监视器的序列号

Linking System.Windows.Form.Screen to the monitor's serial code

我正在尝试找到一种方法来存储与特定监视器有关的数据。使用 System.Windows.Forms.ScreenDeviceName 属性 只包含像 \.\DISPLAY1 这样的字符串,它仅基于监视器的索引。我需要该显示器的唯一 ID,而不是它在显示设置中的顺序。

我需要link这个显示器ID到它的工作区

如果没有必要,我不会使用 System.Windows.Forms.Screen

我也有类似的要求。我正在解析 Windows 注册表中的 EDIDs 以获取特定于监视器的 ID 和一些其他信息。 EDID 信息存储在以下键中:

"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\DISPLAY\{DevideId}\Device Parameters"

我使用额外的 WMI 查询来获取 {DeviceId}。 EDID 包含显示器型号以及序列号和屏幕尺寸。也看看 EDID data format 。希望对你有所帮助。

这是我解析 EDID 信息的代码片段:

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();

我采用了与霍夫迈斯特略有不同的方法;我正在使用 P/Invoke 获取表单监视器的显示名称,使用 MonitorFromWindow + GetMonitorInfo (this can be turned into a handy extension method), and then use EnumDisplayDevices 获取显示列表以查看是哪一个,然后发出 PnP 设备 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;
    }
}