Jump to content
jantograaf

WinGetState 5 and restoring/showing windows

Recommended Posts

jantograaf
Posted (edited)

Hi all,

I'm having some trouble restoring a program which uses a normal AutoIT-GUI and is used as a ToolKit for our customers to open different (external) tools, check their network connections or ping different destinations. Almost all customers are running this script on Win7 x64. 

What do I want to do?

When the program is already running and the user tries to start it for a second time, it should exit the second instance after activating/restoring the first instance.

What did I do to integrate this?

I used a little piece of script found on the forums to prevent opening a second instance and modified it as follows:

$g_szVersion = "VSOL ToolKit"
If WinExists($g_szVersion) Then
    Local $gState = WinGetState($g_szVersion)
    MsgBox(0,"","gState = " & $gState) ;For debugging purposes
    WinSetState($g_szVersion,"",@SW_SHOW)
    MsgBox(0,"","gState = " & $gState) ;For debugging purposes
    WinSetState($g_szVersion,"",@SW_RESTORE)
    MsgBox(0,"","gState = " & $gState) ;For debugging purposes
    WinActivate($g_szVersion)
    Exit
EndIf
AutoItWinSetTitle($g_szVersion)

I have tried it with only the @SW_SHOW, only the @SW_RESTORE, both of them in a different order, same difference, it doesn't work.

What does happen then?

It detects the current state (when it's minimized) as '5', which means enabled and hidden. After setting the state to show, the state changes to 7. Restoring doesn't change the state. But what is really odd, is that the window doesn't appear but AutoIt seems to create a néw window with the same title as the existing one. 

Untitled.png.9c1e43de7a0b7ac51905ba2636b8e168.png

It doesn't matter if the window was originally minimized by clicking the minimize button in the GUI or by a line of code in my script. For example, when users open a software package from within my script, a similar function to this one will be called:

Func VSOL_FFTOOLS_SHOW_DOWNLOADS()
   GUISetState(@SW_MINIMIZE,$VSOL_BACKEND)
   ShellExecute(@UserProfileDir & "\Downloads")
EndFunc

The only thing that does work, is when the window is running in a non-minimized state (somewhere behind the current window), it gets activated and receives the focus when trying to start the second instance.

If anyone could help me out with this one, I'd be forever thankful :-)

Thanks in advance!

Kind regards,

Jan Geurts

Edited by jantograaf

Share this post


Link to post
Share on other sites
Earthshine

perhaps this

 


My resources are limited. You must ask the right questions

 

Share this post


Link to post
Share on other sites
jantograaf

Alright, thanks @Earthshine, I'll try to implent this and see what it does. I'll keep you posted :-)

Kind regards,

Jan

Share this post


Link to post
Share on other sites
jantograaf
Posted (edited)

Okay, @Earthshine, I've tried it by adding the following code at the beginning of my code:

; Search for another instance of this script
Local $singleton = _SingletonPID('YourScript',1)

; If another instance was found
if $singleton <> 0 Then
    MsgBox(0,"Oops","ToolKit is already running")    ;for debugging purposes
    ; Get a handle on that instance
    $handle = _GetHwndFromPID($singleton)

    ; Activate that window
    WinActivate($handle)

    ; Exit this script
    Exit
EndIf

And this at the end (I've included the comments to attribute the original poster):

; #FUNCTION# ====================================================================================================================
; Name ..........: _SingletonPID
; Description ...: Enforce a design paradigm where only one instance of the script may be running.
; Syntax ........: _SingletonPID($sOccurenceName[, $iFlag = 0])
; Parameters ....: $sOccurenceName      - String to identify the occurrence of the script.
;                  $iFlag               - [optional] Optional parameters (see below). Default is 0.
;                       0 - Exit the script with the exit code -1 if another instance already exists.
;                       1 - Return the PID of the initial executable.
; Return values .: Success - 0 (no additional instances exist).
;                  Failure - The PID of the initial instance.
; Author ........: guinness with initial ideas by Valik for _Singleton() & KaFu for _EnforceSingleInstance().
; Example .......: Yes
; ===============================================================================================================================
Func _SingletonPID($sOccurenceName, $iFlag = 0)
    Local $iReturn = 0
    Local $hAutoItWnd = WinGetHandle($sOccurenceName)
    If @error Then
        AutoItWinSetTitle($sOccurenceName & @AutoItPID)
        $hAutoItWnd = WinGetHandle($sOccurenceName & @AutoItPID)
        AutoItWinSetTitle($sOccurenceName)

        Local Const $hControl = ControlGetHandle($hAutoItWnd, '', 'Edit1')
        ControlSetText($hAutoItWnd, '', $hControl, ControlGetText($hAutoItWnd, '', $hControl) & @CRLF & _
                '|SINGLETON_PID:' & @AutoItPID & @CRLF)
    Else
        If $iFlag = Default Then $iFlag = 0
        If BitAND($iFlag, 1) Then ; $SINGLETON_PID
            $iReturn = Int(StringRegExp(ControlGetText($hAutoItWnd, '', ControlGetHandle($hAutoItWnd, '', 'Edit1')) & @CRLF & _
                    '|SINGLETON_PID:0' & @CRLF, _
                    '\|SINGLETON_PID:(\d+)\R', $STR_REGEXPARRAYGLOBALMATCH)[0])
        Else
            Exit -1
        EndIf
    EndIf
    Return $iReturn
EndFunc   ;==>_SingletonPID

Func _GetHwndFromPID($PID)
    $hWnd = 0
    $winlist = WinList()
    Do
        For $i = 1 To $winlist[0][0]
            If $winlist[$i][0] <> "" Then
                $iPID2 = WinGetProcess($winlist[$i][1])
                If $iPID2 = $PID Then
                    $hWnd = $winlist[$i][1]
                    ExitLoop
                EndIf
            EndIf
        Next
    Until $hWnd <> 0
    Return $hWnd
EndFunc;==>_GetHwndFromPID

So, what happens now: if my script is already running and it's hidden behind another window, I first get a MsgBox saying that there is already a ToolKit running. After clicking OK, it succesfully activates the running instance and exits the second instance. However, when I minimize the window first and then run a second instance, it doesn't pop up. Seems logical, since WinGetState returns 5 (it's hidden and enabled). So I tried adjusting the code so it doesn't just activate the window but also changes the state of the window. Like this :

; Search for another instance of this script
Local $singleton = _SingletonPID('YourScript',1)

; If another instance was found
if $singleton <> 0 Then
    ;MsgBox(0,"Oeps","Er draait al een ToolKit")
    ; Get a handle on that instance
    $handle = _GetHwndFromPID($singleton)
    ; Restore the window
    WinSetState($handle,"",7)
    ; Activate that window
    WinActivate($handle)

    ; Exit this script
    Exit
EndIf

What happens this time?

If the window is active and on the foreground, it minimizes and immediately restores. Strange.
If the window is active and in the background, it seems to restore from taskbar and pop up. Good.
If the window is minimized, it pops up a new window called 'Your Script' as seen in attached screenshot. WT*.

Naamloos.png.848d3fb84376d05ebc0f4b6def10d3f0.png

I can't seem to figure out what happens. The _SingletonPID-function gives me the exact PID, okay, but is the handle returned still the wrong one? Since it doesn't restore the correct window? :(

 

Edited by jantograaf

Share this post


Link to post
Share on other sites
Earthshine

have you tried the built in _Singleton? You don't need the PID one probably

_Singleton

Look at the help file please.


My resources are limited. You must ask the right questions

 

Share this post


Link to post
Share on other sites
jantograaf
Posted (edited)

Thanks for pointing me to the help file, but I tried this already. Using the _Singleton()-function, the return value is 0 when another instance is already running. This can be used perfectly to close the second instance (which I also need), but this cannot be used to activate the first, already running instance. That's why I needed the _SingletonPID() :)

The _SingletonPID() however points me to the hidden AutoIT-window (after getting the window handle for the returned PID) instead of the handle of the GUI window.

I don't expect anyone to write my script for me, I'm just asking for anyone to see where the flaw is. Is it in the _Singleton() or _SingletonPID()-functions, is it in what I'm trying to do or the way I am approaching this or is this some kind of AutoIT GUI specification I just don't seem to grasp yet? It is a normal GUI window which is minimized and for the love of god, I just can't seem to restore it when the user accidentally starts a second instance :lol:

Edited by jantograaf

Share this post


Link to post
Share on other sites
Earthshine

not suggesting we write it for you. could you try the ProcessExists function so that you can get the handle from process you care about? oh and there are GUI experts here who can probably help you, I just automate stuff, not much of a gui guy

  • Thanks 1

My resources are limited. You must ask the right questions

 

Share this post


Link to post
Share on other sites
BrewManNH

GUICreate returns a handle, why can't you use that?

  • Like 1

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

Share this post


Link to post
Share on other sites
jantograaf

@BrewManNH because the handle is created in the first instance of my Toolkit. It needs to be activated when a user tries to start a second instance.

But on another note, close this thread, I've been looking waaaaaaay to far by trying to restore the window using the handle and everything. I just started from scratch, trying to use the _Singleton()-function to detect an already running instance, but using a way simpler solution to reactivate the window. And. It. Worked.

Sometimes when the solution looks to easy, I get fooled. thanks @Earthshine anyways...

The code I ended up using:

Opt('WinTitleMatchMode',2)
Local $SingletonHandle = _Singleton("VSOL ToolKit",1)
If $SingletonHandle = 0 Then
    WinActivate("VSOL ToolKit")
    Exit
EndIf

I feel so enormously stupid right now. 

#sorryforwastingyourtime

  • Like 1

Share this post


Link to post
Share on other sites
Earthshine
Posted (edited)

you rock dude! happy programming!  You can probably edit the very first post and add SOLVED to the Thread Title  so other people searching one day will see how you solved it

Edited by Earthshine
  • Haha 1

My resources are limited. You must ask the right questions

 

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

  • Similar Content

    • AndyK70
      By AndyK70
      I'm trying to fill a ListView with all normal viewable windows to act with them.
      First I tried with WinList:
      Local $aWinList = WinList("[REGEXPTITLE:(?i)(.+)]") Local $aTmp, $iID ;~ _ArrayDisplay($aWinList) For $i = $aWinList[0][0] To 1 Step -1 ; going backwards not disturbing the index while cycling through and deleting some If StringStripWS( $aWinList[$i][0], 3) == "" Or _ Not BitAND(WinGetState($aWinList[$i][1]), $WIN_STATE_VISIBLE) Or _ BitAND(WinGetState($aWinList[$i][1]), $WIN_STATE_MINIMIZED ) Then _ArrayDelete($aWinList, $i) Else ; Window has a Title and is "visible" $aTmp = WinGetPos($aWinList[$i][1]) If $aTmp[0] < -1000 Or $aTmp[1] < -1000 Then ; Window is minimized or tray icon _ArrayDelete($aWinList, $i) EndIf EndIf Next $aWinList[0][0] = UBound($aWinList)-1 ; getting actual # of windows ; Each row is now [ID]=> [Title], [hWnd] But it keeps getting Windows which are definitely not there at least not visible:

      Those windows "Rechner", "Einstellungen", "Netflix", "Microsoft Store", ... are not there!?! 
      It should list only the first three windows, which are real.
      I even tried it with _WinAPI_ UDF:
      $hWnd = _WinAPI_GetForegroundWindow() ; Add items _GUICtrlListView_BeginUpdate($idListview) If $hWnd <> 0 Then $iI = 0 Do If _WinAPI_IsWindow($hWnd) And _WinAPI_IsWindowVisible Then _GUICtrlListView_AddItem($idListview, WinGetTitle($hWnd)) _GUICtrlListView_AddSubItem($idListview, $iI, $hWnd, 1) $iI += 1 $hWnd = _WinAPI_GetWindow($hWnd, $GW_HWNDNEXT) EndIf Until $hWnd = 0 EndIf But it is the same...
       
      How can i distinguish those invisible windows from normal ones?
      PS: I'm using Windows 10, maybe it is important to know?
    • SteveStrop
      By SteveStrop
      Hi 
       
      I'm trying to:
      1) Open a hidden browser session
      2) Do some stuff in the background
      3)  Make the hidden window visible
       
      This is my code:
       
      Opt("WinTitleMatchMode", 2) ;1=start, 2=subStr, 3=exact, 4=advanced, -1 to -4=Nocase $oIE = _IECreate("https://www.google.co.uk/",0,0) $oSearchBox = _IEGetObjById($oIE, "lst-ib") $oSearchBox.innertext = "AutoIT" $oForm = _IEFormGetObjByName($oIE,"f") _IEFormSubmit($oForm) WinSetState("Google","",@SW_SHOW)  
      It dosen't work
      The last line does make the window visible but it is an empty greyed out box that disappears as soon as I click on it.
      I have a sort of workaround that hides the browser window as soon as I have created it which works fine:
      $oIE = _IECreate("https://www.google.co.uk/") WinSetState("Google","",@SW_HIDE) . . . WinSetState("Google","",@SW_SHOW) But this looks a bit pants as the newly created window flashes on then off of the screen.
      Am I using the wrong method to make the browser window visible?
      Thanks
       
       
    • anthonyjr2
      By anthonyjr2
      I have come across a weird issue that I am not sure how to solve. For some reason, whenever I call WinActivate in certain scenarios instead of opening the single window that already exists, it will create a new blank tab. It should be able to be recreated with this:
      AutoItSetOption("WinTitleMatchMode", 2) Sleep(1000) For $i=0 To 10 WinActivate("Internet Explorer") Next I am not actually doing this in my code, it is just to demonstrate the fact that new tabs keep popping up. Does anyone know why this happens?
       
      EDIT: So I am not sure how reproducible this is, because it only happens every once in a while for me. I will update with more info if I find out anything.
    • abdulrahmanok
      By abdulrahmanok
      hi brothers, 
      I'm trying to take screenshot from webpage and if found code that could take screenshot even when window is minimized but sometimes i get image like Image Attached I'm trying for 4 hours ago to know where is the problem ...
      Notice: for some reason I'm need to run site minimized
       

      The Code:
       
      ;coded by UEZ 2012 #include <Array.au3> #include <Constants.au3> #include <WindowsConstants.au3> #include <GDIPlus.au3> #include <IE.au3> Opt("WinTitleMatchMode", 2) Local $oIE = _IECreate("https://mybanner.qu.edu.qa/PROD/twbkwbis.P_WWWLogin") opt('winwaitdelay', 0) ;to make it run a little bit faster WinWait("User Login") $handle = WinMove("User Login - Internet Explorer","",0,0,877, 516) WinSetState("User Login","",@SW_SHOWMINIMIZED) _IELoadWait($oIE) Sleep(2000) WinSetState("User Login","",@SW_SHOWDEFAULT) $aWinlist = GetAllWindow() $handle = WinMove("Student Employment Time Attendance","",0,0,877, 516) $MyWindow=WinGetHandle("[Class:IEFrame]") ConsoleWrite($MyWindow) For $i = 0 To UBound($aWinlist) - 1 $hWnd = $aWinlist[$i][1] If Not WinActive($hWnd) Then ExitLoop Next If $i = UBound($aWinlist) Then Exit MsgBox(0, "Info", "No inactive window found", 10) _GDIPlus_Startup() $hBitmap = Capture_Window($MyWindow, 877, 516) _GDIPlus_ImageSaveToFile($hBitmap, @ScriptDir & "Test.jpg") _GDIPlus_BitmapDispose($hBitmap) ShellExecute(@ScriptDir & "Test.jpg") _GDIPlus_Shutdown() Exit Func Capture_Window($hWnd, $w, $h) Local $hDC_Capture = _WinAPI_GetWindowDC($hWnd) Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC_Capture) Local $hHBitmap = _WinAPI_CreateCompatibleBitmap($hDC_Capture, $w, $h) Local $hObject = _WinAPI_SelectObject($hMemDC, $hHBitmap) DllCall("user32.dll", "int", "PrintWindow", "hwnd", $hWnd, "handle", $hMemDC, "int", 0) _WinAPI_DeleteDC($hMemDC) ; Local $hObject = _WinAPI_SelectObject($hMemDC, $hObject) _WinAPI_ReleaseDC($hWnd, $hDC_Capture) Local $hBmp = _GDIPlus_BitmapCreateFromHBITMAP($hHBitmap) _WinAPI_DeleteObject($hHBitmap) Return $hBmp EndFunc ;==>Capture_Window Func GetAllWindow() ;code by Authenticity - modified by UEZ Local $aWin = WinList(), $aWindows[1][4] Local $iStyle, $iEx_Style, $iCounter = 0 Local $i, $hWnd_state, $aWinPos For $i = 1 To $aWin[0][0] $iEx_Style = BitAND(_WinAPI_GetWindowLong($aWin[$i][1], $GWL_EXSTYLE), $WS_EX_TOOLWINDOW) $iStyle = BitAND(WinGetState($aWin[$i][1]), 2) If $iEx_Style <> -1 And Not $iEx_Style And $iStyle Then $aWinPos = WinGetPos($aWin[$i][1]) If $aWinPos[2] > 1 And $aWinPos[3] > 1 Then $aWindows[$iCounter][0] = $aWin[$i][0] $aWindows[$iCounter][1] = $aWin[$i][1] $aWindows[$iCounter][2] = $aWinPos[2] $aWindows[$iCounter][3] = $aWinPos[3] $iCounter += 1 EndIf ReDim $aWindows[$iCounter + 1][4] EndIf Next ReDim $aWindows[$iCounter][4] Return $aWindows EndFunc ;==>GetAllWindow  
    • FrancescoDiMuro
      By FrancescoDiMuro
      Good morning
      I would like to know if I can use the Excel UDF to manipulating a .csv file without having Office installed on the PC I'm going to work...
      I read somewhere that it could be done, but I'm here to ask and be sure of what I remember... 
      I'd like to post another question...
      How can I retrieve the handle of a windows from a PID of an .exe?
      I have my script that does a ShellExecute ( which returns the PID of the .exe ), and then, switching a parameter read from a .ini file, adapt the Window on the screen ( Maximize, Minimize, On Top )...
      I tried, but without having success with this:
       
      #Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_Outfile_x64=prova.exe #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** #include <ButtonConstants.au3> #include <GUIConstantsEx.au3> #include <StaticConstants.au3> #include <WindowsConstants.au3> #include <MsgBoxConstants.au3> #include <AutoItConstants.au3> #include <WinAPIEx.au3> #include <Array.au3> Local $sFileConfigurazione = @ScriptDir & "\configurazione_exe.ini" If(FileExists($sFileConfigurazione)) Then Local $aSezioniIni = IniReadSection($sFileConfigurazione, "CONFIGURAZIONE_EXE") If @error Then MsgBox($MB_ICONERROR, "Errore!", "Errore durante l'apertura del file: " & @CRLF & $sFileConfigurazione & @CRLF & "Errore: " & @error) Else ; Lancio dell'applicativo indicato nel file di configurazione Local $iPID = ShellExecute($aSezioniIni[1][1]) Local $hWnd If($iPID <> 0) Then Local $aWinList = WinList() For $i = 1 To $aWinList[0][0] If(WinGetProcess($aWinList[$i][1] = $iPID)) Then $hWnd = $aWinList[$i][1] EndIf Next Switch($aSezioniIni[2][1]) Case $aSezioniIni[2][1] = "MIN" WinSetState($hWnd, "", @SW_MINIMIZE) Case $aSezioniIni[2][1] = "MAX" WinSetState($hWnd, "", @SW_MAXIMIZE) Case $aSezioniIni[2][1] = "TOP" WinSetOnTop($hWnd, "", $WINDOWS_ONTOP) EndSwitch EndIf EndIf EndIf It just set on top the .exe I'm launching...
      Thanks
×