lib/Win32Interop/DisplayConfig.cs

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
 
// Source: adapted from https://stackoverflow.com/questions/16082330/communicating-with-windows7-display-api
// TODO add explicit value type marshaling for everything in this class to be thorough
public class DisplayConfig
{
    // See WinUser.h from Windows SDK for windows internal method definition
    // See wingdi.h from Windows SDK for windows internal nested enum definitions
    // See generic win32 error codes for windows internal return val definiion
    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    public static extern int GetDisplayConfigBufferSizes(
        QueryDisplayConfigFlags flags,
        ref uint numPathArrayElements,
        ref uint numModeInfoArrayElements
    );
 
    // See WinUser.h from Windows SDK for windows internal method definition
    // See wingdi.h from Windows SDK for windows internal nested enum definitions
    // See generic win32 error codes for windows internal return val definiion
    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    private static extern int QueryDisplayConfig(
        // DatabaseCurrent not supported (since we can't marshal required CurrentTopologyId values for it)
        QueryDisplayConfigFlags flags,
        ref uint numPathArrayElements,
        [Out] DisplayConfigPathInfo[] pathInfoArray,
        ref uint modeInfoArrayElements,
        [Out] DisplayConfigModeInfo[] modeInfoArray,
        // The actual type is a nullable out DisplayConfigTopologyId only required if flags has DatabaseCurrent
        // An IntPtr can still be marshaled allowing us to pass something recognized as a null pointer
        // TODO support somehow passing an actual DisplayConfigTopologyId if needed (marshalas IsAny?)
        IntPtr currentTopologyId
    );
 
    // Wrapper to get around incompatibilities with more generic typing in powershell and make it clear topology id is not supported
    public static int QueryDisplayConfig(
        QueryDisplayConfigFlags flags,
        ref uint pathsCount,
        [In, Out] ref DisplayConfigPathInfo[] paths,
        ref uint modesCount,
        [In, Out] ref DisplayConfigModeInfo[] modes
    ) {
        paths = new DisplayConfigPathInfo[pathsCount];
        modes = new DisplayConfigModeInfo[modesCount];
        int result = QueryDisplayConfig(flags, ref pathsCount, paths, ref modesCount, modes, IntPtr.Zero);
        // QueryDisplayConfig may return more than predicted, truncate the array to the predicted size.
        Array.Resize(ref paths, (int)pathsCount);
        Array.Resize(ref modes, (int)modesCount);
        return result;
    }
 
    // See WinUser.h from Windows SDK for windows internal method definition
    // See generic win32 error codes for windows internal return val definiion
    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    public static extern int DisplayConfigGetDeviceInfo(
        [In, Out] ref DisplayConfigTargetDeviceName targetDeviceName
    );
 
    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    public static extern int DisplayConfigGetDeviceInfo(
        [In, Out] ref DisplayConfigGetAdvancedColorInfo getAdvancedColorInfo
    );
 
    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    public static extern int DisplayConfigGetDeviceInfo(
        [In, Out] ref DisplayConfigTargetPreferredMode targetPreferredMode
    );
 
    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    public static extern int DisplayConfigGetDeviceInfo(
        [In, Out] ref DisplayConfigAdapterName adapterName
    );
 
    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    public static extern int DisplayConfigGetDeviceInfo(
        [In, Out] ref DisplayConfigSourceDeviceName sourceDeviceName
    );
 
    // TODO marshall and implement the below for logging purposes
    // [DllImport("User32.dll", CharSet = CharSet.Auto)]
    // public static extern int DisplayConfigGetDeviceInfo(
    // [In, Out] ref DisplayConfigTargetAdapterName targetAdapterName
    // );
 
    // See WinUser.h from Windows SDK for windows internal method definition
    // See wingdi.h from Windows SDK for windows internal nested enum definitions
    // See generic win32 error codes for windows internal return val definiion
    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    public static extern int SetDisplayConfig(
        uint numPathArrayElements,
        [In] DisplayConfigPathInfo[] pathArray,
        uint numModeInfoArrayElements,
        [In] DisplayConfigModeInfo[] modeInfoArray,
        SetDisplayConfigFlags flags
    );
 
    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    public static extern int DisplayConfigSetDeviceInfo(
        [In, Out] ref DisplayConfigSetAdvancedColorInfo setAdvancedColorInfo
    );
 
