JEESWG'S FUNCTIONS TUTORIAL
[updated: 2019-12-05]
==================================================
CONTENTS
> BUILT-IN / CUSTOM FUNCTIONS
> OUTLINE
> LOCAL / GLOBAL / SUPER-GLOBAL VARIABLES
> STATIC VARIABLES
> BYREF PARAMETERS
> VARIADIC FUNCTIONS
> OMIT PARAMETERS: CUSTOM FUNCTIONS / COM OBJECTS
> MULTIPLE INPUT PARAMETERS
> MULTIPLE OUTPUT PARAMETERS
> INPUT PARAMETERS: COUNT
> INPUT PARAMETERS: CHECK IF PARAMETER OMITTED
> DUAL ROLE INPUT/OUTPUT PARAMETER (BYREF AND OBJECTS)
> PARAMETERS ARE CLEARED AFTER USE
> HANDLE BINARY DATA
> PARAMETER VARIABLES VERSUS OTHER VARIABLES
> CREATING VARIABLES DYNAMICALLY WITHIN FUNCTIONS
> REFERRING TO FUNCTIONS DYNAMICALLY
> OVERRIDE BUILT-IN FUNCTIONS
> #INCLUDE PATH, #INCLUDE <LIBNAME> AND AUTO-INCLUDE
> PERFORM ACTIONS AT SCRIPT STARTUP
> COMBINE SCRIPTS: A SCRIPT WITH MULTIPLE AUTO-EXECUTE SECTIONS
> RECURSIVE FUNCTIONS
> FAT ARROW FUNCTIONS (AHK V2)
> NESTED FUNCTIONS (AHK V2)
> CLOSURES (AHK V2)
> LINKS
==================================================
> BUILT-IN / CUSTOM FUNCTIONS
In AutoHotkey you can use built-in functions.
Code: Select all
vPos := InStr("abcde", "c")
MsgBox, % vPos ;3
vText := StrReplace("abcde", "c", "_")
MsgBox, % vText ;ab_de
vText := RegExReplace("abcde", "[ae]", "_")
MsgBox, % vText ;_bcd_
oArray := StrSplit("a,b,c,d,e", ",")
MsgBox, % oArray[3] ;c
MsgBox, % oArray.3 ;c
Code: Select all
;E.g. here we create a custom 'Add' function.
vNum1 := 11
vNum2 := 22
vOutput1 := 11 + 22
vOutput2 := vNum1 + vNum2
vOutput3 := 11 + vNum2
vOutput4 := Add(11, 22)
vOutput5 := Add(vNum1, vNum2)
vOutput6 := Add(11, vNum2)
MsgBox, % vOutput1 "`r`n" vOutput2 "`r`n" vOutput3 "`r`n" vOutput4 "`r`n" vOutput5 "`r`n" vOutput6 "`r`n"
Add(var1, var2)
{
return var1 + var2
}
;E.g. here we create a custom 'Concatenate' function.
vText1 := "abc"
vText2 := "def"
vOutput1 := "abc" "def"
vOutput2 := vText1 vText2
vOutput3 := "abc" vText2
vOutput4 := Concatenate("abc", "def")
vOutput5 := Concatenate(vText1, vText2)
vOutput6 := Concatenate("abc", vText2)
MsgBox, % vOutput1 "`r`n" vOutput2 "`r`n" vOutput3 "`r`n" vOutput4 "`r`n" vOutput5 "`r`n" vOutput6 "`r`n"
Concatenate(var1, var2)
{
return var1 var2
}
Examples of functions:
Explorer window interaction (folder windows/Desktop, file/folder enumeration/selection/navigation/creation) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=35041
GUIs via DllCall: get/set internal/external control text - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=40514
commands as functions (AHK v2 functions for AHK v1) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=37&t=29689
AutoHotkey via DllCall: AutoHotkey functions as custom functions - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=37871
==================================================
> OUTLINE
Here is a guide to the sections that will follow:
Key terms (which will be explained further):
- scope: local/global/super-global variables - a variable has a local version (inside a function), and a global version (outside a function), sometimes these are the same
- static variables - the content of static variables is remembered after a function is executed, normally variables are cleared
- ByRef parameters - an input parameter that can also be used as an output parameter
- variadic functions - functions with a variable number of parameters, normally functions have a fixed number of parameters
Ways to increase the number of input variables:
- lots of variables as input parameters
- variables containing comma(/delimiter)-separated lists
- use global variables
- use arrays as input parameters
- variadic functions
Ways to increase the number of output variables:
- note: return is limited to one output parameter
- variables containing comma(/delimiter)-separated lists
- use global variables
- return an array
- use multiple ByRef parameters as input/output parameters
Also:
- how many parameters were passed to the function
- omitting parameters / default values
- can an input variable also be an output variable (ByRef, objects)
- are variables inside functions cleared when the function ends
- handling binary data
- are variables inside functions local or global (and: parameter variables versus variables inside the function)
- creating variables dynamically within functions
- referring to functions dynamically
- built-in/custom functions with the same name
- including scripts (and functions) using #Include (and what happens when a function isn't found)
- what happens when a function isn't found
- fat arrow functions (AHK v2)
- nested functions (AHK v2)
- closures (AHK v2)
==================================================
> LOCAL / GLOBAL / SUPER-GLOBAL VARIABLES
- Variables within the body of a function or class definition are either super-global, global or local.
- Variables in the main body of a script are either super-global or global.
- Parameter variables are neither super-global, global nor local, they are special cases.
- It is possible for a global variable, and a variable local to a function, to share the same name, however, they are completely separate variables.
Code: Select all
varG := "G" ;global
global varSG := "SG" ;super-global
Func1(varP1, varP2, varP3) ;parameter variables
{
global varG2 := "G2" ;global
static varS := "S" ;static (+ local)
varX := "X" ;local (it is local as that is the default)
}
Func2(varP1, varP2, varP3) ;parameter variables
{
local varL := "L" ;local
static varS := "S" ;static (+ local)
varX := "X" ;global (it is global because a variable above was defined as local)
}
- Defining a variable as global within a function, means that the global version of the variable is used.
- Parameter variables, those used as parameters when calling the function, e.g. Func(var1, var2, var3) are special cases. They are not global/local/static like the other variables that appear within the body of a function.
- AHK v1: If one or more variables are defined as local, all other (non-parameter) variables in the function are defined as global. This is assume-global mode.
- AHK v2: If one or more variables are defined as local, this does not affect any other variables in the function.
- If one or more variables are defined as global, this does not affect any other variables in the function.
- If one or more variables are defined as static (static variables are local) this does not affect any other variables in the function.
- The word 'global' on its own, can be used to make every variable in a function global by default. This is assume-global mode.
- The word 'static' on its own, can be used to make every variable in a function static (and local) by default. This is assume-static mode.
- Prior to AHK v1.1.27. The word 'local' on its own, cannot be used to make every variable in a function local by default, it triggers an error message.
- From AHK v1.1.27 onwards. The word 'local' on its own, can be used to make every variable in a function local by default. This is force-local mode.
Code: Select all
varA := "A"
varB := "B"
global varC := "C"
TestGbl1()
TestGbl2()
TestGbl3()
TestGbl4()
TestGbl5()
TestGbl6()
TestGbl7()
return
TestGbl1() ;local, local, super-global
{
;all variables are local apart from any super-globals
MsgBox, % varA "_" varB "_" varC ;__C
}
TestGbl2() ;global, global, super-global
{
;all variables are global
global
MsgBox, % varA "_" varB "_" varC ;A_B_C
}
TestGbl3() ;global, local, super-global
{
;varA is global
;all other variables are local apart from any super-globals
global varA
MsgBox, % varA "_" varB "_" varC ;A__C
}
;TestGbl4X()
;{
; ;doesn't work (although using 'static varC' instead would work)
; global varA
; local varC
; MsgBox, % varA "_" varB "_" varC
;}
TestGbl4() ;all local
{
;defining one or more variables as local,
;makes all other variables global
local varB, varC
MsgBox, % varA "_" varB "_" varC ;A__
}
TestGbl5() ;all local
{
;defining one or more variables as local,
;makes all other variables global
local varC
MsgBox, % varA "_" varB "_" varC ;A_B_
}
TestGbl6() ;global, local, static (+ local)
{
;this makes varA global and varC static (static variables are local),
;all other variables are local apart from any super-globals
global varA
static varC
MsgBox, % varA "_" varB "_" varC ;A__
}
TestGbl7() ;global, global, static (+ local)
{
;all variables are global
;apart from varC which will be local (static variables are local)
global
static varC
MsgBox, % varA "_" varB "_" varC ;A_B_
}
Code: Select all
varA := "A"
varB := "B"
global varC := "C"
TestGbl8()
TestGbl9()
return
TestGbl8() ;all local
{
;all variables are local
local
MsgBox, % varA "_" varB "_" varC ;__
}
TestGbl9() ;global, local, local
{
;all variables are local
;apart from varA which is global
local
global varA
MsgBox, % varA "_" varB "_" varC ;__
}
;TestGbl9X()
;{
; ;doesn't work (although it does work if you swap 'global varA' and 'local')
; global varA
; local
; MsgBox, % varA "_" varB "_" varC
;}
- The same syntax works for 'local' and 'static'.
- The first line makes all variables global.
- The other lines demonstrate getting/setting the contents of a global variable.
- There is a concrete example in the codebox after.
Code: Select all
;note: these lines are each individual examples, they are not intended to be used together
global
global var1
global var1 := 1
global var1, var2
global var1 := 1, var2
global var1, var2 := 2
global var1 := 1, var2 := 2
Code: Select all
var1 := "a", var2 := "b"
Func()
MsgBox, % var1 " " var2 ;1 b
Func()
{
global var1 := 1, var2
MsgBox, % var1 " " var2 ;1 b
}
- The first MsgBox shows that 'global var := 1' does not set the contents of var at the start of the script.
- The second MsgBox suggests that 'global var := 1' did make var super-global.
Code: Select all
MsgBox, % var ;(blank)
Func()
MsgBox, % var ;a
global var := 1
MsgBox, % var ;1
Func()
{
var := "a"
}
> STATIC VARIABLES
- Static variables: the contents of a static variable are remembered between function calls. E.g. you could use a static variable to count each time a function is called.
- With a static variable, the only thing that is 'static' ('constant'), is that its contents are remembered between function calls. The variable's address, size, and content can be changed at any time.
- If you use a line such as this: static var := 123, var is only set to that value once (when the script loads), the value is not reset to 123 each time you use the function.
- When the function call is completed, the variable will maintain its value.
- Static lines can be described as 'ghost lines', they are only executed once, after that, they are invisible to the script.
- All static variables are local.
- A local variable is either static or non-static.
- For any (non-parameter) non-static local variables that appear within the body of a function, their content is discarded when the function call has ended.
Code: Select all
Loop, 3
MsgBox, % TestStc1A() " " TestStc1B()
Loop, 3
MsgBox, % TestStc2A() " " TestStc2B()
MsgBox, % ColorNameToRGB("red") " " ColorNameToRGB("yellow") ;FF0000 FFFF00
return
TestStc1A()
{
vCount++
return vCount
}
TestStc1B()
{
static vCount
vCount++
return vCount
}
TestStc2A()
{
vText .= "a"
return vText
}
TestStc2B()
{
static vText
vText .= "a"
return vText
}
ColorNameToRGB(vName)
{
static oArray := {red:"FF0000",yellow:"FFFF00",green:"00FF00",blue:"0000FF"}
return oArray[vName]
}
> BYREF PARAMETERS
- In AHK, function parameters are either ByVal (by value), the default, or ByRef (by reference), specified by using 'ByRef'.
- A ByRef parameter is an input parameter that can also be used as an output parameter.
- If you pass a variable into a ByRef parameter, the function can modify that variable.
- Note: if you pass something that isn't a variable into a ByRef parameter, e.g. 123 or 1+1, since you didn't pass a variable, there is no variable for the function to modify.
- Note: if you pass obj.key, the function receives the value of obj.key, and has no knowledge of obj.key, it cannot modify obj.key in the same way it would modify a variable.
- Note: if you pass a non-variable to a ByRef parameter, the parameter is passed ByVal (a value is passed), and not ByRef (a variable reference is passed).
- In the example below, ByVal parameters (default), and ByRef parameters, are contrasted.
Code: Select all
var1 := "A", var2 := "B"
TestByRef1A(var1, var2)
MsgBox, % var1 var2 ;AB
var1 := "A", var2 := "B"
TestByRef1B(var1, var2)
MsgBox, % var1 var2 ;__
TestByRef1A(var1, var2)
{
;this will modify the variables inside the function only
var1 := "_", var2 := "_"
}
TestByRef1B(ByRef var1, ByRef var2)
{
;this will modify the variables inside and outside the function
var1 := "_", var2 := "_"
}
- In the example below, pass a variable, and IsByRef returns 1.
- Pass a value, and IsByRef returns 0.
Code: Select all
vText := "abc"
vNum1 := 123
vNum2 := 123.456
oArray := []
MsgBox, % TestByRef(vText) ;1
MsgBox, % TestByRef(vNum1) ;1
MsgBox, % TestByRef(vNum2) ;1
MsgBox, % TestByRef(oArray) ;1
MsgBox, % TestByRef("abc") ;0
MsgBox, % TestByRef(123) ;0
MsgBox, % TestByRef(123.456) ;0
MsgBox, % TestByRef(1+1) ;0
MsgBox, % TestByRef([]) ;0
TestByRef(ByRef vItem)
{
return IsByRef(vItem)
}
- ByRef parameters can be used to return multiple values from a function.
Code: Select all
vPath := A_ScriptFullPath
SplitPath(vPath, vName, vDir, vExt, vNameNoExt, vDrive)
MsgBox, % Format("{}`r`n{}`r`n{}`r`n{}`r`n{}`r`n{}", vPath, vName, vDir, vExt, vNameNoExt, vDrive)
SplitPath(vPath, ByRef vName, ByRef vDir, ByRef vExt, ByRef vNameNoExt, ByRef vDrive)
{
SplitPath, vPath, vName, vDir, vExt, vNameNoExt, vDrive
}
- Specifying parameters as ByRef, allows data to be passed to functions more quickly.
- Here is a rough benchmark test.
- (Note: there are many considerations, not addressed here, when seeking good benchmark test results.)
Code: Select all
#NoEnv
AutoTrim, Off
SetBatchLines, -1
ListLines, Off
;test ByVal v. ByRef
vLen := 1000000
vCount := 1000000
;vLen *= 10, vCount /= 10 ;much faster
vLen /= 10, vCount *= 10 ;much slower (adding iterations slows more than increasing string length)
vText := Format("{:" vNum "}", "") ;multiple zeros
vText := StrReplace(vText, " ", "a")
Clipboard .= "`r`n"
vTickCount := A_TickCount
Loop % vCount
vText2 := StrUpperByVal(vText)
Clipboard .= "`t" (A_TickCount - vTickCount)
vTickCount := A_TickCount
Loop % vCount
vText2 := StrUpperByRef(vText)
Clipboard .= "`t" (A_TickCount - vTickCount)
vTickCount := A_TickCount
Loop % vCount
vText2 := StrUpperByVal(vText)
Clipboard .= "`t" (A_TickCount - vTickCount)
vTickCount := A_TickCount
Loop % vCount
vText2 := StrUpperByRef(vText)
Clipboard .= "`t" (A_TickCount - vTickCount)
MsgBox, % "done"
return
StrUpperByVal(vText)
{
StringUpper, vText, vText
return vText
}
StrUpperByRef(ByRef vText)
{
StringUpper, vText, vText
return vText
}
> VARIADIC FUNCTIONS
- Variadic functions can have a variable number of parameters.
- They have 0 or more normal parameters, and then a 'variadic parameter'.
- All of the items in the 'variadic parameter' are retrieved as keys in an array by the function. With key names 1, 2, 3 etc.
- At present, ByRef parameters cannot be used with the 'variadic parameter'.
Code: Select all
MsgBox, % Concatenate1("a", "b", "c")
oArray := ["a", "b", "c"]
MsgBox, % Concatenate1(oArray*)
MsgBox, % Concatenate1(["a", "b", "c"]*)
MsgBox, % Concatenate2("a", "b", "c")
Concatenate1(oParams*)
{
;a variadic function with no leading normal parameters
Loop, % oParams.Length()
vOutput .= oParams[A_Index]
return vOutput
}
Concatenate2(var1, var2, oParams*)
{
;a variadic function with leading normal parameters
vOutput := var1 var2
Loop, % oParams.Length()
vOutput .= oParams[A_Index]
return vOutput
}
> OMIT PARAMETERS: CUSTOM FUNCTIONS / COM OBJECTS
- From v1.1.12 onwards, optional parameters can be omitted in functions. This also made the ComObjMissing function unnecessary.
- However, if you wanted a parameter, used with a COM object, to sometimes be omitted, and to sometimes be used, you could use the alternative mentioned below:
jeeswg's objects tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=29232
Code: Select all
m := ComObjMissing() ;AHK v1
m := ComObject(0xA, 0x80020004) ;AHK v1/v2
https://autohotkey.com/docs/AHKL_ChangeLog.htm#v1.1.12.00
ComObjActive()1.1.12.00 - August 14, 2013
Optional parameters can be omitted by writing two consecutive commas, as in InStr(a, b,, 2). Unlike previous versions, this now works for objects (including COM objects) and built-in functions. [a,,b] can be used to create a sparse array.
https://autohotkey.com/docs/commands/ComObjActive.htm
==================================================Creates an object which may be used in place of an optional parameter's default value when calling a method of a COM object. [v1.1.12+]: This function is obsolete. Instead, simply write two consecutive commas, as in Obj.Method(1,,3)
ParamObj := ComObjMissing()
> MULTIPLE INPUT PARAMETERS
Ways to increase the number of input variables.
Code: Select all
;lots of input parameters
InFuncParams(var1, var2, var3)
{
return var1 " " var2 " " var3
}
;string containing comma(/delimiter)-separated list
InFuncSepList(vList, vDelim:="")
{
oTemp := StrSplit(vList, vDelim)
return oTemp.1 " " oTemp.2 " " oTemp.3
}
;global variables
InFuncGlobal()
{
global var1, var2, var3
return var1 " " var2 " " var3
}
;input parameter array
InFuncLinearArray(oArray)
{
return oArray.1 " " oArray.2 " " oArray.3
}
;input parameter array
InFuncAssocArray(oArray)
{
return oArray.key1 " " oArray.key2 " " oArray.key3
}
;variadic function
InFuncVariadic(oParams*)
{
return oParams.1 " " oParams.2 " " oParams.3
}
vOutput := ""
vOutput .= InFuncParams("abc", "def", "ghi") "`r`n"
vOutput .= InFuncSepList("abc,def,ghi", ",") "`r`n"
var1 := "abc", var2 := "def", var3 := "ghi"
vOutput .= InFuncGlobal() "`r`n"
vOutput .= InFuncLinearArray(["abc", "def", "ghi"]) "`r`n"
vOutput .= InFuncAssocArray({key1:"abc", key2:"def", key3:"ghi"}) "`r`n"
vOutput .= InFuncVariadic("abc", "def", "ghi") "`r`n"
vOutput .= InFuncVariadic(["abc", "def", "ghi"]*) "`r`n"
MsgBox, % vOutput
> MULTIPLE OUTPUT PARAMETERS
Ways to increase the number of output variables.
Code: Select all
;string containing comma(/delimiter)-separated list
OutFuncSepList()
{
var1 := "abc", var2 := "def", var3 := "ghi"
return var1 "," var2 "," var3
}
;global variables
OutFuncGlobal()
{
global var1, var2, var3
var1 := "abc", var2 := "def", var3 := "ghi"
}
;output array
OutFuncLinearArray()
{
var1 := "abc", var2 := "def", var3 := "ghi"
return [var1, var2, var3]
}
;output parameter array
OutFuncAssocArray()
{
var1 := "abc", var2 := "def", var3 := "ghi"
return {key1:var1, key2:var2, key3:var3}
}
;ByRef parameters
OutFuncByRefParams(ByRef var1, ByRef var2, ByRef var3)
{
var1 := "abc", var2 := "def", var3 := "ghi"
}
;ByRef parameters
OutFuncByRefArray(ByRef oArray)
{
var1 := "abc", var2 := "def", var3 := "ghi"
oArray := [var1, var2, var3]
}
vOutput := ""
vOutput .= OutFuncSepList() "`r`n"
OutFuncGlobal()
vOutput .= var1 "," var2 "," var3 "`r`n"
oArray := OutFuncLinearArray()
vOutput .= oArray.1 "," oArray.2 "," oArray.3 "`r`n"
oArray := OutFuncAssocArray()
vOutput .= oArray.key1 "," oArray.key2 "," oArray.key3 "`r`n"
OutFuncByRefParams(var1, var2, var3)
vOutput .= var1 "," var2 "," var3 "`r`n"
OutFuncByRefArray(oArray)
vOutput .= oArray.1 "," oArray.2 "," oArray.3 "`r`n"
MsgBox, % vOutput
> INPUT PARAMETERS: COUNT
How many parameters were passed to the function.
Code: Select all
VariadicFunc(oParams*)
{
return oParams.Length()
}
MsgBox, % VariadicFunc("a") ;1
MsgBox, % VariadicFunc("a", "b") ;2
MsgBox, % VariadicFunc("a", "b", "c") ;3
> INPUT PARAMETERS: CHECK IF PARAMETER OMITTED
Omit parameters / default values.
Code: Select all
OmitFunc1(var1:="default", var2:="default", var3:="default")
{
return var1 " " var2 " " var3
}
OmitFunc2(oParams*)
{
var1 := oParams.HasKey(1) ? oParams.1 : "omitted"
var2 := oParams.HasKey(2) ? oParams.2 : "omitted"
var3 := oParams.HasKey(3) ? oParams.3 : "omitted"
return var1 " " var2 " " var3
}
MsgBox, % OmitFunc1(1,, 3)
MsgBox, % OmitFunc1(1, 2)
MsgBox, % OmitFunc1(,, 3)
MsgBox, % OmitFunc2(1,, 3)
MsgBox, % OmitFunc2(1, 2)
MsgBox, % OmitFunc2(,, 3)
> DUAL ROLE INPUT/OUTPUT PARAMETER (BYREF AND OBJECTS)
Can an input variable also be an output variable. Passing variables ByRef, and passing objects ByRef/non-Byref.
Code: Select all
FuncInOutStr(ByRef var)
{
var := "after"
}
var := "before"
MsgBox, % var ;before
FuncInOutStr(var)
MsgBox, % var ;after
oArray.MyKey := "before"
FuncInOutObj(oArray)
{
oArray.MyKey := "after"
}
obj := {}
obj.MyKey := "before"
MsgBox, % obj.MyKey ;before
FuncInOutObj(obj)
MsgBox, % obj.MyKey ;after
> PARAMETERS ARE CLEARED AFTER USE
Are variables inside functions cleared when the function ends.
Code: Select all
;global - not cleared
;ByRef - not cleared
FuncTestClear1()
{
global addr
obj := ["a", "b", "c"]
addr := &obj
obj2 := Object(addr)
MsgBox, % obj2.2 ;c
}
FuncTestClear2(ByRef obj2:="")
{
global addr
obj := ["a", "b", "c"]
addr := &obj
obj2 := Object(addr)
MsgBox, % obj2.2 ;c
}
FuncTestClear3(obj2:="")
{
global addr
obj := ["a", "b", "c"]
addr := &obj
obj2 := Object(addr)
MsgBox, % obj2.2 ;c
}
FuncTestClear1()
;obj2 := Object(addr) ;crashes AHK
MsgBox, % obj2.2
FuncTestClear2()
;MsgBox, % addr
;obj2 := Object(addr) ;crashes AHK
;MsgBox, % obj2.2
FuncTestClear2(obj2)
;MsgBox, % addr
;obj2 := Object(addr) ;crashes AHK
MsgBox, % obj2.2
MsgBox
FuncTestClear3(obj2)
;MsgBox, % addr
;obj2 := Object(addr) ;crashes AHK
MsgBox, % obj2.2
> HANDLE BINARY DATA
Using the variable address/ByRef parameters for handling binary data.
Note: in AHK v2, you can output binary data as the return value.
Code: Select all
FuncWriteBinAddr(vAddr)
{
NumPut(0x11223344, vAddr+0, 0, "UInt")
}
FuncWriteBinVar(ByRef vData)
{
VarSetCapacity(vData, 4, 0)
NumPut(0x11223344, &vData, 0, "UInt")
}
FuncWriteBinVarOut() ;AHK v2 only
{
VarSetCapacity(vData, 4, 0)
NumPut(0x11223344, &vData, 0, "UInt")
return vData
}
;address
VarSetCapacity(vData, 4, 0)
FuncWriteBinAddr(&vData)
vNum := NumGet(&vData, 0, "UInt")
MsgBox, % Format("0x{:X}", vNum)
;ByRef
FuncWriteBinVar(vData)
vNum := NumGet(&vData, 0, "UInt")
MsgBox, % Format("0x{:X}", vNum)
;return value
;vData := FuncWriteBinVarOut()
;vNum := NumGet(&vData, 0, "UInt")
;MsgBox, % Format("0x{:X}", vNum)
> PARAMETER VARIABLES VERSUS OTHER VARIABLES
Are variables inside functions local or global (and: parameter variables versus variables inside the function).
Code: Select all
;function variables to consider:
;local/global/static
;param (ByRef)/param (non-ByRef)
;(variable used with return)
global vGbl
Func1(vGbl:="")
{
vGbl := 1 ;does not affect global vGbl because it's a parameter variable
}
Func2()
{
vGbl := 2
}
vGbl := 0
Func1()
MsgBox, % vGbl
Func2()
MsgBox, % vGbl
> CREATING VARIABLES DYNAMICALLY WITHIN FUNCTIONS
Creating variables dynamically within functions.
Code: Select all
;dynamic variables can overwrite global variables
;var1 was already defined within the function body, so the global variables are not overwritten
;var2/var3 were not already defined, so the *global* variables are assigned to
FuncDynamicVars()
{
var1 := ""
Loop, 3
var%A_Index% := A_Index
}
var1 := "a", var2 := "b", var3 := "c"
MsgBox, % var1 " " var2 " " var3 ;a b c
FuncDynamicVars()
MsgBox, % var1 " " var2 " " var3 ;a 2 3
;local used, so no global variables are overwritten
FuncDynamicVarsLocal()
{
local
var1 := ""
Loop, 3
var%A_Index% := A_Index
}
var1 := "a", var2 := "b", var3 := "c"
MsgBox, % var1 " " var2 " " var3 ;a b c
FuncDynamicVarsLocal()
MsgBox, % var1 " " var2 " " var3 ;a 2 3
> REFERRING TO FUNCTIONS DYNAMICALLY
Referring to functions dynamically.
Either by name, or via a Func object created via Func, or via a BoundFunc object created using Bind() which can also specify the contents for the first n parameters.
Code: Select all
FuncConcat(var1, var2, var3)
{
return var1 var2 var3
}
vFunc := "FuncConcat"
MsgBox, % %vFunc%(1, 2, 3) ;123
oFunc := Func("FuncConcat")
;MsgBox, % IsObject(oFunc) ;1
MsgBox, % %oFunc%(1, 2, 3) ;123
oFunc := Func("FuncConcat")
;MsgBox, % IsObject(oFunc) ;1
MsgBox, % %oFunc%(1, 2, 3) ;123
oFunc := Func("FuncConcat").Bind()
MsgBox, % %oFunc%(1, 2, 3) ;123
oFunc := Func("FuncConcat").Bind(4)
MsgBox, % %oFunc%(2, 3) ;423
oFunc := Func("FuncConcat").Bind(4, 5)
MsgBox, % %oFunc%(3) ;453
oFunc := Func("FuncConcat").Bind(4, 5, 6)
MsgBox, % %oFunc%() ;456
> OVERRIDE BUILT-IN FUNCTIONS
Built-in/custom functions with the same name.
Code: Select all
;the built-in function is overridden
Sqrt(vNum)
{
return "root: " (vNum**0.5)
}
MsgBox, % Sqrt(4)
> #INCLUDE PATH, #INCLUDE <LIBNAME> AND AUTO-INCLUDE
Including scripts (and functions) using #Include.
And what happens when a function isn't found (auto-include).
#Include / #IncludeAgain - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/_Include.htm
APPROACH 1: #Include FileFullPathA script behaves as though the included file's contents are physically present at the exact position of the #Include directive (as though a copy-and-paste were done from the included file).
To include a file explicitly:
#Include C:\Program Files\AutoHotkey\MyFile.ahk
APPROACHES 2/3:
These 2 lines serve a similar purpose:
#Include <MyPrefix_MyFunc>
MyPrefix_MyFunc() ;a call to a function that is not present in the script
#Include <MyPrefix_MyFunc>
This checks 3 'Lib' folders for scripts called 'MyPrefix_MyFunc.ahk', and then checks those 3 'Lib' folders for 'MyPrefix.ahk'. If it finds a file, that file in included as though it were copied and pasted at that location.
MyPrefix_MyFunc()
Assuming that the function 'MyPrefix_MyFunc' isn't already defined. This checks 3 'Lib' folders for scripts called 'MyPrefix_MyFunc.ahk', and then checks those 3 'Lib' folders for 'MyPrefix.ahk'. If it finds a file, that file in included, not as though it were copied and pasted at that location, but in a different way described lower down.
APPROACH 2: #Include <LibName>
To check if 6 possible filenames exist and include the first matching file.
#Include <MyPrefix_MyFunc>
3 library folders are checked: local/user/standard:
%A_ScriptDir%\Lib ;local library
%A_MyDocuments%\Lib ;user library
%A_AhkDir%\Lib ;standard library [there is no such variable in AHK at present][where 'A_AhkDir' would be the dir taken from 'A_AhkPath']
6 files are checked for, listed in the order that they are checked for:
%A_ScriptDir%\Lib\MyPrefix_MyFunc.ahk
%A_MyDocuments%\Lib\MyPrefix_MyFunc.ahk
%A_AhkDir%\Lib\MyPrefix_MyFunc.ahk
%A_ScriptDir%\Lib\MyPrefix.ahk
%A_MyDocuments%\Lib\MyPrefix.ahk
%A_AhkDir%\Lib\MyPrefix.ahk
If no matching file is found, an error is raised:
'Error: Call to nonexistent function.'
APPROACH 3: AUTO-INCLUDE
If a call is done to a function that does not exist:
e.g. 'MyPrefix_MyFunc()'
then that is very similar to doing '#Include <MyPrefix_MyFunc>'
but with one key difference,
again 6 files are checked for a possible match,
however, instead, the file contents are not 'copied and pasted' at the point at which the function was called,
however, the function is executed.
Thus an auto-include 'includes' a script, in the sense that all of its functions are made available for use, however, it does not 'copy and paste' the file contents to a specific point.
Note: If no matching file is found, an error is raised:
'Error: Call to nonexistent function.'
FURTHER NOTE:
- Think of any files that are included as in an abstract place (the error messages even indicate a completely separate line numbering system), away from the main script.
- Both #Include and auto-include cause files to be 'present' in an abstract place. However, any #Include line has an additional effect, any time it is 'executed' in the flow of the script, it effectively executes all of the code in that file (as though it were copied and pasted at that point).
See also:
#Include <LibName> v. auto-include - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=53647
==================================================
> PERFORM ACTIONS AT SCRIPT STARTUP
A function that will run on script startup (due to 'static').
Code: Select all
MyFunc()
{
static vDummy := MyFunc()
SoundBeep
MsgBox, % "hello world"
}
GeekDude's tips and tricks - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=7190
Static init functions (was: Goto Eof) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=13&t=4172&p=23235#p23235
jeeswg's documentation extension tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=33596
==================================================
> COMBINE SCRIPTS: A SCRIPT WITH MULTIPLE AUTO-EXECUTE SECTIONS
- An auto-execute section, is code at the top of a script, executed before the rest of the script.
- If you want to combine multiple scripts by using #Include, only one of those scripts can be at the top of the combined script.
- A workaround, is to use functions that call themselves, when the script starts.
- The principle of a function, that calls itself when the script starts, is outlined above in the section: 'PERFORM ACTIONS AT SCRIPT STARTUP'.
- Here is an example of combining 2 scripts, consisting of a master script, and 2 child scripts:
Code: Select all
;combine 2 scripts - master.ahk
#Include combine 2 scripts - child 1.ahk
#Include combine 2 scripts - child 2.ahk
Code: Select all
;combine 2 scripts - child 1.ahk
AutoExec1()
{
static vDummy := AutoExec1()
global vInit1 := 1
}
q::
MsgBox, % vInit1
return
Code: Select all
;combine 2 scripts - child 2.ahk
AutoExec2()
{
static vDummy := AutoExec2()
global vInit2 := 2
}
w::
MsgBox, % vInit2
return
Code: Select all
[code]
;combine 2 scripts - child 1 (with normal auto-execute section).ahk
vInit1 := 1
q::
MsgBox, % vInit1
return
> FAT ARROW FUNCTIONS (AHK V2)
Code: Select all
;fat arrow functions
;Variables and Expressions - Definition & Usage | AutoHotkey v2
;https://lexikos.github.io/v2/docs/Variables.htm#fat-arrow
sumfn := Sum(a, b) => a + b
MsgBox(%sumfn%(1, 2)) ;3
concatfn := Concat(a, b) => a b
MsgBox(%concatfn%(1, 2)) ;12
> NESTED FUNCTIONS (AHK V2)
Code: Select all
;nested functions
;Functions - Definition & Usage | AutoHotkey v2
;https://lexikos.github.io/v2/docs/Functions.htm#nested
outer(x)
{
inner(y)
{
MsgBox(x " " y)
}
inner("b")
inner("c")
}
outer("a")
> CLOSURES (AHK V2)
Code: Select all
;closures
;Functions - Definition & Usage | AutoHotkey v2
;https://lexikos.github.io/v2/docs/Functions.htm#closures
;To create a closure, pass the name of a nested function to Func.
make_greeter(format)
{
greet(subject)
{
MsgBox(Format(format, subject))
}
return Func("greet")
}
greetfn := make_greeter("hello {}!")
greetfn.call(A_UserName)
greetfn.call("world")
> RECURSIVE FUNCTIONS
- A recursive function is a function that calls itself.
- Here, two different functions for calculating factorials are demonstrated, one recursive, one non-recursive.
Code: Select all
q:: ;demonstrating a factorial function
vOutput := ""
Loop, 20
vOutput .= Fact(A_Index) "`t" FactAlt(A_Index) "`r`n"
MsgBox, % vOutput
return
;non-recursive
Fact(vNum)
{
local
if (vNum < 1) || (vNum > 20)
return
vOutput := 1
Loop, % vNum
vOutput *= A_Index
return vOutput
}
;a recursive function
FactAlt(vNum)
{
local
if (vNum < 1) || (vNum > 20)
return
if (vNum = 1)
return 1
return vNum * FactAlt(vNum-1)
}
> LINKS
[shows an example of a callback function]
GUIs via DllCall: list windows/child windows (controls) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=36405
[Sort supports callback functions]
Sort - Syntax & Usage | AutoHotkey
https://autohotkey.com/docs/commands/Sort.htm
[RegExMatch/RegExReplace support callback functions]
Regular Expression Callouts | AutoHotkey
https://autohotkey.com/docs/misc/RegExCallout.htm
[OnClipboardChange/OnError/OnExit/OnMessage support callback functions]
Alphabetical Command and Function Index | AutoHotkey
https://autohotkey.com/docs/commands/
==================================================