Jump to content

Bug: Menu and Controls stop working after SW_SHOW from another process


Go to solution Solved by argumentum,

Recommended Posts

Posted

I came across this issue in one of my projects and decided to recreate a smaller script to reproduce the issue and see if anyone knows how I can get around this issue.

  • The main GUI script has the ability to hide to it's system tray icon
  • Clicking on the tray icon will show the GUI again
  • In this case, the menu items (Help > About) and controls still work

I have a situation where I use another script to show the GUI again since it is normally hidden in the system tray.

  • GUI is already hidden in system tray
  • This second script runs to show the GUI
  • GUI does show and everything appears visually correct
  • In this case, the menu items (Help > About) and controls do NOT work

Yet if I hide the GUI again during the same run, and show itself again from the tray icon, everything works again.

Therefore, it only seems to happen when showing the GUI from another process.

Script1:

#NoTrayIcon

#include <AutoItConstants.au3>
#include <TrayConstants.au3>
#include <GUIConstantsEx.au3>
#include <MsgBoxConstants.au3>
#include <StaticConstants.au3>

Global $hGUI

Opt("TrayMenuMode", 3)
Opt("TrayAutoPause", 0)
Opt("TrayOnEventMode", 1)

TraySetOnEvent($TRAY_EVENT_PRIMARYDOWN, 'idGUI')
TraySetClick(16)

$idGUI = TrayCreateItem("Show GUI")
TrayItemSetOnEvent($idGUI, "idGUI")
TrayCreateItem("")
$idExit = TrayCreateItem('Exit')
TrayItemSetOnEvent($idExit, "_Exit")
TraySetIcon(@AutoItExe, 1)
TraySetToolTip("Show GUI")

Example()

Func Example()
    Local $sDefaultstatus = "Ready"

    $hGUI = GUICreate("My GUI menu", 300, 200)

    Local $idMnu_File = GUICtrlCreateMenu("&File")
    Local $idMnu_Help = GUICtrlCreateMenu("Help")
    Local $idMni_Info = GUICtrlCreateMenuItem("About", $idMnu_Help)

    Local $idMnu_View = GUICtrlCreateMenu("View", -1, 1)

    Local $idBtn_Hide = GUICtrlCreateButton("Hide GUI", 50, 130, 70, 20)
    GUICtrlSetState(-1, $GUI_FOCUS)
    Local $idBtn_Cancel = GUICtrlCreateButton("Exit", 180, 130, 70, 20)

    Local $idLbl_Status = GUICtrlCreateLabel($sDefaultstatus, 0, 165, 300, 16, BitOR($SS_SIMPLE, $SS_SUNKEN))

    GUISetState(@SW_SHOW)


    ; Loop until the user exits.
    While 1
        Switch GUIGetMsg()
            Case $idBtn_Hide
                GUISetState(@SW_HIDE, $hGUI)
            Case $idBtn_Cancel, $GUI_EVENT_CLOSE
                ExitLoop
            Case $idMni_Info
                MsgBox($MB_SYSTEMMODAL, "Info", "Only a test...")
        EndSwitch
    WEnd
    GUIDelete()
EndFunc   ;==>Example

Func _Exit()
    Exit
EndFunc

Func idGUI()
    ; get GUI window state
    Local $iState = WinGetState($hGUI)
    If $iState <> $WIN_STATE_VISIBLE Then GUISetState(@SW_SHOW, $hGUI)
EndFunc

 

Script2:

#include <WinAPISysWin.au3>

; show and activate the GUI window
$hWnd = WinGetHandle("My GUI menu")
_WinAPI_ShowWindow($hWnd, @SW_SHOW)
_WinAPI_SetForegroundWindow($hWnd)

 

By the way, I have tried all of the other functions such as WinActivate and various WinAPI to show and give focus to the GUI window. They all still work to show the GUI but also still have controls and menu items that fail.

Does anyone know why this issue occurs or what I can do to fix it?

Thank you for your time. :)

Posted

Also, I realize that I am using OnEvent for tray stuff and GUIGetMsg for GUI stuff. In my main project, I use OnEvent for everything. The issue occurs either way and therefore this mix of OnEvent and GUIGetMsg in this example was just for the purpose of making a quick smaller example.

  • Solution
Posted
#NoTrayIcon

#include <AutoItConstants.au3>
#include <TrayConstants.au3>
#include <GUIConstantsEx.au3>
#include <MsgBoxConstants.au3>
#include <StaticConstants.au3>

Global $hGUI

Opt("TrayMenuMode", 3)
Opt("TrayAutoPause", 0)
Opt("TrayOnEventMode", 1)

TraySetOnEvent($TRAY_EVENT_PRIMARYDOWN, 'idGUI')
TraySetClick(16)

Global $idGUI = TrayCreateItem("Show GUI")
TrayItemSetOnEvent($idGUI, "idGUI")
TrayCreateItem("")
Global $idExit = TrayCreateItem('Exit')
TrayItemSetOnEvent($idExit, "_Exit")
TraySetIcon(@AutoItExe, 1)
TraySetToolTip("Show GUI")

Example()

Func Example()
    Local $sDefaultstatus = "Ready"

    $hGUI = GUICreate("My GUI menu", 300, 200)

    Local $idMnu_File = GUICtrlCreateMenu("&File")
    Local $idMnu_Help = GUICtrlCreateMenu("Help")
    Local $idMni_Info = GUICtrlCreateMenuItem("About", $idMnu_Help)

    Local $idMnu_View = GUICtrlCreateMenu("View", -1, 1)

    Local $idBtn_Hide = GUICtrlCreateButton("Hide GUI", 50, 130, 70, 20)
    GUICtrlSetState(-1, $GUI_FOCUS)
    Local $idBtn_Cancel = GUICtrlCreateButton("Exit", 180, 130, 70, 20)

    Local $idLbl_Status = GUICtrlCreateLabel($sDefaultstatus, 0, 165, 300, 16, BitOR($SS_SIMPLE, $SS_SUNKEN))

    GUISetState(@SW_SHOW)


    ; Loop until the user exits.
    While 1
        Switch GUIGetMsg()
            Case $idBtn_Hide
;~                 GUISetState(@SW_HIDE, $hGUI)
                WinSetState($hGUI, "", @SW_HIDE)

            Case $idBtn_Cancel, $GUI_EVENT_CLOSE
                ExitLoop
            Case $idMni_Info
                MsgBox($MB_SYSTEMMODAL, "Info", "Only a test...")
        EndSwitch
    WEnd
    GUIDelete()
EndFunc   ;==>Example

Func _Exit()
    Exit
EndFunc

Func idGUI()
    If Not BitAND(WinGetState($hGUI), $WIN_STATE_VISIBLE) Then WinSetState($hGUI, "", @SW_SHOW)
;~     ; get GUI window state
;~     Local $iState = WinGetState($hGUI)
;~     If $iState <> $WIN_STATE_VISIBLE Then GUISetState(@SW_SHOW, $hGUI)
EndFunc

yes, is an old bug, and the way it is. Fortunately you can get around it.

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Posted
15 minutes ago, argumentum said:

yes, is an old bug, and the way it is. Fortunately you can get around it.

You literally just saved the rest of my weekend, Sir. Thank you! 🍷

So the trick was to essentially use WinSetState instead of GUISetState.

I really needed this to work, one way or another. I feel very lucky for the fact that there was a simple workaround for this issue and thankful that you were able to help me solve this issue. Now I can relax. :)

Posted
20 hours ago, argumentum said:
Func idGUI()
    If Not BitAND(WinGetState($hGUI), $WIN_STATE_VISIBLE) Then WinSetState($hGUI, "", @SW_SHOW)
;~     ; get GUI window state
;~     Local $iState = WinGetState($hGUI)
;~     If $iState <> $WIN_STATE_VISIBLE Then GUISetState(@SW_SHOW, $hGUI)
EndFunc

So while this part worked and fixed the problem, it had an unexpected side effect of causing the controls on the GUI to have a delay in showing them. Even with the tiny example with only two buttons. Same with my project. The GUI shows immediately and the controls have a delayed load which is unusual.

But I found another method that shows the GUI and controls instantly.

Func idGUI()
    ; get GUI window state
    If Not BitAND(WinGetState($hGUI), $WIN_STATE_VISIBLE) Then _WinAPI_ShowWindow($hGUI, @SW_SHOW)
EndFunc

I have no idea why WinSetState has such a delay with showing the controls while _WinAPI_ShowWindow does not have the delay.

But that is what I love about having so many options; so many different methods to achieve the same thing. :)

Posted

Interesting thread indeed.
Based on WildByDesign and argumentum scripts, I just kept the minimum code in the following script, to reproduce the issue (no trayitem)

1) First script : we run it and click the Hide button => the gui is hidden ;

; 1st script : click button to hide the gui

#include <GUIConstantsEx.au3>
#include <MsgBoxConstants.au3>

Example()

Func Example()

    Local $hGUI = GUICreate("My GUI menu", 300, 200)

    Local $idMnu_File = GUICtrlCreateMenu("&File")
    Local $idMnu_Help = GUICtrlCreateMenu("Help")
    Local $idMni_Info = GUICtrlCreateMenuItem("About", $idMnu_Help)

    Local $idMnu_View = GUICtrlCreateMenu("View", -1, 1)

    Local $idBtn_Hide = GUICtrlCreateButton("Hide GUI", 50, 130, 70, 20)
    GUICtrlSetState(-1, $GUI_FOCUS)

    Local $idBtn_Cancel = GUICtrlCreateButton("Exit", 180, 130, 70, 20)

    GUISetState(@SW_SHOW)

    While 1
        Switch GUIGetMsg()
            Case $idBtn_Hide
                ; watch out, another script won't be able to activate correctly the hidden GUI if GUISetState is used here.
                GUISetState(@SW_HIDE, $hGUI) ; <===============================

                ; another script will be able to activate correctly the hidden GUI if WinSetState is used here.
                ; WinSetState($hGUI, "", @SW_HIDE) ; <=========================

            Case $idBtn_Cancel, $GUI_EVENT_CLOSE
                ExitLoop

            Case $idMni_Info
                MsgBox($MB_TOPMOST, "Info", "Only a test...")
        EndSwitch
    WEnd
    GUIDelete()
EndFunc   ;==>Example

2) Second script : we run it and the gui reappears, but its controls/menu don't respond :

; 2nd script : show & activate the hidden GUI

#include <WinAPISysWin.au3>

; show and activate the GUI window . Watch out: controls won't react if the GUI was hidden using GUISetState in the 1st script
Local $hWnd = WinGetHandle("My GUI menu")
_WinAPI_ShowWindow($hWnd, @SW_SHOW)
_WinAPI_SetForegroundWindow($hWnd)

As argumentum suggested (thanks !) everything works fine if we use in the 1st script WinSetState (instead of GuiSetState) to hide the GUI

@Melba23 wrote comments about it in this post. Dear Melba, in case you have some free time, could you please share your opinion about this, e.g. the fact that we need to use WinSetState in the 1st script (which isn't recommended when working with GUI's) because the hidden GUI needs to be shown/activated by an external script ?

Thanks :)

"I think you are searching a bug where there is no bug... don't listen to bad advice."

Posted (edited)

playing with the idea a little  :)

Script1.au3

Spoiler
; Script1.au3
#NoTrayIcon

#include <AutoItConstants.au3>
#include <TrayConstants.au3>
#include <GUIConstantsEx.au3>
#include <MsgBoxConstants.au3>
#include <StaticConstants.au3>

Global $hGUI, $idLbl_Status

Opt("TrayMenuMode", 3)
Opt("TrayAutoPause", 0)
Opt("TrayOnEventMode", 1)

$idGUI = TrayCreateItem("Show GUI")
TrayItemSetOnEvent($idGUI, "idGUI")
TrayCreateItem("")
$idExit = TrayCreateItem('Exit')
TrayItemSetOnEvent($idExit, "_Exit")

TraySetOnEvent($TRAY_EVENT_PRIMARYDOWN, 'idGUI')
TraySetClick(16)
TraySetIcon(@AutoItExe, 1)
TraySetToolTip("Show GUI")

Example()

Func Example()
    $hGUI = GUICreate("My GUI menu", 300, 200)
    Local $idBtn_Hide = GUICtrlCreateButton("Hide GUI", 50, 130, 70, 20)
    Local $idBtn_Cancel = GUICtrlCreateButton("Exit", 180, 130, 70, 20)
    $idLbl_Status = GUICtrlCreateLabel("Ready", 0, 184, 300, 16, BitOR($SS_SIMPLE, $SS_SUNKEN))
    GUISetState(@SW_SHOW)

    While 1
        Switch GUIGetMsg()
            Case $idBtn_Hide
                GUISetState(@SW_HIDE, $hGUI)
            Case $idBtn_Cancel, $GUI_EVENT_CLOSE
                ExitLoop
        EndSwitch
        _ExtMsg()
    WEnd

    GUIDelete()
EndFunc   ;==>Example

Func _ExtMsg($sTitle = StringTrimRight(@ScriptName, 4))
    Local Static $hWnd = 0
    If $hWnd = 0 Then $hWnd = WinGetHandle(AutoItWinGetTitle())
    Local $sCurTitle = AutoItWinGetTitle()
    If $sCurTitle <> $sTitle Then
        Switch $sCurTitle
            Case "Show GUI"
                idGUI()
            Case "Hide GUI"
                GUICtrlSetData($idLbl_Status, "Hiden & Standby")
                GUISetState(@SW_HIDE)
            Case "Get Status"
                ControlSetText($hWnd, "", "Edit1", GUICtrlRead($idLbl_Status))
            Case Else
                If StringLeft($sCurTitle, 8) = "Execute:" Then
                    ConsoleWrite("Execute: " & StringTrimLeft($sCurTitle, 8) & @CRLF) ; *** info
                    Execute(StringTrimLeft($sCurTitle, 8))
                EndIf
        EndSwitch
        AutoItWinSetTitle($sTitle)
    EndIf
EndFunc   ;==>_ExtMsg

Func _Exit()
    Exit
EndFunc   ;==>_Exit

Func idGUI()
    Local $iState = WinGetState($hGUI)
    If Not BitAND($iState, $WIN_STATE_VISIBLE) Then GUISetState(@SW_SHOW, $hGUI)
    GUICtrlSetData($idLbl_Status, "Visible & Ready")
EndFunc

 


Script2.au3

Spoiler
; Script2.au3
#include <GUIConstantsEx.au3>

Test()

Func Test()
    Local $hWnd = WinGetHandle("Script1")
    If Not IsHWnd($hWnd) Then Return MsgBox(16, "Error", "'Script1' not found")
    ConsoleWrite("$hWnd=" & $hWnd & @CRLF)

;~  ; for debuging
;~  WinMove($hWnd, "", (@DesktopWidth / 2) - 250, (@DesktopHeight / 2) - 250, 500, 500) ; Move the AutoIt Hidden Window and re-size for a better view.
;~  WinSetState($hWnd, "", @SW_SHOW) ; Show the AutoIt Hidden Window, normally this is hidden, but in the interest of this example I"m displaying it.
;~  ControlSetText($hWnd, "", "Edit1", "Show the AutoIt Hidden Window")

    Local $hGui = GUICreate(@ScriptName, 250, 170, -1, -1)
    Local $idBtn1 = GUICtrlCreateButton("Show GUI", 10, 10, 100, 25)
    Local $idBtn2 = GUICtrlCreateButton("Hide GUI", 10, 40, 100, 25)
    Local $idBtn3 = GUICtrlCreateButton("SetData", 10, 70, 100, 25)
    Local $idBtn4 = GUICtrlCreateButton("GetData", 10, 100, 100, 25)
    Local $idBtn5 = GUICtrlCreateButton("_Exit()", 10, 130, 100, 25)
    GUISetState(@SW_SHOW, $hGui)

    Local $sResult = ""

    While WinExists($hWnd)
        Local $aWPos = WinGetPos($hGui)
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop
            Case $idBtn1
                WinSetTitle($hWnd, "", "Show GUI")
            Case $idBtn2
                WinSetTitle($hWnd, "", "Hide GUI")
            Case $idBtn3
                WinSetTitle($hWnd, "", 'Execute:GUICtrlSetData($idLbl_Status, "Hello from the other side")')
            Case $idBtn4
                $sResult = _GetResult($hWnd, "Get Status")
                ConsoleWrite("$sResult=" & $sResult & @CRLF)
            Case $idBtn5
                WinSetTitle($hWnd, "", "Execute:_Exit()")
        EndSwitch
    WEnd
    GUIDelete($hGui)
EndFunc   ;==>Test

Func _GetResult($hWnd, $sCmd, $iTimeout = 1000)
    Local $iStartTime = TimerInit()
    ControlSetText($hWnd, "", "Edit1", "") ; first we clear Edit1
    WinSetTitle($hWnd, "", $sCmd)          ; then we send Cmd
    Local $sIncomeText = ""
    While TimerDiff($iStartTime) < $iTimeout
        $sIncomeText = ControlGetText($hWnd, "", "Edit1")
        If $sIncomeText <> "" Then ; Check if new content arrived
            ExitLoop
        EndIf
        Sleep(40)
    WEnd
    ConsoleWrite("   processed in: " & Round(TimerDiff($iStartTime) / 1000, 3) & " seconds " & @LF)
    Return $sIncomeText
EndFunc   ;==>_GetResult

 


 

Edited by ioa747
update

I know that I know nothing

Posted
45 minutes ago, ioa747 said:

playing with the idea a little  :)

This is really creative. Thanks for sharing.

I like how ideas turn into ideas and those ideas turn into more ideas.

And now, I think I have some ideas for this technique. :)

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
  • Recently Browsing   0 members

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