Skip to content

Instantly share code, notes, and snippets.

@emoacht
Created March 10, 2015 05:03
Show Gist options
  • Save emoacht/64807a778733c0160beb to your computer and use it in GitHub Desktop.
Save emoacht/64807a778733c0160beb to your computer and use it in GitHub Desktop.
Manage display devices.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
public static class DisplayManager
{
#region Type
public enum ChangeResult
{
Success,
RestartRequired,
Failed
}
#endregion
#region Win32
[DllImport("User32.dll", SetLastError = true)]
public static extern IntPtr MonitorFromWindow(
IntPtr hwnd,
MONITOR_DEFAULTTO dwFlags);
public enum MONITOR_DEFAULTTO : uint
{
/// <summary>
/// If no display monitor intersects, returns null.
/// </summary>
MONITOR_DEFAULTTONULL = 0x00000000,
/// <summary>
/// If no display monitor intersects, returns a handle to the primary display monitor.
/// </summary>
MONITOR_DEFAULTTOPRIMARY = 0x00000001,
/// <summary>
/// If no display monitor intersects, returns a handle to the display monitor that is nearest to the rectangle.
/// </summary>
MONITOR_DEFAULTTONEAREST = 0x00000002,
}
[DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetMonitorInfo(
IntPtr hMonitor,
ref MONITORINFOEX lpmi);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct MONITORINFOEX
{
public uint cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] // CCHDEVICENAME is defined to be 32.
public string szDevice;
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("User32.dll", EntryPoint = "EnumDisplaySettingsW", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumDisplaySettings(
[MarshalAs(UnmanagedType.LPWStr)]
string lpszDeviceName,
int iModeNum,
ref DEVMODE lpDevMode);
[DllImport("User32.dll", EntryPoint = "EnumDisplaySettingsExW", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumDisplaySettingsEx(
[MarshalAs(UnmanagedType.LPWStr)]
string lpszDeviceName,
int iModeNum,
ref DEVMODE lpDevMode,
uint dwFlags);
[DllImport("User32.dll", EntryPoint = "ChangeDisplaySettingsExW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int ChangeDisplaySettingsEx(
[MarshalAs(UnmanagedType.LPWStr)]
string lpszDeviceName,
[In]
ref DEVMODE lpDevMode,
IntPtr hwnd,
uint dwflags,
IntPtr lParam);
[DllImport("User32.dll", EntryPoint = "ChangeDisplaySettingsExW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int ChangeDisplaySettingsEx(
[MarshalAs(UnmanagedType.LPWStr)]
string lpszDeviceName,
IntPtr lpDevMode,
IntPtr hwnd,
uint dwflags,
IntPtr lParam);
// iModeNum of EnumDisplaySettings/EnumDisplaySettingsEx
public const int ENUM_CURRENT_SETTINGS = -1; // Retrieve the current settings for the display device.
public const int ENUM_REGISTRY_SETTINGS = -2; // Retrieve the settings for the display device that are currently stored in the registry.
// dwFlags of EnumDisplaySettingsEx (default is 0)
public const uint EDS_RAWMODE = 0x00000002;
public const uint EDS_ROTATEDMODE = 0x00000004;
// dwflags of ChangeDisplaySettingsEx (default is 0)
public const uint CDS_UPDATEREGISTRY = 0x00000001;
public const uint CDS_TEST = 0x00000002;
public const uint CDS_FULLSCREEN = 0x00000004;
public const uint CDS_GLOBAL = 0x00000008;
public const uint CDS_SET_PRIMARY = 0x00000010;
public const uint CDS_VIDEOPARAMETERS = 0x00000020;
public const uint CDS_ENABLE_UNSAFE_MODES = 0x00000100;
public const uint CDS_DISABLE_UNSAFE_MODES = 0x00000200;
public const uint CDS_RESET = 0x40000000;
public const uint CDS_RESET_EX = 0x20000000;
public const uint CDS_NORESET = 0x10000000;
// Result of ChangeDisplaySettingsEx
public const int DISP_CHANGE_SUCCESSFUL = 0; // The settings change was successful.
public const int DISP_CHANGE_RESTART = 1; // The computer must be restarted for the graphics mode to work.
public const int DISP_CHANGE_FAILED = -1; // The display driver failed the specified graphics mode.
public const int DISP_CHANGE_BADMODE = -2; // The graphics mode is not supported.
public const int DISP_CHANGE_NOTUPDATED = -3; // Unable to write settings to the registry.
public const int DISP_CHANGE_BADFLAGS = -4; // An invalid set of flags was passed in.
public const int DISP_CHANGE_BADPARAM = -5; // An invalid parameter was passed in. This can include an invalid flag or combination of flags.
public const int DISP_CHANGE_BADDUALVIEW = -6; // The settings change was unsuccessful because the system is DualView capable.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DEVMODE
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] // CCHDEVICENAME is defined to be 32.
public string dmDeviceName;
public ushort dmSpecVersion;
public ushort dmDriverVersion;
public ushort dmSize;
public ushort dmDriverExtra;
public uint dmFields;
public POINT dmPosition;
public DMDO dmDisplayOrientation;
public uint dmDisplayFixedOutput;
public ushort dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] // CCHFORMNAME is defined to be 32.
public string dmFormName;
public ushort dmLogPixels;
public uint dmBitsPerPel;
public uint dmPelsWidth;
public uint dmPelsHeight;
public uint dmDisplayFlags;
public uint dmDisplayFrequency;
public uint dmICMMethod;
public uint dmICMIntent;
public uint dmMediaType;
public uint dmDitherType;
public uint dmReserved1;
public uint dmReserved2;
public uint dmPanningWidth;
public uint dmPanningHeight;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
}
public enum DMDO : uint
{
/// <summary>
/// The display orientation is the natural orientation of the display device.
/// </summary>
DMDO_DEFAULT = 0,
/// <summary>
/// The display orientation is rotated 90 degrees (measured clockwise) from DMDO_DEFAULT.
/// </summary>
DMDO_90 = 1,
/// <summary>
/// The display orientation is rotated 180 degrees (measured clockwise) from DMDO_DEFAULT.
/// </summary>
DMDO_180 = 2,
/// <summary>
/// The display orientation is rotated 270 degrees (measured clockwise) from DMDO_DEFAULT.
/// </summary>
DMDO_270 = 3,
}
#endregion
/// <summary>
/// Get the resolution settings of the monitor to which a specified Window belongs.
/// </summary>
/// <param name="sourceVisual">Source Window</param>
/// <returns>If success, resolution in Size. If failed, empty Size.</returns>
public static Size GetResolution(Visual sourceVisual)
{
var deviceName = GetCurrentDeviceName(sourceVisual);
if (String.IsNullOrEmpty(deviceName))
return Size.Empty;
DEVMODE devMode;
if (!TryGetCurrentMode(deviceName, out devMode))
return Size.Empty;
return new Size(devMode.dmPelsWidth, devMode.dmPelsHeight);
}
/// <summary>
/// Change the resolution settings of the monior to which a specified Window belongs.
/// </summary>
/// <param name="sourceVisual">Source Window</param>
/// <param name="oldResolution">Old resolution</param>
/// <param name="newResolution">New resolution</param>
/// <param name="savesToRegistory">Whether to save the setting to the registry</param>
/// <returns>Result of change</returns>
public static ChangeResult ChangeResolution(Visual sourceVisual, out Size oldResolution, Size newResolution, bool savesToRegistory = true)
{
oldResolution = Size.Empty;
var deviceName = GetCurrentDeviceName(sourceVisual);
if (String.IsNullOrEmpty(deviceName))
return ChangeResult.Failed;
DEVMODE devMode;
if (!TryGetCurrentMode(deviceName, out devMode))
return ChangeResult.Failed;
return SetResolution(deviceName, devMode, out oldResolution, newResolution, savesToRegistory);
}
/// <summary>
/// Revert the resolution to the settings in the registry.
/// </summary>
/// <param name="sourceVisual">Source Window</param>
/// <returns>Result of revert</returns>
public static ChangeResult RevertResolution(Visual sourceVisual)
{
var deviceName = GetCurrentDeviceName(sourceVisual);
if (String.IsNullOrEmpty(deviceName))
return ChangeResult.Failed;
Size oldResolution;
return SetResolution(deviceName, default(DEVMODE), out oldResolution, Size.Empty, false);
}
private static ChangeResult SetResolution(string deviceName, DEVMODE devMode, out Size oldResolution, Size newResolution, bool savesToRegistry)
{
oldResolution = new Size(devMode.dmPelsWidth, devMode.dmPelsHeight);
int result;
if (newResolution != Size.Empty)
{
// Change the resolution.
devMode.dmPelsWidth = (uint)newResolution.Width;
devMode.dmPelsHeight = (uint)newResolution.Height;
result = ChangeDisplaySettingsEx(
deviceName,
ref devMode,
IntPtr.Zero,
(savesToRegistry ? CDS_UPDATEREGISTRY : 0),
IntPtr.Zero);
}
else
{
// Revert the resolution.
result = ChangeDisplaySettingsEx(
deviceName,
IntPtr.Zero,
IntPtr.Zero,
0,
IntPtr.Zero);
}
Debug.WriteLine(result);
switch (result)
{
case DISP_CHANGE_SUCCESSFUL:
return ChangeResult.Success;
case DISP_CHANGE_RESTART:
return ChangeResult.RestartRequired;
default:
return ChangeResult.Failed;
}
}
/// <summary>
/// Get current device name of display device to which a specified Window belongs.
/// </summary>
/// <param name="sourceVisual">Source Window</param>
/// <returns>Device name of display device</returns>
private static string GetCurrentDeviceName(Visual sourceVisual)
{
var source = PresentationSource.FromVisual(sourceVisual) as HwndSource;
if (source == null)
return null;
var monitorHandle = MonitorFromWindow(
source.Handle,
MONITOR_DEFAULTTO.MONITOR_DEFAULTTONEAREST);
var monitorInfo = new MONITORINFOEX { cbSize = (uint)Marshal.SizeOf(typeof(MONITORINFOEX)) };
if (!GetMonitorInfo(monitorHandle, ref monitorInfo))
return null;
return monitorInfo.szDevice;
}
/// <summary>
/// Try to get current graphics mode.
/// </summary>
/// <param name="deviceName">Device name of source display device</param>
/// <param name="devMode">outcome DEVMODE</param>
/// <returns>True if success</returns>
private static bool TryGetCurrentMode(string deviceName, out DEVMODE devMode)
{
devMode = new DEVMODE { dmSize = (ushort)Marshal.SizeOf(typeof(DEVMODE)) };
return EnumDisplaySettings(
deviceName,
ENUM_CURRENT_SETTINGS,
ref devMode);
}
/// <summary>
/// Enumerate graphics modes that are compatible with current monitors.
/// </summary>
/// <param name="deviceName">Device name of source display device</param>
/// <returns>Graphics modes</returns>
private static IEnumerable<DEVMODE> EnumerateCompatibleModes(string deviceName)
{
var devMode = new DEVMODE { dmSize = (ushort)Marshal.SizeOf(typeof(DEVMODE)) };
int index = 0;
while (EnumDisplaySettingsEx(
deviceName,
index,
ref devMode,
0))
{
yield return devMode;
index++;
}
}
/// <summary>
/// Enumerate all graphics modes reported by the adapter driver in all orientations.
/// </summary>
/// <param name="deviceName">Device name of source display device</param>
/// <returns>Graphics modes</returns>
private static IEnumerable<DEVMODE> EnumerateAllModes(string deviceName)
{
var devMode = new DEVMODE { dmSize = (ushort)Marshal.SizeOf(typeof(DEVMODE)) };
int index = 0;
while (EnumDisplaySettingsEx(
deviceName,
index,
ref devMode,
EDS_RAWMODE | EDS_ROTATEDMODE))
{
yield return devMode;
index++;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment