Created
March 10, 2015 05:03
-
-
Save emoacht/64807a778733c0160beb to your computer and use it in GitHub Desktop.
Manage display devices.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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