Changing the display resolution in a multi-monitor environment…
Hi…
I use to work with my laptop in a docking station and an external monitor connected. Twice a day (at least) I have to undock and dock the machine on a projector. While plug-n-play is a nice feature, it does not always work with every projector. Well, there must be a way…
So I started to search a bit and found some code snippets (in VB.NET, C# and C). Since my first snippet was in VB.NET I have stuck to it. I found some examples talking on how to do this in a single monitor environment. But none really for multi-monitor. So here is my scribble code in which the gifted coder finds all she or he needs to build a very own solution ;-)
It is a lot of good old C-Interop thing and I think most of you will find the predefined structures helpful. I will check with www.pinvoke.net later and look if I can add some information there.
It is not an application (yet)… well, weekend lies ahead...
Enjoy.
CU
0xff
Imports System.Runtime.InteropServices
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _
Public Structure DISPLAY_DEVICE
<MarshalAs(UnmanagedType.U4)> _
Public cb As Integer
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=32)> _
Public DeviceName As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=128)> _
Public DeviceString As String
<MarshalAs(UnmanagedType.U4)> _
Public StateFlags As Integer
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=128)> _
Public DeviceID As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=128)> _
Public DeviceKey As String
End Structure
'DEVMODE structure, see https://msdn.microsoft.com/en-us/library/ms535771(VS.85).aspx
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _
Friend Structure DEVMODE
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=32)> _
Public dmDeviceName As String
Public dmSpecVersion As Short
Public dmDriverVersion As Short
Public dmSize As Short
Public dmDriverExtra As Short
Public dmFields As Integer
Public dmPositionX As Integer
Public dmPositionY As Integer
Public dmDisplayOrientation As Integer
Public dmDisplayFixedOutput As Integer
Public dmColor As Short
Public dmDuplex As Short
Public dmYResolution As Short
Public dmTTOption As Short
Public dmCollate As Short
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=32)> _
Public dmFormName As String
Public dmLogPixels As Short
Public dmBitsPerPel As Short
Public dmPelsWidth As Integer
Public dmPelsHeight As Integer
Public dmDisplayFlags As Integer
Public dmDisplayFrequency As Integer
Public dmICMMethod As Integer
Public dmICMIntent As Integer
Public dmMediaType As Integer
Public dmDitherType As Integer
Public dmReserved1 As Integer
Public dmReserved2 As Integer
Public dmPanningWidth As Integer
Public dmPanningHeight As Integer
End Structure
Friend Class NativeMethods
' PInvoke declaration for EnumDisplaySettings Win32 API
<DllImport("user32.dll", CharSet:=CharSet.Ansi)> _
Public Shared Function EnumDisplaySettings( _
ByVal lpszDeviceName As String, _
ByVal iModeNum As Integer, _
ByRef lpDevMode As DEVMODE) As Integer
End Function
' PInvoke declaration for ChangeDisplaySettings Win32 API
<DllImport("user32.dll", CharSet:=CharSet.Ansi)> _
Public Shared Function ChangeDisplaySettings( _
ByRef lpDevMode As DEVMODE, _
ByVal dwFlags As Integer) As Integer
End Function
<DllImport("user32.dll", CharSet:=CharSet.Ansi)> _
Public Shared Function EnumDisplayDevices( _
ByVal lpDevice As String, _
ByVal iDevNum As Integer, _
ByRef lpDisplayDevice As DISPLAY_DEVICE, _
ByVal dwFlags As Integer) As Boolean
End Function
<DllImport("user32.dll", CharSet:=CharSet.Ansi)> _
Public Shared Function ChangeDisplaySettingsEx( _
ByVal lpDeviceName As String, _
ByRef lpDevMode As DEVMODE, _
ByVal HWND As IntPtr, _
ByVal dwflags As Integer, _
ByVal lParam As IntPtr) As Integer
End Function
End Class
Public Class ResolutionChanger
Private _DisplayDevices As New List(Of DISPLAY_DEVICE)
Private _Monitors As New List(Of DISPLAY_DEVICE)
Public ReadOnly Property Monitors() As List(Of DISPLAY_DEVICE)
Get
Return _Monitors
End Get
End Property
Public ReadOnly Property DisplayDevices() As List(Of DISPLAY_DEVICE)
Get
Return _DisplayDevices
End Get
End Property
Private Shared Function CreateDevMode() As DEVMODE
Dim dm As New DEVMODE
dm.dmDeviceName = New String(New Char(32) {})
dm.dmFormName = New String(New Char(32) {})
dm.dmSize = CShort(Marshal.SizeOf(dm))
Return dm
End Function
Private Shared Function CreateDisplayDevice() As DISPLAY_DEVICE
Dim dd As New DISPLAY_DEVICE
dd.cb = System.Runtime.InteropServices.Marshal.SizeOf(GetType(DISPLAY_DEVICE))
dd.DeviceName = New String(New Char(32) {})
dd.DeviceString = New String(New Char(128) {})
dd.DeviceID = New String(New Char(128) {})
dd.DeviceKey = New String(New Char(128) {})
Return dd
End Function
Public Enum DisplayChangeResultCode
DISP_CHANGE_SUCCESSFUL = 0
DISP_CHANGE_RESTART = 1
DISP_CHANGE_FAILED = -1
DISP_CHANGE_BADMODE = -2
DISP_CHANGE_NOTUPDATED = -3
DISP_CHANGE_BADFLAGS = -4
DISP_CHANGE_BADPARAM = -5
DISP_CHANGE_BADDUALVIEW = -6
End Enum
#Region "DevMode Flags"
'Flags for DevMode structure, see https://msdn.microsoft.com/en-us/library/ms535771(VS.85).aspx
Public Const DM_ORIENTATION As Integer = &H1
Public Const DM_PAPERSIZE As Integer = &H2
Public Const DM_PAPERLENGTH As Integer = &H4
Public Const DM_PAPERWIDTH As Integer = &H8
Public Const DM_SCALE As Integer = &H10
Public Const DM_POSITION As Integer = &H20
Public Const DM_NUP As Integer = &H40
Public Const DM_DISPLAYORIENTATION As Integer = &H80
Public Const DM_COPIES As Integer = &H100
Public Const DM_DEFAULTSOURCE As Integer = &H200
Public Const DM_PRINTQUALITY As Integer = &H400
Public Const DM_COLOR As Integer = &H800
Public Const DM_DUPLEX As Integer = &H1000
Public Const DM_YRESOLUTION As Integer = &H2000
Public Const DM_TTOPTION As Integer = &H4000
Public Const DM_COLLATE As Integer = &H8000
Public Const DM_FORMNAME As Integer = &H10000
Public Const DM_LOGPIXELS As Integer = &H20000
Public Const DM_BITSPERPEL As Integer = &H40000
Public Const DM_PELSWIDTH As Integer = &H80000
Public Const DM_PELSHEIGHT As Integer = &H100000
Public Const DM_DISPLAYFLAGS As Integer = &H200000
Public Const DM_DISPLAYFREQUENCY As Integer = &H400000
Public Const DM_ICMMETHOD As Integer = &H800000
Public Const DM_ICMINTENT As Integer = &H1000000
Public Const DM_MEDIATYPE As Integer = &H2000000
Public Const DM_DITHERTYPE As Integer = &H4000000
Public Const DM_PANNINGWIDTH As Integer = &H8000000
Public Const DM_PANNINGHEIGHT As Integer = &H10000000
Public Const DM_DISPLAYFIXEDOUTPUT As Integer = &H20000000
#End Region
Public Const CDS_UPDATEREGISTRY As Integer = &H1
Public Const CDS_FULLSCREEN As Integer = &H4
Public Const CDS_NORESET As Integer = &H10000000
Public Const CDS_SET_PRIMARY As Integer = &H10
Public Const CDS_RESET As Integer = &H40000000
Public Sub EnumDisplayDevices()
Dim Device As DISPLAY_DEVICE
Dim Device2 = CreateDisplayDevice()
Dim Monitor1 = CreateDisplayDevice()
Dim Monitor2 = CreateDisplayDevice()
Dim result As Boolean
Dim DeviceCounter As Integer
'To enlist all devices we have to roll over the EnumDisplayDevices function until
'result is negative
'This is done twice, first for the Display Devices, then for the monitors
DeviceCounter = 0
Do
Device = CreateDisplayDevice()
result = NativeMethods.EnumDisplayDevices(Nothing, DeviceCounter, Device, &H1)
If result = True Then
_DisplayDevices.Add(Device)
DeviceCounter += 1
End If
Loop Until result = False
For Each DispDevice As DISPLAY_DEVICE In _DisplayDevices
DeviceCounter = 0
Do
Device = CreateDisplayDevice()
result = NativeMethods.EnumDisplayDevices(DispDevice.DeviceName, DeviceCounter, Device, &H1)
If result = True Then
_Monitors.Add(Device)
DeviceCounter += 1
End If
Loop Until result = False
Next
End Sub
'Lots of different ways to change resolution
'Remark: The device parameter is always the list(of DISPLAY_DEVICE) DisplayDevices
Public Shared Sub ChangeResolution(ByRef device As DISPLAY_DEVICE, ByVal width As Integer, ByVal height As Integer)
Const ENUM_CURRENT_SETTINGS As Integer = -1
Dim DevM As DEVMODE = CreateDevMode()
Dim enumResult As Integer
Dim changeResult As DisplayChangeResultCode
DevM.dmFields = DM_PELSWIDTH Or DM_PELSHEIGHT
enumResult = NativeMethods.EnumDisplaySettings(device.DeviceName, ENUM_CURRENT_SETTINGS, DevM)
DevM.dmPelsWidth = width
DevM.dmPelsHeight = height
DevM.dmDeviceName = device.DeviceName
changeResult = CType(NativeMethods.ChangeDisplaySettings(DevM, 0), DisplayChangeResultCode)
If changeResult <> DisplayChangeResultCode.DISP_CHANGE_SUCCESSFUL Then
Throw New Exception("Failed to change resolution: " & changeResult.ToString)
End If
End Sub
Public Shared Sub ChangeResolution(ByRef device As DISPLAY_DEVICE, ByVal width As Integer, ByVal height As Integer, ByVal freq As Integer)
Const ENUM_CURRENT_SETTINGS As Integer = -1
Dim DevM As DEVMODE = CreateDevMode()
Dim enumResult As Integer
Dim changeResult As DisplayChangeResultCode
DevM.dmFields = DM_PELSWIDTH Or DM_PELSHEIGHT Or DM_DISPLAYFREQUENCY
enumResult = NativeMethods.EnumDisplaySettings(device.DeviceName, ENUM_CURRENT_SETTINGS, DevM)
DevM.dmPelsWidth = width
DevM.dmPelsHeight = height
DevM.dmDisplayFrequency = freq
DevM.dmDeviceName = device.DeviceName
changeResult = CType(NativeMethods.ChangeDisplaySettings(DevM, 0), DisplayChangeResultCode)
If changeResult <> DisplayChangeResultCode.DISP_CHANGE_SUCCESSFUL Then
Throw New Exception("Failed to change resolution: " & changeResult.ToString)
End If
End Sub
Public Shared Sub ChangeResolution(ByVal devices As List(Of DISPLAY_DEVICE))
Const ENUM_CURRENT_SETTINGS As Integer = -1
Dim DevM1 As DEVMODE = CreateDevMode()
Dim DevM2 As DEVMODE = CreateDevMode()
Dim enumResult As Integer
Dim changeResult As DisplayChangeResultCode
DevM1.dmFields = DM_PELSWIDTH Or DM_PELSHEIGHT Or DM_DISPLAYFREQUENCY Or DM_POSITION
enumResult = NativeMethods.EnumDisplaySettings(devices.Item(0).DeviceName, ENUM_CURRENT_SETTINGS, DevM1)
DevM1.dmPelsWidth = 1024
DevM1.dmPelsHeight = 768
DevM1.dmDisplayFrequency = 50
DevM1.dmPositionX = -1024
DevM1.dmPositionY = 0
DevM1.dmDeviceName = devices.Item(0).DeviceName
DevM2.dmFields = DM_PELSWIDTH Or DM_PELSHEIGHT Or DM_DISPLAYFREQUENCY Or DM_POSITION
enumResult = NativeMethods.EnumDisplaySettings(devices.Item(1).DeviceName, ENUM_CURRENT_SETTINGS, DevM2)
DevM2.dmPelsWidth = 800
DevM2.dmPelsHeight = 600
DevM2.dmDisplayFrequency = 50
DevM2.dmPositionX = 0
DevM2.dmPositionY = 0
DevM2.dmDeviceName = devices.Item(1).DeviceName
changeResult = CType(NativeMethods.ChangeDisplaySettingsEx(devices.Item(0).DeviceName, DevM1, Nothing, (0), Nothing), DisplayChangeResultCode)
If changeResult <> DisplayChangeResultCode.DISP_CHANGE_SUCCESSFUL Then
Throw New Exception("Failed to change resolution: " & changeResult.ToString)
End If
changeResult = CType(NativeMethods.ChangeDisplaySettingsEx(devices.Item(1).DeviceName, DevM2, Nothing, (CDS_SET_PRIMARY), Nothing), DisplayChangeResultCode)
If changeResult <> DisplayChangeResultCode.DISP_CHANGE_SUCCESSFUL Then
Throw New Exception("Failed to change resolution: " & changeResult.ToString)
End If
changeResult = CType(NativeMethods.ChangeDisplaySettingsEx(Nothing, Nothing, Nothing, 0, Nothing), DisplayChangeResultCode)
If changeResult <> DisplayChangeResultCode.DISP_CHANGE_SUCCESSFUL Then
Throw New Exception("Failed to change resolution: " & changeResult.ToString)
End If
End Sub
Public Shared Sub ChangeResolution(ByRef device As DISPLAY_DEVICE, ByVal width As Integer, ByVal height As Integer, ByVal freq As Integer, ByVal PositionX As Integer, ByVal PositionY As Integer)
Const ENUM_CURRENT_SETTINGS As Integer = -1
Dim DevM As DEVMODE = CreateDevMode()
Dim enumResult As Integer
Dim changeResult As DisplayChangeResultCode
DevM.dmFields = DM_PELSWIDTH Or DM_PELSHEIGHT Or DM_DISPLAYFREQUENCY Or DM_POSITION
enumResult = NativeMethods.EnumDisplaySettings(device.DeviceName, ENUM_CURRENT_SETTINGS, DevM)
DevM.dmPelsWidth = width
DevM.dmPelsHeight = height
DevM.dmDisplayFrequency = freq
DevM.dmPositionX = PositionX
DevM.dmPositionY = PositionY
DevM.dmDeviceName = device.DeviceName
changeResult = CType(NativeMethods.ChangeDisplaySettings(DevM, 0), DisplayChangeResultCode)
If changeResult <> DisplayChangeResultCode.DISP_CHANGE_SUCCESSFUL Then
Throw New Exception("Failed to change resolution: " & changeResult.ToString)
End If
End Sub
End Class
Comments
- Anonymous
August 13, 2008
PingBack from http://hoursfunnywallpaper.cn/?p=1261