mbunds

What is the best way to handle unexpected windows?

14 posts in this topic

#1 ·  Posted (edited)

I have an AutoIt script that works very well manipulating an external application as long as the application under control does not present an error. I have written the script to send an "item id" to an input box on the external application GUI, followed by "Send("{ENTER}")", and then I use a "WinWaitActive" to ensure that the external application GUI changes to the expected screen before moving on, all of which works perfectly.

The problem arises if the "item id" sent is incorrect, or if the external application has that particular item "on hold", causing  the external app to present a dialog box with an alert, which can be dismissed by clicking its' "OK" button. Under normal circumstances, the input data is immediately accepted, and the application under control changes to an "expected" screen, which AutoIt detects and then runs off to do the usual function stuff.

Since the script is "Win-Waiting" for the expected box (currently with an infinite timeout), but is instead presented with a dialog box that the script is not looking for, I am in brain-lock about what to do about it.

I tried registering an "adLib"  timing function to break out of the waiting loop (by changing the "winWait" to a While (NOT) loop testing "WinExists", but apparently the error dialog to which I am trying to switch detection is a "Blocking" type that prevents the "adLib" function from running.

I thought I had read that if "WinWaitActive" is used with a timer value, it could be caused to execute the following line upon timeout, or the line after if it gets its $hwnd before timeout. That would simplify things, but I don't remember where I saw the example.

I have been using AutoIt for a very long time, but never really came upon a circumstance like this where I need to be able to detect the presence of an "unexpected" pop-up while waiting for another...

Any clues would surely be appreciated!

Edited by mbunds
Spell check was a little too helpful...

Share this post


Link to post
Share on other sites



many infos, but where is the info fom Au3Info of this unexpected windoe? Post this as text. Best solution is don't send dirty DATA.

Share this post


Link to post
Share on other sites
3 minutes ago, AutoBert said:

many infos, but where is the info fom Au3Info of this unexpected windoe? Post this as text. Best solution is don't send dirty DATA.

The "unexpected" window (dialog) is not actually unexpected, because I know exactly what conditions will trigger each possible dialog, and there are only four:

  • Incorrect or non-existent record...
  • Job is on Hold...
  • User is already clocked into job...
  • Invalid characters...

...And of course, the "main" window that AutoIt waits for when all goes as planned and no error dialogs pop-up.

All but "Job is on Hold" are under my control to validate, and the only "unexpected" part of this equation is that I have no way of knowing ahead of time, which job might be "on Hold". 

I totally agree with you about sending dirty data, and if any does get sent it will be my fault for not validating it before attempting to enter it.
But the data isn't always rejected because it is dirty; a valid job number can also be rejected by the program being controlled if the administrator places that job on "hold", which I cannot validate ahead of time.

I have attached an image of the message box that it displays when the entry is rejected because of a "hold".

Essentially, I have never written a script that can identify "one of x" possible windows, where the script pauses (or loops) waiting for "some" window to pop-up, and even knowing exactly which ones might appear doesn't clue me in.

The Au3Info is not the problem, but here is the function I wanted to call with the Au3 information which is used the same way in all of the script modules that wait for windows, and click buttons, etc:

 

Func _JobStartErrorTrap()

    If WinExists("Unable to Start Job", "The selected job is closed.") Then
        WinActivate("Unable to Start Job", "The selected job is closed.")
        ControlClick($hwnd, "", "[CLASS:Button; TEXT:Ok]")

        $logtext = "Function _JobStartErrorTrap() -------- CONSOLE GUI - Unable to start job; the selected job is closed."
        writelog($logtext)

    EndIf

    $JobStartErrorExpire = True; True when the dialog box is closed
    $LoopRelease = True

EndFunc   ;==>_JobStartErrorTrap

 

 

This work just fine, stand-alone.

My problem is figuring out how to detect that this dialog has popped up while the script is looking for a different one.

I am considering something like:

Loop (some kind of loop)

    Select

        Case
        WinExists("Title1", "Text 1")

        Case
        WinExists("Title2", "Text 2")

        Case
        WinExists("Title3", "Text 3")

        Case Else
        Unhandled exception

    EndSelect

EndLoop

But this seems kind of clunky to me considering many of the nice functions available in AutoIt.
 

 

2016-07-08_9-55-34.png

Share this post


Link to post
Share on other sites

Well, functions like WinWaitActive halt the script, so you can't make it do something else while waiting. If you face the possibility of >1 possible popups as a result of some action, and need to take different actions for each (i.e. you can't simply ignore one while waiting for another) a loop like that seems like a fine solution. For error popups that could popup at any time and that you constantly want to be scanning/polling for without having to worry about it in every part of your application, AdlibRegister comes to mind.

I don't automate GUIs a whole lot so maybe some of the gurus have more to add :) But that loop would certainly work for me.


Roses are FF0000, violets are 0000FF... All my base are belong to you.

Share this post


Link to post
Share on other sites
10 hours ago, SadBunny said:

Well, functions like WinWaitActive halt the script, so you can't make it do something else while waiting. If you face the possibility of >1 possible popups as a result of some action, and need to take different actions for each (i.e. you can't simply ignore one while waiting for another) a loop like that seems like a fine solution. For error popups that could popup at any time and that you constantly want to be scanning/polling for without having to worry about it in every part of your application, AdlibRegister comes to mind.

I don't automate GUIs a whole lot so maybe some of the gurus have more to add :) But that loop would certainly work for me.

Thank you kindly! I just like to avoid those (quite common) situations where I invest a bunch of effort creating a (more-or-less) complex routine that works well enough, and then I am given, or shown, or discover a function that accomplishes the same actions in one line of code. Those wiley PERL programmers are always doing things like that.

It's a little surprising that I have never run into a scenario where AutoIt would be expected to respond to more than one window or dialog. I would have thought this kind of thing would have been a lot more common, but obviously it is rarer than expected because by now I usually receive a lot of great suggestions from the AutoIt community.

I'll go with the loop for now, which should do the job until something more elegant comes along.

Share this post


Link to post
Share on other sites

I don't see what Adlib wont work.  I use it for situations similar.

Put it at the top of your script it should be running before the unexpected window comes up, and have the contents of that function be passive.

 

Example:


If WinExists() Then

WinActivate()

Send("{Enter}")

EndIf

 

1 person likes this

Share this post


Link to post
Share on other sites

My preferred way to solve this problem would be AdLibRegister.

If that doesn't work then you could have a look at OutlookEX UDF. Function _OL_Open allows to start a second script that waits for a window to pop up and then clicks it away.
The second script is terminated when the main script ends.

1 person likes this

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2017-04-18 - Version 1.4.8.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (NEW 2017-02-27 - Version 1.3.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2015-04-01 - Version 0.4.0.0) - Download - General Help & Support - Example Scripts
Excel - Example Scripts - Wiki
Word - Wiki
PowerPoint (2015-06-06 - Version 0.0.5.0) - Download - General Help & Support

Tutorials:
ADO - Wiki

 

Share this post


Link to post
Share on other sites

I would choose any of these two ways to handle that. Create a multiprocess script one for the task and the other to handle the popups) or simple set a window event hook maybe using RegisterShellHookWindow or SetWinEventHook to handle the popups.

Saludos

1 person likes this

Share this post


Link to post
Share on other sites
38 minutes ago, ViciousXUSMC said:

I don't see what Adlib wont work.  I use it for situations similar...

...I had better take another look at the AdLib register function... my first run at this was failing, and I assumed it was because the dialog I wanted to detect was "blocking", which the AutoIt help file said would prevent the AdLib timer from running, yet I see a few people are using it in the same way I would like to. It must have been failing for some other reason. Sooner or later I will have to write a watch-dog routine which will have to respond to any number of events.

8 minutes ago, Danyfirex said:

I would choose any of these two ways to handle that. Create a multiprocess script one for the task and the other to handle the popups) or simple set a window event hook maybe using RegisterShellHookWindow or SetWinEventHook to handle the popups.

This sounds like an excellent approach for creating the watch dog routines! I'll have to figure out how to work with multiple threads from within AutoIt, but SetWinEventHook looks like a great idea as long as I can get the handles of the windows and controls presented by the foreign GUI; this one is constructed of layers that wrap all kinds of weird GUI management systems of various types, some visible to AutoIt, and some not. The foreign GUI is a real mash-up of WinForms, .NET wrappers, WPF, and, believe it or not, COBOL.

A million thanks to all:

SadBunny
ViciousXUSMC (HOOAH!!!!!)
water
Danyfirex
 

You've got me well on the way!

Share this post


Link to post
Share on other sites

Hello. Remember that I've said Multi-Process. It's diferent to Multi-Thread. AutoIt does not support multi-Thread.

 

Saludos

Share this post


Link to post
Share on other sites
3 hours ago, Danyfirex said:

Hello. Remember that I've said Multi-Process. It's diferent to Multi-Thread. AutoIt does not support multi-Thread.

 

Saludos

Thanks!

It's been apparent that AutoIt has no mechanisms for creating and managing multi-threaded objects, but isn't it true that AutoIt can attach to the interface of a custom multi-threaded dll?

Of course the interface would have to expose the appropriate properties, methods and events to allow AutoIt to monitor and control execution, but it seems like it should be able to do so using the .dll manipulation functions in its library.

In the name of simplicity, I would take your advice and use the multi-process approach first, but it's nice to know that I could expand functionality by calling and controlling complex processes.

Share this post


Link to post
Share on other sites

#12 ·  Posted (edited)

Sorry if I missed something, but I mostly just scanned the answers.  I have Track-It! as a trouble-ticket system at work and if I don't log out before quitting the program , it will display a window asking if I want to log off the previous instance when I try to log in again.  I use this to overcome an unexpected, but not unrecognized window in my "Good Morning" script that I use to open all the windows I need open at the beginning of the day.

While Not WinExists("BMC Track-It! Technician Client - aleph01")
    If WinExists("User Logged In") Then
        Sleep(100)
        ControlClick("User Logged In", "", 6)
    ElseIf WinExists("TC - Track-It! Error") Then
        Sleep(100)
        ControlClick("TC - Track-It! Error", "", "[CLASS:WindowsForms10.BUTTON.app.0.201d787; INSTANCE:1]")
    Else
        Sleep(200)
    EndIf
WEnd

In this way, I wait until the expected window appears, but if the "unexpected" windows appears, I have it covered.  You would, of course, have to modify for your use.

Hope you find this helpful.  And I hope I didn't make a fool of myself, considering my limited scripting skills.  Just trying to help.

Edited by aleph01

Meds.  They're not just for breakfast anymore. :'(

Share this post


Link to post
Share on other sites
3 minutes ago, aleph01 said:

Sorry if I missed something, but I mostly just scanned the answers.  I have Track-It! as a trouble-ticket system at work and if I don't log out before quitting the program , it will display a window asking if I want to log off the previous instance when I try to log in again.  I use this to overcome an unexpected, but not unrecognized window in my "Good Morning" script that I use to open all the windows I need open at the beginning of the day.

While Not WinExists("BMC Track-It! Technician Client - aleph01")
    If WinExists("User Logged In") Then
        Sleep(100)
        ControlClick("User Logged In", "", 6)
    ElseIf WinExists("TC - Track-It! Error") Then
        Sleep(100)
        ControlClick("TC - Track-It! Error", "", "[CLASS:WindowsForms10.BUTTON.app.0.201d787; INSTANCE:1]")
    Else
        Sleep(200)
    EndIf
WEnd

In this way, I wait until the expected window appears, but if the "unexpected" windows appears, I have it covered.  You would, of course, have to modify for your use by adding more ElseIfs.

Hope you find this helpful.  And I hope I didn't make a fool of myself, considering my limited scripting skills.  Just trying to help.

 


Meds.  They're not just for breakfast anymore. :'(

Share this post


Link to post
Share on other sites

#14 ·  Posted (edited)

20 hours ago, aleph01 said:

Sorry if I missed something, but I mostly just scanned the answers.  I have Track-It! as a trouble-ticket system at work and if I don't log out before quitting the program , it will display a window asking if I want to log off the previous instance when I try to log in again.  I use this to overcome an unexpected, but not unrecognized window in my "Good Morning" script that I use to open all the windows I need open at the beginning of the day.

While Not WinExists("BMC Track-It! Technician Client - aleph01")
    If WinExists("User Logged In") Then
        Sleep(100)
        ControlClick("User Logged In", "", 6)
    ElseIf WinExists("TC - Track-It! Error") Then
        Sleep(100)
        ControlClick("TC - Track-It! Error", "", "[CLASS:WindowsForms10.BUTTON.app.0.201d787; INSTANCE:1]")
    Else
        Sleep(200)
    EndIf
WEnd

In this way, I wait until the expected window appears, but if the "unexpected" windows appears, I have it covered.  You would, of course, have to modify for your use.

Hope you find this helpful.  And I hope I didn't make a fool of myself, considering my limited scripting skills.  Just trying to help.

This is almost exactly the approach I finally settled upon:

    Select

        Case $PunchMode = "CIS" ; ********************************************** "CIS" = Clock In *******************************************************************************

            ;MsgBox(0, "$JobSubmit", $JobSubmit)

            Send($JobSubmit, 0)
            ; ABOVE: Write formatted, 15 character string "$%JxxxxxWxxSxxxxx"; where Jxxxxx = 6 character Job Number, Wxxx = 3 character Suffix,
            ; and Sxxxxx = 6 character Sequence number, to text box on Global Shop Time Clock GUI. Shorter strings must be padded to length, which
            ; is tested and executed by the For-Next loops above...
            ;Sleep(100)
            Send("{ENTER}"); Enter the value submitted above...

            $LoopRelease = False

            While Not $LoopRelease; Ends checking for windows presence when window is detected.

                Select

                    Case WinActive("Global Shop On-line System - Wkstn ID:", "Work Order Header Information")

                        $logtext = "Function SubmitToClock() -------- CONSOLE GUI - The job was successfully submitted and accepted by the Global Shop GUI Time Clock."
                        writelog($logtext)

                        $LoopRelease = True

                    Case WinActive("Sequence Is Active", "The selected sequence is already active.")
                        ;MsgBox($MB_SYSTEMMODAL, "", "I see the window, Sequence Is Active...")
                        WinActivate("Sequence Is Active", "The selected sequence is already active.")
                        ControlClick("Sequence Is Active", "The selected sequence is already active.", "[CLASS:Button; TEXT:&Ok]")

                        $logtext = "Function SubmitToClock() -------- CONSOLE GUI - Submission Failure - The selected sequence is already active."
                        writelog($logtext)

                        $LoopRelease = True

                    Case WinActive("On-line GUI Response Return Message", "Response status 98 supporting data: The Job Is On Hold")
                        ;MsgBox($MB_SYSTEMMODAL, "", "I see the window, On-line GUI Response Return Message...")
                        WinActivate("On-line GUI Response Return Message", "Response status 98 supporting data: The Job Is On Hold")
                        ControlClick("On-line GUI Response Return Message", "Response status 98 supporting data: The Job Is On Hold", "[CLASS:Button; TEXT:&Ok]")

                        $logtext = "Function SubmitToClock() -------- CONSOLE GUI - Submission Failure - Response status 98 supporting data: The Job Is On Hold."
                        writelog($logtext)

                        $LoopRelease = True

                    Case WinActive("Unable to Start Job", "Unable to start job.  The selected job is closed.")

                        ;MsgBox($MB_SYSTEMMODAL, "", "I see the window, Unable to Start Job...")

                        WinActivate("Unable to Start Job", "Unable to start job.  The selected job is closed.")

                        ControlClick("Unable to Start Job", "Unable to start job.  The selected job is closed.", "[CLASS:Button; INSTANCE:1]")

                        $logtext = "Function SubmitToClock() -------- CONSOLE GUI - Submission Failure - Unable to start job; the selected job is closed."
                        writelog($logtext)

                        $LoopRelease = True

                    Case WinActive("On-line GUI Message", "General response return message:")
                        ;MsgBox($MB_SYSTEMMODAL, "", "I see the window, On-line GUI Message...")
                        WinActivate("On-line GUI Message", "General response return message:")
                        ControlClick("On-line GUI Message", "General response return message:", "[CLASS:Button; TEXT:&Ok]")

                        $logtext = "Function SubmitToClock() -------- CONSOLE GUI - Submission Failure - General response return message - Undetermined Error."
                        writelog($logtext)

                        $LoopRelease = True

                EndSelect

            WEnd


            ;MsgBox($MB_SYSTEMMODAL, "", "The loop has been terminated. Loop Release = " & $LoopRelease)

        Case $PunchMode = "CHJ" ; ********************************************** "CHJ" .........And so on. This works very well.

I used the "While Not WinExists" test loop at first, as you did, but it failed because for the most part, all of the windows in this GUI exist all of the time. Fortunately, "window changes" can be detected based on their text content. My solution looks a little different from yours for this reason, using the Loop Release instead of window detection, because there is a lot weirdness in the way the GUI of the app under control is constructed:

2016-07-13_14-36-33.jpg

ABOVE: Basically, this GUI loads and activates ALL of its windows, displaying ALL visible text from EVERY "Child" element. They all have the same class ID, varying only in their instance number, the "child" windows have no handles (but the control elements do, but are not discoverable), and none appear as processes which are independent of the main thread.

Because the core application is constructed using elements programmed in extinct languages (COBOL, which is not really extinct, but almost, and something that looks a lot like Sanskrit) wrapped in compatibility layers, this GUI loads and activates ALL of its core windows, which means ANY window I test using "WinActive" returns "True", whether it is visible on top of the stack, or not. This GUI then shows or hides its screen members by manipulating the Z order of its elements, requiring some very tricky scripting to sort out which window I want to manipulate controls upon. Fortunately, the "unexpected" dialog windows follow proper WinForms creation and messaging protocols, so they may be detected and dealt with similarly to your example.

Today however I am working within the final Case statement which has run up hard against the weirdness of this GUI, this part being the "Clock Out" command issued by AutoIt. When the employee is to be clocked out, AutoIt sends the employee ID number to validate the user so he may clock out. If the user is currently logged in to a job, it is no problem to simply send the commands that will complete the job for the day, and clock out of the system immediately afterward:

            ControlClick($hWnd, "", "[CLASS:Button; TEXT:Update Jobs]")
            Sleep(250)
            ControlClick($hWnd, "", "[CLASS:Button; TEXT:Process]") 
... These commands are sent "Blind" because the GUI provides no way to distinguish this window from any of its others. The window that follows when this function is successful has button elements that ARE detectable, so a WInWait command will work.

The problem arises if the employee is clocked in to the system, but not a job; in this instance, the GUI does not display the "Active Job" screen because the employee is not logged in to a job; commands to complete the job are being sent "blind" to the "Active Job" screen which is not visible because it is buried in the stack, but is Active and therefore exists, so AutoIt sends the commands which cause the GUI to display the error dialog.

All of this is FYI, because unless I can discover a way to detect the Z-Order of the desired window, nobody will be able to help with this, because AutoIt cannot tell one window from another (NOT AUTOIT's FAULT; just  this stupidly built GUI) except for the changes in the window text that occur as each different Employee/Job is being processed.

It's a fun puzzle nonetheless.

 

Edited by mbunds
Correction...

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