Sign in to follow this  
Followers 0
ExpertNoob

Windows watchdog - don't mess up my windows' placements !

1 post in this topic

Hi folks,

Every thing wants to mess up my windows !

At first there was Zuma. I play in full screen, and it changes display geometry, so when I get back to my desktop, all windows are messed up ! Sometimes desktop icons too.

Then I noticded "The Drift": whenever I connect to my PC using Remote Desktop, windows are slightly pushed down and to the right. Since I connect to my PC on and off all day long, at the end of the day, when I get home, guess what !

One day I decided I would restore all windows to their normal positions and sizes before leaving so I wouldn't have to do it later. When I got home and unlocked my session, they were pushed like I was connecting through RD !!! I have no explanation for this, but the conclusion is obvious: everyone and their dog have an opinion about my windows' (MY windows' !!) best positions and sizes !

Me too, god damn it ! :angry:

This started to sound like a conspiracy against my poor windows, and I decided enough is enough !

So I looked around for one big dog to guard those damn windows' positions when my back is turned..

And came up with many tools to restore desktop icons' positions (like IconRestorer), but nothing for the windows' placements. It looked like I was the only one suffering from the windows' placements mess up syndrom :blink:

Maybe it IS a conspiracy !!

So, reluctantly, and with the daily suffering of seeing my beloved windows take the spin, I slowly came to the conclusion that I needed to do it myself. Me ! :

So, if you share my pain, suffer no more !

This little AutoIt script of mine woks surprisingly well, and I'm sure it can even be made better :

#Include <WinAPI.au3> ; _WinAPI_Get/SetWindowPlacement()
#include <Process.au3> ; ProcessExists()

Global Const $sleepTime = 4000

Opt ( "TrayAutoPause", 0 )

Global $ZumaMinimized = True, $noRDesktop = True, $desktopLocked = False
Global $i = 0, $j = 0, $list
Dim $plsZuma[100][2], $plsRDP[100][2], $plsLogon[100][2] ; windows placements

; Windows Terminal Services stuff
Global Const $WTSUserName = 5, $WTSWinStationName = 6, $WTSConnectState = 8, $WTSClientAddress = 14, $WTSClientDisplay = 15, $WTSClientName = 10
Global Const $WTS_CURRENT_SESSION = -1
Global Const $SessStates[10] = ["Active", "Connected", "Connect Query", "Shadow", "Disconnected", "Idle", "Listen", "Reset", "Down", "Init"]
Global Const $hWTSAPI32 = DllOpen("wtsapi32.dll") ; WTSRegisterSessionNotification()

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Get session ID
Global $mySessID = 0
Dim $myResult = DllCall("Kernel32.dll", "dword", "WTSGetActiveConsoleSessionId")
If @error Then
MsgBox(16, "WindowWatchDog", "Failed to get current session ID with WTSGetActiveConsoleSessionId() !")
Exit
EndIf
$mySessID = $myResult[0]

; Catch session change events
$hGUI = GUICreate("Catch RDP Session Connection", 600, 400)
DllCall($hWTSAPI32, "int", "WTSRegisterSessionNotification", "hwnd", $hGUI, "dword", 1) ; NOTIFY_FOR_ALL_SESSIONS
If @error Then
MsgBox(0,"", "Err WTSRegisterSessionNotification")
Exit
EndIf
GUIRegisterMsg(0x2B1, "WTSSESSION_CHANGE") ; WM_WTSSESSION_CHANGE

; Save windows placements every $sleepTime. Restore when appropriate
While True

;-------------------------------------------------------------------------------------------------
$listZuma = WinList( "[REGEXPTITLE:^Zuma Deluxe 1.0$]" )
if 2 == $listZuma[0][0] Then ; Zuma running
     If BitAnd(WinGetState($listZuma[1][1], ""), 16) Then ; minimized now
         If Not $ZumaMinimized Then  ; was up
             RestorePlacements($plsZuma)
             _NoHaltMsgBox(0, "Zuma", "Restored placements")
             Sleep($sleepTime)
         EndIf
         $ZumaMinimized = True
         CollectPlacements($plsZuma)
     Else
         $ZumaMinimized = False
     EndIf
EndIf

;-------------------------------------------------------------------------------------------------
If $noRDesktop Then CollectPlacements($plsRDP)

;-------------------------------------------------------------------------------------------------
If Not ProcessExists("logonui.exe") Then ; desktop not locked anymore
     If $desktopLocked Then ; desktop was locked
         sleep(3000)
         RestorePlacements($plsLogon)
         _NoHaltMsgBox(0, "Logon", "Restored placements")
         $desktopLocked = False ; do placement only once !
         Sleep($sleepTime)
     EndIf
     CollectPlacements($plsLogon)
Else ; desktop locked
     $desktopLocked = True ; session gone, continue collecting placements :
     CollectPlacements($plsLogon)
EndIf

;-------------------------------------------------------------------------------------------------
Sleep($sleepTime)
WEnd

Exit
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Func WTSSESSION_CHANGE($hWndGUI, $MsgID, $WParam, $LParam)

If Not ( $mySessID = Dec(Hex($LParam)) ) Then Return ; Out, bad session ! Go get your own instance of this wonderful script !

; WTS_REMOTE_CONNECT = 0x3, WTS_REMOTE_DISCONNECT = 0x4
; WTS_SESSION_UNLOCK = 0x8, WTS_SESSION_LOGON = 0x5
If $WParam = 3 Then ; Connect event
     If Not StringInStr(SessionInfo($WTSWinStationName, $LParam), "RDP-Tcp#") Then Return
     If "" = SessionInfo($WTSUserName, $LParam) Then Return
     If $SessStates[0] <> SessionInfo($WTSConnectState, $LParam) Then Return ; Active
    
     sleep(3000) ; give time for display to refresh and stablize
     RestorePlacements($plsRDP)
     _NoHaltMsgBox(0, "RDP", "Restored placements")
     $noRDesktop = False ; do placement only once !
EndIf

If $WParam = 4 Then $noRDesktop = True; session disconnected

EndFunc

Func SessionInfo($request = $WTSClientAddress, $session = $WTS_CURRENT_SESSION)
#cs
     BOOL WTSQuerySessionInformation(    hServer: WTS_CURRENT_SERVER_HANDLE=0 to indicate the RD Session Host server on which your application is running.
         __in HANDLE hServer,            SessionId: WTS_CURRENT_SESSION=-1                                                                          
         __in DWORD SessionId,
         __in WTS_INFO_CLASS WTSInfoClass,
         __out LPTSTR *ppBuffer,
         __out DWORD *pBytesReturned
     ); ret = 0 : failure
     WTSInfoClass:
         WTSClientProtocolType = 16 => USHORT : 0=The console session, 1=Value retained for legacy purposes, 2=The RDP protocol.
         WTSIsRemoteSession = 29 => TRUE or FALSE
         WTSSessionId = 4 => ULONG value containing the session identifier
         WTSUserName = 5 => null-terminated string containing the name of the user associated with the session
         WTSWinStationName = 6 => null-terminated string containing the name of the Remote Desktop Services session, NOT the winstation !
         WTSConnectState = 8 => enum {WTSActive,WTSConnected,WTSConnectQuery,WTSShadow,WTSDisconnected,WTSIdle,WTSListen,WTSReset,WTSDown,WTSInit}
         WTSClientName = 10 => null-terminated string containing the name of the client
         WTSClientAddress = 14 => struct {DWORD AddressFamily; BYTE Address[20];} WTS_CLIENT_ADDRESS, IP@ is Address[2..n]
         WTSClientDisplay = 15 => struct {DWORD HorizontalResolution; DWORD VerticalResolution; DWORD ColorDepth;} WTS_CLIENT_DISPLAY
         WTSLogonTime = 18 => ERROR_NOT_SUPPORTED (!!)
#ce

Local $ret, $stString, $s

Switch $request
     Case $WTSUserName, $WTSWinStationName, $WTSClientName, $WTSConnectState, $WTSClientAddress, $WTSClientDisplay
         $ret = DllCall("Wtsapi32.dll","int", "WTSQuerySessionInformationW", "Ptr", 0, "int", $session, "int", $request, "ptr*", 0, "dword*", 0)
         If (@error = True) Or ($ret[0] = False) Then Return "WTSQuerySessionInformationW() Error !!"
     Case Else
         Return $request & " unsupported !!"
EndSwitch

Switch $request
     Case $WTSUserName, $WTSWinStationName, $WTSClientName
         $stString = DllStructGetData (  DllStructCreate( "WCHAR[" & Ceiling($ret[5]/2) & "]", $ret[4] ) , 1 )
         $stString = BinaryToString($stString, 4)
    
     Case $WTSConnectState
         $stString = $SessStates[ DllStructGetData (     DllStructCreate( "int", $ret[4] ) , 1 ) ]
        
     Case $WTSClientAddress
         $stString = DllStructGetData (  DllStructCreate( "byte[" & $ret[5] & "]", $ret[4] )         , 1 ) ; 14 WTSClientAddress
         Local $ip = StringTrimLeft($stString,14)
         $stString = Dec(StringLeft($ip,2))
         For $i = 1 to 3
             $ip = StringTrimLeft($ip,2)
             $stString &= "." & Dec(StringLeft($ip,2))
         Next
    
     Case $WTSClientDisplay
         Local $display = DllStructCreate( "DWORD[" & Ceiling($ret[5]/4) & "]", $ret[4] )                            ; 15 WTSClientDisplay
         $stString = DllStructGetData($display, 1,1) &" x "& DllStructGetData($display, 1,2) & " pixels, "
         $display = DllStructGetData($display, 1,3)
         If $display < 5 Then $display = $display * 4
         If $display = 15 Then $display = 16 ; oO
         If $display = 8 Then
             $stString &= "RGB colors"
         Else
             $stString &= "color depth = " & $display & " bits"
         EndIf
EndSwitch

DllCall("Wtsapi32.dll", "int", "WTSFreeMemory", "ptr", $ret[4])
Return $stString
EndFunc

Func RestorePlacements(ByRef $pls)
For $i = 1 to (UBound($pls, 1) - 1)
     If 0 <> WinGetState($pls[$i][0]) Then _WinAPI_SetWindowPlacement( $pls[$i][0], DllStructGetPtr($pls[$i][1]) )
Next
EndFunc

Func CollectPlacements(ByRef $pls)
$j = 0
$list = WinList()
ReDim $pls[ $list[0][0] ][2]
For $i = 1 to $list[0][0]
     $hwd = $list[$i][1]
     If BitAnd( WinGetState($hwd), 2 ) Then ; 2 = Window is visible
         $pls[$j][0] = $hwd
         $pls[$j][1] = _WinAPI_GetWindowPlacement($hwd)
         $j+=1
     EndIf
Next
$j-=1
ReDim $pls[ $j ][2]
EndFunc

Func _NoHaltMsgBox($code=0, $title = "",$text = "" ,$timeout = 0)
Run (@AutoItExe & ' /AutoIt3ExecuteLine "MsgBox(' & $code & ', '''& $title & ''', '''& $text &''',' & $timeout & ')"')
EndFunc

This works like a charm, AutoIt PAWA ! :thumbsup:

It needs to be extended to handle desktop icon positions too. IconRestorer is not dynamic like this script, which takes care of things and restores stuff automatically when needed..

1 person likes this

Share this post


Link to post
Share on other sites



Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0