创建机器码地址, 当它被调用时会重定向到脚本中的函数.
Address := CallbackCreate(Function , Options, ParamCount)
类型: 函数对象
每当调用 Address 时会自动调用此函数对象. 此函数同时会接收到传递给 Address 的参数.
回调函数保留对函数对象的引用, 并在脚本调用 CallbackFree 时释放它.
类型: 字符串
如果为空或省略, 每次调用 Function 时, 都会启动一个新线程, 使用标准调用约定, 并将参数单独传递给 Function. 否则, 请指定一个或多个下列选项. 在选项间使用空格分隔(例如 "C Fast"
).
Fast 或 F: 避免每次调用 Function 时都启动新的线程. 尽管这样执行的更好, 但必须避免调用 Address 的线程发生变化(例如当回调函数被传入的消息触发). 这是因为 Function 能改变在它被调用时正在运行的线程的全局设置(例如 A_LastError 和上次找到的窗口). 有关详情, 请参阅备注.
CDecl 或 C: 让 Address 遵循 "C" 调用约定. 此选项通常省略, 因为在回调函数中使用标准调用约定更为常用. 64 位版本的 AutoHotkey 会忽略这个选项, 它使用 x64 调用约定.
&: 将参数列表的地址(单个整数) 传递给 Function, 而不是传递给各个参数. 可以使用 NumGet 检索参数值. 当使用标准的 32 位调用约定时, ParamCount 必须以 DWORDs 指定参数列表的大小(字节数除以 4).
类型: 整数
如果为空或省略, 则它默认为 Function.MinParams
, 这通常是 Function 定义中强制参数的数目. 否则, 请指定 Address 的调用者会传递给它的参数数目. 在这两种情况中, 必须确保调用者准确传递此数目的参数.
类型: 整数
CallbackCreate 返回一个机器码地址. 此地址通常通过 DllCall 或使用 NumPut 放置在结构中传递给外部函数, 但也可以由 DllCall 直接调用. 将地址传递给 CallbackFree 将删除回调.
在下列任何一种情况下, 此函数失败并抛出异常:
MinParams
属性, 也没有 Call
方法.MinParams
属性, 该属性超过回调所提供的参数数量.MinParams
属性; 或 2) &
选项与标准 32 位调用约定一起使用.分配给回调地址的函数最多可以接受 31 个参数. 允许使用可选参数, 当多个调用者调用 Function 时, 这非常有用.
正确地解析参数需要了解 x86 调用约定是如何工作的. 由于 AutoHotkey 没有类型化参数, 因此假定回调的参数列表由整数组成, 可能需要进行一些重新解释.
AutoHotkey 32-bit: 所有传入参数都是无符号 32 位整数. 较小的类型被填充为 32 位, 而较大的类型被分割为一系列 32 位的参数.
如果传入的参数是一个有符号整数, 任何负数都可以通过下面的方法来表示:
; 方法 #1 if (wParam > 0x7FFFFFFF) wParam := -(~wParam) - 1 ; 方法 #2: 依赖于 AutoHotkey 原生使用带符号的 64 位整数这一事实. wParam := wParam << 32 >> 32
AutoHotkey 64-bit: 所有传入的参数都是有符号的 64 位整数. AutoHotkey 本身不支持无符号 64 位整数. 较小的类型被填充为 64 位, 而较大的类型总是通过地址传递.
AutoHotkey 32-bit/64-bit: 如果传入的参数是 8 位或 16 位(或 x64 上的 32 位), 则值的上位可能包含 "garbage" 可以使用 bitwise-and 进行过滤, 如下例所示:
Callback(UCharParam, UShortParam, UIntParam) { UCharParam &= 0xFF UShortParam &= 0xFFFF UIntParam &= 0xFFFFFFFF ;... }
如果调用者希望传入参数是字符串, 那么它实际接收的是字符串的地址. 要获取这样的字符串, 请使用 StrGet:
MyString := StrGet(MyParameter)
如果某个传入参数为结构的地址, 则可以参照 DllCall 结构中描述的步骤提取其中的各个成员.
按址接收参数: 如果使用 &
选项, Function 接收第一个回调参数的 地址. 例如:
callback := CallbackCreate(TheFunc, "F&", 3) ; 32 位中必须指定参数列表的大小. DllCall(callback, "float", 10.5, "int64", 42) TheFunc(params) { MsgBox NumGet(params, 0, "float") ", " NumGet(params, A_PtrSize, "int64") }
32 位程序中的大多数回调都使用 stdcall 调用约定, 该约定需要固定数量的参数. 在这种情况下, 必须将 ParamCount 设置为参数列表的大小, 其中 Int64 和 Double 都计为两个 32 位参数. 使用 Cdecl 或 64 位调用约定时, ParamCount 无效.
如果 Function 使用不带任何参数的 Return, 或者指定空白值, 如 ""(或者根本不使用 Return), 则将 0 返回给回调的调用者. 否则, 函数应该返回一个整数, 然后将其返回给调用者. AutoHotkey 32-bit 将返回值截断为 32 位, 而 AutoHotkey 64-bit 则支持 64 位返回值. 不支持返回大于此值的结构(按值).
默认/慢速模式使该 Function 以默认值(如 SendMode 和 DetectHiddenWindows) 重新启动. 这些默认值可以在脚本启动中更改.
与之相比, 快速模式会继承在调用 Function 时正在运行的线程的全局设置. 而且, Function 对全局设置的任何修改(包括上次找到的窗口) 都会在当前线程生效. 因此, 快速模式应该只在确切知道 Function 会被哪个线程调用时才使用.
要避免被自己(或其他任何线程) 中断, 回调可以在它的首行使用 Critical. 但是, 通过小于 0x0312 消息达到的间接调用 Function 时, 这并不是完全有效的(增加 Critical 的 interval 也许有帮助). 而且, Critical 不会阻止 Function 执行一些可能导致间接调用它自己的动作, 例如调用 SendMessage 或 DllCall.
删除回调函数并释放它对函数对象的引用.
CallbackFree(Address)
每次使用 CallbackCreate 都会分配少量内存(32 或 48 字节加上系统开销). 由于操作系统会在脚本退出时自动释放该内存, 因此任何分配了少量, 固定 数量的回调的脚本都不必显式释放内存.
但是, 如果回调函数持有的函数对象是动态的(例如, 闭包或绑定函数), 当不再需要回调函数时, 释放回调函数可能是特别重要的; 否则, 函数对象将不会被释放.
DllCall, OnMessage, OnExit, OnClipboardChange, Sort 回调, Critical, PostMessage, SendMessage, 函数, Windows 消息, 线程
EnumAddress := CallbackCreate(EnumWindowsProc, "Fast") ; 快速模式是可以的, 因为它只会在这个线程中被调用. DetectHiddenWindows True ; 由于是快速模式, 所以此设置也会在回调中生效. ; 把控制传给 EnumWindows(), 它会重复调用回调: DllCall("EnumWindows", "Ptr", EnumAddress, "Ptr", 0) MsgBox Output ; 显示由回调收集的信息. EnumWindowsProc(hwnd, lParam) { global Output win_title := WinGetTitle(hwnd) win_class := WinGetClass(hwnd) if win_title Output .= "HWND: " . hwnd . "`tTitle: " . win_title . "`tClass: " . win_class . "`n" return true ; 告知 EnumWindows() 继续执行, 一直到枚举完所有的窗口. }
演示如何在脚本中通过把 GUI 窗口的 WindowProc 重定向到新的 WindowProc 来子类化窗口. 此时, 文本控件的背景颜色被改变为自定义颜色.
TextBackgroundColor := 0xFFBBBB ; BGR 格式的自定义颜色. TextBackgroundBrush := DllCall("CreateSolidBrush", "UInt", TextBackgroundColor) MyGui := Gui() Text := MyGui.Add("Text",, "Here is some text that is given`na custom background color.") ; 64 位脚本必须调用 SetWindowLongPtr 代替 SetWindowLong: SetWindowLong := A_PtrSize=8 ? "SetWindowLongPtr" : "SetWindowLong" WindowProcNew := CallbackCreate(WindowProc) ; 避免子类化中使用快速模式. WindowProcOld := DllCall(SetWindowLong, "Ptr", MyGui.Hwnd, "Int", -4 ; -4 为 GWL_WNDPROC , "Ptr", WindowProcNew, "Ptr") ; 返回值必须设置为 "Ptr" 或 "UPtr" 而不是 "Int". MyGui.Show WindowProc(hwnd, uMsg, wParam, lParam) { Critical if (uMsg = 0x0138 && lParam = Text.Hwnd) ; 0x0138 为 WM_CTLCOLORSTATIC. { DllCall("SetBkColor", "Ptr", wParam, "UInt", TextBackgroundColor) return TextBackgroundBrush ; 返回 HBRUSH 来通知操作系统我们改变了 HDC. } ; 否则(因为上面没有返回), 将所有的未处理事件传递给原来的 WindowProc. return DllCall("CallWindowProc", "Ptr", WindowProcOld, "Ptr", hwnd, "UInt", uMsg, "Ptr", wParam, "Ptr", lParam) }