Sporadische Fehler - Bildschirmauflösung verändern

  • Hallo zusammen,

    ich habe ein Script, welches bei uns im Einsatz ist und bei allen angeschlossenen Bildschirmen die Auflösung auf eine vordefinierte Auflösung ändert.
    Das klappt soweit sehr gut.

    Verwendetes Script
    [autoit]

    #Region ;Header
    ;===============================================================================================================================
    ;Description......: Set Screen Resolution
    ;Author...........: Veronesi
    ;Date.............: 03.09.2010
    ;===============================================================================================================================
    #NoTrayIcon
    #AutoIt3Wrapper_UseX64=n
    #AutoIt3Wrapper_icon=WEY.ico
    #AutoIt3Wrapper_Res_Comment=Set Screen Resolution
    #AutoIt3Wrapper_Res_Description=Set Screen Resolution
    #AutoIt3Wrapper_Res_Fileversion=1.1.0.1
    #AutoIt3Wrapper_Res_Fileversion_AutoIncrement=y
    #AutoIt3Wrapper_Res_LegalCopyright=Veronesi

    [/autoit] [autoit][/autoit] [autoit]

    Opt("MustDeclareVars", 1) ;Must declare variables
    OnAutoItExitRegister("_Exit") ;Create defined Exit Callback Register
    _Singleton(@ScriptName) ;Allow only one instance of this script at the same time
    HotKeySet("{ESC}","_Exit") ;ESC-Hotkey
    HotKeySet("{F8}","_SetResolution") ;ESC-Hotkey

    [/autoit] [autoit][/autoit] [autoit]

    #include-once
    #include <Misc.au3>
    #EndRegion ;End of Header
    ;===============================================================================================================================

    [/autoit] [autoit][/autoit] [autoit]

    #Region ;Variables, Constants
    Dim Const $ScreenWidth = 2560
    Dim Const $ScreenHeight = 1600
    Dim Const $BitPP = 32
    Dim Const $RefreshRate = 60

    [/autoit] [autoit][/autoit] [autoit]

    Dim $aMonitorInfo
    #EndRegion ;End of Variables, Constants
    ;===============================================================================================================================

    [/autoit] [autoit][/autoit] [autoit]

    #Region ;Main
    While 1
    Sleep(5000)
    WEnd
    Exit
    #EndRegion ;End of Main
    ;===============================================================================================================================

    [/autoit] [autoit][/autoit] [autoit]

    #Region ;Functions
    Func _SetResolution()
    $aMonitorInfo = _GetMonitorInfo()
    For $i = 1 To $aMonitorInfo[0][0]
    _ChangeScreenRes($i, $ScreenWidth, $ScreenHeight, $BitPP, $RefreshRate)
    Next
    Return 1
    EndFunc ;==>_SetResolution

    [/autoit] [autoit][/autoit] [autoit]

    Func _ChangeScreenRes($i_DisplayNum = 1, $i_Width = @DesktopWidth, $i_Height = @DesktopHeight, $i_BitsPP = @DesktopDepth, $i_RefreshRate = @DesktopRefresh)
    ;===============================================================================
    ; Function Name: _ChangeScreenRes()
    ; Description: Changes the current screen geometry, colour and refresh rate.
    ; Version: 1.0.0.0
    ; Parameter(s): $i_DisplayNum - Display to change, starting at 1
    ; $i_Width - Width of the desktop screen in pixels. (horizontal resolution)
    ; $i_Height - Height of the desktop screen in pixels. (vertical resolution)
    ; $i_BitsPP - Depth of the desktop screen in bits per pixel.
    ; $i_RefreshRate - Refresh rate of the desktop screen in hertz.
    ; Requirement(s): AutoIt Beta > 3.1
    ; Return Value(s): On Success - Screen is adjusted, @ERROR = 0
    ; On Failure - sets @ERROR = 1
    ; Forum(s):
    ; Author(s): Original code - psandu.ro, PartyPooper
    ; Modifications - bobchernow
    ;===============================================================================
    Local Const $DM_PELSWIDTH = 0x00080000
    Local Const $DM_PELSHEIGHT = 0x00100000
    Local Const $DM_BITSPERPEL = 0x00040000
    Local Const $DM_DISPLAYFREQUENCY = 0x00400000
    Local Const $CDS_TEST = 0x00000002
    Local Const $CDS_UPDATEREGISTRY = 0x00000001
    Local Const $DISP_CHANGE_RESTART = 1
    Local Const $DISP_CHANGE_SUCCESSFUL = 0
    Local Const $HWND_BROADCAST = 0xffff
    Local Const $WM_DISPLAYCHANGE = 0x007E
    If $i_Width = "" Or $i_Width = -1 Then $i_Width = @DesktopWidth; default to current setting
    If $i_Height = "" Or $i_Height = -1 Then $i_Height = @DesktopHeight; default to current setting
    If $i_BitsPP = "" Or $i_BitsPP = -1 Then $i_BitsPP = @DesktopDepth; default to current setting
    If $i_RefreshRate = "" Or $i_RefreshRate = -1 Then $i_RefreshRate = @DesktopRefresh; default to current setting
    Local $DEVMODE = DllStructCreate("byte[32];int[10];byte[32];int[6]")
    Local $s_Display

    [/autoit] [autoit][/autoit] [autoit]

    $s_Display = "\\.\Display" & $i_DisplayNum

    [/autoit] [autoit][/autoit] [autoit]

    Local $B = DllCall("user32.dll", "int", "EnumDisplaySettings", "ptr", 0, "int", 0, "ptr", DllStructGetPtr($DEVMODE))

    [/autoit] [autoit][/autoit] [autoit]

    If @error Then
    $B = 0
    SetError(1)
    Return $B
    Else
    $B = $B[0]
    EndIf
    If $B <> 0 Then
    DllStructSetData($DEVMODE, 2, BitOR($DM_PELSWIDTH, $DM_PELSHEIGHT, $DM_BITSPERPEL, $DM_DISPLAYFREQUENCY), 5)
    DllStructSetData($DEVMODE, 4, $i_Width, 2)
    DllStructSetData($DEVMODE, 4, $i_Height, 3)
    DllStructSetData($DEVMODE, 4, $i_BitsPP, 1)
    DllStructSetData($DEVMODE, 4, $i_RefreshRate, 5)

    [/autoit] [autoit][/autoit] [autoit]

    ;~ $B = DllCall("user32.dll", "int", "ChangeDisplaySettingsEx","str", $s_Display, "ptr", DllStructGetPtr($DEVMODE), "hwnd", 0, "dword", $CDS_TEST, "lparam", 0)
    ;~ If @error Then
    ;~ $B = -1
    ;~ Else
    ;~ $B = $B[0]
    ;~ EndIf
    ;~ Select
    ;~ Case $B = $DISP_CHANGE_RESTART
    ;~ $DEVMODE = ""
    ;~ Return 2
    ;~ Case $B = $DISP_CHANGE_SUCCESSFUL
    DllCall("user32.dll", "int", "ChangeDisplaySettingsEx","str", $s_Display, "ptr", DllStructGetPtr($DEVMODE), "hwnd", 0, "dword", $CDS_UPDATEREGISTRY, "lparam", 0)
    ;~ DllCall("user32.dll", "int", "SendMessage", "hwnd", $HWND_BROADCAST, "int", $WM_DISPLAYCHANGE, _
    ;~ "int", $i_BitsPP, "int", $i_Height * 2 ^ 16 + $i_Width)
    $DEVMODE = ""
    Return 1
    ;~ Case Else
    ;~ $DEVMODE = ""
    ;~ SetError(1)
    ;~ Return $B
    ;~ EndSelect
    EndIf
    EndFunc;==>_ChangeScreenRes

    [/autoit] [autoit][/autoit] [autoit]

    Func _GetMonitorInfo()
    ;Returns some informations about your screens
    ;Call: _GetMonitorInfo()
    ;Return: Array With
    ;[0][0] Nr of Monitors
    ;[1][0] X Resolution of Monitor 1
    ;[1][1] Y Resolution of Monitor 1
    ;[1][2] X Position of Monitor 1
    ;[1][3] Y Position of Monitor 1

    [/autoit] [autoit][/autoit] [autoit]

    ;[2][0] X Resolution of Monitor 2
    ;[2][1] Y Resolution of Monitor 2
    ;[2][2] X Position of Monitor 2
    ;[2][3] Y Position of Monitor 2
    ;......

    [/autoit] [autoit][/autoit] [autoit]

    Local $NrOfMonitors, $ResolutionX[32], $ResolutionY[32], $PositionX[32], $PositionY[32], $Ret
    Local $cbMonitorEnumProc = DllCallbackRegister("MonitorEnumProc", "ubyte", "ptr;ptr;ptr;int")

    [/autoit] [autoit][/autoit] [autoit]

    If @error Then Return SetError(1, 0, False)
    Local $strctCount = DllStructCreate("uint Count;uint Width[12];uint Height[12];int left[12];int top[12]")
    If @error Then Return SetError(2, @error, False)
    Local $iCount

    [/autoit] [autoit][/autoit] [autoit]

    DllStructSetData($strctCount, "Count", 0)

    [/autoit] [autoit][/autoit] [autoit]

    $Ret = DllCall("User32.dll", "ubyte","EnumDisplayMonitors","ptr", 0,"ptr", 0, "ptr", DllCallbackGetPtr($cbMonitorEnumProc), "ptr", DllStructGetPtr($strctCount))
    If @error Or $Ret[0] = 0 Then Return SetError(3, @error, False)

    [/autoit] [autoit][/autoit] [autoit]

    DllCallbackFree($cbMonitorEnumProc)

    [/autoit] [autoit][/autoit] [autoit]

    $iCount = Int(DllStructGetData($strctCount, "Count"))
    If @error Then Return SetError(4, @error, False)

    [/autoit] [autoit][/autoit] [autoit]

    Local $aMonitors[$iCount+1][4] = [[$iCount]]

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 1 To $iCount
    $aMonitors[$i][0] = Int(DllStructGetData($strctCount, "Width",$i))
    If @error Then Return SetError(5, @error, False)
    $aMonitors[$i][1] = Int(DllStructGetData($strctCount, "Height",$i))
    If @error Then Return SetError(6, @error, False)
    $aMonitors[$i][2] = Int(DllStructGetData($strctCount, "left",$i))
    If @error Then Return SetError(7, @error, False)
    $aMonitors[$i][3] = Int(DllStructGetData($strctCount, "top",$i))
    If @error Then Return SetError(8, @error, False)
    Next

    [/autoit] [autoit][/autoit] [autoit]

    $strctCount = 0
    Return $aMonitors
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func MonitorEnumProc($hMonitor, $hdcMonitor, $lprcMonitor, $dwData)
    Local $strctRECT = DllStructCreate("long left;long top;long right;long bottom", $lprcMonitor)
    Local $strctCount = DllStructCreate("uint Count;uint Width[12];uint Height[12];int left[12];int top[12]", $dwData)
    Local $iNumber = DllStructGetData($strctCount, "Count")
    Local $Height = Int(DllStructGetData($strctRECT, "bottom"))-Int(DllStructGetData($strctRECT, "top"))
    Local $Width = Int(DllStructGetData($strctRECT, "right"))-Int(DllStructGetData($strctRECT, "left"))

    [/autoit] [autoit][/autoit] [autoit]

    DllStructSetData($strctCount, "Width", $Width, $iNumber+1)
    If @error Then Return SetError(9, @error, False)
    DllStructSetData($strctCount, "Height", $Height, $iNumber+1)
    If @error Then Return SetError(10, @error, False)
    DllStructSetData($strctCount, "left", Int(DllStructGetData($strctRECT, "left")), $iNumber+1)
    If @error Then Return SetError(11, @error, False)
    DllStructSetData($strctCount, "top", Int(DllStructGetData($strctRECT, "top")), $iNumber+1)
    If @error Then Return SetError(12, @error, False)
    DllStructSetData($strctCount, "Count", $iNumber+1)
    If @error Then Return SetError(13, @error, False)
    Return True
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _Exit()
    HotKeySet("{ESC}") ;Delete Hotkey
    HotKeySet("{F8}") ;Delete Hotkey
    Exit
    EndFunc ;==>_Exit
    #EndRegion ;End of Functions

    [/autoit]

    Das Problem ist nun, dass wir im laufenden PC Betrieb die Monitore abhängen und neue anschliessen müssen. (Kabel und Monitor Test)
    Da pro Tag über 500 neue Monitore an diesen Testplatz angeschlossen werden, können wir nicht jedes mal den PC neu starten. Auf dem PC ist Win7 64bit.

    In 460 von 500 Fällen erkennt Windows automatisch die Monitore und stellt die Auflösung korrekt ein. In den restlichen Fällen kommt mein Script zum Einsatz, welches per F8 Hotkey die Auflösung setzt. Das klappt auch - aber "bloss" in 80% der Fällen.

    Manchmal kann die Auflösung per Script einfach nicht gesetzt werden. (Auch wenn man es mehrmals hintereinander versucht)
    Es kommt auch kein Fehler oder so von der Funktion zurück! (Fehlerbehandlung ist in diesem Script wieder gelöscht worden)

    Geht man dann aber manuell in die Systemsteuerung und stellt es dort ein, dann geht es. D.h. Windows selber kann es, aber mein Script nicht.

    1. Frage: Warum kann das mein Script manchmal nicht? Sieht jemand einen unbehandelten Fehler?
    2. Frage: Manchmal kommt beim starten des Scripts oder auch während der Laufzeit einen AutoIt Error mit der Meldung, dass eine (Array) Variable benutzt wurde, die noch nicht definiert ist. Findet jemand den Fehler?
    Manchmal habe ich das Gefühl, wenn man das Script lange laufen lässt, und dann beendet und sofort so schnell wie möglich das Script neu startet, dass dann dieser Fehler öfters auftaucht. Wie wenn noch was im Speicher wäre...

    Hinweis: Die auskommentierten Zeilen in der Funktion _ChangeScreenRes habe ich auch schon drin gehabt. Es tauchen die gleichen sporadischen Fehler auf.

    Vielen Dank!
    Veronesi

  • Nicht mal 12 Stunden um und du pusht schon? tz...tz...tz... :P

    Hab dein Skript nur kurz überflogen, aber zu

    2. Frage: Manchmal kommt beim starten des Scripts oder auch während der Laufzeit einen AutoIt Error mit der Meldung, dass eine (Array) Variable benutzt wurde, die noch nicht definiert ist


    Such in deinem Skript alle Funktionen die ein Array zurückgeben und prüfe ob die Variable auch wirklich ein Array ist.

    [autoit]

    If Not isArray($_dein_array) Then
    MsgBox(64, "Array Error", "Die Funktion XY hat kein Array zurückgegeben." & @CRLF & "Bitte überprüfen!")
    Exit
    EndIf

    [/autoit]


    Wenn Autoit das nächstemal abbricht, dann weißt du wenigstens wo genau ;)

  • Guten Morgen!

    Ich habe das mal versucht. Doch leider konnte ich den 2. Fehler nicht mehr direkt reproduzieren.
    Er taucht zwar noch auf, aber mit einem etwas geänderten Script:

    Wenn ich anstelle eines HotKeys eine Adlibregister definiere und die Auflösung alle 5 Sekunden einstellen lasse, dann tritt der Fehler auf einem XP System auf, wenn man per Remote auf diesen PC verbindet und die Verbindung wieder trennt.

    Da dies aber ein Spezialfall ist, lasse ich die 2. Fehlermeldung mal beiseite!

    Viel mehr würde mich interessieren, warum mein Script manchmal die Auflösung nicht einstellen kann, manuell aber schon!
    Oder anderst gefragt: Gibt es eine andere Methode um die Auflösungen zu verändern? Ich meine Windows kann das ja auch tun....

    Grüsse
    Veronesi

  • Hast du es mal mit #RequireAdmin versucht?
    Ich weiß jetzt nicht, auf welchen Systemen es nicht funktioniert, aber Windoof ist ja etwas eigen mit den Rechten...

  • Hast du es mal mit #RequireAdmin versucht?
    Ich weiß jetzt nicht, auf welchen Systemen es nicht funktioniert, aber Windoof ist ja etwas eigen mit den Rechten...

    Hallo!
    Nein, habe ich in diesem Script noch nicht. Doch da es ja meistens funktioniert dürfte es nicht daran liegen.

    Das Problem ist, dass wenn ich ein #RequireAdmin dranhänge, der Benutzer immer die UAC bestätigen musst, wenn er mein Programm startet.
    Das möchten wir in diesem Fall nicht. Und die UAC deaktivieren kommt auf diesem Netzwerk-PC vom Administrator aus nicht in Frage.

    Das Problem ist doch, dass es meistens geht, aber nicht immer. Wenn man dann manuell in der Systemsteuerung die Aufösung ändert, funktioniert alles.
    Also hat Windows vermutlich noch "andere Möglichkeiten" um die Auflösung korrekt zu setzen.

    Trotzdem danke für den Input!
    Veronesi