函数
目录
- 入门和简单示例
- 参数
- 可选参数
- 返回值给调用者
- 可变参数函数
- 局部变量
- 动态调用函数
- 优化布尔求值
- 在函数中使用子程序
- Return, Exit 及一般说明
- 使用 #Include 在多个脚本间共享函数
- 函数库: 标准库和用户库
- 内置函数
入门和简单示例
函数类似于子程序 (Gosub), 不过它还可以从调用者那里接受参数 (输入). 而且, 函数还可以返回值给调用者. 参考下面的例子, Add 函数接受两个参数并返回它们之和:
Add(x, y) { return x + y ; "Return" 可直接返回 表达式 的结果. }
上面就是一个函数 定义, 因为它创建了一个名称为 "Add" (不区分大小写) 的函数, 并且确立了调用它时必须准确的提供两个参数 (x 和 y). 要调用此函数, 请把它的结果通过 := 运算符 赋值给变量. 例如:
Var := Add(2, 3) ; 数字 5 将被保存到 Var.
当然, 调用函数时也可以不保存其返回值:
Add(2, 3)
不过这种情况下, 函数的任何返回值都会被丢弃; 所以除非函数还有除了返回值之外的功能(译者注: 比如函数内修改了全局变量; 通过 ByRef 修改了变量值...等等), 否则这次调用毫无意义.
由于函数调用属于 表达式 语法, 所以在其参数列表中的任何变量名称都不应该用百分号包围. 同时, 表示原义字符串的变量则应该用双引号包围. 例如:
if InStr(MyVar, "fox") MsgBox The variable MyVar contains the word fox.
最后, 通常在命令的参数中也可以调用函数 (除了像 StringLen 中的 OutputVar 和 InputVar 这种参数之外). 另外, 在不支持 表达式 的参数中调用函数时必须加上 "% " 前缀(百分号加一个空格), 例如:
MsgBox % "The answer is: " . Add(3, 2)
在原生支持表达式的参数中也允许加上 "% " 前缀, 不过它会被简单的忽略了.(译者注: 总之在你不确定是否应该加 '% ' 前缀的情况下, 加上总是没错的.)
参数
定义函数时, 其参数都在其名称后面的括号中列出 (函数名和左括号之间不能有空格). 如果函数不接受任何参数, 请把括号留空, 例如:GetCurrentTimestamp()
.
ByRef 参数: 从函数的角度看, 参数本质上是 局部变量, 除非它们被定义为 ByRef, 例如:
Swap(ByRef Left, ByRef Right) { temp := Left Left := Right Right := temp }
在上述例子中, ByRef 的使用让相应的参数变成从调用者传递进来的变量的一个别名. 换句话说, 参数和调用者的变量都引用内存中相同的内容. 这样使得 Swap 函数可以通过移动 Left 的内容到 Right 中来修改调用者的变量, 反之亦然.
与之相比, 在上述例子中如果没有使用 ByRef, Left 和 Right 将是调用者变量的副本, 因此 Swap 函数不会对外部产生影响.
由于 return 只能返回一个值给函数的调用者, 所以可以使用 ByRef 返回更多的结果. 这是通过函数将调用者传递进来的变量 (通常为空值的变量) 赋值实现的.
传递大字符串给函数时, 使用 ByRef 提高了性能, 并且通过避免生成字符串的副本节约了内存. 同样地, 使用 ByRef 送回长字符串给调用者通常比类似 Return HugeString
的方式执行的更好.
[AHK_L 60+]: 如果传递给 ByRef 参数的变量是不可修改的, 那么函数会表现的就像没有 "ByRef" 关键字一样. 例如, Swap(A_Index, i)
保存 A_Index 的值到 i 中, 但是当 Swap 函数返回时赋给 Left 的值会被丢弃.(译者注: 因为 A_Index 是只读的内置变量.)
[v1.1.01+]:IsByRef() 函数可以用来判断调用者是否为指定的 ByRef 参数提供了变量.
已知限制:
- 对象的属性在 ByRef 的参数中不会被视为变量. 例如, 如果
foo.bar
传递给 ByRef 参数, 那么它将表现的就像没有 ByRef 关键字一样. - 不能传递 Clipboard, 内置变量 或 环境变量 给函数的 ByRef 参数, 无论脚本中有没有 #NoEnv 语句.
- 尽管函数可以递归调用它自己, 但是如果它传递它自己的一个 局部变量 或非 ByRef 参数给自己的 ByRef, 那么新一层的 ByRef 参数将引用它自己那个名称的局部变量而不是之前层的. 然而, 当函数传递给它自己 全局变量, 静态变量 或 ByRef 参数时不会产生这样的问题.
- 如果一个参数在函数调用中被解析为一个变量 (例如
Var
或++Var
或Var*=2
), 它左边或右边的其他参数可能在它被传递给函数前修改这个变量. 例如, 当 Var 初始为 0 时func(Var, Var++)
会意外地传递 1 和 0, 即使函数的首个参数不是 ByRef 类型时. 因为这种行为是违反常规的, 所以可能在将来的版本中改变. - ByRef 不直接支持函数调用 COM 客户端, 或调用 COM 方法. 作为替代, 脚本必须接受或传递一个 wrapper object (包装对象) 包含特殊的 VarType (变量类型) 和变量内存地址.
可选参数
定义函数时, 可以把它的一个或多个参数标记为可选的. 这可以通过在参数后添加一个等号(在 v1.1.09+ 中还可以为 :=
)后面跟着指定一个默认值来表示. 下面这个函数中的 Z 就是一个可选参数:
Add(X, Y, Z:=0) { return X + Y + Z }
从 v1.1.09 起,=
和 :=
都支持. 为了与表达式赋值保持一致且与将来的 AutoHotkey 版本兼容, 建议使用后者.
当调用者传递 三个 参数给上面的函数时, Z 的默认值被忽略. 但当调用者仅传递 两个 参数时, Z 自动接受默认值 0.
可选参数不能孤立地放在参数列表的中间. 换句话说, 在首个可选参数右边的所有参数都必须定义为可选的. [AHK_L 31+]: 调用函数时可以省略参数列表中间的可选参数, 如下所示: 从 v1.1.12 开始, 动态调用函数或方法时也支持这种特性.
Func(1,, 3) Func(X, Y:=2, Z:=0) { ; 注意: 这里的 Z 必须是可选参数. MsgBox %X%, %Y%, %Z% }
从 v1.0.46.13 开始, ByRef 参数也支持默认值; 例如: Func(ByRef p1 = "")
. 每当调用者省略这样的参数时, 函数会创建一个包含默认值的局部变量; 不过, 这种情况下 "ByRef" 关键字没有任何意义.
参数的默认值必须是下列形式中的一种: true
, false
, 原义的整数, 原义的浮点数 或 引号包围的/原义的字符串例如 "fox" 或 "" (但在 1.0.46.13 之前的版本中字符串只支持 "").
返回值给调用者
Test := returnTest() MsgBox % Test returnTest() { return 123 }
如果要从函数中返回额外的结果, 可以使用 ByRef:
returnByRef(A,B,C) MsgBox % A "," B "," C returnByRef(ByRef val1, ByRef val2, ByRef val3) { val1 := "A" val2 := 100 val3 := 1.1 return }
[v1.0.97+]: 可以使用 对象 和 数组 返回多值甚至是命名值:
Test1 := returnArray1() MsgBox % Test1[1] "," Test1[2] Test2 := returnArray2() MsgBox % Test2[1] "," Test2[2] Test3 := returnObject() MsgBox % Test3.id "," Test3.val returnArray1() { Test := [123,"ABC"] return Test } returnArray2() { x := 456 y := "EFG" return [x, y] } returnObject() { Test := {id: 789, val: "HIJ"} return Test }
可变参数函数 [AHK_L 60+]
定义函数时, 在最后一个参数后面写一个星号来标记此函数为可变参数的, 这样让它可以接收可变数目的参数:
Join(sep, params*) { for index,param in params str .= param . sep return SubStr(str, 1, -StrLen(sep)) } MsgBox % Join("`n", "one", "two", "three")
调用可变参数函数时, 通过保存在函数的最后参数中的对象可以访问剩余的参数. 函数的首个超出参数是 params[1]
, 第二个是 params[2]
以此类推. 和所有的标准对象一样, 使用 params.MaxIndex()
可以确定最大的索引值 (这里为参数的数目). 但是如果没有参数, MaxIndex 会返回空字符串.
注意:
可变参数函数调用
虽然可变参数函数可以 接受 可变数目的参数, 不过在函数调用中使用相同的语法可以把数组作为参数传递给 任何 函数:
substrings := ["one", "two", "three"] MsgBox % Join("`n", substrings*)
注意:
- 在源数组中参数的编号从 1 开始.
- 数组中的可选参数可以完全省略.
- 调用自定义函数时数组参数可以包含命名项, 在其他情况下则不支持命名项.
- 目标函数也可以是可变的, 此时命名项被复制, 即使它们没有一致的形式参数.
- 这样的语法还可以用于调用对象的方法或获取对象的属性; 例如,
Object.Property[Params*]
. 从 v1.1.12 开始, 它还可以用于设置属性.
已知限制:
- 只有最右边的那个参数才可以这样展开. 例如, 支持
Func(x, y*)
但不支持Func(x*, y)
. - 在星号 (
*
) 和参数列表中最后的形式参数间不能存在任何的非空白字符.
局部变量和全局变量
局部变量
局部变量是特定于单个函数的, 只在该函数内可见. 因此, 局部变量可能具有与全局变量相同的名称, 并且两个变量都有单独的内容. 不同的函数也可以安全地使用相同的变量名.
当函数返回值时, 所有非静态的本地变量都自动释放( 变为空).
默认情况下, 函数内访问或创建的变量是本地的, 但下列情况例外:
- 超级全局 变量, 包括 classes.
- 内置变量例如 Clipboard, ErrorLevel, 和 A_TimeIdle.
- 动态变量引用 如果该名称没有本地变量, 则可以解析为现有的全局变量.
- 创建伪数组的命令 即使只声明第一个元素, 也可以将所有元素创建为全局元素.
默认值也可以被覆盖如下所示.
全局变量
要在函数中引用现有的全局变量 (或创建新的), 需要在使用前声明此变量为全局的. 例如:
LogToFile(TextToLog) { global LogFileName ; 此全局变量之前已经在函数外的某个地方赋值了. FileAppend, %TextToLog%`n, %LogFileName% }
假设全局模式: 如果函数需要访问或创建大量的全局变量, 通过在函数的首行使用单词 "global" 或声明局部变量可以把函数定义为假设其所有的变量都是全局的 (除了它的参数). 例如:
SetDefaults() { global ; 如果此函数的首行是类似于 "local MyVar" 这样的, 那么这个单词可以省略. MyGlobal := 33 ; 把 33 赋值给全局变量, 必要时首先创建这个变量. local x, y:=0, z ; 在这种模式中局部变量必须进行声明, 否则会假设它们为全局的. }
函数还可以使用这种假设全局模式来创建全局 数组, 例如赋值给 Array%A_Index%
的循环.
超级全局变量 [v1.1.05+]: 如果全局声明出现在任何函数的外面, 默认情况下它可以对所有函数有效. 这样可以避免在每个函数中重复声明变量的需要. 不过, 如果声明了含有相同名称的函数参数或局部变量, 那么它会优先于全局变量. 由 class 关键字创建的变量也是超级全局的.
静态变量
静态变量总是隐式的局部变量, 但和局部变量的区别是它们的值在多次调用期间是记住的. 例如:
LogToFile(TextToLog) { static LoggedLines = 0 LoggedLines += 1 ; 保持局部的计数 (它的值在多次调用期间是记住的). global LogFileName FileAppend, %LoggedLines%: %TextToLog%`n, %LogFileName% }
静态初始化: 在 1.0.46 以前的版本中, 所有的静态变量都是以空值开始; 所以要检查静态变量首次被使用的唯一方法是检查它是否为空值. 在 v1.0.46+, 静态变量可以初始化为 ""
外的其他值, 通过在后面跟着 :=
或 =
及后面这些形式的其中一种:true
,false
, 原义的整数, 原义的浮点数或引号包围的/原义的字符串, 如 "fox"
. 例如:static X:=0, Y:="fox"
. 每个静态变量只初始化一次 (在脚本开始执行前).
[AHK_L 58+]: 支持 Static var := expression
. 根据这些表达式在脚本中出现的顺序对它们进行计算, 紧接着才进入脚本的自动执行段.
假设静态模式 [v1.0.48+]: 通过在函数的首行使用单词 "static" 可以把函数定义为假设其所有的变量都是静态的 (除了它的参数). 例如:
GetFromStaticArray(WhichItemNumber) { static static FirstCallToUs := true ; 静态声明初始化仍然只运行一次 (在脚本执行前). if FirstCallToUs ; 在首次调用而不在后续的调用时创建静态数组. { FirstCallToUs := false Loop 10 StaticArray%A_Index% := "Value #" . A_Index } return StaticArray%WhichItemNumber% }
在假设静态模式中, 任何非静态变量都必须声明为局部变量或全局变量.
关于局部和全局的更多信息
通过逗号分隔多个变量这样可以在同一行声明它们, 例如:
global LogFileName, MaxRetries := 5 static TotalAttempts = 0, PrevResult
在 v1.0.46+, 通过后面跟着 :=
or =
及任意 表达式 局部或全局变量可以在同一行初始化, 如同声明那样 (在声明时 =
运算符和 :=
作用相同). 与 静态初始化 不同, 在每次调用函数时都会对局部变量和全局变量进行初始化, 但只在控制流实际达到它们所在的语句时. 换句话说, 像 local x = 0
这样的一行和写成单独的两行有同样的效果: local x
后面跟着 x = 0
.
因为单词 local, global 和 static 都是在脚本运行时立即处理的, 所以不能使用 IF 语句 有条件的声明变量. 换句话说, IF 或 ELSE 的 区块 内的声明无条件对声明和函数的闭括号之间的所有行生效. 同时还需注意当前还不支持声明动态变量, 例如 global Array%i%
.
对于创建 数组 的命令 (例如 StringSplit), 如果 假设全局模式 没有生效或数组的首个元素已经声明为局部变量时创建的数组是局部的 (如果函数的某个参数被传递时也是如此, 即使此参数为 ByRef 类型, 因为参数类似于局部变量). 相反地, 如果首个元素已经 声明为全局的, 那么创建全局的数组. 不过, 后面 混乱的常见根源 也适用于这些情况. StringSplit 创建的数组首个元素为 ArrayName0. 对于其他创建数组的命令, 例如 WinGet List, 首个元素为 ArrayName (即没有数字).
在函数中, 任何动态变量引用, 例如 Array%i%
, 总是解析为局部变量, 仅当这样名称的局部变量不存在而全局变量存在时才解析为全局变量. 如果两者都不存在并且需要创建此变量时, 当 假设全局模式 没有生效时它被创建为局部变量. 因此, 仅当函数被定义为 假设全局 函数时, 函数中才可以手动创建全局 数组 (通过使用类似 Array%i% := A_Index
的方法).
混乱的常见根源: 任何对 non-dynamic (非动态引用) 都会在脚本运行时创建那个变量. 例如, 在函数外使用时, MsgBox %Array1%
会在脚本运行的时候创建 Array1 为全局变量. 相反地, 在函数中使用 MsgBox %Array1%
会在脚本运行的时候创建 Array1 为函数的一个局部变量 (除非 假设全局模式 生效), 即使 Array 和 Array0 都声明为全局的.
动态调用函数
在 v1.0.47.06+, 通过百分号可以动态调用函数 (包括 内置函数). 例如,%Var%(x, "fox")
将调用名称保存在 Var 中的函数. 同样地, Func%A_Index%()
将调用 Func1() 或 Func2() 等, 这取决于 A_Index 的当前值.
在 v1.1.07.00+, 在 %Var%()
中的 Var 可包含函数名, 函数对象 或 模仿函数的对象. 如果此函数不存在, 则调用 默认基对象的 __Call 元函数.
如果由于下面的某个原因无法调用函数, 计算包含调用的表达式时可能会过早静默停止, 这样可能会产生问题.
- 调用不存在的函数, 这可以通过使用
If IsFunc(VarContainingFuncName)
来避免. 除了 内置函数, 被调用函数的 定义 必须明确存在于脚本中, 通过类似 #Include 或对 库函数 的非动态调用的方法. - 传递过少的参数, 这可以通过检查 IsFunc() 的返回值来避免 (这是强制参数的数目加上一的数字). 注意: 在 v1.0.48+, 允许传递过多的参数; 每个额外的参数在被完全计算 (包括任何函数调用) 后丢弃.
最后, 对函数的动态调用比正常调用稍慢, 因为正常的调用在脚本开始运行前解析 (查询).
优化布尔求值
当在 表达式 中使用 AND, OR 和 三元运算符 时, 会对它们进行优化来提高性能 (不管当前是否存在函数调用). 通过不计算表达式中那些不影响最终结果的部分来进行优化运算. 为了说明这个概念, 请看这个例子:
if (ColorName <> "" AND not FindColor(ColorName)) MsgBox %ColorName% could not be found.
在上面的例子中, 如果 ColorName 变量为空则永远不会调用 FindColor() 函数. 这是由于 AND 的左侧结果将为 false, 因此其右边不可能让最终的结果为 true.
由于此特性, 所以需要注意到, 如果在 AND 或 OR 的右侧调用函数, 那么函数可能永远不会产生副作用 (例如改变全局变量的内容).
还需要注意在嵌套的 AND 和 OR 串联表达式的求值优化. 例如, 在后面的表达式中每当 ColorName 为空时只会进行最左边的比较. 这是因为此时最左边的比较已经足以确定最终的结果:
if (ColorName = "" OR FindColor(ColorName, Region1) OR FindColor(ColorName, Region2)) break ; 搜索内容为空或找到了匹配.
从上面的例子可以看出, 任何耗时的函数一般应该在 AND 或 OR 的右侧调用从而提高性能. 这种方法还能避免调用接受了一个不合适的值例如空字符串的函数.
在 v1.0.46+, 三元条件运算符 (?:) 也通过不计算丢弃的分支进行求值优化.
在函数中使用子程序
尽管在一个函数中不能 定义 其他函数, 但它可以含有子程序. 与其他子程序一样使用 Gosub 运行它们且使用 Return 返回 (此时 Return 属于 Gosub 而不是函数).
已知限制: 当前每个子程序 (标签) 的名称在整个脚本中必须是唯一的. 如果存在重复的标签, 在运行前程序会通知您.
如果函数使用 Gosub 跳转到公共子程序 (在函数外部的子程序), 那么所有外面的变量都是全局的而且在子程序返回前无法引用函数自身的 局部变量. 不过, A_ThisFunc 仍将包含正在执行的函数名称.
尽管不能使用 Goto 从函数中跳转到外面, 但可以在函数中使用 Gosub 到外部/公共的子程序, 然后在那里使用 Goto.
尽管一般不鼓励使用 Goto, 但它能用来在同个函数中的一个位置跳转到其他位置. 这样有助于简化包含许多个返回点而所有这些点在返回前需要进行一些清理的复杂函数.
函数可以包含能被外部调用的子程序, 例如计时器,GUI g 标签和菜单项. 通常把它们封装到一个单独的文件中供 #Include 使用, 这样避免了它们和脚本的 自动执行段 的冲突. 不过, 还存在下面这些限制:
- 如果它们所在的函数曾经被正常调用, 那么这样的子程序应该只使用 静态 和 全局 变量 (不使用 局部 变量). 这是由于中断函数调用线程的子程序 线程 (反之亦然) 可以改变被中断线程看到的局部变量的值. 此外, 在任何时候函数返回到其调用者时, 它包含的所有局部变量都被置空来释放占用的内存.
- 这样的子程序应该只使用 全局变量 (不使用 静态变量) 作为 GUI 控件的变量.
- 当一个子程序 线程 进入函数后, 在此线程中任何对 动态变量 的引用都被视为是 全局 的 (包括创建数组的那些命令).
Return, Exit 及一般说明
如果函数内的执行流在遇到 Return 前到达了函数的闭括号, 那么函数结束并返回空值 (空字符串) 给其调用者. 当函数明确省略 Return 的参数时也返回空值.
当函数使用 Exit 命令终止 当前线程 时, 其调用者不会接收到返回值. 例如, 这个语句 Var := Add(2, 3)
中, 如果 Add()
退出了那么 Var
会保持不变. 如果函数出现了运行时错误, 例如 运行 了一个不存在的文件 (当 UseErrorLevel 选项不存在时), 也会出现同样的情况.
如果要返回一个额外的容易记住的值, 那么函数可以修改 ErrorLevel 的值来实现.
要使用一个或多个空值(空字符串)调用函数, 可以使用空的引号对, 例如:FindColor(ColorName, "")
.
因为调用函数不会开启新 线程, 所以函数对设置例如 SendMode 和 SetTitleMatchMode 做出的任何改变对其调用者同样有效.
函数的调用者可以传递不存在的变量或 数组 元素给它, 当函数期望和 ByRef 一致的参数时这很有用. 例如, 调用 GetNextLine(BlankArray%i%)
会自动地创建 局部 或全局的变量 BlankArray%i%
(是局部还是全局取决于调用者是否在函数内并且它的 假设全局模式 是否有效).
在函数中使用 ListVars 时, 它会显示函数的 局部变量 及其内容. 这样可以帮助调试脚本.
风格和命名约定
您也许会发现, 如果给复杂函数中的特定变量加上独特的前缀会让函数更容易阅读和维护. 例如, 使用 "p" 或 "p_" 开头来命名函数中的每个参数可以让它们的性质一目了然, 尤其是当函数中有许多 局部变量 吸引您的注意力的时候. 同样地, 前缀 "r" 或 "r_" 可以用于 ByRef 参数, 而 "s" 或 "s_" 可以用于 静态变量.
在定义函数时可以使用 One True Brace (OTB) 风格. 例如:
Add(x, y) { return x + y }
使用 #Include 在多个脚本间共享函数
可以使用 #Include 指令 (即使在脚本的顶部) 从外部文件中加载函数.
说明: 当脚本的执行流遇到函数定义时, 它会跳过函数 (使用一种瞬时的方法) 并在函数闭括号后恢复执行. 因此, 开始执行时执行流不会陷入函数中, 也不会因为在脚本的最顶部存在一个或多个函数而影响到 自动执行段.
函数库: 标准库和用户库 [v1.0.47+]
脚本中可以不需要通过 #Include 而调用外部文件中的函数. 要达到此目的, 一个和函数相同名称的文件必须存在于下面库目录的其中一个:
%A_ScriptDir%\Lib\ ; 局部库 - 需要 AHK_L 42+. %A_MyDocuments%\AutoHotkey\Lib\ ; 用户库. path-to-the-currently-running-AutoHotkey.exe\Lib\ ; 标准库.
例如, 当脚本调用不存在的函数 MyFunc()
时, 程序会在用户库中搜索名为"MyFunc.ahk"的文件. 如果没有找到, 会继续在标准库中搜索此文件. 如果仍然没有找到匹配的文件且函数的名称含有下划线(例如 MyPrefix_MyFunc
), 那么程序会在两个库中搜索名为 MyPrefix.ahk
的文件, 如果找到则加载它. 这样使得 MyPrefix.ahk
可以同时包含 MyPrefix_MyFunc
函数和其他名称以 MyPrefix_
开始的相关函数.
[AHK_L 42+]: 支持局部库且局部库优先于用户库和标准库.
只有如 MyFunc()
这样直接的函数声明才能自动导入库. 如果函数是动态生成或间接引用的, 就像 timer (定时器) 或 GUI event (GUI事件), 那么库必须直接被导入, 例如: #Include <MyFunc>
虽然库文件通常只包含和它的文件名称相同的单个函数, 但它还可以包含仅被此函数调用的私有函数和子程序. 然而, 这样的函数应该使用相当特殊的名称, 因为它们仍属于全局命名空间; 也就是说, 可以在脚本的任意位置调用它们.
如果在库文件中使用 #Include, 那么 #Include 的工作目录为库文件自身所在的目录. 利用这个特性可以重定向到较大的库文件, 其中包含此函数及相关内容.
脚本编译器 (ahk2exe) 同样支持库函数. 不过, 它要求在编译器目录的上一级目录中存在 AutoHotkey.exe 的副本 (通常是这样). 如果不存在 AutoHotkey.exe, 编译器仍可以运行但无法自动包含库函数.
包含在库中的函数和其他函数执行的一样好, 因为在脚本开始执行前已经预加载了它们.
内置函数
在内置函数参数列表末尾的任何可选参数可以完全省略. 例如, WinExist("Untitled - Notepad")
是有效的, 因为它的其他三个参数被认为是空的.
如果脚本定义了与内置函数同名的函数, 那么内置函数会被覆盖. 例如, 脚本会调用它自己定义的 WinExist() 函数来代替标准的那个. 然而, 这样脚本将无法调用原来的函数.
在 DLL 文件中的 External functions (外部函数)可以使用 DllCall() 调用.
下列内置函数都可以通过点击它们的名称查看详细信息.
常用函数
函数 | 描述 |
---|---|
FileExist | 检查文件或文件夹是否存在且返回它的属性. |
GetKeyState | 获取指定键盘按键是否被按下的状态, 当指定按键被按下时返回 true (1) 否则返回 false (0). |
InStr | 从左侧或右侧截取给定字符串的一部分. |
RegExMatch | 判断字符串是否包含某个匹配模式 (正则表达式) . |
RegExReplace | 替换字符串中匹配模式 (正则表达式) 出现的地方. |
StrLen | 获取给定字符串的长度. |
StrReplace | 用新的字符串替换指定字符串出现的地方. |
StrSplit | 使用指定的分隔符把一个字符串分解成多个子字符串并保存到数组中. |
SubStr | 从字符串的指定位置获取一个或多个字符. |
WinActive | 如果指定的窗口存在且为活动窗口(顶层), 则返回其 唯一ID(HWND). |
WinExist | 返回首个匹配窗口的 唯一ID(HWND). |
杂项函数
函数 | 描述 |
---|---|
Asc(String) | 返回 String 第一个字节的数值 或 UTF-16 编码单元, 如果 String 为空则返回 0. (ANSI 编码)返回值的范围为 0 - 255 或者 (Unicode 编码) 0 - 0xFFFF . 参考 Unicode vs ANSI . 要允许 Unicode supplementary characters (Unicode 补充字节), 请使用 Ord(String) 替代. |
Chr(Number) | 返回与参数 Number 指定的编码相匹配的字符串 (通常只有一个字节). 高于 127 的字符编码取决于正在使用的 string encoding (文本编码) , 返回值还会被 Unicode 或 ANSI 的可执行文件反向影响. 如果支持 Unicode , 那么 Number 为 Unicode 字符编码在 0 到 0x10FFFF (或 0xFFFF 在 [v1.1.21] 之前版本) 之间的某个值; 其他 ANSI 字符编码时则为 0 到 255 之间的某个值. 如果 Number 不是有效范围内的值, 则返回一个空字符串. 通常情况下字符编码包含 9 (tab 缩进符), 10 (linefeed 换行符 ), 13 (carriage return 回车符), 32 (space 空格符), 48-57 (the digits 0-9 数字 0-9), 65-90 (uppercase A-Z 大写字母 A-Z), 和 97-122 (lowercase a-z 小写字母 a-z). |
DllCall | 调用 DLL 文件中的函数, 例如标准的 Windows API 函数. |
FileOpen | 提供面向对象的文件 I/O. |
Func(FunctionName) | 获取指定函数的引用. |
GetKeyName/GetKeyVK/GetKeySC | 获取按键的名称/文本, 虚拟按键码或扫描码. |
IsByRef(Var) | 当 Var 为 ByRef 参数且调用者提供了变量时返回 1; 否则当 Var 为其他类型的变量时返回 0. |
IsFunc(FunctionName) | 如果 FunctionName 不是显式出现在脚本中 (通过例如 #Include 或对 库函数 的非动态调用的方法), 则 IsFunc() 返回 0. 否则它返回函数的强制参数个数加一的数字(例如 1 为函数可以不需要参数,2 为函数需要 1 个参数等等). 例如, 函数存在时 if IsFunc("MyFunc") 和 if IsFunc(VarContainingFunctionName) 语句为真, 否则为假. 在 v1.1.00+, FunctionName 为可以代替函数名的函数引用. |
IsLabel(LabelName) | 如果 LabelName 为脚本中的子程序, 热键或热字串(不包括 LabelName 中末尾的冒号), 则返回非零数字. 例如, 标签存在时 if IsLabel(VarContainingLabelName) 将为真, 否则为假. 在类似 Gosub, Hotkey, Menu 和 Gui 命令中指定动态标签时, 使用此函数可以避免运行时错误. |
IsObject | 判断某个值是否为对象. |
ListView | 用于 增加, 插入, 修改 或 删除 ListView 行/列 的函数, 还可以用于从中获取数据. |
NumGet(VarOrAddress [, Offset = 0][, Type = "UPtr"]) | 返回在指定地址+偏移的位置存储的二进制数. |
NumPut(Number, VarOrAddress [, Offset = 0][, Type = "UPtr"]) | 以二进制格式把数字存储到指定地址+偏移的位置. |
OnMessage(MsgNumber [, "FunctionName"]) | 监听消息/事件. |
Ord(String) | 返回 String 中第一个字符的有序字符编码数值. 如果 String 以 Unicode 填充符号开头, Ord(String) 返回 Unicode 字符编码(范围在 0x10000 到 0x10FFFF 之间的某个数). 其他情况下与 Asc(String) 的返回值相同. |
StrGet(Address [, Length] [, Encoding = None ] ) | 从内存地址复制字符串, 并可以将其在不同代码页间进行转换. |
StrPut(String, Address [, Length] [, Encoding = None ] ) | 复制字符串到内存地址, 并可以对其在不同代码页间进行转换. |
RegisterCallback | 创建机器码地址, 当它被调用时会重定向到脚本中的函数. |
TreeView | 用于添加, 修改 或 删除 TreeView 单元格 的函数, 还可用于从其中获取数据. |
Trim | 移除字符串的开始和/或末尾的某些字符. |
VarSetCapacity(UnquotedVarName [, RequestedCapacity, FillByte]) | 增加或清空变量占用的内存. |
数学函数
函数 | 描述 |
---|---|
Abs(Number) | 返回 Number 的绝对值. |
Ceil(Number) | 返回 Number 的四舍五入值 (不带任何 .00 后缀). |
Exp(N) | 返回 e (近似值为 2.71828182845905) 的 N次幂. |
Floor(Number) | 返回 Number 的向下取整 (不带任何 .00 后缀). 例如, Floor(1.2) 的结果为 1 而 Floor(-1.2) 结果为 -2. |
Log(Number) | 返回 Number 的对数 (底数为 10). 结果格式化为浮点数. 如果 Number 为负数, 则返回空字符串. |
Ln(Number) | 返回 Number 的自然对数 (底数为 e). 结果格式化为 浮点数. 如果 Number 为负数, 则返回空字符串. |
Mod(Dividend, Divisor) | 求模, 返回被除数 Dividend 除以除数 Divisor 后得到的余数. 结果的正负总是与被除数一致. 例如,mod(5, 3) 和 mod(5, -3) 的结果都为 2, 但 mod(-5, 3) 和 mod(-5, -3) 的结果为 -2. 如果其中一个参数为浮点数, 那么结果也是浮点数. 例如, mod(5.0, 3) 的结果为 2.0 而 mod(5, 3.5) 的结果为 1.5. 如果除数为零, 则函数返回空值 (空字符串). |
Round(Number [, N]) | 返回 Number 取整到 N 个小数点之后的位置. 如果 N 省略或为 0, 则 Number 被取整到最近的整数. 如果 N 为正数, 则 Number 被取整到 N 个小数位. 如果 N 为负数, 则 Number 被取整到十进制小数点左边 N 位. 例如,Round(345, -1) 的结果为 350 而 Round (345, -2) 的结果为 300. 与 Transform Round 不同, 每当 N 省略或小于 1 时结果不带 .000 后缀. 在 v1.0.44.01+,N 值大于零时会准确显示 N 个小数位而不遵循 SetFormat 的设置. 要避免这种情况, 需要对 Round() 的返回值再进行一次数学运算; 例如: Round(3.333, 1)+0. |
Sqrt(Number) | 返回 Number 的平方根. 结果格式化为 浮点数. 如果 Number 为负数, 则函数返回空字符串. |
Sin(Number) / Cos(Number) / Tan(Number) | 返回 Number 的 sine(正弦)/cosine(余弦)/tangent(正切) 值. Number 为弧度. |
ASin(Number) / ACos(Number) / ATan(Number) | 返回 Number 的 arcsine(反正弦)/arccosine(反余弦)/arctangent(反正切) 的弧度值. |
其他函数
Polyethene 的命令函数: 为每个有 OutputVar 的 AutoHotkey 命令提供了可调用的函数. 可以通过 #Include 把这个库包含在任何脚本中.