Bildschirmeinstellungen ermitteln und ändern

Veröffentlicht: 12. Nov 2001 | Aktualisiert: 13. Jun 2004

Von Mathias Schiffer

Aktuelle Bildschirmeinstellungen ermitteln

Die Ermittlung der aktuellen Bildschirmauflösung ermöglicht die API-Funktion GetDeviceCaps in Verbindung mit den Konstanten HORZRES und VERTRES. Sie verlangt weiterhin ein Handle auf einen "Device Context" (DC) für den Bildschirm, den die Funktion GetDC unter Übergabe des Parameters 0 ermittelt:

Private Const HORZRES   As Long = 8& 
Private Const VERTRES   As Long = 10& 
Private Declare Function GetDC _ 
  Lib "user32" ( _ 
  ByVal hWnd As Long _ 
  ) As Long 
Private Declare Function GetDeviceCaps _ 
  Lib "gdi32" ( _ 
  ByVal hdc As Long, _ 
  ByVal nIndex As Long _ 
  ) As Long 
' --- Anwendung 
Dim hDCScreen As Long 
Dim lScreenWidth As Long 
Dim lScreenHeight As Long 
  hDCScreen = GetDC(0&) 
  lScreenWidth = GetDeviceCaps(hDCScreen, HORZRES) 
  lScreenHeight = GetDeviceCaps(hDCScreen, VERTRES) 
  MsgBox "Bildschirmauflösung: " & _ 
         CStr(lScreenWidth) & " x " & _ 
         CStr(lScreenHeight) & " Pixel.", _ 
         vbInformation

Tipp: Oft sieht man den Ansatz, die Bildschirmauflösung in Pixelskala noch einfacher mithilfe des Screen-Objekts und dessen Eigenschaften Width, Height, TwipsPerPixelX und TwipsPerPixelY zu ermitteln. Berichten vereinzelter Entwickler zufolge werden jedoch die Eigenschaften des Screen-Objekts nach einer Änderung der Bildschirmauflösung durch den Anwender nicht in jedem Fall korrekt aktualisiert (offenbar in Abhängigkeit der eingesetzten Hardware und der Gerätetreiber dafür). Angesichts der fehlenden Möglichkeit einer Einflussnahme auf diese Faktoren rate ich von diesem Vorgehen ab. Die Möglichkeit, diese Daten mithilfe von GetDeviceCaps zu ermitteln, ist ein nur minimal größerer Aufwand.

Möchten Sie die aktuelle eingestellte Farbtiefe ermitteln, können Sie ebenfalls GetDeviceCaps verwenden. In Kombination mit der Konstanten BITSPIXEL liefert die Funktion die Anzahl der Bits zurück, die für das Speichern der Farbinformationen für ein einzelnes Pixel verwendet werden. Die Anzahl dadurch darstellbarer Farben errechnet sich aus der Farbtiefe durch einfaches Potenzieren:

Private Const BITSPIXEL As Long = 12& 
' --- Anwendung 
Dim hDCScreen As Long 
Dim lScreenColDepth As Long 
  hDCScreen = GetDC(0&) 
  lScreenColDepth = GetDeviceCaps(hDCScreen, BITSPIXEL) 
  MsgBox "Farbtiefe: " & _ 
         CStr(lScreenColDepth) & " Bits pro Pixel = " & _ 
         CStr(2 ^ lScreenColDepth) & " Farben.", _ 
         vbInformation

Eine zentrale Information über die Bildschirmeinstellung ist die Bildwiederholfrequenz in Hertz. Ein Blick in die Dokumentation zu GetDeviceCaps legt die Verwendung der Konstanten VREFRESH nahe - verrät aber auch, dass die Verwendung dieser Konstanten nur unter Windows NT, 2000 und XP funktioniert. Unter Windows 9x/Me liefert der Aufruf konsequent den Wert 0 zurück:

Private Const VREFRESH  As Long = 116& 
' --- Anwendung 
Dim hDCScreen As Long 
Dim lScreenRefresh As Long 
  hDCScreen = GetDC(0&) 
  lScreenRefresh = GetDeviceCaps(hDCScreen, VREFRESH) 
  MsgBox "Bildwiederholfrequenz: " & _ 
         CStr(lScreenRefresh) & " Hz." & vbNewLine & _ 
         "<STRONG>Hinweis:</STRONG> Funktioniert nicht unter Win9xMe!", _ 
         vbInformation

Mit einer Ausnahme haben wir damit dank GetDeviceCaps alle interessanten Informationen über die aktuellen Bildschirmeinstellungen auf einfache Weise ermittelt. Doch auch die Ermittlung der Bildwiederholfrequenz unter Win9x/Me ist nicht etwa Hexenwerk in der Registry. Später in diesem Artikel wird gezeigt, wie diese Information mithilfe der Funktion EnumDisplayettings mit nur wenig Mehraufwand zu ermitteln ist.

Bildschirmeinstellungen ändern

Was aber, wenn wir die Bildschirmeinstellungen verändern möchten? Die Suche nach einem GetDeviceCaps-Äquivalent "SetDeviceCaps" bleibt erfolglos, einen direkten Rückwärtsgang zum obigen Vorgehen gibt es also nicht. Zuständig ist für unser Ansinnen die API-Funktion ChangeDisplaySettings, die zusätzlich die Verwendung der umfangreichen Struktur DEVMODE (für "Device Mode") erforderlich macht:

Private Const CCHDEVICENAME As Long = 32& 
Private Const CCHFORMNAME   As Long = 32& 
Private Type DEVMODE 
  dmDeviceName As String * CCHDEVICENAME 
  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 * CCHFORMNAME 
  dmUnusedPadding As Integer 
  dmBitsPerPel As Long 
  dmPelsWidth As Long 
  dmPelsHeight As Long 
  dmDisplayFlags As Long 
  dmDisplayFrequency As Long 
End Type 
Private Declare Function ChangeDisplaySettings _ 
  Lib "user32" Alias "ChangeDisplaySettingsA" ( _ 
  ByRef lpDEVMODE As DEVMODE, _ 
  ByVal Flags As Long _ 
  ) As Long

Die für unseren Zweck besonders interessanten Elemente der DEVMODE-Struktur sind dmPelsWidth (Breite der Anzeige in Pixelskala), dmPelsHeight (Höhe der Anzeige in Pixelskala), dmBitsPerPel (Farbtiefe in Bits pro Pixel) und dmDisplayFrequency (Bildwiederholfrequenz). Zusätzliche Aufmerksamkeit verlangt das Element dmSize, dem vor Verwendung der DEVMODE-Struktur mittels Len-Funktion die Größe der Struktur mitgeteilt werden muss. Weiterhin interessiert uns das Element dmFields, in dem durch Or-Verknüpfung der folgenden Konstanten angegeben wird, welche DEVMODE-Informationen ein Funktionsaufruf berücksichtigen soll:

Private Const DM_BITSPERPEL       As Long = &H40000 
Private Const DM_PELSWIDTH        As Long = &H80000 
Private Const DM_PELSHEIGHT       As Long = &H100000 
Private Const DM_DISPLAYFREQUENCY As Long = &H400000

DM_PELSWIDTH

Das Element dmPelsWidth der DEVMODE-Struktur soll berücksichtigt werden.

DM_PELSHEIGHT

Das Element dmPelsHeight der DEVMODE-Struktur soll berücksichtigt werden.

DM_BITSPERPEL

Das Element dmBitsPerPel der DEVMODE-Struktur soll berücksichtigt werden.

DM_DISPLAYFREQUENCY

Das Element dmDisplayFrequency der DEVMODE-Struktur soll berücksichtigt werden.

Der zweite Parameter der Funktion ChangeDisplaySettings gibt an, auf welche Weise die Bildschirmeinstellungen geändert werden sollen. Für diesen Parameter sind die folgenden Konstanten vorgesehen:

Private Const CDS_UPDATEREGISTRY As Long = &H1& 
Private Const CDS_TEST           As Long = &H2&

CDS_TEST - Es soll geprüft werden, ob die Grafikkarte die angefragten Bildschirmeinstellungen unterstützt. Die Bildschirmeinstellungen werden nicht geändert.
0 - Die Bildschirmeinstellungen sollen vorübergehend geändert werden. Nach einem Neustart werden die ursprünglichen Bildschirmeinstellungen wiederhergestellt.
CDS_UPDATEREGISTRY - Die Bildschirmeinstellungen sollen dauerhaft geändert werden. Die entsprechenden Angaben in der Registry werden aktualisiert, so dass die neuen Bildschirmeinstellungen auch nach einem Neustart des Systems aktuell bleiben.

Der Rückgabewert der Funktion gibt nicht nur Aufschluss über den Erfolg des Aufrufs, sondern liefert auch zusätzliche Informationen:

Private Const DISP_CHANGE_SUCCESSFUL As Long = 0& 
Private Const DISP_CHANGE_RESTART    As Long = 1& 
Private Const DISP_CHANGE_FAILED     As Long = -1& 
Private Const DISP_CHANGE_BADMODE    As Long = -2& 
Private Const DISP_CHANGE_NOTUPDATED As Long = -3& 
Private Const DISP_CHANGE_BADFLAGS   As Long = -4& 
Private Const DISP_CHANGE_BADPARAM   As Long = -5&

DISP_CHANGE_SUCCESSFUL

Die Bildschirmeinstellungen wurden geändert.

DISP_CHANGE_RESTART

Der Aufruf war erfolgreich, aber das System muss neu gestartet werden, damit die neuen Bildschirmeinstellungen verwendet werden (wie Sie Windows von Ihrem Sourcecode aus neu starten können, erfahren Sie im MSDN Quickie "Windows 95, 98 und NT herunterfahren").

DISP_CHANGE_FAILED

Die Grafikarte konnte die Bildschirmeinstellungen nicht ändern.

DISP_CHANGE_BADMODE

Der Grafikmodus wird nicht unterstützt.

DISP_CHANGE_BADFLAGS

Der Aufruf schlug wegen ungültiger Flags fehl.

DISP_CHANGE_BADPARAM

Es wurde ein ungültiger Parameter übergeben.

DISP_CHANGE_NOTUPDATED

(Nur Windows NT/2000/XP) Die neuen Bildschirmeinstellungen konnten nicht in der Registry gespeichert werden.

Um zu überprüfen, ob die Grafikkarte einen gewünschten Modus unterstützt, können wir also so vorgehen:

Dim DM As DEVMODE 
Dim lResult As Long 
  ' DEVMODE-Struktur vorbereiten: 
  With DM 
    .dmSize = Len(DM) 
    .dmFields = DM_PELSWIDTH _ 
                Or DM_PELSHEIGHT _ 
                Or DM_BITSPERPEL 
    .dmPelsWidth = 1024      ' Breite 
    .dmPelsHeight = 768      ' Höhe 
    .dmBitsPerPel = 24       ' 2^24 =  16777216 Farben 
  End With 
  ' Testen, ob die Einstellung 1024x768 Pixel mit 16 Mio Farben unterstützt wird: 
  lResult = ChangeDisplaySettings(DM, CDS_TEST) 
  ' Ausgabe des Ergebnisses: 
  Select Case lResult 
    Case DISP_CHANGE_SUCCESSFUL 
      MsgBox "Bildschirmmodus kann sofort gesetzt werden.", vbInformation 
    Case DISP_CHANGE_RESTART 
      MsgBox "Bildschirmmodus kann nach Neustart gesetzt werden.", vbInformation 
    Case Else 
      MsgBox "Bildschirmmodus kann nicht gesetzt werden.", vbCritical 
  End Select

Um von der Grafikkarte unterstützte Bildschirmeinstellungen nach erfolgreichem Test zu setzen, wird die Konstante CDS_TEST für eine Änderung bis zum Neustart des Systems durch 0 ersetzt. Für eine dauerhafte Änderung verwenden Sie an dieser Stelle die Konstante CDS_UPDATEREGISTRY.

Tipp: Es empfiehlt sich, nach Windows-Art vor der endgültigen Änderung von Bildschirmeinstellungen den Anwender beurteilen zu lassen, ob ein vorgesehener Anzeigemodus für ihn brauchbar ist. Dafür schalten Sie zunächst für nur wenige Sekunden auf den neuen Modus um. Danach sollte wieder auf den vorherigen Bildschirmmodus gewechselt werden, um den Anwender zu fragen, ob der zeitweise gesetzte Bildschirmmodus für ihn stabil sichtbar war. Es ist nämlich durchaus nicht ungewöhnlich, dass eine Grafikkarte zwar problemlos mit einem Bildschirmmodus zurecht kommt, der Monitor des Anwenders aber mit der Darstellung eines solchen Modus überlastet ist. Dies kann dazu führen, dass der Anwender die Kontrolle über seinen Rechner verliert, weil er kein stabiles Bild mehr sehen kann. Erst nach Bestätigung durch den Anwender sollten Sie dann die vorgesehenen Bildschirmeinstellungen tatsächlich aktivieren.

Womit wir bei der Frage angekommen wären: Woher erfahren wir eigentlich, welche Kombinationen an Bildeinstellungen eine Grafikkarte unterstützt?

Unterstützte Bildschirmeinstellungen ermitteln

Die Grafikmodi, die von einem Grafikkarten-Treiber unterstützt werden, werden mithilfe der API-Funktion EnumDisplaySettings ermittelt:

Private Const ENUM_CURRENT_SETTINGS  As Long = &HFFFFFFFE 
Private Const ENUM_REGISTRY_SETTINGS As Long = &HFFFFFFFD 
Private Declare Function EnumDisplaySettings _ 
  Lib "user32" Alias "EnumDisplaySettingsA" ( _ 
  ByVal lpszDeviceName As String, _ 
  ByVal lModeNumber As Long, _ 
  ByRef lpDEVMODE As DEVMODE _ 
  ) As Long

Im ersten Parameter von EnumDisplaySettings - lpszDeviceName - wird der Name eines Anzeigegeräts übergeben (z.B. "\\.\DISPLAY1"). Für das aktuelle Anzeigegerät vereinfacht sich dieser Parameter durch Übergabe von vbNullString.

Im dritten Parameter findet sich die bekannte DEVMODE-Struktur, in der Windows Informationen über die jeweiligen Bildschirmeinstellungen zurück liefert.

Der für die Bedienung interessanteste Parameter ist lModeNumber: Wird er beim Aufruf von EnumDisplaySettings zu 0 gesetzt, ermittelt Windows den ersten verfügbaren Bildschirmmodus. Den zweiten verfügbaren Modus ermittelt die Funktion bei Übergabe von 1, usw., bis durch den Funktionsrückgabewert 0 signalisiert wird, dass keine weiteren Bildschirmmodi zur Verfügung stehen.

Spezielle Bedeutung kommt den oben bereits definierten Konstanten ENUM_CURRENT_SETTINGS und ENUM_REGISTRY_SETTINGS zu:

Setzen Sie für lpModeNumber die Konstante ENUM_CURRENT_SETTINGS ein, wird die Funktion EnumDisplaySettings angewiesen, in lpDEVMODE die aktuellen Bildschirmeinstellungen zurück zu liefern. Hier wird die Bildwiederholfrequenz auch unter Windows 9x/Me korrekt übergeben - unser eingangs erwähntes Problem bei der Verwendung von VREFRESH mit GetDeviceCaps unter Windows 9x/Me ist damit gelöst:

' Ermittlung der Bildwiederholfrequenz (auch unter Windows 9x/Me): 
Dim DM As DEVMODE 
Dim lResult As Long 
  ' DEVMODE-Struktur vorbereiten: 
  With DM 
    .dmSize = Len(DM) 
    .dmFields = DM_DISPLAYFREQUENCY 
  End With 
  ' Abrufen der aktuellen Auflösung: 
  EnumDisplaySettings vbNullString, ENUM_CURRENT_SETTINGS, DM 
  MsgBox "Aktuelle Bildwiederholfrequenz: " _ 
         & CStr(DM.dmDisplayFrequency) _ 
         & " Hertz", vbInformation

Der Konstanten ENUM_CURRENT_SETTINGS kommt analog die Bedeutung zu, die derzeit in der Registry eingetragenen Daten über den Bildschirmmodus nach einem Neustart zu ermitteln.

Tipp: Wenn Sie die Bildwiederholfrequenzen aller verfügbaren Bildschirmeinstellungen auflisten, wird Ihnen je nach eingesetzter Hardware auffallen, dass einige Einstellungen eine Bildwiederholfrequenz von 0 bzw. 1 aufweisen. Natürlich handelt es sich dabei nicht Angaben in Hertz, sondern um unbekannte Standardwerte, die anhand von Jumper-Steckern auf der Grafikkarte oder über andere Hilfsprogramme konfiguriert werden. Windows hat über die Hertz-Werte, die sich hinter diesen Einstellungen verbergen, keine Informationen und kann sie daher auch nicht zurück liefern. Im Windows-eigenen Dialog für Bildschirmeinstellungen werden Sie diese Bildschirmmodi daher auch nicht auffinden - sie werden dort der Einfachheit halber nicht aufgeführt. Wenn Sie in Ihren Anwendungen von EnumDisplaySettings angegebene Bildschirmmodi mit diesen "Bildwiederholfrequenzen" ignorieren, handeln Sie also Windows-konform.

Weitere Informationen zu den eingesetzten API-Funktionen und ihrer Bedienung finden Sie in Ihrer MSDN Library oder im MSDN Online.