tmighty
tmighty

Reputation: 11399

How do I get the current orientation (angle) of a monitor?

I would like to know how I could get the current orientation of a monitor.

These are my results, and orientation is not correct.

Orientation of Screen #1 should NOT be 0, but it is 0. Orientation of Screen #2 should be 0, but it is 1920 instead. Orientation of Screen #3 should be 0, but it is 3840 instead.

I expect the orientation to be either 0, 1, 2 or 3, not these huge numbers.

Screen (0): 1920x1080x32 Freq: 144 Orientation: 0
Screen (1): 1920x1080x32 Freq: 60 Orientation: 1920
Screen (2): 1080x1920x32 Freq: 60 Orientation: 3840



Option Explicit
Private Const DM_DISPLAYORIENTATION = &H80 ' DISPLAY -- XP only
Private Declare Function EnumDisplaySettingsEx Lib "User32.dll" _
Alias "EnumDisplaySettingsExA" (ByVal lpszDeviceName As String, _
ByVal iModeNum As Long, ByRef lpDevMode As DEVMODE, _
ByVal dwFlags As Long) As Long
  
Private Declare Function EnumDisplayDevices Lib "user32" _
  Alias "EnumDisplayDevicesA" ( _
  DeviceName As Any, _
  ByVal iDevNum As Long, _
   lpDisplayDevice As DISPLAY_DEVICE, _
  ByVal dwFlags As Long) As Long
  
Private Declare Function ChangeDisplaySettingsEx Lib "User32.dll" _
Alias "ChangeDisplaySettingsExA" (ByVal lpszDeviceName As String, _
ByRef lpDevMode As DEVMODE, ByVal hWnd As Long, _
ByVal dwFlags As Long, ByRef lParam As Any) As Long
 
Private Type DEVMODE
  dmDeviceName As String * 32
  dmSpecVersion As Integer
  dmDriverVersion As Integer
  dmSize As Integer
  dmDriverExtra As Integer
  dmFields As Long
  dmOrientation As Integer
  dmPaperSize As Integer
  dmPaperLength As Integer
  dmPaperWidth As Integer
  dmScale As Integer
  dmCopies As Integer
  dmDefaultSource As Integer
  dmPrintQuality As Integer
  dmColor As Integer
  dmDuplex As Integer
  dmYResolution As Integer
  dmTTOption As Integer
  dmCollate As Integer
  dmFormName As String * 32
  dmUnusedPadding As Integer
  dmBitsPerPixel As Integer
  dmPelsWidth As Long
  dmPelsHeight As Long
  dmDisplayFlags As Long
  dmDisplayFrequency As Long
  dmICMMethod As Long
  dmICMIntent As Long
  dmMediaType As Long
  dmDitherType As Long
  dmReserved1 As Long
  dmReserved2 As Long
  dmPanningWidth As Long ' (Win 2000)
  dmPanningHeight As Long ' (Win 2000)
End Type
 
Private Type DISPLAY_DEVICE
  cb As Long
  DeviceName As String * 32
  DeviceString As String * 128
  StateFlags As Long
  DeviceID As String * 128
  DeviceKey  As String * 128
End Type
 
' die benötigten DEVMODE dmFields-Konstanten
Private Const DM_BITSPERPEL = &H40000 ' Die Struktur soll mit der Farbtiefe gefüllt werden
Private Const DM_PELSWIDTH = &H80000 ' Die Struktur soll mit der Bildschirmbreite in Pixeln gefüllt werden
Private Const DM_PELSHEIGHT = &H100000 ' Die Struktur soll mit der  Bildschirmhöhe in Pixeln gefüllt werden
Private Const DM_DISPLAYFREQUENCY = &H400000 ' Die Struktur soll mit Wiederholrate in Hertz gefüllt werden
Private Const DM_DISPLAYFLAGS = &H200000 ' Die Struktur soll mit den Eigenschaften der Grafikkarte gefüllt werden
 
' DEVMODE DisplayFlags-Konstanten
Private Const DM_GRAYSCALE = 1  ' Gerät unterstützt keine Farben, Grautöne werden unterstützt
Private Const DM_INTERLACED = 2  ' Gerät unterstützt Farben
 
' DISPLAY_DEVICE StateFlags-Konstanten
Private Const DISPLAY_DEVICE_ATTACHED_TO_DESKTOP = &H1 ' Das Gerät ist Teil des Desktops
Private Const DISPLAY_DEVICE_MIRRORING_DRIVER = &H8 ' Dieses Gerät ist ein unsichtbarer Pseudo-Monitor
Private Const DISPLAY_DEVICE_MODESPRUNED = &H8000000 ' Dieses Gerät hat mehr  Grafikmodies als das
' Ausgabegerät unterstützt
Private Const DISPLAY_DEVICE_PRIMARY_DEVICE = &H4 ' Das Gerät ist die Standardgrafikkarte
Private Const DISPLAY_DEVICE_VGA_COMPATIBLE = &H10 ' Das Gerät ist VGA-kompatibel
 
' EnumDisplaySettings iModeNum-Konstanten
Private Const ENUM_CURRENT_SETTINGS = -1 ' Die Funktion soll die Struktur
' mit den aktuellen Einstellungen füllen
Private Const ENUM_REGISTRY_SETTINGS = -2 ' Die Funktion soll die Struktur
' mit den Registry-Einstellungen füllen
 
' ChangeDisplaySettings dwFlags-Konstanten
Private Const CDS_UPDATEREGISTRY = &H1 ' Die Einstellungen werden in der Registry gespeichert
Private Const CDS_TEST = &H2 ' Testet die Auflösung ohne die Auflösung zu ändern,
' die Rückgabe ist eine des Rückgabe-Konstanten
Private Const CDS_FULLSCREEN = &H4 ' Der Grafikmodus soll im Vollbild angezeigt werden,
' diese Einstellung kann nicht gespeichert werden
Private Const CDS_GLOBAL = &H8 ' Speichert die Einstellungen für alle Benutzer
' (in Verbindung mit CDS_UPDATEREGISTRY)
Private Const CDS_SET_PRIMARY = &H10 ' Die angegebene Grafikkarte soll die
' Standardgrafikkarte werden
Private Const CDS_RESET = &H40000000 ' Ändert die Auflösung, auch wenn sie die
' selbe ist, die momentan angezeigt wird
Private Const CDS_NORESET = &H10000000 ' Speichert die Einstellungen in der Registry,
' die Änderungen werden erst nach dem Neustart wirksam (in
' Verbindung mit CDS_UPDATEREGISTRY)
 
' ChangeDisplaySettings Rückgabe-Konstanten
Private Const DISP_CHANGE_SUCCESSFUL = 0 ' Das Ändern oder Testen der Auflösung war erfolgreich
Private Const DISP_CHANGE_RESTART = 1 ' Das Ändern der Auflösung erfordert einen Neustart
Private Const DISP_CHANGE_FAILED = -1 ' Das Ändern oder Testen der Auflösung ist gescheitert
Private Const DISP_CHANGE_BADMODE = -2 ' Die angegebene Auflösung wird nicht unterstützt
Private Const DISP_CHANGE_NOTUPDATED = -3 ' (Win NT/2000) Die Einstellungen wurden nicht gespeichert
Private Const DISP_CHANGE_BADFLAGS = -4 ' Es wurden falsche Flags angegeben
Private Const DISP_CHANGE_BADPARAM = -5 ' Es wurden falsche Parameter angegeben
Private Const EDS_ROTATEDMODE = 4

Private Function pGetAllInfos() As String

    Dim Dev As DEVMODE
    Dev.dmSize = Len(Dev)
    Dev.dmFields = DM_BITSPERPEL Or DM_PELSWIDTH Or DM_PELSHEIGHT Or DM_DISPLAYFREQUENCY Or DM_DISPLAYFLAGS Or DM_DISPLAYORIENTATION
    
    Dim Disp As DISPLAY_DEVICE
    Disp.cb = Len(Disp)
 
    Dim iMon&
    iMon = 0
    
    Dim s$
    s = ""
    
    Do
        Dim lRet&
        lRet = EnumDisplayDevices(ByVal 0&, iMon, Disp, 0&)
        If lRet <> 1 Then
            Exit Do
        Else

        'not sure if setting the dmFields anew is necessary, but I'm paranoid
        Dev.dmFields = DM_BITSPERPEL Or DM_PELSWIDTH Or DM_PELSHEIGHT Or DM_DISPLAYFREQUENCY Or DM_DISPLAYFLAGS Or DM_DISPLAYORIENTATION

        lRet = EnumDisplaySettingsEx(Disp.DeviceName, ENUM_CURRENT_SETTINGS, Dev, EDS_ROTATEDMODE)
        If lRet <> 0 Then
        
            s = s & vbNewLine & "Screen (" & CStr(iMon) & "): " & _
            Dev.dmPelsWidth & "x" & Dev.dmPelsHeight & "x" & _
            Dev.dmBitsPerPixel & " Freq: " & Dev.dmDisplayFrequency & _
            " Orientation: " & Dev.dmOrientation
        Else
            Exit Do
        End If
        
        DoEvents
        iMon = iMon + 1
    End If
Loop

pGetAllInfos = s

Edit:

I have found a VB.NET code that works perfectly fine. So there must be something wrong in the VB6 code:

Imports System
Imports System.Runtime.InteropServices

Public Class Display
    Public Enum Orientations
        DEGREES_CW_0 = 0
        DEGREES_CW_90 = 3
        DEGREES_CW_180 = 2
        DEGREES_CW_270 = 1
    End Enum

    Public Shared Function Rotate(ByVal DisplayNumber As UInteger, ByVal Orientation As Orientations) As Boolean
        If DisplayNumber = 0 Then Throw New ArgumentOutOfRangeException("DisplayNumber", DisplayNumber, "First display is 1.")

        Dim result = False

        Dim d As DISPLAY_DEVICE = New DISPLAY_DEVICE()
        d.cb = Marshal.SizeOf(d)

        Dim dm As DEVMODE = New DEVMODE()

        If Not NativeMethods.EnumDisplayDevices(Nothing, DisplayNumber - 1, d, 0) Then Throw New ArgumentOutOfRangeException("DisplayNumber", DisplayNumber, "Number is greater than connected displays.")

        If 0 <> NativeMethods.EnumDisplaySettings(d.DeviceName, NativeMethods.ENUM_CURRENT_SETTINGS, dm) Then
            If (dm.dmDisplayOrientation + Orientation) Mod 2 = 1 Then ' Need to swap height and width?
                Dim temp = dm.dmPelsHeight
                dm.dmPelsHeight = dm.dmPelsWidth
                dm.dmPelsWidth = temp
            End If

            Select Case Orientation
                Case Orientations.DEGREES_CW_90
                    dm.dmDisplayOrientation = NativeMethods.DMDO_270
                Case Orientations.DEGREES_CW_180
                    dm.dmDisplayOrientation = NativeMethods.DMDO_180
                Case Orientations.DEGREES_CW_270
                    dm.dmDisplayOrientation = NativeMethods.DMDO_90
                Case Orientations.DEGREES_CW_0
                    dm.dmDisplayOrientation = NativeMethods.DMDO_DEFAULT
                Case Else
            End Select

            Dim ret = NativeMethods.ChangeDisplaySettingsEx(d.DeviceName, dm, IntPtr.Zero, DisplaySettingsFlags.CDS_UPDATEREGISTRY, IntPtr.Zero)

            result = ret = 0
        End If

        Return result
    End Function

    Public Shared Sub ResetAllRotations()
        Try
            Dim i As UInteger = 0
            While Threading.Interlocked.Increment(i) <= 64
                Rotate(i, Orientations.DEGREES_CW_0)
            End While
        Catch ex As ArgumentOutOfRangeException
            ' Everything is fine, just reached the last display
        End Try
    End Sub
End Class

Friend Class NativeMethods
    <DllImport("user32.dll")>
    Friend Shared Function ChangeDisplaySettingsEx(ByVal lpszDeviceName As String, ByRef lpDevMode As DEVMODE, ByVal hwnd As IntPtr, ByVal dwflags As DisplaySettingsFlags, ByVal lParam As IntPtr) As DISP_CHANGE
    End Function

    <DllImport("user32.dll")>
    Friend Shared Function EnumDisplayDevices(ByVal lpDevice As String, ByVal iDevNum As UInteger, ByRef lpDisplayDevice As DISPLAY_DEVICE, ByVal dwFlags As UInteger) As Boolean
    End Function

    <DllImport("user32.dll", CharSet:=CharSet.Ansi)>
    Friend Shared Function EnumDisplaySettings(ByVal lpszDeviceName As String, ByVal iModeNum As Integer, ByRef lpDevMode As DEVMODE) As Integer
    End Function

    Public Const DMDO_DEFAULT As Integer = 0
    Public Const DMDO_90 As Integer = 1
    Public Const DMDO_180 As Integer = 2
    Public Const DMDO_270 As Integer = 3

    Public Const ENUM_CURRENT_SETTINGS As Integer = -1

End Class

' See: https://msdn.microsoft.com/en-us/library/windows/desktop/dd183565(v=vs.85).aspx
<StructLayout(LayoutKind.Explicit, CharSet:=CharSet.Ansi)>
Friend Structure DEVMODE
    Public Const CCHDEVICENAME As Integer = 32
    Public Const CCHFORMNAME As Integer = 32

    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CCHDEVICENAME)>
    <FieldOffset(0)>
    Public dmDeviceName As String
    <FieldOffset(32)>
    Public dmSpecVersion As Short
    <FieldOffset(34)>
    Public dmDriverVersion As Short
    <FieldOffset(36)>
    Public dmSize As Short
    <FieldOffset(38)>
    Public dmDriverExtra As Short
    <FieldOffset(40)>
    Public dmFields As DM

    <FieldOffset(44)>
    Private dmOrientation As Short
    <FieldOffset(46)>
    Private dmPaperSize As Short
    <FieldOffset(48)>
    Private dmPaperLength As Short
    <FieldOffset(50)>
    Private dmPaperWidth As Short
    <FieldOffset(52)>
    Private dmScale As Short
    <FieldOffset(54)>
    Private dmCopies As Short
    <FieldOffset(56)>
    Private dmDefaultSource As Short
    <FieldOffset(58)>
    Private dmPrintQuality As Short

    <FieldOffset(44)>
    Public dmPosition As POINTL
    <FieldOffset(52)>
    Public dmDisplayOrientation As Integer
    <FieldOffset(56)>
    Public dmDisplayFixedOutput As Integer

    <FieldOffset(60)>
    Public dmColor As Short
    <FieldOffset(62)>
    Public dmDuplex As Short
    <FieldOffset(64)>
    Public dmYResolution As Short
    <FieldOffset(66)>
    Public dmTTOption As Short
    <FieldOffset(68)>
    Public dmCollate As Short
    <FieldOffset(72)>
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CCHFORMNAME)>
    Public dmFormName As String
    <FieldOffset(102)>
    Public dmLogPixels As Short
    <FieldOffset(104)>
    Public dmBitsPerPel As Integer
    <FieldOffset(108)>
    Public dmPelsWidth As Integer
    <FieldOffset(112)>
    Public dmPelsHeight As Integer
    <FieldOffset(116)>
    Public dmDisplayFlags As Integer
    <FieldOffset(116)>
    Public dmNup As Integer
    <FieldOffset(120)>
    Public dmDisplayFrequency As Integer
End Structure

' See: https://msdn.microsoft.com/en-us/library/windows/desktop/dd183569(v=vs.85).aspx
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)>
Friend 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 DisplayDeviceStateFlags
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=128)>
    Public DeviceID As String
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=128)>
    Public DeviceKey As String
End Structure

' See: https://msdn.microsoft.com/de-de/library/windows/desktop/dd162807(v=vs.85).aspx
<StructLayout(LayoutKind.Sequential)>
Friend Structure POINTL
    Private x As Long
    Private y As Long
End Structure

Friend Enum DISP_CHANGE As Integer
    Successful = 0
    Restart = 1
    Failed = -1
    BadMode = -2
    NotUpdated = -3
    BadFlags = -4
    BadParam = -5
    BadDualView = -6
End Enum

' http://www.pinvoke.net/default.aspx/Enums/DisplayDeviceStateFlags.html
<Flags()>
Friend Enum DisplayDeviceStateFlags As Integer
    ''' <summary>The device is part of the desktop.</summary>
    AttachedToDesktop = &H1
    MultiDriver = &H2
    ''' <summary>The device is part of the desktop.</summary>
    PrimaryDevice = &H4
    ''' <summary>Represents a pseudo device used to mirror application drawing for remoting or other purposes.</summary>
    MirroringDriver = &H8
    ''' <summary>The device is VGA compatible.</summary>
    VGACompatible = &H10
    ''' <summary>The device is removable; it cannot be the primary display.</summary>
    Removable = &H20
    ''' <summary>The device has more display modes than its output devices support.</summary>
    ModesPruned = &H8000000
    Remote = &H4000000
    Disconnect = &H2000000
End Enum

' http://www.pinvoke.net/default.aspx/user32/ChangeDisplaySettingsFlags.html
<Flags()>
Friend Enum DisplaySettingsFlags As Integer
    CDS_NONE = 0
    CDS_UPDATEREGISTRY = &H1
    CDS_TEST = &H2
    CDS_FULLSCREEN = &H4
    CDS_GLOBAL = &H8
    CDS_SET_PRIMARY = &H10
    CDS_VIDEOPARAMETERS = &H20
    CDS_ENABLE_UNSAFE_MODES = &H100
    CDS_DISABLE_UNSAFE_MODES = &H200
    CDS_RESET = &H40000000
    CDS_RESET_EX = &H20000000
    CDS_NORESET = &H10000000
End Enum

<Flags()>
Friend Enum DM As Integer
    Orientation = &H1
    PaperSize = &H2
    PaperLength = &H4
    PaperWidth = &H8
    Scale = &H10
    Position = &H20
    NUP = &H40
    DisplayOrientation = &H80
    Copies = &H100
    DefaultSource = &H200
    PrintQuality = &H400
    Color = &H800
    Duplex = &H1000
    YResolution = &H2000
    TTOption = &H4000
    Collate = &H8000
    FormName = &H10000
    LogPixels = &H20000
    BitsPerPixel = &H40000
    PelsWidth = &H80000
    PelsHeight = &H100000
    DisplayFlags = &H200000
    DisplayFrequency = &H400000
    ICMMethod = &H800000
    ICMIntent = &H1000000
    MediaType = &H2000000
    DitherType = &H4000000
    PanningWidth = &H8000000
    PanningHeight = &H10000000
    DisplayFixedOutput = &H20000000
End Enum

Upvotes: 2

Views: 238

Answers (1)

tmighty
tmighty

Reputation: 11399

I got it. The declaration of DevMode was wrong.

Here is the correct one:

 Private Type PointL
    x As Long
    Y As Long
End Type

Private Const CCDEVICENAME = 32
Private Const CCFORMNAME = 32
 
Private Type DevMode
    dmDeviceName As String * CCDEVICENAME
    dmSpecVersion As Integer
    dmDriverVersion As Integer
    dmSize As Integer
    dmDriverExtra As Integer
    dmFields As Long
    dmPosition As PointL
    dmDisplayOrientation As Long
    dmDisplayFixedOutput As Long
    dmColor As Integer
    dmDuplex As Integer
    dmYResolution As Integer
    dmTTOption As Integer
    dmCollate As Integer
    dmFormName(CCFORMNAME - 1) As Byte
    dmLogPixels As Integer
    dmBitsPerPel As Long
    dmPelsWidth As Long
    dmPelsHeight As Long
    dmDisplayFlags As Long
    dmDisplayFrequency As Long
End Type

Upvotes: 4

Related Questions