Saturday 1 March 2008

autoitscripts-a

Tutorial - HelloWorld

This tutorial explains the basics of creating an AutoIt script and running it. The tutorial assumes that you have already fully installed AutoIt v3 using the supplied installer.

First open a folder where you wish to create the script. Right-click in the folder and select New / AutoIt v3 Script.

A new file will be created, immediately allowing you to rename it to something more appropriate. Change 'New AutoIt v3 Script' to 'helloworld', leaving the '.au3' in the name intact if it is visible.

Now we have created the script file we want to edit it to make it do something useful. Right-click on helloworld.au3 and select Edit Script.

The SciTE editor will open and you will see something like this:

The code you see is simply some comments that you can use to organise your scripts. The lines that start with a semi-colon ; and are treated as comments (ignored). ; is similar to the REM statement in a DOS batch file.

Now we want to tell AutoIt to display a message box - this is done with the MsgBox function.

At the bottom of the file type the following:

MsgBox(0, "Tutorial", "Hello World!")

All functions take parameters, MsgBox takes three - a flag, a title and a message. The flag is a number that changes the way MsgBox displays - we will use 0 for now. The title and message are both string parameters - when using strings in AutoIt surround the text in double or single quotes. "This is some text" or 'This is some text' - both will work fine.

Now save the script and close the editor. You've just written your very first AutoIt script! To run the script simply double-click the helloworld.au3 file (you may also right-click the file and select Run Script) .

You should see this:

Now, let's look at the flag parameter for the MsgBox function again. From the manual page we can see various values listed which change the way MsgBox displays. The value of 0 simply shows a simple message box with an OK button. A value of 64 displays the message box with an information icon.

Edit the script again and change the 0 to 64 so you have:

MsgBox(64, "Tutorial", "Hello World!")

Run the script and you will see:

Experiment with the various flag values to see what kind of results you can get. Remember, if you want to use more than one flag value then simply add the required values together.

Tutorial - Notepad

This tutorial explains how to automate the opening of Notepad, automatically type some text and then close Notepad. It is assumed that you are already familiar with creating and running AutoIt scripts as shown in the HelloWorld tutorial.

First create an empty script called npad.au3 and then edit the file (using Notepad or SciTe as you prefer).

The first thing we need to know is the name of the Notepad executable. It is notepad.exe - you can get this information by looking at the properties of the Notepad shortcut icon in the Start Menu. To execute Notepad we use the AutoIt Run function. This function simply launches a given executable and then continues.

Type in the first line of script as:

Run("notepad.exe")

Run the script - if all goes well then a new instance of Notepad should open.

When automating applications AutoIt can check for window title so that it knows which window it should work with. With Notepad the window title is obviously Untitled - Notepad. AutoIt is case-sensitive when using window titles so you must get the title exactly right - the best way to do this is to use the AutoIt Window Information Tool. Run the Information Tool from Start Menu \ AutoIt v3 \ AutoIt Window Info.

With the Info Tool open click on the newly opened Notepad window to activate it; the Info Tool will give you information about it. The information we are interested in is the window title.

Highlight the title in the AutoIt Info Tool window and press CTRL-C to copy it to the clipboard - we can then paste the title into our script without fear of misspelling it.

After Running a copy of Notepad we need to wait for it to appear and become active before we send any keystrokes. We can wait for a window using the WinWaitActive function. Most window-related functions in AutoIt take a window title as a parameter.

Enter the following as the second line in the script (use CTRL-V or Edit\Paste to paste our window title from the clipboard).

WinWaitActive("Untitled - Notepad")

After we are sure the Notepad window is visible we want to type in some text. This is done with the Send function.

Add this line to our script.

Send("This is some text.")

The entire script will now look like this:

Run("notepad.exe")
WinWaitActive("Untitled - Notepad")
Send("This is some text.")

Close the copy of Notepad that we previously opened (you will need to do this every time you run the script otherwise you will end up with lots of copies running!). Run the script.

You should see Notepad open, and then some text will magically appear!


Next we want to close notepad, we can do this with the WinClose function.

WinClose("Untitled - Notepad")

When Notepad tries to close you will get a message asking you to save the changes. Use the Window Info Tool to get details of the dialog that has popped up so that we can respond to it :)

So, we add a line to wait for this dialog to become active (we will also use the window text to make the function more reliable and to distinguish this new window from the original Notepad window):

WinWaitActive("Notepad", "Do you want to save")

Next we want to automatically press Alt-N to select the No/Don't save button (Underlined letters in windows usually indicate that you can use the ALT key and that letter as a keyboard shortcut). In the Send function to send an ALT key we use ! .

Send("!n")

Our complete script now looks like this:

Run("notepad.exe")
WinWaitActive("Untitled - Notepad")
Send("This is some text.")
WinClose("Untitled - Notepad")
WinWaitActive("Notepad", "Do you want to save")
Send("!n")

Run the script and you will see Notepad open, some text appear, then close! You should be able to use the techniques learned in this tutorial to automate many other applications.

Tutorial - WinZip

This tutorial explains how to automate the installation of WinZip 9 SR-1. It is assumed that you are already familiar with creating and running AutoIt scripts and the use of the AutoIt Window Information Tool to read window titles and text, as shown in the HelloWorld and Notepad tutorials.

The WinZip installation consists of approximately 10 dialog boxes that you must click buttons (usually Next) to continue. We are going to write a script that simply waits for these dialog boxes to appear and then clicks the appropriate buttons. As usual with these types of installations the window title of each dialog is the same (WinZip Setup) so we must use window text to tell the difference between windows. Screenshots of each dialog will be provided and you can click on the picture to see the Information Tool output for that dialog.

First create a directory that we will use for the WinZip installer and our script file. Copy the WinZip installer to this directory and create a blank script called winzipinstall.au3.

We will now run through the installation manually and write the script as we go. The script lines to automate each dialog will be shown after each picture (remember to click on the picture to see the AutoIt Window Information Tool output). You can also look at the completed script for reference.

The first script line is easy, we want to run the winzip90.exe installer. So the first line is:

Run("winzip90.exe")

The first dialog will pop-up:

We need to wait for this window to popup and become active, and then we need to send the keystroke ALT-s to click the Setup button. The resulting script lines are:

WinWaitActive("WinZip® 9.0 SR-1 Setup", "&Setup")
Send("!s")

(Remember to click on the picture to see the AutoIt Window Information Tool output, this is especially important as the title contains the special (R) character which would be difficult to type).

The installation location dialog will appear next:

We need to wait for this screen to be active and then click ENTER to accept the install location. The script lines are:

WinWaitActive("WinZip Setup", "into the following folder")
Send("{ENTER}")

The WinZip Features dialog will appear next:

Notice that this window has exactly the same title as the first of WinZip Setup - in fact all the dialogs in the setup have this title! In order to tell the difference between these windows we must also use the window text - on each screen try to pick the most unique text you can. In this case I've chosen WinZip features include. After the window has appeared we will want to press ALT-n:

WinWaitActive("WinZip Setup", "WinZip features include")
Send("!n")

The License dialog will appear next:

Wait for the window to appear and then press ALT-y to accept the agreement:

WinWaitActive("License Agreement")
Send("!y")

Setup continues in a similar fashion for a few dialogs. The picture of each dialog is shown along with the script lines needed to automate it.

WinWaitActive("WinZip Setup", "Quick Start Guide")
Send("!n")

WinWaitActive("WinZip Setup", "switch between the two interfaces")
Send("!c")
Send("!n")

WinWaitActive("WinZip Setup", "&Express setup (recommended)")
Send("!e")
Send("!n")

WinWaitActive("WinZip Setup", "WinZip needs to associate itself with your archives")
Send("!n")

This is the final dialog of the setup. Notice that the Finish button doesn't have a keyboard shortcut - luckily it is the default button on this dialog so we can just press ENTER to select it. If this were not the case then we would have to TAB between controls or better yet use the ControlClick function.

WinWaitActive("WinZip Setup", "Thank you for installing this evaluation version")
Send("{ENTER}")

After installation WinZip will automatically start:

We simply wait for the main WinZip window to appear and then close it with the WinClose function.

WinWaitActive("WinZip (Evaluation Version)")
WinClose("WinZip (Evaluation Version)")

Here is the completed script - note that I have commented each dialog separately which makes it easier to follow and to change in the future (the next version of WinZip may be slightly different).

And that's it! Run the winzipinstaller.au3 script and watch as WinZip is installed in just a few seconds! The techniques used in this tutorial can be used to automate the installation of most programs.

As an exercise for the reader, try and redo the script but instead of using the Send function (which sends keys to the active window) to click on buttons try and use ControlClick instead which can be much more reliable. You will need to read up on Controls to do this.

Tutorial - Regular Expression

Here's a smallish guide on unravelling the seeming mysteries of StringRegExp().

StringRegExp( "test", "pattern" [, flag ] )

