Jump to content

Troubles with WinSetState and restoring a minimized window.


JimS
 Share

Recommended Posts

I am writing a script that will be a command-line wrapper for launching programs from shortcuts.  The purpose of the script is to make sure only one instance of a program gets launched.  It finds the process based on the program name.  If it doesn't find a process, it launches the program with run().  If it finds a process for that exe, it uses the PID to find the handle of the window for that PID.  All the rest of the activity is done using the handle.  My initial testing is using osk.exe. 

The script works, sort of, but has problems with certain states and making the window show up again on subsequent runs.

The problem is that I am using WinGetState() to get the window state, again using the handle but the state returned is inconsistent with expectations.  I'm using a mix of WinSetState and WinActivate to make the window show up, but that also is not behaving as expected.   For example, I launch the osk.exe program using the script, then manually minimize the program using the minimize button.  When I run the script again, it finds the state of the window as (5) - it doesn't have the bitwise 16 state for minimized, or the 2 state for visible.  Then, when my function sets the state to 15 (exists, visible, enabled, active) it doesn't display the window. Running it again, it says the state is (7), and even though I am displaying the state (15) before the autoit script closes, it still says state is 7 on subsequent runs of the script before it executes my function.

If I restore the window manually by clicking the taskbar icon, it still claims the window state is 7.  If I minimize again with the minimize button, it still says its state is 7.

If I float over the minimized taskbar icon, then float over the Aero Glass image of the window, it shows the window in the position it's supposed to be, which is the normal Windows 7 response.  When I run the script again when it is in this state, it says the state is 7, or exists, visible and enabled but not active.  Then it says it sets the window state to 15 but the program window is still not showing.

If I restore it ,manually by clicking on the taskbar icon, then minimize it again by clicking again on the taskbar icon, when running the script it now says the state is 23, and the window properly activates and displays when it sets the state to 15.  Minimizing again by using the minimize button then running the script it says the window state is 7.

Attempting to fix this, I have  added some _winapi commands to my function but that didn't change a thing. 

Does the minimize button do something to the window other than minimize it? Or is it a flaw in my function?

My function looks like this at the moment:

Func _FixWinState($wstate)

    If Not BitAND($wstate, 2) Then
        WinSetState($hwnd, "", @SW_SHOW)
    EndIf
    If Not BitAND($wstate, 4) Then
        WinSetState($hwnd, "", @SW_ENABLE)
    EndIf
    If Not BitAND($wstate, 8) Then
        WinActivate($hwnd)
    EndIf
    If BitAND($wstate, 16) Or BitAND($wstate, 32) Then
        WinSetState($hwnd, "", @SW_RESTORE)
    EndIf
    If not WinActive($hwnd) Then
        WinActivate($hwnd)
    endif
    _winapi_setForegroundWindow($hwnd)
    _winapi_setActiveWindow($hwnd)
    _winapi_switchtothiswindow($hwnd)


EndFunc   ;==>_FixWinState

 

$hwnd is global, $wstate is the window state from $wstate= WinGetState($hwnd) before this function is called. 

 

Any ideas?  

Also, if I use the script to launch MSTSC.EXE with a command-line of a saved .rdf file to configure the remote desktop session, it launches it just fine, but if I run a second pass of the script instead of restoring the minimized remote desktop window or doing what I described above with the OSK test, it creates a new remote desktop "window" but the new one is not a new session, it is a window with no size, just a blank header bar and frame about an inch long.  I can't help but think the issues are related somehow.

 

Link to comment
Share on other sites

I think I found the problem.  It is not the window states that's the issue.  Somewhere in my logic for finding the window handle for the process, something isn't working right and it's grabbing a handle for a window that has no relation to the PID.

My logic for finding the window's handle is to iterate winlist() and check WinGetProcess for each entry in the table by the handle of the table entry (the [1] value) and when it got a hit it is supposed to then use that handle for all subsequent functions.  However, it seems that when the window in question is minimized using the minimize button, it's not listing that minimized window and the routine ends up using the last-iterated window's handle.  It isn't because I was using $hwnd as the global for the window handle, because I changed the global variable for the window handle and it does the same thing.

How do I find the window handle for a minimized window if I only know the executable name and can find the PID, if the WinList() function doesn't see a minimized-by-the-minimize-button window to include it in its result set?

Could this be a bug in WinList()?

Also for unknown reasons, the IF statement where I check each window's PID against the executable's PID is evaluating true when the last window is tested, which is why the wrong handle is being assigned.  The window handle should end up null, not take the handle of the last-iterated window, because the logic following the IF should not be executing.

In the debug console output note the difference in handle, title and classname when run with osk.exe minimized using the taskbar icon vs minimized using the minimize button...

This is my FindWindow function (note the debug to console lines...)  (h/t Mark Robbins for the SplashError function) 

Unless if..then logic has changed considerably since I started in IT over 35 years ago, if it doesn't get a "hit" on wingetprocess matching the PID of the executable (osk.exe that is...) then it should be throwing the splash screen and not setting a value to any of the variables between "then" and "endif".

I'm going to add a debug write of the value of that evaluation if I can, ahead of the IF that should be evaluating false all the way through, to see if maybe it's a bug in if..then (why that would be is beyond me...)

 

