Functions [v1.0.31+]


Table of Contents

Introduction and Simple Example

A function is similar to a subroutine (Gosub) except that it can accept parameters (inputs) from its caller. In addition, a function may optionally return a value to its caller. Consider the following simple function which accepts two numbers and returns their sum:

Add(x, y)
{
	return x + y   ; "Return" expects an expression.
}

The above is known as a function definition because it creates a function named "Add" (case insensitive) and establishes that anyone who calls it must provide exactly two parameters (x and y). To call the function, assign its result to a variable with the := operator as in this example:

Var := Add(2, 3)  ; The number 5 will be stored in Var.

A function may also be called without storing its return value:

Add(2, 3)

But in this case, any value returned by the function is discarded; so unless the function produces some effect other than its return value, the call would serve no purpose.

Since a function call is an expression, any variable names in its parameter list should not be enclosed in percent signs. By contrast, literal strings should be enclosed in double quotes. For example:

if InStr(MyVar, "fox")
	MsgBox The variable MyVar contains the word fox.

Finally, functions may be called in the parameters of any command (except OutputVar and InputVar parameters such as those of StringLen). However, parameters that do not support expressions must use the "% " prefix as in this example:

MsgBox % "The answer is: " . Add(3, 2)

Parameters

When a function is defined, its parameters are listed in parentheses next to its name (there must be no spaces between its name and the open-parenthesis). If a function should not accept any parameters, leave the parentheses empty; for example: GetCurrentTimestamp().

From the function's point of view, parameters are essentially the same as local variables unless they are defined as ByRef as in this example:

Swap(ByRef Left, ByRef Right)
{
	temp := Left
	Left := Right
	Right := temp
}

In the example above, the use of ByRef causes each parameter to become an alias for the variable passed in from the caller. In other words, the parameter and the caller's variable both refer to the same contents in memory. This allows the Swap function to alter the caller's variables by moving Left's contents into Right and vice versa.

By contrast, if ByRef were not used in the example above, Left and Right would be copies of the caller's variables and thus the Swap function would have no external effect.

Since return can only send back one value, ByRef can be used to send extra results back to a function's caller. This is achieved by having the caller pass in a variable (usually empty) in which the function stores a value.

Known limitation: It is not possible to pass an environment variable to a function's ByRef parameter.

Optional Parameters [v1.0.35+]

When defining a function, one or more of its parameters can be marked as optional. This is done by appending an equals sign and a default value to the parameter. In the following example, the Z parameter is marked optional:

Add(X, Y, Z = 0)
{
	return X + Y + Z
}

When the caller passes three parameters to the function above, Z's default value is ignored. But when the caller passes only two parameters, Z automatically receives the value 0.

It is not possible to have optional parameters isolated in the middle of the parameter list. In other words, all parameters that lie to the right of the first optional parameter must also be marked optional.

A parameter's default value must be one of the following: true, false, a literal integer, a literal floating point number, or a literal empty string ("").

Local Variables

All variables referenced or created inside a function are local by default (except built-in variables such as Clipboard, ErrorLevel, and A_TimeIdle). Each local variable's contents are visible only to commands used inside the function. Consequently, a local variable may have the same name as a global variable and both will have separate contents. Finally, all local variables start off blank each time the function is called.

Global variables: To refer to an existing global variable inside a function (or create a new one), declare the variable as global prior to using it. For example:

LogToFile(TextToLog)
{
	global LogFileName  ; This global variable was previously given a value somewhere outside this function.
	FileAppend, %TextToLog%`n, %LogFileName%
}

If a function needs to reference or create a large number of global variables, it can be defined to assume that all its variables are global (except its parameters) by making its first line either the word "global" or the declaration of a local variable. For example:

SetDefaults()
{
	global  ; This word can be omitted if the first line of this function will be something like "local MyVar".
	Var := 33  ; Assigns 33 to a global variable, first creating the variable if necessary.
	local x, y, z  ; Local variables must be declared in this mode, otherwise they would be assumed global.
	...
}

This assume-global mode can also be used by a function to create a global array, such as a loop that assigns values to Array%A_Index%.

Static variables: A variable may be declared as static to cause its value to be remembered between calls. For example:

LogToFile(TextToLog)
{
	static LineCount
	LineCount += 1  ; Maintain a tally locally (its value is remembered between calls).
	global LogFileName
	FileAppend, %LineCount%: %TextToLog%`n, %LogFileName%
}

