Sign in to follow this  
Followers 0
OldMike

Need Help With Basic Focus Control

12 posts in this topic

Clued into AutoIT by a colleague at Experts Exchange and have found it to be a great upgrade from VBScripts. My need is pretty basic, but just can't seem to get it going. I need to launch four separate instances of an application (ABBYY OCR program), which I have been able to do just fine. I think I am also able to capture what the PID is when it launches. Each time I launch I send a few key strokes to select a canned macro from within the application; also working fine. But later on I need to be able to regain focus of a specific instance and do a few more key strokes to shut it down; that I cannot do. I don't know if I need to name each window with a unique name (have not been able to set a new title) and the refer to the name to get control back (how?) or, since I have the PID how do I get the keystrokes to go to the specific process I want? Would REALLY appreciate some explicit lines of code, or just send me to a sample program that opens, then minimizes, then regains control of a Windows app.

What I have so far:

$PID1 = run ("C:\Program Files (x86)\ABBYY FineReader 10\FineReader.exe","",@SW_MAXIMIZE,"")

send ("n")

send ("^t")

send ("{ENTER}")

I assume $PID1 (then $PID2 when I open another session) is the PID or handle(?) that can be used later to select this instance.

Mike

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

Hello OldMike,

Welcome to the AutoIt Forums ;)

First, pid is an abbreviation for Process ID. However in your example you are using the abbreviation as a variable, being in which you could name that variable anything you like with a few limitations, which you can read more about variables and declaring them here if you wish.

With the Function Run, it returns the actual pid. With that said, you could use $oldMike = Run(), and the actual pid would be assigned to $oldMike.

To have 4 different instances of the same program, it would be wise to declare a different variable to each Run. You can use $pid1, $pid2, and so on. You could use $abby1, $abby2, to help remind you of the process name. To Automate, Close, or Call a particular instance of the Program you are running, you would call it by its individual variable, such as ProcessClose($abby2).

Hope this helps :)

Realm

Edit: Fixed typos, and provided a link to Language Reference - Variables in the online help file.

Edited by Realm

My Contributions: Unix Timestamp: Calculate Unix time, or seconds since Epoch, accounting for your local timezone and daylight savings time. RegEdit Jumper: A Small & Simple interface based on Yashied's Reg Jumper Function, for searching Hives in your registry.  

Share this post


Link to post
Share on other sites

Thanks; very helpful. I have been able to change the title, then make Send go to the correct window. I do not need to use ProcessClose now, because if I can successfully get Send to go to the correct session I can use keystrokes to shut the program down gracefully. However I have seen some strange behavior, plus my method is fragile.

Strange: if I launch the programs with {@SW_MAXIMUM) I can see the titles change. So to test I launch the first ABBYY as TEST1, and the second as TEST2 and the window title changes. I can then activate TEST1 and Send to it successfully, but when I do the TITLE2 changes back to original, but it will still respond to an activation of TEST2. So the title change-back seems to just be cosmetic. Does that make sense? (I hate stuff that 'just happens' rather than I understand it).

Fragile: I can launch the script and then both ABBYY sessions launch and run and shut down, but only if I stay quiet. If I should open Windows Explorer to watch the folders holding the PDF the Send commands start going to the open window, making for some bazaar behavior. So the two windows are independently controllable so long as nothing else happens, but if another window opens independent of the script the Send command goes to the new window, not the one I think I am addressing. How can I assure that it goes where I want? Would SendKeepAlive override the Windows Explorer window?

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

Hello OldMike,

It's really difficult to help you, without seeing your script or at least and example of it. My best educated guess would be that your right, it's just cosmetic as long as you can still call it by your custom title. However I am assuming that your trying to use ControlSend() with the $pid as ControlSend($pid,'',). If this is the case, the various Control functions do not use PID to call, they perform with Handles or Window Titles. So with that in mind, This is how I obtain the handles of each instance, while changing the title of the window at that same time. This example also shows how to use the handles in the control functions.

*Note - You will have to change the Window Titles 'ABBYY' that I had assigned, to the real Title name. If you don't know the actual title name, it can be obtained with the Au3Info.exe application found in your AutoIt3 Installation Folder.

Local $abby_Handles[4] 

For $a = 0 To 3 
    Run("C:\Program Files (x86)\ABBYY FineReader 10\FineReader.exe","",@SW_MAXIMIZE,"")
    WinWaitActive('ABBYY','')
    WinSetTitle('ABBYY','','Test' & $a)
    $abby_Handles[$a] = WinGetHandle('Test' & $a,'')
Next

ControlSend($abby_Handles[0],'','Edit1','Send something to the first instance of abby')
ControlSend($abby_Handles[1],'','Edit1','Send something to the second instance of abby')
ControlSend($abby_Handles[2],'','Edit1','Send something to the third instance of abby')
ControlSend($abby_Handles[3],'','Edit1','Send something to the fourth instance of abby')

However, you don't need to create a new Title for a window in order to call on it later, unless that is for your own cosmetic reasons. Run all 4 instances and then use WinList() to obtain each instances handles.

*Note - You will have to change the Window Titles 'ABBYY' that I had assigned, to the real Title name. If you don't know the actual title name, it can be obtained with the Au3Info.exe application found in your AutoIt3 Installation Folder.

Local $abby_Handles

For 1 To 4 
    Run("C:\Program Files (x86)\ABBYY FineReader 10\FineReader.exe","",@SW_MAXIMIZE,"")
Next

Do 
    $abby_Handles = WinList('ABBYY','')
    ;might want to create a timeout check incase of errors
Until $abby_Handles[0][0] = 4 ;keeps checking until 4 instances of Abby are present

ControlSend($abby_Handles[1][1],'','Edit1','Send something to the first instance of abby')
ControlSend($abby_Handles[2][1],'','Edit1','Send something to the second instance of abby')
ControlSend($abby_Handles[3][1],'','Edit1','Send something to the third instance of abby')
ControlSend($abby_Handles[4][1],'','Edit1','Send something to the fourth instance of abby')

Realm

Edit: Fixed some typos, and my error inside the second example while checking for 4 instances.

Edited by Realm

My Contributions: Unix Timestamp: Calculate Unix time, or seconds since Epoch, accounting for your local timezone and daylight savings time. RegEdit Jumper: A Small & Simple interface based on Yashied's Reg Jumper Function, for searching Hives in your registry.  

Share this post


Link to post
Share on other sites

Wow; great help. I've done a lot with VBScript, but just now dabling with AutoIT. I only changed the title because I thought I needed that for addressing later on. A couple of new questions now:

1. You mention addressing using the handles to send control commands. But what I want to send is keys (Send); is that considered a control? I don't see anything on the Send documentation about first getting focus.

2. The result I have so far is that I can get focus/activation on demand, with a twist. If I launch four sessions and set back it will run fine and randomly gain control and shut down any given session and restart. But if I have something else open, such as Windows Explorer, the Send commands go to the open Windws Explorer window. So it appears that the addressing works but is fragile, not top tier. So I need a method that will grant focus to what I want no matter what else might be open beforehand.

3. Making a list of handles is not useful for me (I think) because though I might start them all in order at boot up, thereafter they are random. Some will finish before others (which I can detect), in which case I shut down the one that finished (not by forced shut down; I want to get control of the selected one and send the key strokes that will gracefully shut it down) and then start it from scratch. SO I have to know specifically which session (they each run a different macro) is associated with which handle.

Share this post


Link to post
Share on other sites

#6 ·  Posted (edited)

Here's my code. Doesn't do everything I need to do, just testing ability to launch and shut down.

;====  Manage A Single ABBYY session, watching P1, PDF1, and OCR1
Local $WatchIn = "D:\P"
Local $MoveTo = "D:\PDF"
Local $WatchOut = "D:\OCR"
Local $UpBucket = "D:\Reg Out\"
Local $ABBYY_Active
Local $First_AB = 1
Local $Last_AB = 4
Local $InCount
;== Find the number of PDFs in WatchIn waiting
For $n = $First_AB to $Last_AB
$size = DirGetSize(($WatchIn & $n &"\"),1)  ; Get count of PDFs to be processed
$ABBYY_Active = "ABBYY" & $n
$ABBYY_Active = run ("C:\Program Files (x86)\ABBYY FineReader 10\FineReader.exe","",@SW_MAXIMIZE,"")
Sleep (5000)
Send("n")
Send("^t")
Select
    Case $n=1
         $n=1   ;NOOP
    Case $n=2
        Send("{DOWN}")
    Case $n=3
        Send("{DOWN}")
        Send("{DOWN}")
    Case $n=4
        Send("{DOWN}")
        Send("{DOWN}")
        Send("{DOWN}")
EndSelect
Send("{ENTER}")
Next
Sleep (5000)
For $n = $First_AB to $Last_AB
$ABBYY_Active = "ABBYY" & $n
WinActivate($ABBYY_Active)
Send("!f")
Send("x")
Send("n")
Next
Edited by OldMike

Share this post


Link to post
Share on other sites

#7 ·  Posted (edited)

Hello OldMike

1) Obtaining Focus from a control function works with most windows, however there are an occasional window that needs to be activated before sending a control, if you find this to be situation than a simple WinActivate() should do the trick, or just to be safe, you could activate the individual window, with the same handles WinActivate($abby_Handles[1][1],'')

To send keys, you can look up how to send key reference in the Help File under function Send(),

Edit: I wrote this before I was aware that was what your were using, I assumed you were using ControlSend()

2) OK, evidently you will have to bring this application into focus by using WinActivate() before using the control functions, if you still are having a problem, then try bringing the control into focus with ControlFocus(). If still at loss, the application might be resistant to Automation.

3) The $handles are created in the array that I gave an example to in each script.

Here is a problem with your script:

$ABBYY_Active = "ABBYY" & $n ; you have assigned the name 'ABBYY' & $n, but
$ABBYY_Active = run ("C:\Program Files (x86)\ABBYY FineReader 10\FineReader.exe","",@SW_MAXIMIZE,""); here that gets changed to the PID of the program

If This is referencing a File Menu in the application

Select
        Case $n=1
            $n=1    ;NOOP
        Case $n=2
            Send("{DOWN}")
        Case $n=3
            Send("{DOWN}")
            Send("{DOWN}")
        Case $n=4
            Send("{DOWN}")
            Send("{DOWN}")
            Send("{DOWN}")
    EndSelect

Then you might want to look at WinMenuSelectItem() in the HelpFile

Not inlcuding WinMenuSelectItem, This is how you should have it:

;==== Manage A Single ABBYY session, watching P1, PDF1, and OCR1
Local $WatchIn = "D:\P"
Local $MoveTo = "D:\PDF"
Local $WatchOut = "D:\OCR"
Local $UpBucket = "D:\Reg Out\"
Local $ABBYY_Active
Local $First_AB = 1
Local $Last_AB = 4
Local $InCount
Local $Abbyy[$Last_AB]
;== Find the number of PDFs in WatchIn waiting
For $n = $First_AB to $Last_AB
    $size = DirGetSize(($WatchIn & $n &"\"),1) ; Get count of PDFs to be processed
    $ABBYY_Active[$n] = run ("C:\Program Files (x86)\ABBYY FineReader 10\FineReader.exe","",@SW_MAXIMIZE,"")
    WinWait($ABBYY_Active[$n]) ;removed the sleep, and added a specific window wait
    Send("n")
    Send("^t")
    Select
        Case $n=1
            $n=1    ;NOOP
        Case $n=2
            Send("{DOWN}")
        Case $n=3
            Send("{DOWN}")
            Send("{DOWN}")
        Case $n=4
            Send("{DOWN}")
            Send("{DOWN}")
            Send("{DOWN}")
    EndSelect
    Send("{ENTER}")
Next
Sleep (5000)
For $n = $First_AB to $Last_AB
    WinActivate($ABBYY_Active[$n])
    Send("!f")
    Send("x")
    Send("n")
Next 
[size=2]

To close a given instance of Abbyy, then use WinClose($ABBYY_Active[$n])

Thanks for putting your code into AutoIt Tags, it makes it a lot more readable.

See if that last example does more of what you need, However, if you are planning to use your pc while that is running, you might want to look into ControlSend() which does not utilize your mouse and keyboard.

Realm

Edit: My example will error, I was trying to use PID as Window Handles, give me a moment to correct and repost. I am downloading Abbyy now, so I can understand more of what your attempting to accomplish, give me a bit to play with it.

Edited by Realm

My Contributions: Unix Timestamp: Calculate Unix time, or seconds since Epoch, accounting for your local timezone and daylight savings time. RegEdit Jumper: A Small & Simple interface based on Yashied's Reg Jumper Function, for searching Hives in your registry.  

Share this post


Link to post
Share on other sites

Hello OldMike,

After playing around with abby, I believe I have a working test script for you:

;==== Manage A Single ABBYY session, watching P1, PDF1, and OCR1
Local $WatchIn = "D:\P"
Local $MoveTo = "D:\PDF"
Local $WatchOut = "D:\OCR"
Local $UpBucket = "D:\Reg Out\"
Local $ABBYY_Active
Local $First_AB = 1
Local $Last_AB = 4
Local $InCount
Local $Abbyy[$Last_AB]
;== Open Abby and obtain handles.
For $a=1 To 4
    Run("C:\Program Files\ABBYY FineReader 10\FineReader.exe","",@SW_MAXIMIZE,"")
Next
Do
    $ABBYY_Active = WinList("ABBYY FineReader 10",'')
    ;might want to create a timeout check incase of errors
Until $ABBYY_Active[0][0] = 4 ;keeps checking until 4 instances of Abby are present
Sleep(2000)
;== Find the number of PDFs in WatchIn waiting
For $n = $First_AB to $Last_AB
;~  $size = DirGetSize(($WatchIn & $n &"\"),1) ; Get count of PDFs to be processed
    WinActivate($ABBYY_Active[$n])
    Sleep(250)
    Send("^n")
    Sleep(250)
    Send("^t")
    Sleep(250)
    Select
        Case $n=1
            ControlClick('Automation Manager', '', 'SysListView321', 'left', 1, 27, 11) ;selects Convert PDF/Imageas to Microsoft Word
        Case $n=2
            ControlClick('Automation Manager', '', 'SysListView321', 'left', 1, 27, 28) ;selects Convert Photo to Microsoft Word
        Case $n=3
            ControlClick('Automation Manager', '', 'SysListView321', 'left', 1, 27, 44) ;selects Scan to Image File
        Case $n=4
            ControlClick('Automation Manager', '', 'SysListView321', 'left', 1, 27, 61) ;selects Scan to Microsoft Excel
    EndSelect
    Sleep(250)
    ControlClick('Automation Manager', '','Button2','left')
Next
Sleep (5000)
For $n = $First_AB to $Last_AB
    WinClose($ABBYY_Active[$n])
Next

give it a shot, and let me know if it is working for you. Did you notice how the array stores the handles, and how they are called as needed throughout the script?

Realm


My Contributions: Unix Timestamp: Calculate Unix time, or seconds since Epoch, accounting for your local timezone and daylight savings time. RegEdit Jumper: A Small & Simple interface based on Yashied's Reg Jumper Function, for searching Hives in your registry.  

Share this post


Link to post
Share on other sites

Thanks; I feel like I'm making progress. I added the following in the setup:

Local $ABBYY_Active[$Last_AB]

The script runs, but ever gets past the WinWait statement. Waited for a few minutes, and still not moving on.

Share this post


Link to post
Share on other sites

#10 ·  Posted (edited)

Thanks; I feel like I'm making progress. I added the following in the setup:

Local $ABBYY_Active[$Last_AB]

The script runs, but ever gets past the WinWait statement. Waited for a few minutes, and still not moving on.

Did you notice that I modified that part to WinActivate, since it's no longer waiting for a window to appear, now you need to activate the window needed.

Edit: The last example I Posted works perfectly on my end, which I just noticed that I commented out the part that checks the size of your PDFs, I did that for my tests, you might want to uncomment that out!

Edited by Realm

My Contributions: Unix Timestamp: Calculate Unix time, or seconds since Epoch, accounting for your local timezone and daylight savings time. RegEdit Jumper: A Small & Simple interface based on Yashied's Reg Jumper Function, for searching Hives in your registry.  

Share this post


Link to post
Share on other sites

I have a theory on why it does not move on. When ABBYY launches it immediately pops up a window asking if I want to save the previous batch. Answering 'no' is what the Send("n") statement if for. But perhaps this window comes up so fast that the main window cannot report being active fast enough and focus moves on to the pop up, so WinWait is waiting for a report from the main window, but it is not being summoned. So I'll try removing the test for launch, keep it for shutting down later on.

Share this post


Link to post
Share on other sites

Answering 'no' is what the Send("n") statement if for.

My bad, I thought it was just a typo and you wanted a new doc to create first. in my last example, take out the '^' CTRL in the send! maybe that will help, if not, you can work the window creation process like this:

[size=2]For $a=1 To 4[/size]
    Run("C:\Program Files\ABBYY FineReader 10\FineReader.exe","",@SW_MAXIMIZE,"")
    WinWait("ABBYY FineReader 10")
    Do
        $text = WinGetText("ABBYY FineReader 10",'')
    Until StringInStr($text,"no")
    Send('n')
[size=2]Next[/size]

My Contributions: Unix Timestamp: Calculate Unix time, or seconds since Epoch, accounting for your local timezone and daylight savings time. RegEdit Jumper: A Small & Simple interface based on Yashied's Reg Jumper Function, for searching Hives in your registry.  

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
Sign in to follow this  
Followers 0