Jump to content
Sign in to follow this  
marc0v

Dealing with suspended/hanged window (process)

Recommended Posts

marc0v

If an autoit script tries to do WinGetTitle, WinSetState, WinActivate, WinSetOnTop... on a suspended or hanged window the script will freeze until the target window (process or thread) un-freeze or disappear.

A workaround, if you suspect a window to get sometimes hanged, is to do asynchronous calls or time-outed calls.

Asynchronous/time-outed calls can fail if the target window is hanged, but your script won't be frozen. So you get a chance to take further action.

It is noticeable that WinGetState works on suspended/hanged window (seemingly), so you can make a loop after an asynchronous call to check if your action succeded or failed in changing the state of the window.

This is related to 'Bug' or 'Not a Bug' (this isn't yet decided)

http://www.autoitscript.com/trac/autoit/ticket/1294

Here are 3 examples

1. time-outed call to get the window title (changing the window title could also be done with WM_SETTEXT in place of WM_GETTEXT)

    http://msdn.microsoft.com/en-us/library/ms644952(VS.85).aspx

2. asynchronous call to change the window state : minimize, maximize, restore, show, hide, activate

    http://msdn.microsoft.com/en-us/library/ms633549(VS.85).aspx

3. asynchronous call to change the window position : show, hide, window pos, window size, zorder, topmost attibute

    http://msdn.microsoft.com/en-us/library/ms633545(VS.85).aspx

In each example you need to provide a valid window title (or an advanced window identifier) in the "$wintitle" variable.

1 : get the window title with a time-out

; Messages
Global Const $WM_GETTEXTLENGTH = 0x000E
Global Const $WM_GETTEXT = 0x000D

; Abort get title operation if target window is not responding, a time-out (ms) must be provided
Global Const $SMTO_ABORTIFHUNG = 0x0002

$wintitle = "provide_valid_window_title_here"

$infourl = "http://msdn.microsoft.com/en-us/library/ms644952(VS.85).aspx"

$ms_gettitle_timeout = 250
; if time-out is really short (just a few ms), the window might not have the time to return its title even if it is not hanged
; in such case an empty string is returned, and error is 'hung window' (5)
;
; on the other hand, an un-responding window can however returns its title in some cases...
; with something like [ (not responding)] appended at the end of the title (langage-dependant suffix)
; in such case no error is returned (so it is not a reliable test to check if a process/thread is hanged)
$gottitle = WINGETTITLE_TIMEOUT($wintitle, $ms_gettitle_timeout)
$err = @error
$exterr = @extended

ClipPut($infourl)
msgbox(0,"Finished", _
    "Done with:" & $wintitle & @CR & _
    "handle:" & WinGetHandle($wintitle) & @CR & _
    @CR & _
    "got title:[" & StringReplace($gottitle, Chr(0), "{NULL}") & "]" & @CR & _
    "error:" & $err & @CR & _
    "extended error:" & $exterr & @CR & _
    @CR & _
    "more infos at:" & $infourl & @CR & _
    "this URL has been copied to your clipboard to be easily pasted in your Internet Browser")

; same syntax as WinGetTitle plus a time-out
Func WINGETTITLE_TIMEOUT( _
$id_window, _
$mstimeout = 500)
    Local $hwnd, $arrdll, $ilen, $vout, $sfulltitle
    Local Const $inowinexterror = 4 ; no window
    Local Const $ihungwinexterror = 5 ; hung window

    $hwnd = WinGetHandle($id_window)
    If $hwnd = "" Then Return SetError($inowinexterror, 0, "")

    $arrdll = SENDMESSAGE_TIMEOUT($ilen, $hwnd, $WM_GETTEXTLENGTH, 0, 0, $SMTO_ABORTIFHUNG, $mstimeout, - 1, "int", "int")
    If @error Then Return SetError(@error, @extended, "")
    If $arrdll[1] = 0 Then Return SetError($inowinexterror, 0, "")
    If $arrdll[0] = 0 Then Return SetError($ihungwinexterror, 0, "")

    $arrdll = SENDMESSAGE_TIMEOUT($vout, $hwnd, $WM_GETTEXT, $ilen + 1, "", $SMTO_ABORTIFHUNG, $mstimeout, - 1, "int", "str")
    If @error Then Return SetError(@error, @extended, "")
    If $arrdll[1] = 0 Then Return SetError($inowinexterror, 0, "")
    If $arrdll[0] = 0 Then Return SetError($ihungwinexterror, 0, "")

    Return $arrdll[4]
EndFunc

; used by WINGETTITLE_TIMEOUT, can be used to send other kind of messages with a time-out
Func SENDMESSAGE_TIMEOUT( _
ByRef $vout, _
$hwnd, _
$imsg, _
$wparam, _
$lparam, _
$nflags, _
$mstimeout, _
$r = 0, _
$t1 = "int", _
$t2 = "int")
    Local $arrdll, $dllerror, $dllexterror

    $arrdll = DllCall("user32.dll", "long", "SendMessageTimeout", "hwnd", $hwnd, "int", $imsg, _
                $t1, $wparam, $t2, $lparam, "int", $nflags, "int", $mstimeout, "int*", "")
    $dllerror = @error
    $dllexterror = @extended
    If $dllerror <> 0 Then
        $vout = 0
        Return SetError($dllerror, $dllexterror, 0)
    EndIf

    $vout = $arrdll[7]
    If $r >= 0 And $r <= 4 Then Return $arrdll[$r]
    Return $arrdll
EndFunc

2 : asynchronously change the window state : minimize, maximize, restore, show, hide, activate

$wintitle = "provide_valid_window_title_here"

$infourl = "http://msdn.microsoft.com/en-us/library/ms633549(VS.85).aspx"

; this will un-minimize and activate a window, workarounded a bit since there is no SW_ACTIVATE state
; the window will retain its maximized state if it was maximized
; this will also show the window if hidden (?)
WINSETSTATE_ASYNC($wintitle, @SW_MINIMIZE)
WINSETSTATE_ASYNC($wintitle, @SW_RESTORE)
Sleep(2000)

; this will hide the window
WINSETSTATE_ASYNC($wintitle, @SW_HIDE)
Sleep(1000)

; this will minimize the window, works on hidden windows
WINSETSTATE_ASYNC($wintitle, @SW_MINIMIZE)
Sleep(500)

; this will just show the window (but not restore it or activate it if minimized)
WINSETSTATE_ASYNC($wintitle, @SW_SHOW)
Sleep(1000)

ClipPut($infourl)
msgbox(0,"Finished", _
    "Done with:" & $wintitle & @CR & _
    "handle:" & WinGetHandle($wintitle) & @CR & _
    @CR & _
    "more infos at:" & $infourl & @CR & _
    "this URL has been copied to your clipboard to be easily pasted in your Internet Browser")

; same syntax as WinSetState less the "text" parameter
Func WINSETSTATE_ASYNC( _
$id_window, _
$winstate)
    Local $aRet, $hwnd
    Local Const $inowinexterror = 4

    $hwnd = WinGetHandle($id_window)
    If $hwnd = "" Then Return SetError($inowinexterror, 0, 0)

    $aRet = DllCall("user32.dll", "int", "ShowWindowAsync", "hwnd", $hwnd, "int", $winstate)
    If @error Then Return SetError(@error, @extended, 0)

    Return $aRet[0]
EndFunc

3 : asynchronously change the window position : show, hide, window pos, window size, zorder, topmost attibute

; ===============================================================================================================================
; SetWindowPos Constants, SetWindowPos does not seem fitted to activate a window (look at ASynchronous WinSetState instead)
; ===============================================================================================================================
Global Const $SWP_NOSIZE = 0x0001
Global Const $SWP_NOMOVE = 0x0002
Global Const $SWP_NOZORDER = 0x0004
Global Const $SWP_NOREDRAW = 0x0008

; seems to avoid activating the last active control inside the window rather than the window itself (?)
; this may avoid to have the cursor appearing inside an edit control within a non-active window
Global Const $SWP_NOACTIVATE = 0x0010

Global Const $SWP_FRAMECHANGED = 0x0020
Global Const $SWP_DRAWFRAME = 0x0020

; if the SWP_SHOWWINDOW or SWP_HIDEWINDOW flag is set, the window cannot be moved or sized
Global Const $SWP_SHOWWINDOW = 0x0040
Global Const $SWP_HIDEWINDOW = 0x0080

Global Const $SWP_NOCOPYBITS = 0x0100
Global Const $SWP_NOOWNERZORDER = 0x0200
Global Const $SWP_NOREPOSITION = 0x0200
Global Const $SWP_NOSENDCHANGING = 0x0400
Global Const $SWP_DEFERERASE = 0x2000

; the most important flag to target possibly hanged/suspended processes
; if the targeted process is hanged/suspended nothing will happen, but your script won't be frozen
; check the result of your action with WinGetState (which works on hanged/suspended processes)
Global Const $SWP_ASYNCWINDOWPOS = 0x4000
; ===============================================================================================================================
; End of SetWindowPos Constants
; ===============================================================================================================================

; ===============================================================================================================================
; Z order Constants, Z order is the order in which windows are piled on the desktop
; ===============================================================================================================================
Global Const $HWND_BOTTOM = 1 ; Places the window at the bottom of the Z order (seems buggy, see workaround in example)
Global Const $HWND_NOTOPMOST = -2 ; Places the window above all non-topmost windows
Global Const $HWND_TOP = 0 ; Places the window at the top of the Z order (seems buggy, see workaround in example)
Global Const $HWND_TOPMOST = -1 ; Places the window above all non-topmost windows
; ===============================================================================================================================
; End of Z order Constants
; ===============================================================================================================================

$wintitle = "provide_valid_window_title_here"

$infourl = "http://msdn.microsoft.com/en-us/library/ms633545(VS.85).aspx"

; example 1
; place a visible window at the bottom of $zorder (under all windows)
; workarounded since $HWND_BOTTOM does not always seem to work
WINDOWSTATE($wintitle, $HWND_BOTTOM, 0, 0, 0, 0, $SWP_ASYNCWINDOWPOS + $SWP_NOSIZE + $SWP_NOMOVE + $SWP_HIDEWINDOW + $SWP_NOACTIVATE)
WINDOWSTATE($wintitle, $HWND_BOTTOM, 0, 0, 0, 0, $SWP_ASYNCWINDOWPOS + $SWP_NOSIZE + $SWP_NOMOVE + $SWP_SHOWWINDOW + $SWP_NOACTIVATE)
Sleep(2000)

; example 2
; place a visible window at the top of $zorder (above all not 'always on top' windows, this does not activate the window)
; workarounded since $HWND_TOP does not seem to work
WINDOWSTATE($wintitle, $HWND_TOPMOST, 0, 0, 0, 0, $SWP_ASYNCWINDOWPOS + $SWP_NOSIZE + $SWP_NOMOVE + $SWP_SHOWWINDOW + $SWP_NOACTIVATE)
WINDOWSTATE($wintitle, $HWND_NOTOPMOST, 0, 0, 0, 0, $SWP_ASYNCWINDOWPOS + $SWP_NOSIZE + $SWP_NOMOVE + $SWP_SHOWWINDOW + $SWP_NOACTIVATE)
Sleep(2000)

; example 3
; move a window (visible or not, does not work if window is minimized : window must be restored before being moved)
WINDOWSTATE($wintitle, $HWND_TOP, 40, 40, 0, 0, $SWP_ASYNCWINDOWPOS + $SWP_NOSIZE + $SWP_NOACTIVATE)
Sleep(1000)
WINDOWSTATE($wintitle, $HWND_TOP, 20, 20, 0, 0, $SWP_ASYNCWINDOWPOS + $SWP_NOSIZE + $SWP_NOACTIVATE)
Sleep(1000)

ClipPut($infourl)
msgbox(0,"Finished", _
    "Done with:" & $wintitle & @CR & _
    "handle:" & WinGetHandle($wintitle) & @CR & _
    @CR & _
    "more infos at:" & $infourl & @CR & _
    "this URL has been copied to your clipboard to be easily pasted in your Internet Browser")

Func WINDOWSTATE( _
$id_window, _
$zorder, _
$left, _
$top, _
$width, _
$height, _
$flag)
    Local $aRet, $hwnd
    Local Const $inowinexterror = 4

    $hwnd = WinGetHandle($id_window)
    If $hwnd = "" Then Return SetError($inowinexterror, 0, 0)

    $aRet = DllCall("user32.dll", "int", "SetWindowPos", "hwnd", $hwnd, "hwnd", $zorder, _
        "int", $left, "int", $top, "int", $width, "int", $height, "uint", $flag)
    If @error Then Return SetError(@error, @extended, 0)

    Return $aRet[0]
EndFunc

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  

×

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.