由于 AutoHotkey Basic 和 AutoHotkey_L 的一些基本差异 (尤其是 Unicode 和 x64 版本), 为 AutoHotkey Basic 编写的脚本可能无法正常运行在 AutoHotkey_L 上. 此文档描述了当前已知的兼容性问题和解决方法.
由内置命令, 函数和运算符所需要或返回的存放在内存中的字符串使用特殊的二进制格式, 参考 原生格式. 这种格式取决于使用的 AutoHotkey 版本: Unicode 或 ANSI. 使用一种特殊格式设计的脚本使用错误的 AutoHotkey 版本运行时常常会遇到问题. 如果您不确定您正使用的是哪个版本, 则运行后面的脚本:
MsgBox % A_IsUnicode ? "Unicode" : "ANSI"
ANSI: 字符串存储为系统默认 ANSI 代码页编码, 和在 AutoHotkey Basic 一样. 每个字符为一个字节 (8 位).
Unicode: 字符串存储为 UTF-16 编码, 这里每个字符为两个字节 (16 位).
技术上, 一些 Unicode 字符表示为 两个 16-位代码单元, 一起被称为 "代理项对." 同样地, 一些 ANSI 代码页 (通常称为 双字节字符集) 含有一些双字节字符. 然而, 为了方便和实用它们实际上被视为两个单独的 "字符".
注意尽管 AutoHotkey 的每个版本仅支持一种特定的在内存中的字符串格式, 不过在脚本源代码文件中可以使用一些不同的编码. 如果包含有非 ASCII 字符的脚本使用的错误的编码, 那么将无法正确加载它们. Ahk2Exe 和 AutoHotkey_L 的 Unicode 版本默认要求 UTF-8, 而 ANSI 版本要求 ANSI. 想了解更多信息, 参见 脚本文件代码页.
VarSetCapacity 设置一个变量占用的空间, 单位为字节. 由于一个字符占用空间的大小变化决定于字符串的编码格式, 所以可能需要进行一些计算:
VarSetCapacity(ansi_var, size_in_chars) VarSetCapacity(unicode_var, size_in_chars * 2) VarSetCapacity(native_var, size_in_chars * (A_IsUnicode ? 2 : 1)) VarSetCapacity(native_var, t_size(size_in_chars)) ; 参见 下面VarSetCapacity 内部将添加一个 字符 的容量以确保变量以空终止符结尾. 然而, 如果使用 非原生 编码的字符串将存储到变量中时为了保持一致性,
size_in_chars 通常应该包含空终止符的大小.
使用 "Str" 类型时, 表示字符串使用当前版本原生的编码格式. 由于一些函数可能需要或返回特殊格式的字符串, 所以有时还需要使用下列的字符串格式:
| 字符大小 | C / Win32 类型 | Encoding | |
|---|---|---|---|
| WStr | 16-位 | wchar_t*, WCHAR*, LPWSTR, LPCWSTR | UTF-16 |
| AStr | 8-位 | char*, CHAR*, LPSTR, LPCSTR | ANSI (系统默认 ANSI 代码页) |
| Str | -- | TCHAR*, LPTSTR, LPCTSTR | 相当于 Unicode 版本中的 WStr 和 ANSI 版本中的 AStr. |
如果 "Str" 或在当前版本中的等价类型用于参数中, 那么字符串或变量的地址被传递给函数, 否则创建一个期望格式的字符串临时副本进行传递. 函数可以修改临时副本, 但 不能 写入到这个字符串空终止符右边的任何位置. 一般地, "AStr" 和 "WStr" 不应该用于输出参数中.
注意: "AStr" 和 "WStr" 用于参数和函数的返回值同样是有效的.
一般而言, 如果脚本通过 DllCall 调用接受字符串参数的函数, 那么必须采取以下其中一种方式:
DllCall("DeleteFile", "Ptr", &filename)
DllCall("DeleteFile", "Str", filename)
在这个例子中, &filename 准确按原样传递字符串地址, 所以函数必须期望接受和 "Str" 类型相同编码格式的字符串. 注意在 AutoHotkey Basic 中 "UInt" 必须使用 "Ptr" 代替, 但得到的代码可能不兼容 64 位.
注意: 如果无法根据名称准确找到指定的函数, 在 AutoHotkey_L 中不管指定哪个 DLL 都会在函数名称后添加 "A" 或 "W" 后缀查找. 然而, AutoHotkey Basic 则仅为 User32.dll, Kernel32.dll, ComCtl32.dll 或 Gdi32.dll 中的函数添加 "A" 后缀.
DllCall("DeleteFileA", "AStr", filename)
DllCall("DeleteFileW", "WStr", filename)
当使用 NumPut 或 NumGet 操作字符串时, 对于给定类型的字符串其偏移和类型都必须正确. 可以参考下面的代码:
; 8 位/ANSI 字符串: size_of_char=1 type_of_char="Char" ; 16 位/UTF-16 字符串: size_of_char=2 type_of_char="UShort" nth_char := NumGet(var, (n-1)*size_of_char, type_of_char) NumPut(nth_char, var, (n-1)*size_of_char, type_of_char)
如果 var 含有原生格式的字符串, 那么根据 A_IsUnicode 的值可以确定变量的值.
nth_char := NumGet(var, t_size(n-1), t_char())
NumPut(nth_char, var, t_size(n-1), t_char())
; 为了方便和清晰定义的函数:
t_char() {
return A_IsUnicode ? "UShort" : "Char"
}
t_size(char_count=1) {
return A_IsUnicode ? char_count : char_count*2
}
指针在 32 位版本 (包括 AutoHotkey Basic) 中是 4 个字节大小而在 64 位版本中是 8 个字节. 使用结构或 DllCall 的脚本可能需要为在两种平台上正常运行进行考虑. 受影响的特殊地方包括:
对于大小和偏移计算, 使用 A_PtrSize. 对于 DllCall, NumPut 和 NumGet, 使用适当的 Ptr 类型.
记住一个字段的偏移常常是在它之前所有字段的总大小. 同时注意句柄 (包括类似 HWND 和 HBITMAP 的类型) 实际上是指针类型.
/*
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess; // Ptr
HANDLE hThread;
DWORD dwProcessId; // UInt (4 字节)
DWORD dwThreadId;
} PROCESS_INFORMATION, *LPPROCESS_INFORMATION;
*/
VarSetCapacity(pi, A_PtrSize*2 + 8) ; Ptr + Ptr + UInt + UInt
DllCall("CreateProcess", <简短地省略>, "Ptr", &pi, <省略>)
hProcess := NumGet(pi, 0) ; 默认为 "Ptr".
hThread := NumGet(pi, A_PtrSize) ;
dwProcessId := NumGet(pi, A_PtrSize*2, "UInt")
dwProcessId := NumGet(pi, A_PtrSize*2 + 4, "UInt")
没有指定脚本运行 AutoHotkey_L 时, 默认脚本文件名和当前可执行文件名相同但扩展名为 "ahk". 想了解更多细节, 请参阅 传递命令行参数到脚本.
在变量名中字符 [ ] 和 ? 不再合法. 因此, ? (用于三元操作符) 不再要求其两边为空格. 另请参阅 对象语法.
命令名必须以空格, Tab 或逗号终止. 与 AutoHotkey Basic 不同, 后面的字符都必须符合这个要求: <>:+-*/!~&|^[]. 因此, 像 MsgBox< foo 和 If!foo 这样的语法错误可以在加载时捕捉到, 而不会作为 MsgBox,< foo 或 If !foo 那样解释.
一些 Transform 子命令在 Unicode 版本中发生了变化:
此命令忽略系统区域设置, 除非使用了 StringCaseSense, Locale.
在某些情况下 FileRead 在代码页之间转换文本, 因此可能输出错误的二进制数据.
Loop Read 和 FileReadLine 不会把字符 Control-Z (0x1A) 解释为文件末尾标记. 因此任何的 Control-Z, 即使某一个恰好出现在文件末尾, 都原样加载. FileRead 已经采用这样的方式.
使用大写字母 H 时, 十六进制数字中 A-F 也将为大写. 不过在 AutoHotkey Basic 中总是使用小写数字. 参见 SetFormat.
如果用于运行脚本的 EXE 文件属性中设置 兼容模式 为 Windows 95, 98/ME 或 NT4, 脚本可能无法正常运行. 这是由于兼容模式会把特定的 Windows 版本报告给应用程序, 但预构建的二进制文件不支持这些版本. 例如设置兼容模式为 Windows 95 或 98/ME 将使得 MsgBox %A_OSVersion% 报告 WIN_NT4.
除了自定义操作支持 (Run *verb file) 外, 修订号 57 中还把从 Target 参数中提取操作及其参数的方式做了一些改变. 具体地: