Jump to content

Generic question can AutoIT automate a DOS program


jstump1
 Share

Recommended Posts

Hello - I have used AutoIT for some windows scripting and automation & I think its great...I'm running into a problem though. It appears that it cannot read the text coming from a DOS program I would like to automate. The window info program shows no visible or hidden text.

I tried automating the program using send() and sleep, but the response time of the program is all over the place. The sleep command just doesn't work out unless I want to make it amazingly slow. WinWait doesn't work either because I need something that can actually read the screen for appropriate text before sending keystrokes. I'm not sure how to use StdinWrite and StdoutRead yet, but maybe I'll figure out how these functions work soon. I don't really have any hope that either of these will work (yet) either though. Am I missing something or is this just not a task for AutoIT?

If AutoIT can't read the info coming from the DOS window what can? Any recommendations for some type of scripting solution for this?

I am trying to automate the printing and conversion of over 30,000 police reports to get them into our document management system. Unfortunately there is no way of getting the company who wrote the software to do this for me - they are now defunct.

TIA

James

Link to comment
Share on other sites

Hello jstump1, welcome to the AutoIt forums !

There is an example of how you might use StdOutRead here.

But I have personally had success with the following approach, when the command, where run in the DOS window, will output what you need:

; Save this code to a file with a .au3 file extension.
; To terminate execution, right-click the tray icon and choose "Exit" !
; Writes records to the file C:\BeatReports.txt - this path cannot have space characters in it!
$Params = ""
; Any parameters - put in between the quotes in the next line and remove the semicolon at the beginning of the line:
;$Params = "myFirstParameter mySecondParameter myThirdParameter myFourthParameter"
$CmdStr = "Insert the full path to the .exe file here"
$CmdStr = StringStripWS($CmdStr, 3)
If StringInStr($CmdStr, " ") Then
    $CmdStr = '"' & $CmdStr & '"'
EndIf
If StringLen($Params) Then
    $CmdStr &= " " & $Params
EndIf
Sleep(160)
RunWait(@ComSpec & " /c " & $CmdStr & " >> C:\BeatReports.txt", "", @SW_HIDE)
Sleep(800)

This waits until the .exe command is finished and writes all of its DOS window output to the .txt file.

The .exe file doesn't have to actually be a .exe file, but must work kind of like one in that it will run in a DOS window and output something there. And the full path of the file - something like C:\Program Files\Jacks Defunct Software\KillerApp.exe - can have spaces in it, that is handled in the script, but the .txt file cannot.

Where you might get into problems with this is:

1. The resulting data file might be too large to open in any text editor - problem might occur if it is over 64 MB's

2. Using filepaths or parameters with embedded double-quotes in them when they shouldn't be there.

3. Using filepaths or parameters without quotes when they are needed.

But once your police report data is in .txt files, you should be able to automate manipulating that data easily with AutoIt3 - and make .xml files, .csv files that will open in Excel, etc.

There are probably some parameters you can send to the .exe such that the .txt files won't be too large. What are the accepted parameters of the .exe command?

Edited by Squirrely1

Das Häschen benutzt Radar

Link to comment
Share on other sites

Thanks for the info Squirrely1. I'll probably use that code example on another script in the near future.

It doesn't look like AutoIT is going to do what I need it to do. I'll look for some type of macro recorder for DOS or some other type of automation software. The problem is that I need the ability to read the screen output after each key combination is put into the program, without exiting the program and going back into it all the time. If the characters are just input into the program without checking to see if it is actually ready or at the right spot it just doesn't work. There are no paramaters, etc. that can be input into the executable. I just need to be able to repeat the same tasks over and over, but stopping when the screen output is not what is expected (indicating an error).

I find it curious that the ability to read the contents of a DOS window with a function like WinWait is not in AutoIT. How do I make a feature request? It may be harder than I think but what about a function that copies the screen every so many milliseconds to be read? Call it DOSWait or something. It would need to replicate the edit - mark, edit - copy used in a DOS shell that I use almost every day.

Here is a recap of what I want to accomplish:

Launch the Police Records software (DOS window), navigate to the correct menu, open first report, and then print it to screen. The screen print creates a nicely formatted temp text file to copy/rename to a folder. Go to the next report and repeat the process over 30,000 times. I tried this using Send and Sleep, but it keeps getting off track somwhow. I slow it to a crawl then it mostly works. Once in a while though reports get skipped because something goes wrong...that just doesn't work for 30,000 reports.

If AutoIT could check for specific text on the screen after each Send I could effectively have it stop every time there was a problem. At that point I would make a notation of the case number and then restart the script on the next case number. I'd deal with the ones with problems later.

James

Link to comment
Share on other sites

It doesn't look like AutoIT is going to do what I need it to do.

This is only your second post to the forums, and by your own admission, you don't know AutoIt. Why are you giving up so soon, are the foxes in charge of the whole henhouse and mayhem ruling in the streets that you are in such a hurry and need to get back to the streets?

I'll look for some type of macro recorder for DOS or some other type of automation software.

AutoIt is good for making macros. But if you are more familiar with another program, you could probably use that for entering things in the old .exe

The problem is that I need the ability to read the screen output after each key combination is put into the program, without exiting the program and going back into it all the time.

I gathered that this might be the case. But there is probably some documentation on that program available somewhere around your workstation - even if it is in printed format. If there is a .chm or other help file around, you should zip that documentation and post it in this thread - probably one of the forum administrators will find some helpful parameters for the program in it.

I just need to be able to repeat the same tasks over and over, but stopping when the screen output is not what is expected (indicating an error).

There are many ways to catch such error events and other events using this language. This project seems important enough that someone here would do it for you at little or no cost. It can take several months to learn to use this language though.

I find it curious that the ability to read the contents of a DOS window...is not in AutoIT.

You could automate printscreen and then use an OCR to do this, but there is probably a better way.

How do I make a feature request?

I doubt that you need to make one for this, but there is a place on the AutoIt forums to do this. What you might want to do instead, is either to hire one of the administrators of AutoIt - one of the experts - or just keep trying to write the code yourself, post that code, and let guys like me try to improve upon it.

It may be harder than I think but what about a function that copies the screen every so many milliseconds to be read?... It would need to replicate the edit - mark, edit - copy used in a DOS shell that I use almost every day.

We can probably do this with this language, though this may be the hard part. Post the code what you have, and if you can, and the program isn't too big, zip the .exe and post that .zip

Here is a recap of what I want to accomplish:

Launch the Police Records software (DOS window), navigate to the correct menu, open first report, and then print it to screen. The screen print creates a nicely formatted temp text file to copy/rename to a folder.

To make the text file nicely formatted, we would have a function that is called each time, which removes the unwanted text from the file, while retaining the desired text, and you maybe don't even need a temporary file, but can just create the xml.

...repeat the process over 30,000 times. I tried this using Send and Sleep, but it keeps getting off track somwhow.

Do the best you can, taking your time, and then post your code in this thread so we can see it.

If AutoIT could check for specific text on the screen after each Send I could effectively have it stop every time there was a problem.

We can write a handler for these situations.

Tell us the name of the legacy application. We might be able to find all kinds of documentation and help about it on the web.

Keep in touch jstump1 - I'm not the real AutoIt expert here or anything - I just use AutoIt to keep the mind busy (after "If you don't use it, you loose it!") :D

Edited by Squirrely1

Das Häschen benutzt Radar

Link to comment
Share on other sites

Hey jstump1, I just thought of something important here that I should say before I call it a day -- that legacy application that puts all its menus in a command console probably uses data files which are easily convert-able to any format you want. They might have a file extension that you don't recognize, but you might be able to open them in a regular text editor - as is.

Also, for certain, any program that a police department would have ever used to store the only copy of police reports must have a dumpdata parameter to it, such that you can just go:

LegacyApp.exe -dumpdata C:\MyNeedfulData.html

or some such command. :D

Das Häschen benutzt Radar

Link to comment
Share on other sites

OK Squirrely1 - I get it;) I'll start giving something close to full disclosure. For several reasons however I cannot post any of our program code, data, etc. I'll try to scrub anything I post pretty well.

I've used AutoIT for several things the last two years but nothing complicated. I'm a network admin that hasn't done any serious coding since college. I realized several years ago that writing code was not my passion, just an amusement from time to time. That amusement has turned into batch files, kix scripts, pstools, etc. mostly. This batch-conversion of police reports is the last project I was hoping to get done before I leave my job with a city in Michigan. I've already been working remotely from Kentucky for three months and it is time for myself and the city to move on within a few weeks. So I have a time constraint if I am going to do this.

The application I want to get the data out of is now called TIPSS (Titan Public Safety Systems), but it was formerly Clues. The Clues software had a DOS/Netware version and a Unix Version. Tiburon bought the Unix version just to get the technology and customers. They dumped the product. TIPSS is a small company with a core staff of a programmer and a retired cop. The programmer was new to CLUES, while the retired cop worked previously supporting the dos-based version of Clues. For the moment let's just assume they are out of the picture.

TIPSS is an application built using RAIMA databases (I think). There are several executables like dbc.exe, create.exe, repair.exe, etc. It is built off a product from Subject, Wills, and Company licensed to TIPSS. The data stored in the databases is encrypted. I think I may have access to the encryption key in a config file, but that's just an assumption from looking at the config file. The application encrypts all the data before storing it in a field and thus decrypts the field data after it is read from the database. There is no clear text other than the index fields. Case narratives are stored in some type of file format that is unknown to me. The application upgraded a few years ago to use Word-perfect 8.0 to edit the narratives with the help of a conversion program. The conversion program consists of a file called CV.EXE and a few supporting files. I am pretty sure I understand how CV.EXE works to convert the narratives. The databases are another animal though. It would be much more difficult for me to get the data directly out of the databases and then format it into a usable, human-readable report.

I don't have access to a data dictionary and the documentation available for CLUES/TIPSS only covers the end-user usage of the program - no documentation on the architecture of the product. Remember, I am dealing with what was a secure, advanced law enforcement application. Tech support from either company did not give out any info on the architecture of their application unless it was deemed absolutely necessary. We changed over to a new application suite three years ago and just use TIPSS for reference now. Our Document Management project started scanning police reports from 1980 and we just got to the years which the PD started using TIPSS. It will save a lot of time on manual indexing and add a great full-text search capability if I can just get those reports imported to our Docuware software electronically. All the reports still have to be scanned though to get an image of the official copies, plus scanned images of evidence etc.

My original idea was very simple - I like simple! It just doesn't work though - too simple. Too many skipped reports and no good way of knowing when/where problems arise. I played with sleep times for a day and there seems to be no way of getting it timed right, even when I put incredibly long sleep times in for everything. I tried replacing all the sleeps with WinWait and that works much worse. Too bad WinWait can't read the screen. When I provide text to be detected by WinWait it just sits there because it never sees it.

The FileMove command copies the temp report file (face sheets, narrative, and other documentation compiled into a complete report) for import into my document management system. This report is great because everyone knows how to read and interpret the report, so I don't want to change any formatting. All fields are in predictable locations - perfect for my document system.

Opt("WinWaitDelay",100)

Opt("WinTitleMatchMode",4)

Opt("WinDetectHiddenText",1)

Run('p:\reports.bat')

WinWait("C:\WINDOWS\system32\cmd.exe","")

If Not WinActive("C:\WINDOWS\system32\cmd.exe","") Then WinActivate("C:\WINDOWS\system32\cmd.exe","")

Sleep (500)

Dim $counter

$counter = "1"

Send("4{ENTER}")

Sleep (200)

Send("1{ENTER}")

Sleep (200)

Send("sel{ENTER}")

Sleep (200)

Send("{F1}19961{ENTER}")

Sleep (500)

Send("rep{ENTER}")

Sleep (2000)

Send("s{ENTER}")

Sleep (500)

Send("y{ENTER}")

Sleep (500)

Send("y{ENTER}")

Sleep (500)

Send("y{ENTER}")

Sleep (2000)

FileMove ( "g:\temp\*.prt", "g:\temp\reports\" & $counter & ".txt" )

Sleep (100)

Do

Send("e")

Sleep (500)

Send("y{ENTER}")

Sleep (2000)

$counter = $counter + 1

Send("next{ENTER}")

Sleep (2000)

Send("rep{ENTER}")

Sleep (2000)

Send("s{ENTER}")

Sleep (500)

Send("y{ENTER}")

Sleep (500)

Send("y{ENTER}")

Sleep (500)

Send("y{ENTER}")

Sleep (2500)

FileMove ( "g:\jstump\*.prt", "g:\jstump\reports\" & $counter & ".txt" )

Sleep (100)

Until $counter = "1000"

OK - any ideas on what approach I should start looking at given my time constraint? If possible I'd like to get the data rolling out of the application in a couple of weeks. I'm willing to do whatever within that time frame - even spend a bit of cash.

I appreciate the time anyone spent reading this long post!

James

Link to comment
Share on other sites

There is a way to automate the selecting of text an a cmd.exe window. Go to

Control Panel > Display Properties > Appearance tab > Effects button > uncheck "Hide underlined letters..."

Then, when you right-click on the cmd.exe title bar, you see which key to hit [ or in our case, Send() ] to make the desired action happen. If the cmd.exe window has been maximized [use WinSetState for that ] then you know where on the monitor that the title bar is. You can then use MouseClick("right", 400, 15) to automate a right-click of the titlebar. Then you just use Send() to get the appropriate actions: On my machine it is Send("es") to select everything in the window. With the right-click menu re-opened, you use Send("ey") to copy all the selected text to the clipboard.

Functions you can use to write from the clipboard to a file include:

ClipGet ( ) - returns, as to a variable, the contents of the clipboard.

FileWrite( filehandle or "filename", "line" ) - writes the "line" string (or contents of a variable) to the file. You should study this function as it appears in the Help file, and I can fix the code you write if it isn't working. You should think of using the filehandle method, wherein you use FileOpen near the beginning of your script, so that the file won't have to be opened and closed with each action, and maybe this way, you can get more records into each file you create.

Of course, to clear the contents of the cmd.exe window and start afresh with a new record, without that titlebar menu open, you Send("cls{ENTER}") which will leave only the command prompt showing in the window. Later in the script, you can use various commands to remove those command prompts from your final data files.

I worked on the code a little and the only substantial thing I changed are the If statement and the last line:

; Notes from jstump1:
; Here is a recap of what I want to accomplish:
; Launch the Police Records software (DOS window), navigate to the correct menu, open first report, and then print it 
; to screen. The screen print creates a nicely formatted temp text file to copy/rename to a folder. Go to the next report and
; repeat the process over 30,000 times. I tried this using Send and Sleep, but it keeps getting off track somwhow. I slow it to 
; a crawl then it mostly works. Once in a while though reports get skipped because something goes wrong...that just doesn't 
; work for 30,000 reports.

; Important Note - Computer must not be interefered with by any program that could automatically steal
;   window focus away from "C:\WINDOWS\system32\cmd.exe"

;Opt("WinWaitDelay", 100); line rem'd out
Opt("WinTitleMatchMode", 4)
Opt("WinDetectHiddenText", 1)
Global Const $CONSOLE_TITLE = "C:\WINDOWS\system32\cmd.exe"
Global $timestamp
Global $counter
Run('p:\reports.bat')
;WinWait("C:\WINDOWS\system32\cmd.exe"); rem'd out
Sleep(39000);wait 39 whole seconds to give time for machine to think ...
$timestamp = TimerInit()
WinActivate($CONSOLE_TITLE)
Sleep(300)
; This if statement was re-written ...
If Not WinActive($CONSOLE_TITLE) Then
    Do 
        Sleep(60)
; give the window 15 seconds to become active ...
    Until TimerDiff($timestamp) > 15000
    If Not WinActive($CONSOLE_TITLE) Then
; Exiting if window can't get focus, (it would be because of its "title") ...
        Exit
    EndIf
EndIf
Sleep(500)
Send("4{ENTER}")
Sleep(200)
Send("1{ENTER}")
Sleep(200)
Send("sel{ENTER}")
Sleep(200)
Send("{F1}")
Sleep(100)
Send("19961{ENTER}")
Sleep(500)
Send("rep{ENTER}")
Sleep(2000)
Send("s{ENTER}")
Sleep(500)
Send("y{ENTER}")
Sleep(500)
Send("y{ENTER}")
Sleep(500)
Send("y{ENTER}")
Sleep(2000)
$counter = "1"
; You could use something like "DataFile_" & $counter for a filename,
; instead of a file named just "1.txt" ...
FileMove("g:\temp\*.prt", "g:\temp\reports\" & $counter & ".txt")
Sleep(100)
Do
    Send("e")
    Sleep(500)
    Send("y{ENTER}")
    Sleep(2000)
    Send("next{ENTER}")
    Sleep(2000)
    Send("rep{ENTER}")
    Sleep(2000)
    Send("s{ENTER}")
    Sleep(500)
    Send("y{ENTER}")
    Sleep(500)
    Send("y{ENTER}")
    Sleep(500)
    Send("y{ENTER}")
    Sleep(2500)
    $counter += 1
;$filename = "DataFile_" & $counter & ".txt"
    FileMove("g:\jstump\*.prt", "g:\jstump\reports\" & $counter & ".txt")
    Sleep(100)
Until $counter = "50"; maybe do no more than 50 at a time till you code is perfected
; I suppose you have a way to make sure none of the 10,000 files or how ever many, 
; have the same name,
; even if they are on different disk drives or tapes?

I sent an email to a director of AutoIt named Valik, to point him to this thread in the forum.

But you can find many error handlers in the forums; since you are working with the Send function, you could browse the Games section of the AutoIt forums for some ideas.

But if you could be specific about which part of the code needs an error handler, it would help me to write one that you don't know how to write.

Because there is so little to this code, which you say almost works, I am optimistic that you can finish this project in time. No need for a thousand elaborate lines of code here.

Probably you want to stick with the strategy you have going here, using Send; but make sure the computer is defragged and its system has been maintenanced and is working well before you execute the script.

Edited by Squirrely1

Das Häschen benutzt Radar

Link to comment
Share on other sites

But if you could be specific about which part of the code needs an error handler, it would help me to write one that you don't know how to write.

Because there is so little to this code, which you say almost works, I am optimistic that you can finish this project in time. No need for a thousand elaborate lines of code here.

Probably you want to stick with the strategy you have going here, using Send; but make sure the computer is defragged and its system has been maintenanced and is working well before you execute the script.

Thanks a lot for the ideas and better-looking code! I need to check for unexpected text in at least two places. Sometimes I get an untrapped data share error that bombs out the program and other times the last step in creating the report causes a problem. I ran a rebuild on the database files (they are on a server) to get rid of any indexing issues I could.

I'm looking at this product for an OCR solution. Maybe this could fix my error handling and timing issues. Because of time constraints (and because I'm a network admin) I'd rather use pre-existing tools and functions to overcome my problems when possible.

http://www.structurise.com/textract/

Where I need to handle errors:

After the last send in the loop I need to make sure there is some expected screen text, plus I need to make sure a .prt file has been created. I use the FileMove because the previous file may or may not be overwritten. The temp .prt file can go by various names, so I just move/rename it after every generated report. Unless I can use an OCR function to read the case number (displayed on screen after each Send ("next{ENTER}")) I don't have a good way of naming the files. I was planning on changing the counter to use higher numbers whenever it quit on me to keep the filenames unique. I can look at the last report to see the case number to start with again. I'm starting with case number 19961 right now - 1996 case #1.

Send("y{ENTER}")
   Sleep(2500)
  $counter += 1
;$filename = "DataFile_" & $counter & ".txt"
    FileMove("g:\jstump\*.prt", "g:\jstump\reports\" & $counter & ".txt")
    Sleep(100)

Right here is where I need to check for the untrapped data share error and get the script to stop. It would also be the place to read the screen and get the case number. A case number filename would be cool because it is one less indexing step for the document management software.

Send("next{ENTER}")
  Sleep(2000)

James

Link to comment
Share on other sites

OK - so maybe I am particularly dumb on this project.

I just realized that by checking to see if my temp text file is there I can just stop the script if it is not. At that point I can have it print the counter number on screen and warn me that it has stopped working. I can take care of the issue, change the report number and counter number to start with, and then move on. This is gonna work now without having to read the screen, its just going to take a very long time because the script runs so slow. I just tested it...

I bet it could be a lot faster if I remove all or most of the sleeps and use the screen copy function Squirrely1 wrote along with a function to search for text within the temp text files it produces. I plan on still doing that - I imagine that will be done before my script ever gets near finishing anyway.

James

Link to comment
Share on other sites

@james -

Don't use the version of Write_Data() that I posted above, instead the following script HAS been tested:

; This script demonstrates the usefulness of the function Write_Data(), which puts everything in the cmd.exe
; window onto the clipboard and writes the contents of the clipboard to the passed file.
; This script tested out okay on Windows XP with 2.2 GHz processor
; The function by itself, requires that $CONSOLE_TITLE be defined before it is called.
#region Test_harness
Opt("WinTitleMatchMode", 2)

Global Const $CONSOLE_TITLE = "C:\WINDOWS\system32\cmd.exe"
Global $OurDataFile = @MyDocumentsDir & "\ReportData_1.txt"

Run($CONSOLE_TITLE)
Sleep(1280)

Write_Data($OurDataFile)

WinClose($CONSOLE_TITLE)

ShellExecute($OurDataFile)
Sleep(1000)
#endregion Test_harness

Func Write_Data($myFile)
    Local $allText
    WinMove($CONSOLE_TITLE, "", 0, 0, 1280, @DesktopHeight * 0.94)
    Sleep(120)
    WinActivate($CONSOLE_TITLE)
    Sleep(210)
    MouseClick("right", 400, 7, 1, 0)
    Send("es")
    Sleep(80)
    MouseClick("right", 300, 7, 1, 0)
    Sleep(210)
    Send("ey")
    Sleep(200)
    $allText = ClipGet()
    Sleep(210)
    FileWrite($myFile, $allText)
    Sleep(1200)
EndFunc;==>Write_Data

We got your code man. :D

But I am going to experiment with the StdoutRead method anyway, because ame1011 recommended it and it has been recommended elsewhere.

Edited by Squirrely1

Das Häschen benutzt Radar

Link to comment
Share on other sites

I just realized that by checking to see if my temp text file is there I can just stop the script if it is not.

I'm happy to hear the optimism you have about event handling. :D Edited by Squirrely1

Das Häschen benutzt Radar

Link to comment
Share on other sites

Ok here is what I have ever seen on the forums that makes StdoutRead look like a good option for you james:

; Author:   ptrex
; Demonstrates the use of StdoutRead and StderrRead ...

$sCommand= Run("netstat -a", @SystemDir, @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)

While 1
    $line = StdoutRead($sCommand)
    If @error Then ExitLoop
    ConsoleWrite( "STDOUT read:" & $line & @LF)
Wend

While 1
    $line = StderrRead($sCommand)
    If @error Then ExitLoop
    ConsoleWrite( "STDERR read:" & $line & @LF) 
Wend

Instead of ConsoleWrite, you would use FileWrite in the loops above, and it would be up to you to decide about whether to write the information in StderrRead to the same data files that you use for the StdoutRead loop. ConsoleWrite outputs to that pane that pops up at the bottom of SciTe.

You would also replace the file in the Run() function in this code. You do need the return value from the Run command if you use this code. And you probably will need a Sleep(1800) right after that Run command.

This method really could work better than the window automation method I posted above. But I can in no wise test it here myself, of course, because I don't have TIPPS Clues installed.

You have some Send() commands that open menus - in this code, you would put those in just before the first "While 1", but if you need some of the information that is returned after opening one menu and before opening the next, you only maybe need to put in another couple of While-Wend loops, just like the first ones you use.

Edited by Squirrely1

Das Häschen benutzt Radar

Link to comment
Share on other sites

james - I found a way to make all those data filenames to be the same length, and this safe code demonstrates what it does:

$counter = 1
$filename = "ReportData" & StringFormat("%05u%s", $counter) & ".txt"
MsgBox(0, "", $filename)
; let's say the counter incremented many times by now ...
$counter += 273
$filename = "ReportData" & StringFormat("%05u%s", $counter) & ".txt"
MsgBox(0, "", $filename)
; let's say the counter incremented many times by now ...
$counter += 2200
$filename = "ReportData" & StringFormat("%05u%s", $counter) & ".txt"
MsgBox(0, "", $filename)
Edited by Squirrely1

Das Häschen benutzt Radar

Link to comment
Share on other sites

Hello Everyone -

A special thanks for your help Squirrely1...I now have a working script. It is slow but it is chunking away at the job without anyone's involvement. It is not perfect, but maybe it will be better the time the job is done.

Unfortunately I have not found a good way to make StdOutRead work for me yet.

The function posted earlier called "WriteData" before worked well in theory but not in my actual application. I believe there is a bug in Windows XP SP2 (at least). The edit-select all function worked in testing, selecting all 25 lines of text and copying them to the clipboard. Once my application was running edit-select all would only select and copy 23 lines of text. I changed the function in my implementation to use edit-mark and mouseclicks instead of 'select all'.

; Notes from jstump1:
; Here is a recap of what I want to accomplish:
; Launch the Police Records software (DOS window), navigate to the correct menu, open first report, and then print it
; to screen. The screen print creates a nicely formatted temp text file to copy/rename to a folder. Go to the next report and
; repeat the process until all cases are processed.
; Important Note - Computer must not be interefered with by any program that could automatically steal
; window focus away from "C:\WINDOWS\system32\cmd.exe"
; Case number info:
; 1996 1 TO 3968
; 1997 1 TO 4003
; 1998 1 TO 3839
; 1999 1 TO 3636
; 2000 1 TO 3304
; 2001 1 TO 3586
; 2002 1 TO 3779
; 2003 1 TO 3576
; 2004 1 TO 1638

Opt("WinDetectHiddenText", 1)
Opt("WinTitleMatchMode", 2)
Global Const $CONSOLE_TITLE = "C:\WINDOWS\system32\cmd.exe"
Global $timestamp
Global $ScreenText
Global $FullCaseNumber;a composite of the year, subcasenumber, and a letter designator for report type read from the screen
Global $Year;years of cases are from 1996 to 2004 - setting this for each year
Global $SubCaseNumber;each year has a certain number of cases
Global $ScreenSubCaseNumber;sub case number read from the screen

Run('p:\reports.bat')
Sleep(2000);wait for the program to be ready
$timestamp = TimerInit()
WinActivate($CONSOLE_TITLE)
Sleep(300)
; This if statement was re-written ...
If Not WinActive($CONSOLE_TITLE) Then
    Do
        Sleep(60)
    ; give the window 15 seconds to become active ...
    Until TimerDiff($timestamp) > 15000
    If Not WinActive($CONSOLE_TITLE) Then
    ; Exiting if window can't get focus, (it would be because of its "title") ...
        Exit
    EndIf
EndIf
$Year = 1996;set year to generate reports from
$SubCaseNumber = 353;set starting case number
Sleep(500)
Send("4{ENTER}")
Sleep(200)
Send("1{ENTER}")
Sleep(200)
Send("sel{ENTER}")
Sleep(200)
Send("{F1}")
Sleep(100)
Send($Year & $SubCaseNumber & "{ENTER}")
Sleep(1000)
Send("rep{ENTER}")
Sleep(1000)
Send("s{ENTER}")
Sleep(500)
Send("y{ENTER}")
Sleep(1000)
Send("y{ENTER}")
Sleep(1000)
Send("y{ENTER}")
Sleep(3500)
ReadDosWindow();read the report Window text into a variable called $ScreenText
$FullCaseNumber = StringMid($ScreenText, 64, 13);read the case number into a variable
FileMove("g:\jstump\*.prt", "g:\jstump\reports\" & $FullCaseNumber & ".txt");name the files CaseNumber.txt
Sleep(500)
Do
    WinActivate($CONSOLE_TITLE);re-focus to console just in case
    NextReport()
    ReadDosWindow()
    $ScreenSubCaseNumber = StringMid($ScreenText, 69, 6);read part of the case number for an error test
    $FullCaseNumber = StringMid($ScreenText, 64, 13);read the case number into a variable
    If ($SubCaseNumber = $ScreenSubCaseNumber) And (FileFindFirstFile("g:\jstump\*.prt") <> -1) Then
    ;make sure the case number on screen is what is expected, otherwise fix it
        FileMove("g:\jstump\*.prt", "g:\jstump\reports\" & $FullCaseNumber & ".txt")
        Sleep(100)
        $SubCaseNumber += 1
    Else
        Do
            FixReport()
        Until ($SubCaseNumber = $ScreenSubCaseNumber) And (FileExists("g:\jstump\reports\" & $FullCaseNumber & ".txt"))
        $SubCaseNumber += 1
    EndIf
Until $SubCaseNumber = 3968;set ending case number for the year
Exit

Func NextReport();go to the next report
    Send("e")
    Sleep(500)
    Send("y{ENTER}")
    Sleep(2000)
    Send("sel{ENTER}")
    Sleep(200)
    Send("{F1}")
    Sleep(100)
    Send($Year & $SubCaseNumber & "{ENTER}")
    Sleep(1000)
    Send("rep{ENTER}")
    Sleep(500)
    Send("s{ENTER}")
    Sleep(1000)
    Send("Y{ENTER}")
    Sleep(1000)
    Send("Y{ENTER}")
    Sleep(1000)
    Send("Y{ENTER}")
    Sleep(3000)
EndFunc  ;==>NextReport

Func ReadDosWindow(); copy the window text into a variable called $ScreenText
    WinMove($CONSOLE_TITLE, "", 0, 0, 1280, @DesktopHeight * 0.94)
    Sleep(120)
    WinActivate($CONSOLE_TITLE)
    Sleep(210)
    MouseClick("right", 400, 7, 1, 0)
    Send("ek")
    MouseMove(8, 35)
    MouseDown("left")
    MouseMove(643, 324)
    MouseUp("left")
    Sleep(80)
    MouseClick("right", 400, 7, 1, 0)
    Sleep(210)
    Send("ey")
    Sleep(200)
    $ScreenText = ClipGet()
EndFunc  ;==>ReadDosWindow

Func FixReport();get back on track by reading the screen and executing the right command
    ReadDosWindow()
    $ScreenSubCaseNumber = StringMid($ScreenText, 202, 6);read possible subcase number from screen into a variable
;MsgBox(1, "Window Read info", $ScreenText)
    Select
        Case StringInStr($ScreenText, "Convert narrative before printing? (Y/N):") <> 0
            Send("Y{ENTER}")
            Sleep(3500)
            ReadDosWindow()
            $ScreenSubCaseNumber = StringMid($ScreenText, 69, 6);read part of the case number for an error test
            $FullCaseNumber = StringMid($ScreenText, 64, 13);read the case number into a variable
            If ($SubCaseNumber = $ScreenSubCaseNumber) And (FileFindFirstFile("g:\jstump\*.prt") <> -1) Then
            ;make sure the case number on screen is what is expected, otherwise don't copy it
                FileMove("g:\jstump\*.prt", "g:\jstump\reports\" & $FullCaseNumber & ".txt")
                Sleep(100)
            Else
            EndIf
        Case StringInStr($ScreenText, "incident not found.") <> 0
            FileCopy("g:\jstump\dummyfile.txt", "g:\jstump\reports\missing_report_" & $Year & "-" & $SubCaseNumber & ".txt")
            $FullCaseNumber = "missing_report_" & $Year & "-" & $SubCaseNumber
            $ScreenSubCaseNumber = $SubCaseNumber
        Case StringInStr($ScreenText, "Enter a numeric incident number.") <> 0
            Send("{F1}")
            Sleep(100)
            Send($Year & $SubCaseNumber & "{ENTER}")
            Sleep(800)
        Case StringInStr($ScreenText, "F=Find, P=Print, E=Exit") <> 0
            Send("e")
            Sleep(500)
        Case StringInStr($ScreenText, "Exit report? (Y/N) :") <> 0
            Send("y{ENTER}")
            Sleep(2000)
        Case $ScreenSubCaseNumber = $SubCaseNumber
            Send("rep{ENTER}")
            Sleep(500)
        Case StringInStr($ScreenText, "the list below......... _____") <> 0
            Send("sel{ENTER}")
            Sleep(200)
            Send("{F1}")
            Sleep(100)
            Send($Year & $SubCaseNumber & "{ENTER}")
            Sleep(800)
        Case StringInStr($ScreenText, "(1-99), (S)creen, or (F3)-cancel print") <> 0
            Send("s{ENTER}")
            Sleep(500)
        Case StringInStr($ScreenText, "Print Incident Report face sheets? (Y/N):") <> 0
            Send("y{ENTER}")
            Sleep(500)
        Case StringInStr($ScreenText, "Print the incident narrative? (Y/N):") <> 0
            Send("y{ENTER}")
            Sleep(500)
        Case StringInStr($ScreenText, "UNTRAPPED DATASHARE ERROR") <> 0
            MsgBox(1, $SubCaseNumber & " " & $FullCaseNumber, "Internal Case #, Read Case #, Major error, stopping")
            Exit
    EndSelect
EndFunc  ;==>FixReport

Func DebuggingInfo()
    MsgBox(1, "Window Read info", $ScreenText)
    MsgBox(1, "FullCaseNumber", $FullCaseNumber)
;MsgBox(1, "ScreenSubCaseNumber location", StringInStr($ScreenText, $SubCaseNumber))
;use above code to find location of text on the screen
    $ScreenSubCaseNumber = StringMid($ScreenText, 202, 6)
    MsgBox(1, "Is case number right for main screen?", $ScreenSubCaseNumber)
EndFunc  ;==>DebuggingInfo
Link to comment
Share on other sites

jstump1 -

On cursory inspection, it looks as though line 70 is in the wrong place and that it belongs somewhere lower down in the script. And that line does not just rename but moves a file, which you haven't written to yet. I could imagine that this line has to do with the last file made, as you iterate through the loop, but this technique seems disorganized, and could cost you something. I say move the line to somewhere more appropriate:

FileMove("g:\jstump\*.prt", "g:\jstump\reports\" & $FullCaseNumber & ".txt");name the files CaseNumber.txt

But if I am incorrect about what that line was supposed to do, and you were just creating the full file path for the file you could use this in its place:

$myFullfilepath = "g:\jstump\reports\" & $FullCaseNumber & ".txt"

The problem with that line looks to be that the file "g:\jstump\*.prt" is not created by any code before line 70, and look for the same problem at line 80. Note - ReadDosWindow() does not create the file.

I am using an OEM version of Windows XP SP2 on a four-year old Compaq computer having a 2.2GHz processor and 384MB of RAM. If your computer is running slowly, running cleanmgr.exe from the Run box on the Start Menu would help, then run the file pagedfrg.exe that is in this:

Cleaning the clutter out of the system once every few months, and defragging the paging file - also called the swap file - can really speed up you machine and help it to avoid errors.

Judging by a look at your code so far, it seems you are really picking up on this squirrely scripting language. Keep after it, and remember, optimism can pay off for the patient, especially if they feel pressed for time.

Edited by Squirrely1

Das Häschen benutzt Radar

Link to comment
Share on other sites

Be more careful about how you document your code - a description of what you want a function to accomplish is not necessarily what it does accomplish - you need to go back over your functions again and write down what you want to to do and what they do accomplish as well.

I noticed that in your FixReport function, you have some pretty complicated Case structure. I never want to have to refer you to verse 6 of the Official Canon of Valik, listed below :) this case structure looks like your are trying to bite off more that you can chew in one Select statement - consider what value there might be in breaking up the Select statement. You know you can write a function that just calls your other functions, like this one:

Select
    Case Not $squirrely
        MyFunction01()
        
    Case $TooSquirrely
        MyFunction01()
        
EndSelect
Func MyFunction01()
    Squirrely_Funtion02()
    Squirrely_Funtion03()
EndFunc
Func Squirrely_Funtion02()
    Sleep(5000)
    Switch $squrlyBiz
        Case $highly
; ...
        Case $somewhat
; ...
    EndSwitch
EndFunc
Func Squirrely_Funtion03()
    Exit
EndFunc

Study up on the term modularity - if your code gets too complicated, it can slow down the code production process.

As regards StdoutRead and StderrRead, I heard that some .exe's won't create anything readable by StderrRead. You might still experiment with StdoutRead.

If an experiment works of any kind, post it. And any method that worked in any experiment, can most likely be made to work in your final script. You might want to use some debugging here. I throw in a MsgBox temporarily sometimes, when ConsoleWrite might work better to check the value of a variable or the return value of a function. Some AutoIt functions return a 1 if the function completed successfully, and 0 if otherwise.

One thing I found out about a cmd.exe window is that it has a limited buffer length, that you can increase. If you put a shortcut to the Command Prompt on the desktop, right-click it and choose properties, click on the Options tab, on the left you see Buffer size and length. Try buffer size 90 and buffer length of 8, something higher and click OK.

For launching the .bat file, you might have to use the Command Prompt that was launched from this shortcut, to get the advantage of the increased buffer size. There is a way to do this ...

In Disk Cleanup, which you get to when you launch cleanmgr.exe, choose drive C: or the dir that the Windows system is on, after the "calculating..." is done - which might take a while, click on the "More Options" tab at the top of the window, and the third button down, under System Resotre there is a "Cleanup" button. Hit that and clean out those old restore points which it would be one chance in about 20 that they could ever need those files. Give it time to finish, and then do a defrag of C: to get your system working better. After a reboot or two, and givin the machine time to think while online with Automatic Updates set to Fully Automatic, these steps can really speed up your system especially if they have never been done since Windows was last installed or since a service pack was last installed or since a System Recovery operation was ever done. But if the drive volume that the Window system is installed on is more than 60% full, it may not be able to be properly defragmented.

Edited by Squirrely1

Das Häschen benutzt Radar

Link to comment
Share on other sites

On cursory inspection, it looks as though line 70 is in the wrong place and that it belongs somewhere lower down in the script. And that line does not just rename but moves a file, which you haven't written to yet. I could imagine that this line has to do with the last file made, as you iterate through the loop, but this technique seems disorganized, and could cost you something. I say move the line to somewhere more appropriate:

Squirrely1 - This line is where it needs to be because this is where the program creates its temp file that I move/rename. I don't know the name of the file, just the extension, at the time. I know I could use FileFindFirst() to get the actual name, but that seems overkill because there exists only 1 temp file given the way the program and script work.

One thing I found out about a cmd.exe window is that it has a limited buffer length, that you can increase. If you put a shortcut to the Command Prompt on the desktop, right-click it and choose properties, click on the Options tab, on the left you see Buffer size and length. Try buffer size 90 and buffer length of 8, something higher and click OK.

You probably want to edit what you wrote previously. I was confused about what you said because you mentioned a buffer length of 8 - which made me think you were actually talking about the command history, not screen buffer size. The cmd.exe screen buffer size's default in windows XP is a width of 80 and a height of 300, with most programs having a default of 80 columns and 25 lines. For TIPSS I normally set the screen buffer width to 80 and the height to 25. If it is set any larger than that you have blank lines below the window. Anyway, no matter what is used for the screen buffer an edit-select all in TIPSS only selects 23 lines of the screen :)

I'll be more careful about my documentation that I post on the forum - I should have edited it before posting. I wasn't thinking about the other people reading it when I posted the script. The info was mostly just for me, as this is a one-time script that will not be used by me or anyone else at the City I work for again.

I noticed that in your FixReport function, you have some pretty complicated Case structure. I never want to have to refer you to verse 6 of the Official Canon of Valik,

Yeah - it started out small and got bigger as I ran into more problems. This was the most readable structure I could wrap my head around at the time. Also, read below for more info why I used the Case. It works pretty good though. I have already processed more than 10,000 police reports as I write this.

As regards StdoutRead and StderrRead, I heard that some .exe's won't create anything readable by StderrRead. You might still experiment with StdoutRead.

I have complained about system performance in the past - specifically in regards to running TIPSS. As it turns out I think my original statements about the system having unpredictable response times is inaccurate. In reality is doesn't matter how long the Sleep() times are - sometimes Send()s just don't show up in the application. I know because I have tried using really long sleep times. If the application actually received every Send() properly I wouldn't need that crazy Case satement. And I would be able to use the Std*() functions. Since not all Send()s get there I can not enter, navigate, and exit the program reliably. In the help file it states "stream of a previously run child process" as the opening explanation for each Std*() function. So unless there is a way to look at the stream continuously (evaluating the text stream) while my Sleeps and Sends are going on I have to stick with the method of edit-mark, edit-copy to evaluate what is happening with the app.

You are preaching to the choir when it comes to optimizing system performance...My favorite tools to get a system to run faster are defrag and HijackThis! Windows is usually pretty good after properly using disk cleanup, chkdsk /f, defrag, and HijackThis!. I always check the location and usage of the paging file along with available RAM too. As you say the amount of free disk space can be an issue too. For Pcs that are still running slow I usually use Process Explorer to help find the culprit. Sometimes though a slow PC is the sign of a failing hard drive. Windows will attempt to read/write a sector many times before reporting an error. The best utility I know of to diagnose (and possibly repair/recover) a hard drive is is SpinRite.

TIPSS resides on a network server - I use Diskkeeper on the servers to keep them chunking along without getting too fragged up. The particular server TIPSS is on performs very well. Its usage is low and response times are good.

Now I am working on a script to remove a leading 0 from the case number that is inside each report/case. It is in the first line of each report. I found out after-the-fact that our new software uses a 5-digit case number. TIPSS used a 6-digit case number. Previous typewritten reports used a 4-digit case number. Now it is my job to get eveything in the Document management system on a 5-digit case number and convert the description number in the report to something called a file class number :) If I have issues I'll open another forum item.

Thanks a lot Squirrely1

~jstump1

Link to comment
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
 Share

  • Recently Browsing   0 members

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