"test" = The string to search through for matches.
"pattern" = A string consisting of certain key characters that let the function know PRECISELY what you want to match. No ifs, ands, or buts.. it's a match or it isn't.
flag[optional] = Tells the function if you just want to know if the "pattern" is found, or if you want it to return the first match, or if you want it to return all the matches in the "test" string.

The Very Basics

As you may have figured out, the "pattern" string is the only difficult part of calling a StringRegExp() (forthwith: SRE). I find it best to think of patterns as telling the function to match a string character by character. There are different ways to find a certain character: If you want to match the string "test", that should be simple enough. You want to tell SRE to first search the string for a "t". If it finds one, then it assumes it has a match, and the rest of the pattern is used to try to prove that what it's found is not a match. So, if the next character is an "e", it could still be a match. Let's say the next letter is an "x". SRE knows immediately that it hasn't found a match because the third character you tell it to look for is an "s".

Example 1

MsgBox(0, "SRE Example 1 Result", StringRegExp("text", 'test'))

In this example, the message box should read "0", which means the pattern "test" was not found in the test string "text". I know this seems pretty simple, but now you know why it wasn't found.

The next way of specifying a pattern is by using a set ("[ ... ]"). You can equate a set to the logic function "OR". Let's use the previous Example. We want to find either the string "test" or the string "text". So, the way I start looking for a pattern is to think like SRE would think: The first character I want to match is "t", then the letter "e", this is the same for both strings we want to match. Now we want to match "s" OR "x", so we can use a set as a substitute: "[sx]" means match an "s" or an "x". Then the last letter is a "t" again.

Example 2

MsgBox(0, "SRE Example 2 Result", StringRegExp("text", 'te[sx]t'))
MsgBox(0, "SRE Example 2 Result", StringRegExp("test", 'te[sx]t'))

These should both provide the result "1", because the pattern should match both "test" and "text".

You can also specify how many times to match each character by using "{number of matches}" or you can specify a range by using "{min, max}". The first example below is redundant, but shows what I mean:

Example 3

MsgBox(0, "SRE Example 3 Result", StringRegExp("text", 't{1}e{1}[sx]{1}t{1}'))
MsgBox(0, "SRE Example 3 Result", StringRegExp("aaaabbbbcccc", 'b{4}'))



The Not-So Basics

Right now you're probably thinking "Isn't this just a glorified StringInStr() function?". Well, using a "flag" value of 0, most of the time you're right. But SRE is much more powerful than that. As you use SRE's more and more, you'll find you might know less and less about the type of pattern you are looking for. There are ways to be less and less specific about each character you wish to specify in the pattern. Take, for example, a line from the chat log of a game: "Gnarly Monster hits you for 18 damage." You want to find out how much damage Gnarly Monster hit you for. Well, you can't use StringInStr() because you aren't looking for "18", you're looking for "????", where ? could be any digit.

Here's how I would assemble this pattern. Look at what you do and do not know about what you want to find:
1) You know that it will ALWAYS contain nothing but digits.
2) You know that it will SOMETIMES be 2 characters long.
2a) You know from playing the game that the maximum damage a monster can do is 999.
2b) You know that the minimum damage a monster can do is 0.
3) You know that it will ALWAYS be between 1 and 3 characters long.
4) You know that there are no other digits in the test string.

At this point, I'd like to introduce the FLAG value of "1" and the grouping characters "()". The flag value of "1" means that SRE will not only match your pattern, but also return an array, with each element of the array consisting of a captured "group" of characters. So without veering off course too much, take this example:

Example 4

$asResult = StringRegExp("This is a test example", '(test)', 1)
If @error == 0 Then
MsgBox(0, "SRE Example 4 Result", $asResult[0])
EndIf
$asResult = StringRegExp("This is a test example", '(te)(st)', 1)
If @error == 0 Then
MsgBox(0, "SRE Example 4 Result", $asResult[0] & "," & $asResult[1])
EndIf

So, first the pattern must match somewhere in the test string. If it does, then SRE is told to "capture" any groups ("()") and store them in the return array. You can use multiple captures, as demonstrated by the second piece of code in Example 4.

Ok, back to the Gnarly Monster. Now that we know how to "capture" text, let's construct our pattern: Since you know what you're looking for is digits, there are 3 ways to specify "match any digit": "[:digit:]", "[0-9]", and "\d". The first is probably the easiest to understand. There are a few classes (digit, alnum, space, etc. Check the helpfile for a full list) you can use to specify sets of characters, one of them being digit. "[0-9]" just specifies a range of all the digits 0 through 9. "\d" is just a special character that means the same as the first two. There is no difference between the three, and with all SRE's there are usually at least a couple ways to construct any pattern.

So, first we know we want to capture the digits, so indicate that with the opening parentheses "(". Next, we know we want to capture between 1 and 3 characters, all consisting of digits, so our pattern now looks like "([0-9]{1,3}". And finally close it off with the closing parentheses to indicate the end of our group: "([0-9]{1,3})". Let's try it:

Example 5

$asResult = StringRegExp("Gnarly Monster hits you for 18 damage.", _
'([0-9]{1,3})', 1)
If @error == 0 Then
MsgBox(0, "SRE Example 5 Result", $asResult[0])
EndIf

There you go, the message box correctly displays "18".

Next we need to cover non-capturing groups. The way you indicate these groups is by opening the group with "(?:" instead of just "(". Let's say your log says "You deflect 36 of Gnarly Monster's 279 damage." Now if you run Example 5's SRE on this, you'll come up with "36" instead of "279". Now what I like to do here is just determine what's different between the numbers. One that jumps out at me is that the second number is always followed by a space and then the word "damage". We could just modify our previous pattern to be "([0-9]{1,3} damage)", but what if our script is just looking for the amount of damage, without " damage" tacked onto the end of the number? Here's where you can use a non-capturing group to accomplish this.

Example 6

$asResult = StringRegExp("You deflect 36 of Gnarly Monster's 279 damage.", '([0-9]{1,3})(?: damage)', 1)
If @error == 0 Then
MsgBox(0, "SRE Example 6 Result", $asResult[0])
EndIf

This could get lengthy, but mostly I just wanted to lay out the foundation for how regular expressions work, and mainly how SRE "thinks". A few things to keep in mind:
- Remember to think about the pattern one character at a time
- The StringRegExp() function finds the first character in the pattern, then it's your job to provide enough
evidence to "prove" whether or not it truly is a match. Example 6 is a good display of this.
- Remember [ ... ] means OR ([xyz] match an "x", a "y", OR a "z")
If you have any other questions, consult the help file first! It explains in detail all of the nitty gritty syntax that comes along with SRE's. One thing to look at in particular is the section on "Repeating Characters". It can make your pattern more readible by substituting certain characters for ranges. For example: "*" is equivalent to {0,} or the range from 0 to any number of characters.

Good luck, Regular Expressions can greatly decrease the length of your code, and make it easier to modify later. Corrections and feedback are welcome!

Resources


Wikipedia Article - Regular Expressions - Thanks blindwig.

The 30 Minute Regex Tutorial - by Jim Hollenhorst.


GUI for testing various StringRegExp() patterns - Thanks steve8tch. Credit: w0uter

Thanks to neogia for this tutorial.

Language Reference - Datatypes

In AutoIt there is only one datatype called a Variant. A variant can contain numeric or string data and decides how to use the data depending on the situation it is being used in. For example, if you try and multiply two variants they will be treated as numbers, if you try and concatenate (join) two variants they will be treated as strings.

Some examples:

10 * 20 equals the number 200 (* is used to multiply two numbers)

10 * "20" equals the number 200

"10" * "20" equals the number 200

10 & 20 equals the string "1020" (& is used to join strings)

If a string is used as a number, an implicit call to Number() function is done. So if it doesn't contain a valid number, it will be assumed to equal 0. For example,

10 * "fgh" equals the number 0.

If a string is used as a boolean and it is an empty string "" , it will be assumed to equal False (see below). For example,

NOT "" equals the Boolean true.

Numbers

Numbers can be standard decimal numbers like 2, 4.566, and -7.

Scientific notation is also supported; therefore, you could write 1.5e3 instead of 1500.

Integers (whole numbers) can also be represented in hexadecimal notation by preceding the integer with 0x as in 0x409 or 0x4fff (when using hex notation only 32-bit numbers are valid).

Strings

Strings are enclosed in double-quotes like "this". If you want a string to actually contain a double-quote use it twice like:

"here is a ""double-quote"" - ok?"

You can also use single-quotes like 'this' and 'here is a ' 'single-quote' ' - ok?'

You can mix quote types to make for easier working and to avoid having to double-up your quotes to get what you want. For example if you want to use a lot of double-quotes in your strings then you should use single-quotes for declaring them:

'This "sentence" contains "lots" of "double-quotes" does it not?'

is much simpler than:

"This ""sentence"" contains ""lots"" of ""double-quotes"" does it not?"

