From 265611009cdda5ff3fd0284ed96d10bf8cbc90b9 Mon Sep 17 00:00:00 2001 From: seerge Date: Wed, 8 Mar 2023 22:44:14 +0100 Subject: [PATCH] Improved internal display detection --- NativeMethods.cs | 426 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 325 insertions(+), 101 deletions(-) diff --git a/NativeMethods.cs b/NativeMethods.cs index a15e3132..d5010ccd 100644 --- a/NativeMethods.cs +++ b/NativeMethods.cs @@ -1,7 +1,311 @@ -using System.Diagnostics; +using System.ComponentModel; +using System.Diagnostics; using System.Management; using System.Runtime.InteropServices; using System.Windows.Forms; +using Tools; +using static Tools.ScreenInterrogatory; + +namespace Tools +{ + public static class ScreenInterrogatory + { + public const int ERROR_SUCCESS = 0; + + #region enums + + public enum QUERY_DEVICE_CONFIG_FLAGS : uint + { + QDC_ALL_PATHS = 0x00000001, + QDC_ONLY_ACTIVE_PATHS = 0x00000002, + QDC_DATABASE_CURRENT = 0x00000004 + } + + public enum DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY : uint + { + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER = 0xFFFFFFFF, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HD15 = 0, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SVIDEO = 1, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPOSITE_VIDEO = 2, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPONENT_VIDEO = 3, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DVI = 4, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI = 5, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_LVDS = 6, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_D_JPN = 8, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDI = 9, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL = 10, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED = 11, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EXTERNAL = 12, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED = 13, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDTVDONGLE = 14, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_MIRACAST = 15, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL = 0x80000000, + DISPLAYCONFIG_OUTPUT_TECHNOLOGY_FORCE_UINT32 = 0xFFFFFFFF + } + + public enum DISPLAYCONFIG_SCANLINE_ORDERING : uint + { + DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED = 0, + DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE = 1, + DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED = 2, + DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED_UPPERFIELDFIRST = DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED, + DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED_LOWERFIELDFIRST = 3, + DISPLAYCONFIG_SCANLINE_ORDERING_FORCE_UINT32 = 0xFFFFFFFF + } + + public enum DISPLAYCONFIG_ROTATION : uint + { + DISPLAYCONFIG_ROTATION_IDENTITY = 1, + DISPLAYCONFIG_ROTATION_ROTATE90 = 2, + DISPLAYCONFIG_ROTATION_ROTATE180 = 3, + DISPLAYCONFIG_ROTATION_ROTATE270 = 4, + DISPLAYCONFIG_ROTATION_FORCE_UINT32 = 0xFFFFFFFF + } + + public enum DISPLAYCONFIG_SCALING : uint + { + DISPLAYCONFIG_SCALING_IDENTITY = 1, + DISPLAYCONFIG_SCALING_CENTERED = 2, + DISPLAYCONFIG_SCALING_STRETCHED = 3, + DISPLAYCONFIG_SCALING_ASPECTRATIOCENTEREDMAX = 4, + DISPLAYCONFIG_SCALING_CUSTOM = 5, + DISPLAYCONFIG_SCALING_PREFERRED = 128, + DISPLAYCONFIG_SCALING_FORCE_UINT32 = 0xFFFFFFFF + } + + public enum DISPLAYCONFIG_PIXELFORMAT : uint + { + DISPLAYCONFIG_PIXELFORMAT_8BPP = 1, + DISPLAYCONFIG_PIXELFORMAT_16BPP = 2, + DISPLAYCONFIG_PIXELFORMAT_24BPP = 3, + DISPLAYCONFIG_PIXELFORMAT_32BPP = 4, + DISPLAYCONFIG_PIXELFORMAT_NONGDI = 5, + DISPLAYCONFIG_PIXELFORMAT_FORCE_UINT32 = 0xffffffff + } + + public enum DISPLAYCONFIG_MODE_INFO_TYPE : uint + { + DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE = 1, + DISPLAYCONFIG_MODE_INFO_TYPE_TARGET = 2, + DISPLAYCONFIG_MODE_INFO_TYPE_FORCE_UINT32 = 0xFFFFFFFF + } + + public enum DISPLAYCONFIG_DEVICE_INFO_TYPE : uint + { + DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1, + DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME = 2, + DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE = 3, + DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME = 4, + DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE = 5, + DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE = 6, + DISPLAYCONFIG_DEVICE_INFO_FORCE_UINT32 = 0xFFFFFFFF + } + + #endregion + + #region structs + + [StructLayout(LayoutKind.Sequential)] + public struct LUID + { + public uint LowPart; + public int HighPart; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_PATH_SOURCE_INFO + { + public LUID adapterId; + public uint id; + public uint modeInfoIdx; + public uint statusFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_PATH_TARGET_INFO + { + public LUID adapterId; + public uint id; + public uint modeInfoIdx; + private DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY outputTechnology; + private DISPLAYCONFIG_ROTATION rotation; + private DISPLAYCONFIG_SCALING scaling; + private DISPLAYCONFIG_RATIONAL refreshRate; + private DISPLAYCONFIG_SCANLINE_ORDERING scanLineOrdering; + public bool targetAvailable; + public uint statusFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_RATIONAL + { + public uint Numerator; + public uint Denominator; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_PATH_INFO + { + public DISPLAYCONFIG_PATH_SOURCE_INFO sourceInfo; + public DISPLAYCONFIG_PATH_TARGET_INFO targetInfo; + public uint flags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_2DREGION + { + public uint cx; + public uint cy; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_VIDEO_SIGNAL_INFO + { + public ulong pixelRate; + public DISPLAYCONFIG_RATIONAL hSyncFreq; + public DISPLAYCONFIG_RATIONAL vSyncFreq; + public DISPLAYCONFIG_2DREGION activeSize; + public DISPLAYCONFIG_2DREGION totalSize; + public uint videoStandard; + public DISPLAYCONFIG_SCANLINE_ORDERING scanLineOrdering; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_TARGET_MODE + { + public DISPLAYCONFIG_VIDEO_SIGNAL_INFO targetVideoSignalInfo; + } + + [StructLayout(LayoutKind.Sequential)] + public struct POINTL + { + private int x; + private int y; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_SOURCE_MODE + { + public uint width; + public uint height; + public DISPLAYCONFIG_PIXELFORMAT pixelFormat; + public POINTL position; + } + + [StructLayout(LayoutKind.Explicit)] + public struct DISPLAYCONFIG_MODE_INFO_UNION + { + [FieldOffset(0)] + public DISPLAYCONFIG_TARGET_MODE targetMode; + + [FieldOffset(0)] + public DISPLAYCONFIG_SOURCE_MODE sourceMode; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_MODE_INFO + { + public DISPLAYCONFIG_MODE_INFO_TYPE infoType; + public uint id; + public LUID adapterId; + public DISPLAYCONFIG_MODE_INFO_UNION modeInfo; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS + { + public uint value; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DISPLAYCONFIG_DEVICE_INFO_HEADER + { + public DISPLAYCONFIG_DEVICE_INFO_TYPE type; + public uint size; + public LUID adapterId; + public uint id; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct DISPLAYCONFIG_TARGET_DEVICE_NAME + { + public DISPLAYCONFIG_DEVICE_INFO_HEADER header; + public DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS flags; + public DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY outputTechnology; + public ushort edidManufactureId; + public ushort edidProductCodeId; + public uint connectorInstance; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] + public string monitorFriendlyDeviceName; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] + public string monitorDevicePath; + } + + #endregion + + #region DLL-Imports + + [DllImport("user32.dll")] + public static extern int GetDisplayConfigBufferSizes( + QUERY_DEVICE_CONFIG_FLAGS flags, out uint numPathArrayElements, out uint numModeInfoArrayElements); + + [DllImport("user32.dll")] + public static extern int QueryDisplayConfig( + QUERY_DEVICE_CONFIG_FLAGS flags, + ref uint numPathArrayElements, [Out] DISPLAYCONFIG_PATH_INFO[] PathInfoArray, + ref uint numModeInfoArrayElements, [Out] DISPLAYCONFIG_MODE_INFO[] ModeInfoArray, + IntPtr currentTopologyId + ); + + [DllImport("user32.dll")] + public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName); + + #endregion + + private static DISPLAYCONFIG_TARGET_DEVICE_NAME DeviceName(LUID adapterId, uint targetId) + { + var deviceName = new DISPLAYCONFIG_TARGET_DEVICE_NAME + { + header = + { + size = (uint)Marshal.SizeOf(typeof (DISPLAYCONFIG_TARGET_DEVICE_NAME)), + adapterId = adapterId, + id = targetId, + type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME + } + }; + var error = DisplayConfigGetDeviceInfo(ref deviceName); + if (error != ERROR_SUCCESS) + throw new Win32Exception(error); + + return deviceName; + } + + public static IEnumerable GetAllDevices() + { + uint pathCount, modeCount; + var error = GetDisplayConfigBufferSizes(QUERY_DEVICE_CONFIG_FLAGS.QDC_ONLY_ACTIVE_PATHS, out pathCount, out modeCount); + if (error != ERROR_SUCCESS) + throw new Win32Exception(error); + + var displayPaths = new DISPLAYCONFIG_PATH_INFO[pathCount]; + var displayModes = new DISPLAYCONFIG_MODE_INFO[modeCount]; + error = QueryDisplayConfig(QUERY_DEVICE_CONFIG_FLAGS.QDC_ONLY_ACTIVE_PATHS, + ref pathCount, displayPaths, ref modeCount, displayModes, IntPtr.Zero); + if (error != ERROR_SUCCESS) + throw new Win32Exception(error); + + for (var i = 0; i < modeCount; i++) + if (displayModes[i].infoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_TARGET) + yield return DeviceName(displayModes[i].adapterId, displayModes[i].id); + } + + + } + +} public class NativeMethods { @@ -162,43 +466,6 @@ public class NativeMethods string lpszDeviceName, ref DEVMODE lpDevMode, IntPtr hwnd, DisplaySettingsFlags dwflags, IntPtr lParam); - // ENUM DISPLAYS - [DllImport("user32.dll")] - static extern bool EnumDisplayDevicesA(string lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, uint dwFlags); - - [Flags()] - public enum DisplayDeviceStateFlags : int - { - AttachedToDesktop = 0x1, - MultiDriver = 0x2, - PrimaryDevice = 0x4, - MirroringDriver = 0x8, - VGACompatible = 0x10, - Removable = 0x20, - ModesPruned = 0x8000000, - Remote = 0x4000000, - Disconnect = 0x2000000 - } - - [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; - } - - // ---- - public static DEVMODE CreateDevmode() { DEVMODE dm = new DEVMODE(); @@ -209,83 +476,40 @@ public class NativeMethods } public const int ENUM_CURRENT_SETTINGS = -1; - public const string laptopScreenName = "\\\\.\\DISPLAY1"; + public const string defaultDevice = "\\\\.\\DISPLAY1"; public static string FindLaptopScreen() { string laptopScreen = null; - DISPLAY_DEVICE d = new DISPLAY_DEVICE(); - d.cb = Marshal.SizeOf(d); - - List activeScreens = new List(); - int counter = 0; - int deviceNum = -1; - try { - var searcherActive = new ManagementObjectSearcher(@"\\.\root\wmi", "SELECT * FROM WmiMonitorBasicDisplayParams"); - var resultsActive = searcherActive.Get(); - foreach (var result in resultsActive) + var devices = GetAllDevices(); + int count = 0, displayNum = -1; + + foreach (var device in devices) { - activeScreens.Add(result["InstanceName"].ToString()); + if (device.outputTechnology == DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY.DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL) + displayNum = count; + count++; + //Debug.WriteLine(device.outputTechnology); + //Debug.WriteLine(device.monitorFriendlyDeviceName); } - var searcher = new ManagementObjectSearcher(@"\\.\root\wmi", "SELECT * FROM WmiMonitorConnectionParams"); - var results = searcher.Get(); - - - foreach (var result in results) - { - long technology; - long.TryParse(result["VideoOutputTechnology"].ToString(), out technology); - string instanceName = result["InstanceName"].ToString(); - - if (technology == 0x80000000 && activeScreens.Contains(instanceName)) - { - deviceNum = counter; - //Debug.WriteLine(result["InstanceName"]); - } - counter++; - } - } catch - { - Debug.WriteLine("Failed to detect built in display"); - deviceNum = 0; - } - - - try - { - counter = 0; - for (uint id = 0; EnumDisplayDevicesA(null, id, ref d, 0); id++) - { - if ((d.StateFlags & DisplayDeviceStateFlags.AttachedToDesktop) != 0) - { - //Debug.WriteLine(d.DeviceID); - //Debug.WriteLine(d.DeviceName); - if (counter == deviceNum) - { - laptopScreen = d.DeviceName; - } - counter++; - } - - } - } catch - { - Debug.WriteLine("Failed to enumerate displays"); - } - - // Mismatch between active screens and enumerated screens, fallback to old method - if (counter != activeScreens.Count) - { - laptopScreen = null; + count = 0; foreach (var screen in Screen.AllScreens) - if (screen.DeviceName == laptopScreenName) + { + if (count == displayNum) laptopScreen = screen.DeviceName; + //Debug.WriteLine(screen.DeviceName); + count++; + } + } catch + { + Debug.WriteLine("Can't find internal screen"); } + return laptopScreen; }