Changes from v1.1 to v2.0


Legacy Syntax Removed

Removed literal assignments: var = value

Removed all legacy If statements, leaving only if expression, which never requires parentheses (but allows them, as in any expression).

Removed "command syntax". That is, a "command" is now just a function or method call without parentheses (at the start of a line). That means:

The translation from v1-command to function is generally as follows:

Known issues:

Experimental: Return, Until and Loop (in Count mode) support parentheses around their parameter, without any space between the command name and parenthesis. For example, return(var). However, these commands are not functions; for instance, x := return(y) is not valid. If and While already supported this.

Loop also accepts expressions, though the initial Files, Parse, Read or Reg keyword should not be quoted and must be followed by a comma. OTB is supported.

Goto, gosub, break and continue require an unquoted label name, similar to v1 (goto label jumps to label:). To jump to or call a label dynamically, use parentheses: goto(expression) or gosub(expression). However, these are not functions and cannot be used mid-expression.


Variable names cannot start with a digit and cannot contain the following characters which were previously allowed: @ # $. Only letters, numbers, underscore and non-ASCII characters are allowed.

Names of control flow statements cannot be used as variable, function, class or window group names. This includes If, Else, Loop, For, While, Until, Break, Continue, Goto, Gosub, Return, Try, Catch, Finally and Throw. The purpose of this is primarily to detect errors such as if (ex) break.

Double-derefs are now consistent with variables resolved at load-time. That is, a dynamic variable reference will resolve to a (non-super) global only if the function is assume-global or the variable is declared as global in the function.

Double-derefs which fail because they are empty or too long display an error message and cause the thread to abort, instead of silently producing an empty string. This makes them more consistent with input/output vars. Using characters which are illegal in var names already caused an error message, and still does.

Declaring a local variable in a function does not make the function assume-global.

#MustDeclare [On|Off] enables or disables must-declare mode. If used in a func lib file, it affects only that file and any files it explicitly #includes. However, it inherits its default setting from the main script. It is positional, but cannot be used inside a function (due to the following point). Functions default to either assume-local or must-declare depending on the setting at the point the function is defined, but this can be overridden by using Global, Local or Static without a variable name.

Var varname declares a global variable without making it super-global (visible in all functions by default). These declarations are only necessary when #MustDeclare is enabled.


Quoted literal strings can be written with "double" or 'single' quote marks, but must begin and end with the same mark. Literal quote marks are written by preceding the mark with an escape character - `" or `' - or by using the opposite type of quote mark: '"42" is the answer'. Doubling the quote marks has no special meaning, and causes an error since auto-concat requires a space.

The operators &&, ||, and and or yield whichever value determined the result, similar to JavaScript and Lua. For example, "" or "default" yields "default" instead of 1. Scripts which require a pure boolean value (0 or 1) can use something like !!(x or y) or (x or y) ? 1 : 0.

Auto-concat now requires at least one space or tab in all cases (the v1 documentation says there "should be" a space).

The result of a multi-statement expression such as x(), y() is the last (right-most) sub-expression instead of the first (left-most) sub-expression. In both v1 and v2, the sub-expressions are evaluated in left to right order.

Equals after a comma is no longer assignment: y=z in x:=y, y=z is an ineffectual comparison instead of an assignment.

:= += -= *= /= ++ -- have consistent behaviour regardless of whether they are used on their own or combined with other operators, such as with x := y, y += 2. For instance, x := %y%() no longer assigns an empty string to x if the dynamic call fails. (This example might not be applicable anymore since dynamic calls now throw exceptions on failure.)

Scientific notation can be used without a decimal point (but produces a floating-point number anyway).

The expressions funcName[""]() and funcName.() no longer call a function by name. Omitting the method name as in .() now causes a load-time error message. %funcName%() should be used instead.

var := with no r-value is treated as an error at load-time. In v1 it was equivalent to var := "", but silently failed if combined with another expression - for example: x :=, y :=.

Where a literal string is followed by an ambiguous unary/binary operator, an error is reported at load-time. For instance, "x" &y is probably supposed to auto-concatenate "x" with the address of y, but technically it is an invalid bitwise-and operation.

In an expression, the word new is no longer treated as a variable under any circumstances. More complex expressions are supported; e.g. new (getClass())(params) or new new {...}(inner_prms)(outer_prms).

Class var initializers - which take the form of assignments in the class body but outside of any method body - no longer trigger the __Set meta-function.

word ? x : y, word ++ and word -- are no longer expressions, since word can be a user-defined command. To write a standalone ternary, post-increment or post-decrement expression, either omit the space between the variable and the operator, or wrap the variable or expression in parentheses. (Although word ? x : y isn't valid, keeping the rules simple reduces doubt about more ambiguous cases like word1 word2 ? x : y.)

New operator is replaces if var is type. Since type is a string, it must be enclosed in quote marks (or a variable). The same type names are supported as before, plus object and byref. Additionally x is y, where y is an object, checks if x derives from y, directly or indirectly.

Keywords contains and in are reserved for future use.

Address-of: Since pure numbers and strings are now distinct types, &(var := 123) now returns the address of an int64 instead of the address of a string. For consistency, VarSetCapacity returns 6 (the size of int64 minus a null terminator).

String length is now cached during expression evaluation. This improves performance and allows strings to contain binary zero. In particular:

Many commands and functions still expect null-terminated strings, so will only "see" up to the first binary zero.

The *deref operator has been removed. Use NumGet instead.


Keys are stored differently than on v1 in a few cases:

When calling a function via an object, the object itself is passed as the first parameter regardless of where the function was stored (in the object or one of its bases). In v1 this was not done if the function was stored by name directly in the object.

Where Fn is a function or object, %Fn%() calls Fn.Call() instead of Fn[""](). Functions no longer support the nameless method.

Where Fn = this.Method, this.Method() calls Fn.Call(this) instead of Fn[this](). Function objects should implement a Call method instead of the __Call meta-function.

Where x is an Object, defining x.base[key] prevents x.base.base.__Set() from being called when an assignment like x[key] := val is performed. This is for consistency with __Get and __Call.

new Obj() now fails to create the object if the __New meta-method is defined and it could not be called (e.g. because it requires parameters and none were supplied).

All built-in methods of Objects and SafeArrays no longer support the _ (underscore) prefix, except _NewEnum, which now requires it (for compatibility with COM objects). ObjNewEnum(x) is still equivalent to x._NewEnum() for Objects.

The methods Insert and Remove have been removed. They were superseded by InsertAt, RemoveAt, Push, Pop, Delete and ObjRawSet in v1.1.21.

Objects created within an expression or returned from a function are now held until expression evaluation is complete, and then released. This improves performance slightly and allows temporary objects to be used for memory management within an expression, without fear of the objects being freed prematurely.

Objects can contain string values (but not keys) which contain binary zero. Cloning an object preserves binary data in strings, but no longer preserves data beyond the data length (FIXME: provide a way to set the length).


:= must be used in place of = when initializing a declared variable or optional parameter.

If Expression, allows a same-line action (command or expression) following the comma.

Return %var% now does a double-deref; previously it was equivalent to Return var.

#Include is relative to the directory containing the current file by default.

Labels defined in a function have local scope; they are visible only inside that function and do not conflict with labels defined elsewhere. Timers and menu subroutines can be local, but must be set from within the function.

For k, v in obj now "localizes" k and v. When the loop breaks or completes, k and v are restored to the values they had before the loop started.

Escaping a comma no longer has any meaning. Previously if used in an expression within a command's parameter and not within parentheses, it forced the comma to be interpreted as the multi-statement operator rather than as a delimiter between parameters. It only worked this way for commands, not functions or variable declarations.

The escape sequence `s is now allowed wherever `t is supported. It was previously only allowed by #IfWin and (Join.

*/ can now be placed at the end of a line to end a multi-line comment, to resolve a common point of confusion relating to how /* */ works in other languages. Due to the risk of ambiguity (e.g. with a hotstring ending in */), any */ which is not preceded by /* is no longer ignored (reversing a change made in AHK_L revision 54).

Continuation Sections

Smart LTrim: The default behaviour is to count the number of leading spaces or tabs on the first line below the continuation section options, and remove that many spaces or tabs from each line thereafter. If the first line mixes spaces and tabs, only the first type of character is treated as indentation. If any line is indented less than the first line or with the wrong characters, all leading whitespace on that line is left as is.

Quote marks (single and double) are literal by default; this can be disabled with the Q option.

Newline characters (`n) in expressions are treated as spaces. This allows multi-line expressions to be written using a continuation section with default options (i.e. omitting Join).

The ` and % options have been removed, since there is no longer any need to escape these characters.


In general, v2 produces more consistent results with any code that depends on the type of a value.

In v1, a variable can contain both a string and a cached binary number, which is updated whenever the variable is used as a number. Since this cached binary number is the only means of detecting the type of value, caching performed internally by expressions like var+1 or abs(var) effectively changes the "type" of var as a side-effect. v2 disables this caching, so that str := "123" is always a string and int := 123 is always an integer. Consequently, str needs to be converted every time it is used as a number (instead of just the first time).

The built-in "variables" true, false, A_PtrSize, A_IsUnicode, A_Index and A_EventInfo always return pure integers, not strings. They sometimes return strings in v1 due to certain optimizations which have been superseded in v2.

All literal numbers are converted to pure binary numbers at load time and their string representation is discarded. For example, MsgBox % 0x1 is equivalent to MsgBox 1, while MsgBox % 1.0000 is equivalent to MsgBox 1.0 (because the default float formatting has changed). Storing a number in a variable or returning it from a UDF retains its pure numeric status.

Quoted literal strings and strings produced by concatenating with quoted literal strings are no longer unconditionally considered non-numeric. Instead, they are treated the same as strings stored in variables or returned from functions. This has the following implications:

The way that objects interpret different types of keys has changed. See the Objects section of this document for details.

Relational operators such as =, < and >= work a little differently. If both operands are numeric and at least one operand is pure, they are compared numerically. Otherwise, they are compared alphabetically. So for example, 54 and "530" are compared numerically, while "54" and "530" are compared alphabetically. Additionally, strings stored in variables are treated no differently from literal strings.

Type(Value) returns one of the following strings: String, Integer, Float, Object or the specific class of a built-in object type, such as FileObject or ComObject.


Removed commands:

Renamed commands:

Removed undocumented AutoIt2 commands (also removed in v1.1.09):

Modified Commands/Functions

Chr(0) returns a string of length 1, containing a binary zero. This is a result of improved support for binary zero in strings.

ComObject(pdsp), ComObject(9, pdsp) and ComObject(13, pdsp) no longer call AddRef by default; they default to "taking ownership" of the pointer. Most v1 scripts which use ComObjEnwrap(pdsp) (within the first few pages of Google results) used it incorrectly; i.e. they did not release their own copy of the pointer. For v2, the script must call ObjAddRef(pdsp) before ComObject(pdsp) if it does not "own" the reference (i.e. because the pointer will be released automatically, either when the wrapper object is released or immediately as a side-effect of querying for IDispatch within ComObject()). The flags parameter now only affects SafeArrays.

ControlGetFocus now sets ErrorLevel to 0 rather than 1 if it successfully determined that the window has no focused control.

ControlMove, ControlGetPos and ControlClick now use client coordinates (like GuiControl) instead of window coordinates. Client coordinates are relative to the top-left of the client area, which excludes the window's title bar and borders. (Controls are rendered only inside the client area.)

ControlMove, ControlSend and ControlSetText now use parameter order consistent with the other Control functions; i.e. Control, WinTitle, WinText, ExcludeTitle, ExcludeText are always grouped together (at the end of the parameter list), to aide memorisation.

DllCall: AStr is now input-only. Since the buffer is only ever as large as the input string, it was never useful for output parameters. This applies to WStr instead of AStr if compiled for ANSI (but v2 is currently Unicode-only).

FileAppend defaults to no end-of-line translations, consistent with FileRead and FileOpen. FileAppend and FileRead both have a separate Options parameter which replaces the option prefixes and may include an optional encoding name (superseding FileRead's *Pnnn option). FileAppend, FileRead and FileOpen use "`n" to enable end-of-line translations. FileAppend and FileRead support an option "RAW" to disable codepage conversion (read/write binary data). This replaces *c (see ClipboardAll in the documentation).

File.RawRead and File.RawWrite can now be given a variable containing an address (as a pure integer), where previously the variable's cached numeric string would have been used or overwritten, producing inconsistent results.

If File.RawRead is given a variable, Bytes is optional and defaults to the variable's capacity. The variable's length is updated with the length of the data in characters (Ceil(bytes_read / 2)).

File.RawWrite can accept a string (or variable containing a string), in which case Bytes defaults to the size of the string in bytes. The string may contain binary zero.

File.ReadLine now always supports `r, `n and `r`n as line endings. Line endings are still returned to the script as-is by Read() if EOL translation is not enabled.

FileExist now ignores the . and .. implied in every directory listing, so FileExist("dir\*") is now false instead of true when dir exists but is empty.

FileSelectFile (now named FileSelect) had two multi-select modes, accessible via options 4 and M. Option 4 and the corresponding mode have been removed; they had been undocumented for some time.

FileSetAttrib now overwrites attributes when no +, - or ^ prefix is present, instead of doing nothing. For example, FileSetAttrib(FileGetAttrib(file2), file1) copies the attributes of file2 to file1 (adding any that file2 have and removing any that it does not have).

FileSetAttrib and FileSetTime: the OperateOnFolders? and Recurse? parameters have been replaced with a single Mode parameter identical to that of the Loop File command. For example, FileSetAttrib("+a", "*.zip", "RF") (Recursively operate on Files only).

File.ReadLine() no longer includes the line ending in the return value.

Func(Fn) returns an empty string (instead of 0) if the function does not exist, and returns Fn itself if it is a function reference, instead of 0.

GetKeyState now always returns 0 or 1.

GroupAdd: Removed the Label parameter and related functionality. This was an unintuitive way to detect when GroupActivate fails to find any matching windows; ErrorLevel should be used instead.

Hotkey no longer defaults to the script's bottommost #If/#IfWin. Hotkey/hotstring threads default to the same criterion as the hotkey, so Hotkey, %A_ThisHotkey%, Off turns off the current hotkey even if it is context-sensitive. All other threads default to the last setting used by the auto-execute section, which itself defaults to no criterion (global hotkeys).

Hotkey now always gives special treatment to the following values and never treats them as label/function names: On, Off, Toggle, AltTab, ShiftAltTab, AltTabAndMenu, AltTabMenuDismiss. The old behaviour was to use the label/function if it existed, but only if the Label parameter did not contain a variable reference or expression.

Hotkey, If now recognizes expressions which use the and or or operators. This did not work in v1 as these operators were replaced with && or || at load time.

Hotkey, If.. never sets ErrorLevel. An exception is thrown on failure.

IniRead defaults to an empty string when the Default parameter is omitted, instead of the word ERROR. Additionally, ErrorLevel is set if an error occurs.

Input now always returns end keys a-z in lower-case, consistent with other characters (on non-US keyboard layouts). Previously, Input x started collecting input while Input on its own only terminated any input in progress. Since there is no longer an OutputVar parameter, Input now always starts collecting input while InputEnd terminates input. The original Input call sets ErrorLevel to "End" if InputEnd was called.

InputBox has been given a syntax overhaul to make it easier to use (with fewer parameters). See InputBox for usage.

InStr's CaseSensitive parameter is now evaluated according to the usual boolean rules. In v1, non-numeric values were converted to 0, which is false. In v2, non-numeric values other than "" are considered true.

MsgBox has had its syntax changed to prioritise its most commonly used parameters and improve ease of use. "Smart" comma handling has been removed; that is, it now handles commas the same as other functions. See MsgBox for usage.

NumPut/NumGet: If a variable containing a pure number is passed to the VarOrAddress parameter, the variable's value is used rather than the address of the variable itself.

Object() and {} no longer cause __Set to be invoked for each key-value pair. Object(obj) no longer calls AddRef and returns the object's address. If required, use ObjAddRef(addr := &obj) instead.

OnExit (the command) has been removed; use the OnExit() function which was added in v1.1.20 instead. A_ExitReason has also been removed; its value is available as a parameter of the OnExit callback function.

OnClipboardChange: (the label) is no longer called automatically if it exists. Use the OnClipboardChange() function which was added in v1.1.20 instead.

OnMessage has been changed to treat function names the same way that function references are treated in v1.1.20. That is, OnMessage(x, "MyFunc") registers MyFunc for message x while still allowing other functions to monitor message x. The function must be unregistered using OnMessage(x, "MyFunc", 0), not OnMessage(x, ""), which is now an error. OnMessage(x) is also now an error. On failure, OnMessage throws an exception.

PixelSearch and PixelGetColor use RGB values instead of BGR, for consistency with other functions.

PostMessage: See SendMessage.

Random, , NewSeed was previously used to reseed the random number generator. Since Random no longer has an OutputVar parameter to omit, this is now done by calling RandomSeed instead.

RegExMatch options O and P were removed; O (object) mode is now mandatory.

Registry functions (RegRead, RegWrite, RegDelete): the new syntax added in v1.1.21+ is now the only syntax. Root key and subkey are combined. Instead of RootKey, Key, write RootKey\Key. To connect to a remote registry, use \\ComputerName\RootKey\Key instead of \\ComputerName:RootKey, Key.

RegWrite's parameters were reordered to put Value first, like IniWrite (but this doesn't affect the single-parameter mode, where Value was the only parameter).

When KeyName is omitted and the current loop reg item is a subkey, RegDelete, RegRead and RegWrite now operate on values within that subkey; i.e. KeyName defaults to A_LoopRegKey "\" A_LoopRegName in that case (note that A_LoopRegKey was merged with A_LoopRegSubKey). Previously they behaved as follows:

RegDelete, RegRead and RegWrite now allow ValueName to be specified when KeyName is omitted:

Otherwise, RegDelete with a blank or omitted ValueName now deletes the key's default value (not the key itself), for consistency with RegWrite, RegRead and A_LoopRegName. The phrase "AHK_DEFAULT" no longer has any special meaning. To delete a key, use RegDeleteKey (new).

RegRead had an undocumented 5-parameter mode, where the value type was specified after the output variable. This has been removed.

SendMessage and PostMessage now differentiate between strings and pure numbers. Strings are passed by address, while numbers are passed by value. Passing a plain variable reference allows its length to be updated if modified by the message handler. Previously a string was only passed by address if the expression begin with ", and variables were only updated if the expression begin with & (for previous v2 alphas, this only worked in command mode). Passing &var no longer updates the variable's length (for that, you must pass the variable itself, not the address of its string contents).

SendMessage now sets ErrorLevel to 0 or 1 indicating only whether an error occurred, and returns the message reply (or an empty string on failure).

Sort: the VarName parameter has been split into separate input/output parameters, for flexibility. Usage is now Sort, OutputVar, InputText [, Options] or Output := Sort(Input [, Options]).

StrGet: If Length is negative, its absolute value indicates the exact number of characters to convert, including any binary zeros that the string might contain -- in other words, the result is always a string of exactly that length. If Length is positive, the converted string ends at the first binary zero as in v1.

SysGet now only has numeric sub-commands; its other sub-commands have been split into functions. See Sub-Commands for details.

TrayTip's usage has changed to TrayTip [, Text, Title, Options]. Options is a string of zero or more case-insensitive options delimited by a space or tab. The options are Iconx, Icon!, Iconi, Mute and/or any numeric value as before. TrayTip now shows even if Text is omitted (which is now harder to do by accident than in v1). The Seconds parameter no long exists (it had no effect on Windows Vista or later).

WinSetTitle and WinMove now use parameter order consistent with other Win functions; i.e. WinTitle, WinText, ExcludeTitle, ExcludeText are always grouped together (at the end of the parameter list), to aide memorisation.

WinMove no longer has special handling for the literal word DEFAULT. Omit the parameter or specify an empty string instead (this works in both v1 and v2).


A negative StartingPos for InStr, SubStr, RegExMatch and RegExReplace is interpreted as a position from the end, starting at 1. Position -1 is the right-most character (in v1 this was position 0), and position 0 is invalid.

Functions which accept On/Off/Toggle now also accept 1/0/-1 (which is more convenient in an expresssion).

The following functions return a pure integer instead of a hexadecimal string:

A_ScriptHwnd returns a string due to a technical limitation, but in decimal to be more consistent.

Loop Sub-commands

The sub-command keyword must be written literally; it should not be enclosed in quote marks and cannot be a variable or expression. All other parameters are expressions. All loop sub-commands now support OTB.


Loop, FilePattern [, IncludeFolders?, Recurse?]
Loop, RootKey [, Key, IncludeSubkeys?, Recurse?]

Use the following (added in v1.1.21) instead:

Loop Files, FilePattern [, Mode]
Loop Reg, RootKey\Key [, Mode]

A_LoopRegKey now contains the root key and subkey, and A_LoopRegSubKey was removed.


OutputVar := InputBox([Text, Title, Options, Default])

The Options parameter accepts a string of zero or more case-insensitive options delimited by a space or tab, similar to Gui control options. For example, this includes all supported options: x0 y0 w100 h100 T10.0 Password*. T is timeout and Password has the same usage as the equivalent Edit control option.

The title will be blank if the Title parameter is an empty string. It defaults to A_ScriptName only when completely omitted, consistent with optional parameters of user-defined functions.


Result := MsgBox([Text, Title, Options])

The Options parameter accepts a string of zero or more case-insensitive options delimited by a space or tab, similar to Gui control options.

The return value is the name of the button, without spaces. These are the same strings that were used with IfMsgBox in v1.

The title will be blank if the Title parameter is an empty string. It defaults to A_ScriptName only when completely omitted, consistent with optional parameters of user-defined functions.


Sub-commands of Control, ControlGet, Drive, DriveGet, WinGet, WinSet and Process have been replaced with individual functions, and the main commands have been removed. Names and usage have been changed for several of the functions. The new usage is shown below:

; Where ... means optional Control, WinTitle, etc.

Boolean := ControlGetChecked(...)
Boolean := ControlGetEnabled(...)
Boolean := ControlGetVisible(...)
Integer := ControlGetTab(...)
String  := ControlGetChoice(...)
String  := ControlGetList([Options, ...])
Integer := ControlGetLineCount(...)
Integer := ControlGetCurrentLine(...)
Integer := ControlGetCurrentCol(...)
String  := ControlGetLine(N [, ...])
String  := ControlGetSelected(...)
Integer := ControlGetStyle(...)
Integer := ControlGetExStyle(...)
Integer := ControlGetHwnd(...)

           ControlSetChecked(TrueFalseOnOffToggle [, ...])
           ControlSetEnabled(TrueFalseOnOffToggle [, ...])
           ControlSetStyle(Value [, ...])
           ControlSetExStyle(Value [, ...])
           ControlSetTab(Index [, ...])
           ControlChoose(Index [, ...])
Index   := ControlChooseString(String [, ...])
           ControlEditPaste(String [, ...])

Index   := ControlFindItem(String [, ...])
Index   := ControlAddItem(String [, ...])
           ControlDeleteItem(Index [, ...])

           DriveEject([Drive, Retract := false])
           DriveSetLabel(Drive [, Label])

String  := DriveGetList([Type])
String  := DriveGetFilesystem(Drive)
String  := DriveGetLabel(Drive)
String  := DriveGetSerial(Drive)
String  := DriveGetType(Path)
String  := DriveGetStatus(Path)
String  := DriveGetStatusCD(Drive)
Integer := DriveGetCapacity(Path)
Integer := DriveGetSpaceFree(Path)

; Where ... means optional WinTitle, etc.

Integer := WinGetID(...)
Integer := WinGetIDLast(...)
Integer := WinGetPID(...)
String  := WinGetProcessName(...)
String  := WinGetProcessPath(...)
Integer := WinGetCount(...)
Array   := WinGetList(...)
Integer := WinGetMinMax(...)
Array   := WinGetControls(...)
Array   := WinGetControlsHwnd(...)
Integer := WinGetTransparent(...)
String  := WinGetTransColor(...)
Integer := WinGetStyle(...)
Integer := WinGetExStyle(...)

           WinSetTransparent(N [, ...])
           WinSetTransColor("Color [N]" [, ...]),
           WinSetAlwaysOnTop([Value := "Toggle", ...])
           WinSetStyle(Value [, ...])
           WinSetExStyle(Value [, ...])
           WinSetEnabled(Value [, ...])
           WinSetRegion(Value [, ...])


; These four currently all have OutputVars:
PID     := ProcessExist([PID_or_Name])
PID     := ProcessClose(PID_or_Name)
PID     := ProcessWait(PID_or_Name [, Timeout])
PID     := ProcessWaitClose(PID_or_Name [, Timeout])

           ProcessSetPriority(Priority [, PID_or_Name])

ProcessExist, ProcessClose, ProcessWait and ProcessWaitClose no longer set ErrorLevel; instead, they return the PID.

HWNDs and styles are always returned as pure integers, not hexadecimal strings.

ControlChoose allows 0 to deselect the current item/all items.

ControlGetTab returns 0 if no tab is selected (rare but valid).

WinGetList, WinGetControls and WinGetControlsHwnd return arrays, not newline-delimited lists.

Abbreviated aliases such as Topmost, Trans, FS and Cap were removed. Use the full function name or write your own wrapper function with the name of your choice.

When called using function syntax, all WinSet' functions return 1 on success, 0 on failure, while ErrorLevel is set to 0 on success, 1 on failure. However, some of the other functions only set ErrorLevel.

The following functions were formerly sub-commands of SysGet:

Exists  := MonitorGet(N, Left, Top, Right, Bottom)
Exists  := MonitorGetWorkArea(N, Left, Top, Right, Bottom)
Count   := MonitorGetCount()
Primary := MonitorGetPrimary()
Name    := MonitorGetName(N)

New Commands/Functions

ClipboardAll([Data, Size]) creates an object containing everything on the clipboard (optionally accepting data previously retrieved from the clipboard instead of using the clipboard's current contents). The methods of reading and writing clipboard file data are different. The data format is the same, except that the data size is always 32-bit, so that the data is portable between 32-bit and 64-bit builds. See the v2 documentation for details.

DirExist(Path), with usage similar to FileExist. Note that InStr(FileExist(Pattern), "D") only tells you whether the first matching file is a folder, not whether a folder exists.

RegDeleteKey("RootKey\SubKey") deletes a registry key. (RegDelete now only deletes values, except when omitting all parameters in a registry loop.)

Built-in Variables

A_OSVersion always returns a string in the format, such as 6.1.7601 for Windows 7 SP1. A_OSType has been removed as only NT-based systems are supported.




The following built-in variables can be assigned values:


Gui, GuiControl and GuiControlGet were replaced with GuiCreate() and Gui/GuiControl objects, which are generally more flexible, more consistent, and arguably easier to use.

For details of the usage, see the new v2 documentation.

A GUI is typically not referenced by name/number (although it can still be named with the Gui.Name property). Instead, a GUI object (and window) is created explicitly by calling GuiCreate(), which returns a Gui object. This object has methods and properties which replace the Gui sub-commands. Gui.Add() returns a GuiControl object, which has methods and properties which replace the GuiControl and GuiControlGet commands. One can store this object in a variable, or use Gui.Control["Name"] or GuiCtrlFromHwnd(hwnd) to access retrieve the object. It is also passed as a parameter whenever an event handler (the replacement of a g-label) is called.

The usage of these methods and properties is not 1:1. Many parts have been revised to be more consistent and flexible, and to fix bugs or limitations.

There are no "default" GUIs, as the target Gui or control object is always specified. LV/TV/SB functions were replaced with methods (of the control object), making it much easier to use multiple ListViews/TreeViews.

There are no built-in variables containing information about events. The information is passed as parameters to the function/method which handles the event, including the source Gui or control.

Controls can still be named and be referenced by name, but it's just a name (used with Gui.Control["Name"] and Gui.Submit()), not an associated variable, so there is no need to declare or create a global or static variable. The value is never stored in a variable automatically, but is accessible via GuiCtrl.Value. Gui.Submit() returns a new associative array using the control names as keys.

The vName option now just sets the control's name to Name.

The +HwndVarName option has been removed in favour of GuiCtrl.Hwnd.

There are no more "g-labels" or labels/functions which automatically handle GUI events. The script must register for each event of interest by calling the OnEvent method of the Gui or GuiControl. For example, rather than checking if (A_GuiEvent = "I" && InStr(ErrorLevel, "F", true)) in a g-label, the script would register a handler for the ItemFocus event: MyLV.OnEvent("ItemFocus", "MyFunction"). MyFunction would be called only for the ItemFocus event. It is not necessary to apply AltSubmit to enable additional events.

In general, arrays can be used in place of a list in string form (with the current Gui delimiter, set by Gui +Delimiter). The string form is still supported, but never returned to the script (e.g. multi-select ListBox returns an array of selected items).

Gui sub-commands

Gui New → GuiCreate(). Passing an empty title (not omitting it) now results in an empty title, not the default title.

Gui Add → Gui.Add() or Gui.AddControlType(); e.g. Gui.Add("Edit") or Gui.AddEdit().

Gui Show → Gui.Show(), but it has no Title parameter. The title can be specified as a parameter of GuiCreate() or via the Gui.Title property.

Gui Submit → Gui.Submit(). It works like before, except that Submit() creates and returns a new object which contains all of the "associated variables".

Gui Destroy → Gui.Destroy(). The object still exists (until the script releases it) but cannot be used. A new GUI must be created (if needed).

Gui Font → Gui.SetFont(). It is also possible to set a control's font directly, with GuiCtrl.SetFont().

Gui Color → Gui.BackColor sets/returns the background color. ControlColor (the second parameter) is not supported, but all controls which previously supported it can have a background set by the +Background option instead. Unlike Gui Color, Gui.BackColor does not affect Progress controls or disabled/read-only Edit, DDL, ComboBox or TreeView (with -Theme) controls.

Gui Margin → Gui.MarginX and Gui.MarginY properties.

Gui Menu → Gui.Menu property. Currently it sets/returns a menu name, but in future it will set/return a Menu object.

Gui Cancel/Hide/Minimize/Maximize/Restore → Gui methods of the same name.

Gui Flash → Gui.Flash(), but use false instead of Off.

Gui Tab → TabControl.UseTab(). Defaults to matching a prefix of the tab name as before. Pass true for the second parameter to match the whole tab name, but unlike the v1 "Exact" mode, it is case-insensitive.


The Size event passes 0, -1 or 1 (consistent with WinGetMinmax) instead of 0, 1 or 2.

The ContextMenu event can be registered for each control, or for the whole GUI.

The DropFiles event swaps the FileArray and Ctrl parameters, to be consistent with ContextMenu.

The ContextMenu and DropFiles events use client coordinates instead of window coordinates (Client is also the default CoordMode in v2).

The following control events were removed, but detecting them is a simple case of passing the appropriate numeric notification code (defined in the Windows SDK) to GuiCtrl.OnNotify(): K, D, d, A, S, s, M, C, E and MonthCal's 1 and 2.

Every supported event has a name, not a letter/number. See the v2 documentation for details.

Control events do not pass the event name as a parameter (GUI events never did).

Custom's N and Normal events were replaced with GuiCtrl.OnNotify() and GuiCtrl.OnCommand(), which can be used with any control.

Link's Click event passes (Ctrl, ID or Index, HREF) instead of (Ctrl, Index, HREF or ID), and does not automatically execute HREF if a Click callback is registered.

ListView's Click, DoubleClick and ContextMenu (when triggered by a right-click) events now report the item which was clicked (or 0 if none) instead of the focused item.

ListView's I event was split into multiple named events, except for the f (de-focus) event, which was excluded because it is implied by F (ItemFocus).

ListView's e (ItemEdit) event is ignored if the user cancels.

Slider's Change event is raised more consistently than the v1 g-label; i.e. it no longer ignores changes made by the mouse wheel by default. See the documentation for details.

The BS_NOTIFY style is now added automatically as needed for Button, CheckBox and Radio controls. It is no longer applied by default to Radio controls.

Focus (formerly F) and LoseFocus (formerly f) are supported by more (but not all) control types.

Setting an Edit control's text with Edit.Value or Edit.Text does not trigger the control's Change event, whereas GuiControl would trigger the control's g-label.

LV/TV.Add/Modify now suppress item-change events, so such events should only be raised by user action or SendMessage.


+HwndOutputVar → Gui.Hwnd or GuiCtrl.Hwnd
Gui GuiName: Default

Control Options

+/-Background is interpreted and supported more consistently. All controls which supported Gui Color now support +BackgroundColor and +BackgroundDefault (synonymous with -Background), not just ListView/TreeView/StatusBar/Progress.


Empty sub-command

GuiControlGet's empty sub-command had two modes: the default mode, and text mode, where the fourth parameter was the word Text. If a control type had no single "value", GuiControlGet defaulted to returning the result of GetWindowText (which isn't always visible text). Some controls had no visible text, or did not support retrieving it, so completely ignored the fourth parameter. By contrast, GuiCtrl.Text returns display text, hidden text (the same text returned by ControlGetText) or nothing at all.

The table below shows the closest equivalent property or function for each mode of GuiControlGet and control type.

ActiveX.Value.TextText is hidden. See below.
ComboBox.TextControlGetText()Use Value instead of Text if AltSubmit was used (but Value returns 0 if Text does not match a list item). Text performs case-correction, whereas ControlGetText returns the Edit field's content.
DDL.TextUse Value instead of Text if AltSubmit was used.
ListBox.TextControlGetText()Use Value instead of Text if AltSubmit was used. Text returns the selected item's text, whereas ControlGetText returns hidden text. See below.
ListView.TextText is hidden.
Tab.TextControlGetText()Use Value instead of Text if AltSubmit was used. Text returns the selected tab's text, whereas ControlGetText returns hidden text.
TreeView.TextText is hidden.

ListBox: For multi-select ListBox, Text and Value return an array instead of a pipe-delimited list.

ActiveX: GuiCtrl.Value returns the same object each time, whereas GuiControlGet created a new wrapper object each time. Consequently, it is no longer necessary to retain a reference to an ActiveX object for the purpose of keeping a ComObjConnect connection alive.

Other sub-commands

Pos → GuiCtrl.Pos; returns an associative array instead of a pseudo-array.

Focus → Gui.FocusedCtrl; returns a GuiControl object instead of the ClassNN.

FocusV → Gui.FocusedCtrl.Name

Hwnd → GuiCtrl.Hwnd; returns a pure integer, not a hexadecimal string.

Enabled/Visible/Name → GuiCtrl properties of the same name.


(Blank) and Text sub-commands

The table below shows the closest equivalent property or function for each mode of GuiControl and control type.

ActiveXN/ACommand had no effect.
ListViewN/ACommand had no effect.
Progress.ValueUse the += operator instead of the + prefix.
Slider.ValueUse the += operator instead of the + prefix.
StatusBar.Text or SB.SetText()
TreeViewN/ACommand had no effect.
UpDown.ValueUse the += operator instead of the + prefix.

Other sub-commands

Move → GuiCtrl.Move(...)

MoveDraw → GuiCtrl.Move(..., true)

Focus → GuiCtrl.Focus()

Enable/Disable → set GuiCtrl.Enabled

Hide/Show → set GuiCtrl.Visible

Choose → GuiCtrl.Choose(n), where in is a pure integer. The |n or ||n mode is not supported (use ControlChoose instead, if needed).

ChooseString → GuiCtrl.Choose(s), where s is not a pure integer. The |n or ||n mode is not supported. If the string matches multiple items in a multi-select ListBox, Choose() selects them all, not just the first.

Font → GuiCtrl.SetFont()

+/-Option → GuiCtrl.Opt("+/-Option")

Other Changes

Progress Gui controls no longer have the PBS_SMOOTH style by default, so they are now styled according to the system visual style (e.g. Luna in XP or Aero in Vista/7).

The default margins and control sizes (particularly for Button controls) may differ slightly from v1 when DPI > 100%.

Error Handling


A load-time error is raised for more syntax errors than in v1, such as:

An exception is thrown when any of the following failures occur (instead of ignoring the failure):

Some of the conditions above are detected in v1, but not mid-expression; for instance, A_AhkPath := x is detected in v1 but y := x, A_AhkPath := x is only detected in v2.

The operators +=, -=, -- and ++ treat an empty variable as 0 (for += and -=, this only applies to the left-hand side). In v1, this was true only for standalone use of the operator, not when used mid-expression or with multi-statement comma.


Commands generally use ErrorLevel to report failure, and sometimes throw an exception (e.g. for invalid parameters). This is similar to v1, though behaviour has changed in some specific cases. However, wrapping commands in a TRY block no longer affects this behaviour, so behaviour is more predictable.

Commands and built-in functions throw an exception when invalid parameters are detected (but not all invalid parameters are detected), or when a memory allocation fails.

DllCall throws exceptions instead of setting ErrorLevel.

RegExMatch and RegExReplace never set ErrorLevel, instead throwing an exception if there is an error. This includes syntax errors in the regex pattern and PCRE execution errors.

Math functions return the empty string "" if any of their inputs are non-numeric, or if the operation is invalid (such as division by zero).


Command-line args are stored in an array and assigned to the super-global var A_Args instead of a pseudo-array of numbered global vars. If no args were passed to the script, A_Args contains an empty array. The expression A_Args.Length() returns the number of args and A_Args[1] returns the first arg.

Mouse wheel hotkeys set A_EventInfo to the wheel delta as reported by the mouse driver instead of dividing by 120. Generally it is a multiple of 120, but some mouse hardware/drivers may report wheel movement at a higher resolution.

ErrorLevel can safely be assigned an object without risk of it being lost when the thread is interrupted. In v1, the thread-specific nature of ErrorLevel was implemented by saving the previous thread's ErrorLevel as a string when a new thread starts and restoring it when the new thread terminates.

ErrorLevel is reset to 0 for each new thread. In v1, it retained the value from the previous thread, which is also the same value it is reset to when the new thread finishes.

RegEx newline matching defaults to (*ANYCRLF) and (*BSR_ANYCRLF); `r and `n are recognized in addition to `r`n. The `a option implicitly enables (*BSR_UNICODE).


Scripts are "persistent" while at least one of the following conditions is satisfied:

If the last script thread finishes or a Gui is closed or destroyed and none of the above conditions are satisfied, the script terminates.

By contrast, v1 scripts are "persistent" when at least one of the following is true:

Default Settings