Jump to content
Sign in to follow this  
h61Omrod

Any alternative to Sleep() or WinWaitActive()?

Recommended Posts

h61Omrod

It's my first AutoIt script and almost obviously I'm trying to automate some operations in an external program.

My concern points to time an operation takes to complete, that can vary according to the PC's tasks loading, and how to control that.

I'd work these issues out through the Sleep() or the WinWaitActive() functions. The first one, even though easy to use, is unable to manage situations where the execution time vary significantly. The second one is able to detect the moment when a new window becomes active and so it seems to be the correct solution.

But not in this situation I'm facing: at the end of script (not exactly at the end, because this routine exists inside a loop) I send the command to save the file early modified, and a "Save as" windows opens inside the main program; now I send the keystrokes to perform the file saving, and at that moment... well, at that moment I'm not able to manage correctly the further events.

Using a long Sleep() break could be boaring in case of a small file, but still unsafe in case of a huge file to save on a by accident temporarily busy system: waiting time could run off before the saving completes and the next instructions coming from the script would be lost.

But also WinWaitActive() seems unappropriate, because that is what happens: I'm checking whether the main window of the program is ready to receive new keystrokes; in effect this function tests the moment when the main window becomes active, but this moment happens when the "Save as" windows closes - even though the saving process isn't yet ended -; so the script resume to execute and the further commands (closing the saved file and repeating the loop with a new file) go down the drain.

Any suggestion to work the trouble out? Thanks in advance :-)

h61Omrod

Share this post


Link to post
Share on other sites
MilesAhead

There are several approaches you could take. One would be looking into WMI to get the disk accesses during the last few seconds. Another could be if the program you are manipulating allows more than one instance to be open simultaneously. SaveAs in one app, switch to the other to load some file etc..

Also if the app is an editor, it may display a line count. If you send keys to navigate to end of file, you may be able to read the line count text from the program's window. That will give you some idea how big the file is that will be saved etc..

Share this post


Link to post
Share on other sites
somdcomputerguy

Another way that may work for you is if certain buttons, or controls, in the program are not available while whatever process is executing. For example, see the bit of code I've added below. There I use the ControlCommand function (in a Do..Until loop) to do nothing until a button is enabled.

Do
  Sleep(10)
Until ControlCommand("Forum Spam List Checker", "", "Button1", "IsEnabled") ; Wait until Search/Query is done
Edited by somdcomputerguy

- Bruce /*somdcomputerguy */  If you change the way you look at things, the things you look at change.

Share this post


Link to post
Share on other sites
Mat

This is how I'd try and wait for a file to finish writing:

#include <WinAPI.au3>

#include <Constants.au3>
#include <File.au3>

If StringInStr($CmdLineRaw, "/tryreading") Then
    ; We are going to see if we can tell when the other script stops writing

    Local $hFile
    Do
        $hFile = _WinAPI_CreateFile($CmdLine[$CmdLine[0]], $CREATE_ALWAYS, BitOR($FILE_SHARE_WRITE, $FILE_SHARE_DELETE), 0, 0, 0)
        Sleep(10)
    Until $hFile
    _WinAPI_CloseHandle($hFile)

    MsgBox($MB_OK, Default, "Finished writing!")

    Exit
EndIf

Local $sFile = _TempFile()

Local $hFile = FileOpen($sFile, $FO_OVERWRITE)
If @error then
    ConsoleWrite("Couldn't open the file for writing!" & @LF)
    Exit 1
EndIf

; Run the script that will try and tell us when it is finished.
Local $iChildPid
If @Compiled Then
    $iChildPid = Run("""" & @ScriptFullPath & """ /tryreading """ & $sFile & """")
Else
    $iChildPid = Run("""" & @AutoItExe & """ /AutoIt3ExecuteScript """ & @ScriptFullPath & """ /tryreading """ & $sFile & """")
EndIf

For $i = 1 To 10
    FileWrite($hFile, "This is a test!" & @CRLF)
    FileFlush($hFile)

    ConsoleWrite("Writing " & $i & @LF)
    Sleep(1000) ; Make it seem like a slow write?
Next

FileClose($hFile)

If Not ProcessWaitClose($iChildPid, 5) Then ProcessClose($iChildPid)

FileDelete($sFile)

Basically you try and lock the file for writing (which will fail until the other process closes the handle), and wait till it succeeds.

Share this post


Link to post
Share on other sites
h61Omrod

Thank you all for your replies: your collaboration is great!

In regard of Eclectician suggestions - well - WMI isn't my bread 'n butter, and having to study it in order to solve this issue sounds outrageous =B-( but his further suggestions opened my mind on possible "tricky" solutions I didn't take into account, in my belief there were more "orthodox" ways to go through.

Many thanks to somdcomputerguy - unfortunately there are no buttons to check - and to Mat, which solution I didn't understand at all =:-D - my fault, of course.

What I'm going now to test is: inside a loop, I'll send again and again to the main windows of the program a keystroke to open its "File" menu, hoping it's possible to check when this actually opens - which means the write process has terminated. At that moment I'll exit the loop and go ahead with further commands.

I'll tell you if it works.

Share this post


Link to post
Share on other sites
Mat

Sorry, I adapted it from a couple of snippets I had lying around, so it probably isn't that clear. Here is the only relevant bit of that snippet:

Local $sFile = "<Insert File Path Here>"

    Local $hFile
    Do
        $hFile = _WinAPI_CreateFile($sFile, $CREATE_ALWAYS, BitOR($FILE_SHARE_WRITE, $FILE_SHARE_DELETE), 0, 0, 0)
        Sleep(10)
    Until $hFile
    _WinAPI_CloseHandle($hFile)

    MsgBox($MB_OK, Default, "Finished writing!")

It essentially trying to get exclusive access to the file, which it can't do as long as another program is writing to it. As soon as it can get the access, it returns a handle (which will be non-zero) so the loop exits. We also add a sleep in the loop, you don't need faster responses than 10ms and it will keep your cpu idling.

It'll be more accurate than any alternative.

Share this post


Link to post
Share on other sites
somdcomputerguy

- unfortunately there are no buttons to check -

Buttons aren't the only kind of Controls that the ControlCommand function can work with.

Good luck with your project! :)


- Bruce /*somdcomputerguy */  If you change the way you look at things, the things you look at change.

Share this post


Link to post
Share on other sites
Bowmore

In most applications there is usually some part of the screen that changes colour (greyed out etc.) when it is busy writing a file or some other activity. You could then use PixelGetColor() in a loop to wait for a specific pixel to change colour.


"Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to build bigger and better idiots. So far, the universe is winning."- Rick Cook

Share this post


Link to post
Share on other sites
MilesAhead

In regard of Eclectician suggestions - well - WMI isn't my bread 'n butter, and having to study it in order to solve this issue sounds outrageous

Searching these forums with the terms "wmi disk access" took a few seconds to turn up a working snippet. If you give up before you start everything will seem daunting. I think the file lock idea is probably the best since you know the full path to the file presumably. But it's just one avenue of thought. I happened to have a fake disk indicator light app that flickers in the tray. The programmer used wmi to get disk accesses for the last 2 seconds. Seems there's not a simple WinAPI to get disk activity like there is for CPU activity. I don't know why.

Edited by MilesAhead

Share this post


Link to post
Share on other sites
h61Omrod

What I'm going now to test is: inside a loop, I'll send again and again to the main windows of the program a keystroke to open its "File" menu, hoping it's possible to check when this actually opens - which means the write process has terminated. At that moment I'll exit the loop and go ahead with further commands.

I'll tell you if it works.

And at the end it works...

But - Mat - now I can understand your solution and it sounds more correct and fair to me: I'll try to implement it in my code.

Thank you all for your support and I owe you one.

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  

×