Static variables are always implicitly local. In addition, the only way to detect that a static variable is being used for the first time is to check whether it is blank.


More about locals and globals:

Multiple variables may be declared on the same line by separating them with commas as in these examples:

global LogFileName, MaxRetries
static TotalAttempts, PrevResult

Because the words local, global, and static are processed immediately when the script launches, they cannot be conditionally executed by means of an IF statement. In other words, any use of local, global, or static inside an IF's or ELSE's block will take effect unconditionally. In addition, it is not currently possible to declare a dynamic variable such as global Array%i%.

Within a function, any dynamic variable reference such as Array%i% will always resolve to a local variable unless no variable of that name exists, in which case a global is used if it exists. If neither exists and the usage requires the variable to be created, it will be created as a local variable unless the assume-global mode is in effect.

As a consequence of the above, a function can create a global array manually (by means such as Array%i% := A_Index) only if it has been defined as an assume-global function.

For commands that create arrays (such as StringSplit), the resulting array will be local if the assume-global mode is not in effect or if the array's first element has been declared as a local variable (this is also true if one of the function's parameters is passed, even if that parameter is ByRef). Conversely, if the first element has been declared global, a global array will be created. The first element for StringSplit is ArrayName0. For other array-creating commands, such as WinGet List, the first element is ArrayName (i.e. without the number).

Using #Include to Share Functions Among Multiple Scripts

The #Include directive may be used (even at the top of a script) to load functions from an external file.

Explanation: When the script's flow of execution encounters a function definition, it jumps over it (using an instantaneous method) and resumes execution at the line after its closing brace. Consequently, execution can never fall into a function from above, nor does the presence of one or more functions at the very top of a script affect the auto-execute section.

Short-circuit Boolean Evaluation

When AND and OR are used within an expression, they will short-circuit to enhance performance (regardless of whether any function calls are present). Short-circuiting operates by refusing to evaluate parts of an expression that cannot possibly affect its final result. To illustrate the concept, consider this example:

if (ColorName <> "" AND not FindColor(ColorName))
	MsgBox %ColorName% could not be found.

In the example above, the FindColor() function will never get called if the ColorName variable is empty. This is because the left side of the AND would be false, and thus its right side would be incapable of changing the final outcome to true.

Because of this behavior, it's important to realize that any side-effects produced by a function -- such as altering a global variable's contents -- might never occur if that function is called on the right side of an AND or OR.

It should also be noted that short-circuit evaluation will cascade into nested ANDs and ORs. For example, in the following expression, only the leftmost comparison will occur whenever ColorName is blank. This is because the left side would then be enough to determine the final answer with certainty:

if (ColorName = "" OR FindColor(ColorName, Region1) OR FindColor(ColorName, Region2))
	break   ; Nothing to search for, or we found a match.

As shown by the examples above, any expensive (time-consuming) functions should generally be called on the right side of an AND or OR to enhance performance. This technique can also be used to prevent a function from being called when one of its parameters contains a value it would consider inappropriate, such as an empty string.

Using Subroutines Within a Function

Although a function cannot contain definitions of other functions, it can contain private subroutines. As with other subroutines, use Gosub to launch them and Return to return (in which case the Return would belong to the Gosub and not the function).

If a function uses Gosub to jump to a public subroutine (one that lies outside of the function's braces), all variables will be global and the function's own local variables will be inaccessible until the subroutine returns.

Although the use of Goto is generally discouraged, it can be used inside a function to jump to another position within the same function. This can help simplify complex functions that have many points of return, all of which need to do some clean-up prior to returning.

Although a Goto whose target is outside the function is ignored, it is possible for a function to Gosub an external/public subroutine and then do a Goto from there.

A function may contain subroutines for timers, GUI g-labels, menu items, and similar features. This is generally done to encapsulate them in a separate file for use with #Include, which prevents them from interfering with the script's auto-execute section. However, such subroutines should use only static and global variables (not locals) if their function is ever called normally rather than simply serving as a container for subroutines. This is because a subroutine thread that interrupts a function-call thread (or vice versa) would be able to change the values of local variables seen by the interrupted thread. In addition, any time a function returns to its caller, all of its local variables are made blank to free their memory.

General Remarks

If the flow of execution within a function reaches the function's closing brace prior to encountering a Return, the function ends and returns a blank value (empty string) to its caller. A blank value is also returned whenever the function explicitly omits Return's parameter.

When a function uses the Exit command to terminate the current thread, its caller does not receive a return value at all. For example, the statement Var := Add(2, 3) would leave Var unchanged if Add() exits. The same thing happens if a function causes a runtime error such as running a nonexistent file (when UseErrorLevel is not in effect).

A function may alter the value of ErrorLevel for the purpose of returning an extra value that is easy to remember.

To call a function with one or more blank values (empty strings), use an empty pair of quotes as in this example: FindColor(ColorName, "")

Since the calling of a function does not start a new thread, any changes made by a function to settings such as SetKeyDelay and SetTitleMatchMode will go into effect for its caller too.

The caller of a function may pass a nonexistent variable or array element to it, which is useful when the function expects the corresponding parameter to be ByRef. For example, calling GetNextLine(BlankArray%i%) would create the variable BlankArray%i% automatically (local or global depending on location of the caller and whether it has the assume-global mode is in effect).

ListVars can display a function's local variables along with their contents, which can help debug a script.

Known limitation: Although a function may call itself recursively, if it passes one of its own local variables or parameters to itself ByRef, the new layer's ByRef parameter will refer to its own variable rather than the previous layer's. However, this issue does not occur when a function passes to itself a global variable or a ByRef parameter that refers to a global or some other function's local.

Naming Conventions

You might find that complex functions are more readable and maintainable if their special variables are given a distinct prefix. For example, naming each parameter in a function's parameter list with a leading "p" or "p_" makes their special nature easy to discern at a glance, especially when a function uses several dozen local variables. Similarly, the prefix "r" or "r_" could be used for ByRef parameters, and "s" or "s_" could be used for static variables.

Built-in Functions

The optional parameters at the end of a built-in function's parameter list may be completely omitted. For example, WinExist("Untitled - Notepad") is valid because its other three parameters would be considered to be blank.

A built-in function will be overridden if the script defines its own function of the same name. For example, a script could have its own custom WinExist() function that is called instead of the standard one.

External functions that reside in DLL files may be called with DllCall().

All built-in functions require require v1.0.34+ except WinActive and WinExist, which require v1.0.31+.

Frequently-used Functions

FileExist(FilePattern): Returns a blank value (empty string) if FilePattern does not exist. Otherwise, it returns the attribute string (a subset of "RASHNDOCT") of the first matching file or folder. FilePattern may be the exact name of a file or folder, or it may contain wildcards (* or ?). Since an empty string is seen as "false", the function's return value can always be used as a quasi-boolean value. For example, the statement if FileExist("C:\My File.txt") would be true if the file exists and false otherwise. Similarly, the statement if InStr(FileExist("C:\My Folder"), "D") would be true only if the file exists and is a directory. FilePattern is assumed to be in A_WorkingDir if an absolute path isn't specified. Corresponding commands: IfExist and FileGetAttrib.

GetKeyState(KeyName [, "P" or "T"]): Unlike the GetKeyState command -- which returns D for down and U for up -- this function returns true (1) if the key is down and false (0) if it is up. If KeyName is invalid, an empty string is returned. See GetKeyState for other return values and usage instructions.

InStr(Haystack, Needle [, CaseSensitive?, StartingPos]): Returns the position of the first occurrence of the string Needle in the string Haystack. Unlike StringGetPos, position 1 is the first character; this is because 0 is synonymous with "false", making it an intuitive "not found" indicator. If the parameter CaseSensitive is omitted or false, the search is not case sensitive; otherwise, the case must match exactly. If StartingPos is omitted, it defaults to 1 (the beginning of Haystack). Otherwise, specify 2 to start at Haystack's second character, 3 to start at the third, etc. If StartingPos is beyond the length of Haystack, 0 is returned. If StartingPos is 0, the search is conducted in reverse (right-to-left) so that the rightmost match is found. Regardless of the value of StartingPos, the returned position is always relative to the first character of Haystack. For example, the position of "abc" in "123abc789" will always be 4. Corresponding commands: IfInString and StringGetPos.

StrLen(String): Returns the length of String. If String is a variable to which ClipboardAll was previously assigned, its total size is returned. Corresponding command: StringLen.

WinActive("WinTitle" [, "WinText", "ExcludeTitle", "ExcludeText"]): Returns the Unique ID (HWND) of the active window if it matches the specified criteria. If it does not, the function returns 0. Since all non-zero numbers are seen as "true", the statement if WinActive("WinTitle") is true whenever WinTitle is active. See IfWinActive for more details.

WinExist("WinTitle" [, "WinText", "ExcludeTitle", "ExcludeText"]): Returns the Unique ID (HWND) of the first matching window (0 if none). Since all non-zero numbers are seen as "true", the statement if WinExist("WinTitle") is true whenever WinTitle exists. See IfWinExist for more details.

Miscellaneous Functions

Asc(String): Returns the ASCII code (a number between 1 and 255) for the first character in String. If String is empty, 0 is returned.

Chr(Number): Returns the single character corresponding to the ASCII code indicated by Number. If Number is not between 1 and 255 inclusive, an empty string is returned. Common ASCII codes include 9 (tab), 10 (linefeed), 13 (carriage return), 32 (space), 48-57 (0-9), 65-90 (A-Z), and 97-122 (a-z).

IsLabel(LabelName) [v1.0.38+]: Returns a non-zero number if LabelName exists in the script as a subroutine, hotkey, or hotstring (do not include the trailing colon(s) in LabelName). For example, the statement if IsLabel(VarContainingLabelName) would be true if the label exists, and false otherwise. This is useful to avoid runtime errors when specifying a dynamic label in commands such as Gosub, Hotkey, Menu, and Gui.

OnMessage(MsgNumber [, "FunctionName"]): See the OnMessage page.

VarSetCapacity(UnquotedVarName [, RequestedCapacity]): Enlarges a variable's holding capacity or frees its memory. See VarSetCapacity for details.

General Math

Note: Math functions generally return a blank value (empty string) if any of the incoming parameters are non-numeric.

Abs(Number): Returns the absolute value of Number. The return value is the same type as Number (integer or floating point).

Ceil(Number): Returns Number rounded up to the nearest integer (without any .00 suffix). For example, Ceil(1.2) is 2 and Ceil(-1.2) is -1.

Exp(N): Returns e (which is approximately 2.71828182845905) raised to the Nth power. N may be negative and may contain a decimal point. To raise numbers other than e to a power, use the ** operator.

Floor(Number): Returns Number rounded down to the nearest integer (without any .00 suffix). For example, Floor(1.2) is 1 and Floor(-1.2) is -2.

Log(Number): Returns the logarithm (base 10) of Number. The result is formatted as floating point. If Number is negative, an empty string is returned.

Ln(Number): Returns the natural logarithm (base e) of Number. The result is formatted as floating point. If Number is negative, an empty string is returned.

Mod(Dividend, Divisor): Returns the remainder when Dividend is divided by Divisor. The sign of the result is always the same as the sign of the first parameter. For example, both mod(5, 3) and mod(5, -3) yield 2, but mod(-5, 3) and mod(-5, -3) yield -2. If either input is a floating point number, the result is also a floating point number. For example, mod(5.0, 3) is 2.0 and mod(5, 3.5) is 1.5. If the second parameter is zero, the function yields a blank result (empty string).

Round(Number [, N]): If N is omitted or 0, Number is rounded to the nearest integer. If N is positive number, Number is rounded to N decimal places. If N is negative, Number is rounded by N digits to the left of the decimal point. For example, Round(345, -1) is 350 and Round (345, -2) is 300. Unlike Transform Round, the result has no .000 suffix whenever N is omitted or less than or equal to zero.

Sqrt(Number): Returns the square root of Number. The result is formatted as floating point. If Number is negative, the function yields a blank result (empty string).

Trigonometry

Sin(Number) | Cos(Number) | Tan(Number) : Returns the trigonometric sine|cosine|tangent of Number. Number must be expressed in radians.

ASin(Number): Returns the arcsine (the number whose sine is Number) in radians. If Number is less than -1 or greater than 1, the function yields a blank result (empty string).

ACos(Number): Returns the arccosine (the number whose cosine is Number) in radians. If Number is less than -1 or greater than 1, the function yields a blank result (empty string).

ATan(Number): Returns the arctangent (the number whose tangent is Number) in radians.