    public enum DisplayConfigTopologyId : uint {
        DISPLAYCONFIG_TOPOLOGY_INTERNAL = 0x00000001,
        DISPLAYCONFIG_TOPOLOGY_CLONE = 0x00000002,
        DISPLAYCONFIG_TOPOLOGY_EXTEND = 0x00000004,
        DISPLAYCONFIG_TOPOLOGY_EXTERNAL = 0x00000008,
        DISPLAYCONFIG_TOPOLOGY_FORCE_UINT32 = 0xFFFFFFFF
    }
 
    [StructLayout(LayoutKind.Sequential)]
    public struct LUID
    {
        public uint LowPart;
        public int HighPart;
    }
 
    [Flags]
    public enum DisplayConfigVideoOutputTechnology : uint {
        Other = 4294967295, // -1
        Hd15 = 0,
        Svideo = 1,
        CompositeVideo = 2,
        ComponentVideo = 3,
        Dvi = 4,
        Hdmi = 5,
        Lvds = 6,
        DJpn = 8,
        Sdi = 9,
        DisplayportExternal = 10,
        DisplayportEmbedded = 11,
        UdiExternal = 12,
        UdiEmbedded = 13,
        Sdtvdongle = 14,
        Internal = 0x80000000,
        ForceUint32 = 0xFFFFFFFF
    }
 
    [Flags]
    public enum SetDisplayConfigFlags : uint {
        None = 0x0,
        TopologyInternal = 0x00000001,
        TopologyClone = 0x00000002,
        TopologyExtend = 0x00000004,
        TopologyExternal = 0x00000008,
        TopologySupplied = 0x00000010,
        UseSuppliedDisplayConfig = 0x00000020,
        Validate = 0x00000040,
        Apply = 0x00000080,
        NoOptimization = 0x00000100,
        SaveToDatabase = 0x00000200,
        AllowChanges = 0x00000400,
        PathPersistIfRequired = 0x00000800,
        ForceModeEnumeration = 0x00001000,
        AllowPathOrderChanges = 0x00002000,
        UseDatabaseCurrent = TopologyInternal | TopologyClone | TopologyExtend | TopologyExternal
    }
 
    [Flags]
    public enum DisplayConfigSourceStatus : uint
    {
        None = 0x0,
        InUse = 0x00000001
    }
 
    [Flags]
    public enum DisplayConfigTargetStatus : uint
    {
        Nne = 0x0,
        InUse = 0x00000001,
        FORCIBLE = 0x00000002,
        ForcedAvailabilityBoot = 0x00000004,
        ForcedAvailabilityPath = 0x00000008,
        ForcedAvailabilitySystem = 0x00000010,
    }
 
    [Flags]
    public enum DisplayConfigRotation : uint
    {
        None = 0x0,
        Identity = 1,
        Rotate90 = 2,
        Rotate180 = 3,
        Rotate270 = 4,
        ForceUint32 = 0xFFFFFFFF
    }
 
    [Flags]
    public enum DisplayConfigPixelFormat : uint
    {
        None = 0x0,
        Pixelformat8Bpp = 1,
        Pixelformat16Bpp = 2,
        Pixelformat24Bpp = 3,
        Pixelformat32Bpp = 4,
        PixelformatNongdi = 5,
        PixelformatForceUint32 = 0xffffffff
    }
 
    [Flags]
    public enum DisplayConfigScaling : uint
    {
        None = 0x0,
        Identity = 1,
        Centered = 2,
        Stretched = 3,
        Aspectratiocenteredmax = 4,
        Custom = 5,
        Preferred = 128,
        ForceUint32 = 0xFFFFFFFF
    }
 
    [StructLayout(LayoutKind.Sequential)]
    public struct DisplayConfigRational
    {
        public uint numerator;
        public uint denominator;
    }
 
    [Flags]
    public enum DisplayConfigScanLineOrdering : uint
    {
        Unspecified = 0,
        Progressive = 1,
        Interlaced = 2,
        InterlacedUpperfieldfirst = Interlaced,
        InterlacedLowerfieldfirst = 3,
        ForceUint32 = 0xFFFFFFFF
    }
 
    [StructLayout(LayoutKind.Sequential)]
    public struct DisplayConfigPathInfo
    {
        public DisplayConfigPathSourceInfo sourceInfo;
        public DisplayConfigPathTargetInfo targetInfo;
        public DisplayConfigPathInfoFlags flags;
    }
 
    [Flags]
    public enum DisplayConfigPathInfoFlags : uint
    {
        None = 0x0,
        PathActive = 0x00000001,
        PathSupportVirtualMode = 0x00000008,
        PathBoostRefreshRate = 0x00000010
    }
 
    [Flags]
    public enum DisplayConfigModeInfoType : uint
    {
        None = 0x0,
        Source = 1,
        Target = 2,
        ForceUint32 = 0xFFFFFFFF
    }
 
    [StructLayout(LayoutKind.Explicit)]
    public struct DisplayConfigModeInfo
    {
        [FieldOffset((0))]
        public DisplayConfigModeInfoType infoType;
        [FieldOffset(4)]
        public uint id;
        [FieldOffset(8)]
        public LUID adapterId;
        [FieldOffset(16)]
        public DisplayConfigTargetMode targetMode;
        [FieldOffset(16)]
        public DisplayConfigSourceMode sourceMode;
    }
 
    [StructLayout(LayoutKind.Sequential)]
    public struct DisplayConfig2DRegion
    {
        public uint cx;
        public uint cy;
    }
 
    [Flags]
    public enum D3DmdtVideoSignalStandard : uint
    {
        Uninitialized = 0,
        VesaDmt = 1,
        VesaGtf = 2,
        VesaCvt = 3,
        Ibm = 4,
        Apple = 5,
        NtscM = 6,
        NtscJ = 7,
        Ntsc443 = 8,
        PalB = 9,
        PalB1 = 10,
        PalG = 11,
        PalH = 12,
        PalI = 13,
        PalD = 14,
        PalN = 15,
        PalNc = 16,
        SecamB = 17,
        SecamD = 18,
        SecamG = 19,
        SecamH = 20,
        SecamK = 21,
        SecamK1 = 22,
        SecamL = 23,
        SecamL1 = 24,
        Eia861 = 25,
        Eia861A = 26,
        Eia861B = 27,
        PalK = 28,
        PalK1 = 29,
        PalL = 30,
        PalM = 31,
        Other = 255
    }
 
    [StructLayout(LayoutKind.Sequential)]
    public struct DisplayConfigVideoSignalInfo
    {
        public long pixelRate;
        public DisplayConfigRational hSyncFreq;
        public DisplayConfigRational vSyncFreq;
        public DisplayConfig2DRegion activeSize;
        public DisplayConfig2DRegion totalSize;
        public D3DmdtVideoSignalStandard videoStandard;
        public DisplayConfigScanLineOrdering ScanLineOrdering;
    }
 
    [StructLayout(LayoutKind.Sequential)]
    public struct DisplayConfigTargetMode
    {
        public DisplayConfigVideoSignalInfo targetVideoSignalInfo;
    }
 
    [StructLayout(LayoutKind.Sequential)]
    public struct PointL
    {
        public long x;
        public long y;
    }
 
    [StructLayout(LayoutKind.Sequential)]
    public struct DisplayConfigSourceMode
    {
        public uint width;
        public uint height;
        public DisplayConfigPixelFormat pixelFormat;
        public PointL position;
    }
 
    [StructLayout(LayoutKind.Sequential)]
    public struct DisplayConfigPathSourceInfo
    {
        public LUID adapterId;
        public uint id;
        public uint modeInfoIdx;
        public DisplayConfigSourceStatus statusFlags;
    }
 
    [StructLayout(LayoutKind.Sequential)]
    public struct DisplayConfigPathTargetInfo
    {
        public LUID adapterId;
        public uint id;
        public uint modeInfoIdx;
        public DisplayConfigVideoOutputTechnology outputTechnology;
        public DisplayConfigRotation rotation;
        public DisplayConfigScaling scaling;
        public DisplayConfigRational refreshRate;
        public DisplayConfigScanLineOrdering scanLineOrdering;
        public bool targetAvailable;
        public DisplayConfigTargetStatus statusFlags;
    }
 