Func _FindWindow()
    Local $aWindowList = WinList()
    Local $iCount = 0 ; counter
    For $iCount = 1 To $aWindowList[0][0]
        ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : &aWindowList[' & $iCount & '] = ' & $aWindowList[$iCount][0] & ' ' & $aWindowList[$iCount][1] & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
        ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $WinGetProcess($aWindowList['&$iCount&'] = ' & WinGetProcess($aWindowList[$iCount][1]) & @CRLF & '>Error code: '&@error&@CRLF) ;### Debug Console
        If WinGetProcess($aWindowList[$iCount][1]) = $exePID Then
            $sTitle = $aWindowList[$iCount][0] ; The window's title
            $hwnde = $aWindowList[$iCount][1] ; the window handle for the program's PID
            ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $hwnde = ' & $hwnde & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
            $sName = _ProcessGetName($exePID)
            ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $sName = ' & $sName & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
            $sClassName = WinClass($hwnde)
            ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $sClassName = ' & $sClassName & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
            MsgBox($MB_SYSTEMMODAL, "PID Name Handle Title and Class ",$exePID&" "&$sName &" "&$hwnde&" "&$sTitle&" "&$sClassName)
            Return (0)
        endif   
    Next
    If $hwnde = "" Then 
        SplashError("Can't find window - exiting")
        Exit
    EndIf
EndFunc   ;==>_

This is the debug console results with osk.exe minimized using the minimize button:

@@ Debug(303) : &aWindowList[1] = SciTE interface 0x0000000000081504
>Error code: 0
@@ Debug(304) : $WinGetProcess($aWindowList[1] = 10260
>Error code: 0
@@ Debug(303) : &aWindowList[2] = SciTE interface 0x00000000002D0B72
>Error code: 0
@@ Debug(304) : $WinGetProcess($aWindowList[2] = 10568
>Error code: 0
@@ Debug(303) : &aWindowList[3] =  0x00000000000D1256
>Error code: 0
@@ Debug(304) : $WinGetProcess($aWindowList[3] = 11192
>Error code: 0
@@ Debug(308) : $hwnde = 0x00000000000D1256
>Error code: 0
@@ Debug(310) : $sName = osk.exe
>Error code: 0
@@ Debug(312) : $sClassName = tooltips_class32
>Error code: 0
@@ Debug(169) : $wstate = 5
>Error code: 0
@@ Debug(177) : $wstate = 5
>Error code: 0

This is the debug console results with osk.exe minimized by clicking on the taskbar icon:

@@ Debug(303) : &aWindowList[1] = SciTE interface 0x0000000000041502
>Error code: 0
@@ Debug(304) : $WinGetProcess($aWindowList[1] = 5388
>Error code: 0
@@ Debug(303) : &aWindowList[2] = SciTE interface 0x0000000000110B6E
>Error code: 0
@@ Debug(304) : $WinGetProcess($aWindowList[2] = 9784
>Error code: 0
@@ Debug(303) : &aWindowList[3] = On-Screen Keyboard 0x000000000026101C
>Error code: 0
@@ Debug(304) : $WinGetProcess($aWindowList[3] = 11192
>Error code: 0
@@ Debug(308) : $hwnde = 0x000000000026101C
>Error code: 0
@@ Debug(310) : $sName = osk.exe
>Error code: 0
@@ Debug(312) : $sClassName = OSKMainClass
>Error code: 0
@@ Debug(169) : $wstate = 23
>Error code: 0
@@ Debug(177) : $wstate = 15

See, I'm not nuts ;)

 

Link to comment
Share on other sites

Nowhere in your script do you need to use "==", in AutoIt, "==" is used as a case sensitive string comparison only.

If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Link to comment
Share on other sites

BrewManNH, I figured that if I was using = it was doing a set operation and not a comparison operation.  That's how a lot of languages use = vs ==.  I guess AutoIT is more forgiving, that it will assume = is comparison if the target side of the equation is not settable...  Must be why it wasn't throwing errors.

Link to comment
Share on other sites

Fixed.  Thanks to searching the forum to find that locating handle by PID should be done with the extended winapi function.  Now it finds the minimized window every time.

My new function for finding the handle by PID is:

; Function _FindWindowInfo (find window info from pid - new method)
Func _FindWindowInfo()
     Local $aWinProc = _WinAPI_EnumProcessWindows($exePID)
    If $aWinProc[0][0] = 1 Then
        $hwnde = $aWinProc[1][0]
        $sClassName = $aWinProc[1][1]
    Return (0)
    EndIf
    If $aWinProc[0][0] >1 Then
    Local $iCount = 0 ; counter
    For $iCount = 1 To $aWinProc[0][0]
        $sTitle = WinGetTitle($aWinProc[$iCount][0])
        If WinGetProcess($aWinProc[$iCount][1]) == $exePID Then
            $hwnde = $aWinProc[$iCount][0] ; the window handle for the program's PID
            $sName = _ProcessGetName($aWinProc[$iCount][1])
            $sClassName = $aWinProc[$icount][1]
            MsgBox($MB_SYSTEMMODAL, "PID Name Handle Title and Class ",$exePID&" "&$sName &" "&$hwnde&" "&$sTitle&" "&$sClassName)
            Return (0)
        endif
    Next
    endif
    If $hwnde == "" Then
        SplashError("Can't find window - exiting")
        Exit
    EndIf
EndFunc ;FindWindowInfo

It works as is but I need to find a better test for whether there's a match.  If I knew the class ahead of time that would be great but I'm not going to hardcode every program this might be used for.  Of course the pid for the process found by using the pid will be the pid.  In other words this may fail if there's more than one window returned for a process.  Perhaps window hierarchy or some such - only take the one that's the parent window...

 

Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...