JimS Posted September 6, 2017 Posted September 6, 2017 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.
JimS Posted September 7, 2017 Author Posted September 7, 2017 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
JimS Posted September 7, 2017 Author Posted September 7, 2017 Hey, I just noticed I wasn't using == where I should've been using ==... let me fix that and post back.
BrewManNH Posted September 7, 2017 Posted September 7, 2017 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 GudeHow 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
JimS Posted September 7, 2017 Author Posted September 7, 2017 I also just noticed that I really should use the _winapi_enumprocesswindows function. Trying that...
JimS Posted September 7, 2017 Author Posted September 7, 2017 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.
JimS Posted September 7, 2017 Author Posted September 7, 2017 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...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now