    [Flags]
    public enum QueryDisplayConfigFlags : uint
    {
        None = 0x0,
        AllPaths = 0x00000001,
        OnlyActivePaths = 0x00000002,
        DatabaseCurrent = 0x00000004
    }
 
    [StructLayout(LayoutKind.Sequential)]
    public struct DisplayConfigDeviceInfoHeader
    {
        public DisplayConfigDeviceInfoType type;
        public uint size;
        public LUID adapterId;
        public uint id;
    }
 
    public enum DisplayConfigDeviceInfoType : 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_GET_SUPPORT_VIRTUAL_RESOLUTION = 7,
        DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION = 8,
        DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO = 9,
        DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE = 10,
        DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL = 11,
        DISPLAYCONFIG_DEVICE_INFO_FORCE_UINT32 = 0xFFFFFFFF
    }
 
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct DisplayConfigSourceDeviceName
    {
        public DisplayConfigDeviceInfoHeader header;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string viewGdiDeviceName;
    }
 
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct DisplayConfigAdapterName
    {
        public DisplayConfigDeviceInfoHeader header;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string adapterDevicePath;
    }
 
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct DisplayConfigTargetDeviceName
    {
        public DisplayConfigDeviceInfoHeader header;
        public DisplayConfigTargetDeviceNameFlags flags;
        public DisplayConfigVideoOutputTechnology outputTechnology;
        public ushort edidManufacturerId;
        public ushort edidProductCodeId;
        public uint connectorInstance;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
        public string monitorFriendlyDeviceName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string monitorDevicePath;
    }
 
    [StructLayout(LayoutKind.Sequential)]
    public struct DisplayConfigTargetDeviceNameFlags {
        public DisplayConfigTargetDeviceNameFlagValue value;
    }
 
    [Flags]
 
    public enum DisplayConfigTargetDeviceNameFlagValue : uint
    {
        FRIENDLY_NAME_FROM_EDID = 0x00000001,
        FRIENDLY_NAME_FORCED = 0x00000002,
        EDID_IS_VALID = 0x00000004,
        RESERVED = 0xFFFFFFF8
    }
 
    [StructLayout(LayoutKind.Sequential)]
    public struct DisplayConfigTargetPreferredMode
    {
        public DisplayConfigDeviceInfoHeader header;
        public uint width;
        public uint height;
        public DisplayConfigTargetMode targetMode;
    }
 
    [StructLayout(LayoutKind.Sequential)]
    public struct DisplayConfigGetAdvancedColorInfo
    {
        public DisplayConfigDeviceInfoHeader header;
        public DisplayConfigGetAdvancedColorInfoValues values;
        public DisplayConfigColorEncoding colorEncoding;
        public int bitsPerColorChannel;
    }
 
    [Flags]
    public enum DisplayConfigGetAdvancedColorInfoValues : uint {
        // A type of advanced color is supported
        AdvancedColorSupported = 0x1,
        // A type of advanced color is enabled
        AdvancedColorEnabled = 0x2,
        // Wide color gamut is enabled
        WideColorEnforced = 0x4,
        // Advanced color is force disabled due to system/OS policy
        AdvancedColorForceDisabled = 0x8
    }
 
    public enum DisplayConfigColorEncoding : uint
    {
        DISPLAYCONFIG_COLOR_ENCODING_RGB = 0,
        DISPLAYCONFIG_COLOR_ENCODING_YCBCR444 = 1,
        DISPLAYCONFIG_COLOR_ENCODING_YCBCR422 = 2,
        DISPLAYCONFIG_COLOR_ENCODING_YCBCR420 = 3,
        DISPLAYCONFIG_COLOR_ENCODING_INTENSITY = 4,
    }
     
    [StructLayout(LayoutKind.Sequential)]
    public struct DisplayConfigSetAdvancedColorInfo
    {
        public DisplayConfigDeviceInfoHeader header;
        public DisplayConfigSetAdvancedColorInfoValues values;
    }
 
    [Flags]
    public enum DisplayConfigSetAdvancedColorInfoValues : uint
    {
        EnableAdvancedColor = 0x1
    }
}