When evaluated, strings can have Env variables or Var variables substitution according to Opt() function definition.

Booleans

Booleans are logical values. Only two Boolean values exist: true and false.

They can be used in variable assignments, together with the Boolean operators and, or and not.

Examples:
$Boolean1 = true
$Boolean2 = false
$Boolean3 = $Boolean1 AND $Boolean2

This will result in $Boolean3 being false

$Boolean1 = false
$Boolean2 = not $boolean1

This will result in $Boolean2 being true


If Boolean values are used together with numbers, the following rules apply:

A value 0 will be equal to Boolean false
Any other number value will be equal to Boolean true

Example:
$Number1 = 0
$Boolean1 = true
$Boolean2 = $Number1 and $Boolean1

This will result in $Boolean2 being false


If you use arithmetics together with Boolean values (which is not advisable!), the following rules apply:

A Boolean true will be converted into the numeric value 1
A Boolean false will be converted into the numeric value 0

Example:
$Boolean1 = true
$Number1 = 100
$Number2 = $Boolean1 + $Number1

This will result in $Number2 to be the numeric value 101


If you use strings together with Boolean values, they will be converted as follows:

A Boolean true will be the string value "True"
A Boolean false will be the string value "False"

Example:
$Boolean1=true
$String1="Test is: "
$String2=$String1 & $Boolean1

This will result in $String2 being the string value "Test is: True"

The other way around however is different. When you use string comparisons with Boolean values, the following rules apply:
Only an empty string ("") will be a Boolean false
Any other string values (including a string equal "0") will be a Boolean true

Binary

Binary type can store any byte value. they are converted in hexadecimal representation when stored in a string variable. Example:
$bin = Binary("abc")
$str = String($bin) ; "0x616263"


Pointer

Pointer types store a memory address which is 32bits or 64bits depending on if the 32bit or 64 bit of AutoIt is used. They are converted to hexadecimal representation when stored in a string variable. Window handles (HWnd) as returned from WinGetHandle are a pointer type.

Datatypes and Ranges

The following table shows the internal variant datatypes and their ranges.

Data Sub-type Range and Notes
Int32 A 32bit signed integer number.
Int64 A 64bit signed integer number
Double A double-precision floating point number.
String Can contain strings of up to 2147483647 characters.
Binary Binary data, can contain up to 2147483647 bytes.
Pointer A memory address pointer. 32bit or 64bit depending on the version of AutoIt used.

Some functions in AutoIt only work with 32 bit numbers (e.g. BitAND) and are converted automatically - these functions are documented where required.

Language Reference - User Functions

A function is a section of code that can be called from the script to perform a certain "function". There are two sorts of functions in AutoIt, built-in functions and user functions.

built-in Functions

The full list of built-in functions is here and the notes on using them are here.

User Functions

User functions are declared using the Func...EndFunc statements.

Functions can accept parameters and return values as required.

Function names must start with either a letter or an underscore, and the remainder of the name can contain any combination of letters and numbers and underscores. Some valid function names are:

MyFunc

Func1

_My_Func1

Here is an example of using a function to double a number 5 times:

$val = 10
For $i = 1 To 10
$doubled = MyDouble($val)
MsgBox(0, "", $val & " doubled is " & $doubled)
$val = $doubled
Next

Exit


Func MyDouble($value)
$value = $value * 2
Return $value
EndFunc

Language Reference - Comments

Although only one statement per line is allowed, a long statement can span multiple lines if an underscore " _" preceded by a blank is placed at the end of a "broken" line. String definition cannot be split in several lines, concatenation need to be used.

MsgBox(4096,"", "This is a rather long line, so I " & _
"broke it with the underscore, _, character.")

The semicolon (;) is the comment character. Unless the semicolon is within a string, all text following it is ignored by the script interpreter/compiler.

; The next line contains a meaningful, end-of-line comment
Sleep(5000) ;pause 5 seconds

You can combine underscore and semicolon to put comments on lines and still have a long statement span on next line.

dim $b_ ; This _ is not a continuation character, nor is the next one
dim $k_
Dim $a[8][2] = [ _
[ "Word", 4 ], _ ; Comment 1
[ "Test", 3 ], _
[ "pi", 3.14159], _ ; Associate the name with the value
[ "e", 2.718281828465], _ ; Same here
[ "test;1;2;3", 123], _
[ ';', Asc(';') ], _ ; This comment is removed, but the strings remain.
["", 0] ]

It is also possible to comment of large blocks of script by using the #comments-start and #comments-end directives.

No comments: