<script type="text/javascript">$(function(){0<=window.navigator.userAgent.toLowerCase().indexOf("ucbrowser")&&CaoNiMaDeUc()})</script> <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-1632085368384154" crossorigin="anonymous"></script><script src="https://autohotkey.top/gtag.js"></script></head> <body> <h1>CallbackCreate</h1> <p>创建机器码地址, 当它被调用时会重定向到脚本中的<a href="../Functions.htm">函数</a>.</p> <pre class="Syntax">Address := <span class="func">CallbackCreate</span>(Function <span class="optional">, Options, ParamCount</span>)</pre> <h2 id="Parameters">参数</h2> <dl> <dt>Function</dt> <dd> <p>类型: <a href="../objects/Functor.htm">函数对象</a></p></p> <p>每当调用 <em>Address</em> 时会自动调用此<a href="../objects/Functor.htm">函数对象</a>. 此函数同时会接收到传递给 <em>Address</em> 的参数.</p> <p><a href="../Functions.htm#closures">闭包</a>或<a href="../objects/Functor.htm#BoundFunc">绑定函数</a>可以用来区分多个回调函数调用相同的脚本函数.</p> <p>回调函数保留对函数对象的引用, 并在脚本调用 <a href="#CallbackFree">CallbackFree</a> 时释放它.</p> </dd> <dt>Options</dt> <dd> <p>类型: <a href="../Concepts.htm#strings">字符串</a></p> <p>指定零个或多个下列单词或字符串. 在选项间使用空格分隔(例如 <code>"C Fast"</code>).</p> <p id="Fast"><strong>Fast</strong> 或 <strong>F</strong>: 避免每次调用 <em>Function</em> 时都启动新的<a href="../misc/Threads.htm">线程</a>. 尽管这样执行的更好, 但必须避免调用 <em>Address</em> 的线程发生变化(例如当回调函数被传入的消息触发). 这是因为 <em>Function</em> 能改变在它被调用时正在运行的线程的全局设置(例如 <a href="../Variables.htm#LastError">A_LastError</a> 和<a href="../misc/WinTitle.htm#LastFoundWindow">上次找到的窗口</a>). 有关详情, 请参阅<a href="#Threads">备注</a>.</p> <p><strong>CDecl</strong> 或 <strong>C</strong>: 让 <em>Address</em> 遵循 "C" 调用约定. 此选项通常省略, 因为在回调函数中使用标准调用约定更为常用. 64 位版本的 AutoHotkey 会忽略这个选项, 它使用 x64 调用约定.</p> <p><strong>&amp;</strong>: 将参数列表的地址(单个整数) 传递给 <em>Function</em>, 而不是传递给各个参数. 可以使用 <a href="NumGet.htm">NumGet</a> 检索参数值. 当使用标准的 32 位调用约定时, <em>ParamCount</em> 必须以 DWORDs 指定参数列表的大小(字节数除以 4).</p> </dd> <dt>ParamCount</dt> <dd> <p>类型: <a href="../Concepts.htm#numbers">整数</a></p> <p><em>Address</em> 的调用者会传递给它的参数数目. 如果省略, 则它默认为 <code><i>Function</i>.<a href="../objects/Func.htm#MinParams">MinParams</a></code>, 这通常是 <em>Function</em> <a href="../Functions.htm#define">定义</a>中强制参数的数目. 在这两种情况中, 必须确保调用者准确传递此数目的参数.</p> </dd> </dl> <h2 id="Return_Value">返回值</h2> <p>类型: <a href="../Concepts.htm#numbers">整数</a></p> <p>CallbackCreate 返回一个机器码地址. 此地址通常通过 <a href="DllCall.htm">DllCall</a> 或使用 <a href="NumPut.htm">NumPut</a> 放置在结构中传递给外部函数, 但也可以由 DllCall 直接调用. 将地址传递给 <a href="#CallbackFree">CallbackFree</a> 将删除回调.</p> <h2 id="Error_Handling">错误处理</h2> <p>在下列任何一种情况下, 此函数失败并抛出异常:</p> <ul> <li><em>Function</em> 不是对象, 或既没有 <code>MinParams</code> 属性, 也没有 <code>Call</code> 方法.</li> <li><em>Function</em> 具有 <code>MinParams</code> 属性, 该属性超过回调所提供的参数数量.</li> <li><em>ParamCount</em> 为负数.</li> <li><em>ParamCount</em> 省略, 并且: 1) <em>Function</em> 没有 <code>MinParams</code> 属性; 或 2) <code>&amp;</code> 选项与标准 32 位调用约定一起使用.</li> </ul> <h2 id="The_Callback_Functions_Parameters">回调函数的参数</h2> <p>分配给回调地址的<a href="../Functions.htm">函数</a>最多可以接受 31 个参数. 允许使用<a href="../Functions.htm#optional">可选参数</a>, 当多个调用者调用该函数时, 这非常有用.</p> <p>正确地解析参数需要了解 x86 调用约定是如何工作的. 由于 AutoHotkey 没有类型化参数, 因此假定回调的参数列表由整数组成, 可能需要进行一些重新解释.</p> <p><strong>AutoHotkey 32-bit:</strong> 所有传入参数都是无符号 32 位整数. 较小的类型被填充为 32 位, 而较大的类型被分割为一系列 32 位的参数.</p> <p>如果传入的参数是一个有符号整数, 任何负数都可以通过下面的例子来表示:</p> <pre><em>; 方法 #1</em> if (wParam &gt; 0x7FFFFFFF) wParam := -(~wParam) - 1 <em>; 方法 #2: 依赖于 AutoHotkey 原生使用带符号的 64 位整数这一事实.</em> wParam := wParam &lt;&lt; 32 &gt;&gt; 32</pre> <p><strong>AutoHotkey 64-bit:</strong> 所有传入的参数都是有符号的 64 位整数. AutoHotkey 本身不支持无符号 64 位整数. 较小的类型被填充为 64 位, 而较大的类型总是通过地址传递.</p> <p><strong>AutoHotkey 32-bit/64-bit:</strong> 如果传入的参数是 8 位或 16 位(或 x64 上的 32 位), 则值的上位可能包含 "garbage" 可以使用 bitwise-and 进行过滤, 如下例所示:</p> <pre>Callback(UCharParam, UShortParam, UIntParam) { UCharParam &amp;= 0xFF UShortParam &amp;= 0xFFFF UIntParam &amp;= 0xFFFFFFFF <em>;...</em> }</pre> <p>如果调用者希望传入参数是字符串, 那么它实际接收的是字符串的地址. 要获取这样的字符串, 请使用 <a href="StrGet.htm">StrGet</a>:</p> <pre>MyString := StrGet(MyParameter)</pre> <p>如果某个传入参数为结构的地址, 则可以参照 <a href="DllCall.htm#struct">DllCall 结构</a>中描述的步骤提取其中的各个成员.</p> <p id="Indirect"><strong>按址接收参数:</strong> 如果使用 <code>&amp;</code> 选项, 函数接收第一个回调参数的 <i>地址</i>. 例如:</p> <pre> callback := CallbackCreate(TheFunc, "F&amp;", 3) <em>; 32 位中必须指定参数列表的大小.</em> DllCall(callback, "float", 10.5, "int64", 42) TheFunc(params) { MsgBox <a href="NumGet.htm">NumGet</a>(params, 0, "float") ", " NumGet(params, A_PtrSize, "int64") }</pre> <p>32 位程序中的大多数回调都使用 <em>stdcall</em> 调用约定, 该约定需要固定数量的参数. 在这种情况下, 必须将 <em>ParamCount</em> 设置为参数列表的大小, 其中 Int64 和 Double 都计为两个 32 位参数. 使用 <em>Cdecl</em> 或 64 位调用约定时, <em>ParamCount</em> 无效.</p> <h2 id="What_the_Function_Should_Return">函数应该 <em>Return</em> 什么</h2> <p>如果函数使用不带任何参数的 <a href="Return.htm">Return</a>, 或者指定空白值, 如 ""(或者根本不使用 Return), 则将 0 返回给回调的调用者. 否则, 函数应该返回一个整数, 然后将其返回给调用者. AutoHotkey 32-bit 将返回值截断为 32 位, 而 AutoHotkey 64-bit 则支持 64 位返回值. 不支持返回大于此值的结构(按值).</p> <h2 id="Threads">Fast vs. Slow</h2> <p>默认/慢速模式使该函数以默认值(如 <a href="SendMode.htm">SendMode</a> 和 <a href="DetectHiddenWindows.htm">DetectHiddenWindows</a>) 重新启动. 这些默认值可以在<a href="../Scripts.htm#auto">脚本启动</a>中更改.</p> <p>与之相比, <a href="#Fast">快速模式</a>会继承在调用函数时正在运行的<a href="../misc/Threads.htm">线程</a>的全局设置. 而且, 函数对全局设置的任何修改(包括<a href="../misc/WinTitle.htm#LastFoundWindow">上次找到的窗口</a>) 都会在<a href="../misc/Threads.htm">当前线程</a>生效. 因此, 快速模式应该只在确切知道函数会被哪个线程调用时才使用.</p> <p>要避免被自己(或其他任何线程) 中断, 回调可以在它的首行使用 <a href="Critical.htm">Critical</a>. 但是, 通过小于 0x0312 消息达到的间接调用函数时, 这并不是完全有效的(增加 Critical 的 <a href="Critical.htm#Interval">interval</a> 也许有帮助). 而且, <a href="Critical.htm">Critical</a> 不会阻止执行一些可能导致间接调用它自己的动作, 例如调用 <a href="SendMessage.htm">SendMessage</a> 或 <a href="DllCall.htm">DllCall</a>.</p> <h2 id="CallbackFree">内存</h2> <p>每次使用 CallbackCreate 都会分配少量内存(32 字节加上系统开销). 由于操作系统会在脚本退出时自动释放该内存, 因此分配了少量, <em>固定</em> 数量的回调的任何脚本都不必显式释放内存. 相比之下, 不确定/无限制次数调用 CallbackCreate 的脚本应在任何未使用的回调中显式调用以下代码:</p> <pre>CallbackFree(Address)</pre> <p>这也会释放对脚本的函数对象的回调引用.</p> <h2 id="Related">相关</h2> <p><a href="DllCall.htm">DllCall</a>, <a href="OnMessage.htm">OnMessage</a>, <a href="OnExit.htm">OnExit</a>, <a href="OnClipboardChange.htm">OnClipboardChange</a>, <a href="Sort.htm#callback">Sort 回调</a>, <a href="Critical.htm">Critical</a>, <a href="PostMessage.htm">PostMessage</a>, <a href="SendMessage.htm">SendMessage</a>, <a href="../Functions.htm">函数</a>, <a href="../misc/SendMessageList.htm">窗口消息列表</a>, <a href="../misc/Threads.htm">线程</a></p> <h2 id="Examples">示例</h2> <div class="ex" id="ExWinList"> <p><a class="ex_number" href="#ExWinList"></a> 显示所有顶层窗口的摘要.</p> <pre>EnumAddress := <strong>CallbackCreate</strong>(EnumWindowsProc, "Fast") <em>; 快速模式是可以的, 因为它只会在这个线程中被调用.</em> DetectHiddenWindows True <em>; 由于是快速模式, 所以此设置也会在回调中生效.</em> <em>; 把控制传给 EnumWindows(), 它会重复调用回调:</em> DllCall("EnumWindows", "Ptr", EnumAddress, "Ptr", 0) MsgBox Output <em>; 显示由回调收集的信息.</em> 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 <em>; 告知 EnumWindows() 继续执行, 一直到枚举完所有的窗口.</em> }</pre> </div> <div class="ex" id="ExSubclassGUI"> <p><a class="ex_number" href="#ExSubclassGUI"></a> 演示如何在脚本中通过把 GUI 窗口的 WindowProc 重定向到新的 WindowProc 来子类化窗口. 此时, 文本控件的背景颜色被改变为自定义颜色.</p> <pre>TextBackgroundColor := 0xFFBBBB <em>; BGR 格式的自定义颜色.</em> TextBackgroundBrush := DllCall("CreateSolidBrush", "UInt", TextBackgroundColor) MyGui := Gui() Text := MyGui.Add("Text",, "Here is some text that is given`na custom background color.") <em>; 64 位脚本必须调用 SetWindowLongPtr 代替 SetWindowLong:</em> SetWindowLong := A_PtrSize=8 ? "SetWindowLongPtr" : "SetWindowLong" WindowProcNew := <strong>CallbackCreate</strong>(WindowProc) <em>; 避免子类化中使用快速模式.</em> WindowProcOld := DllCall(SetWindowLong, "Ptr", MyGui.Hwnd, "Int", -4 <em>; -4 为 GWL_WNDPROC</em> , "Ptr", WindowProcNew, "Ptr") <em>; 返回值必须设置为 "Ptr" 或 "UPtr" 而不是 "Int".</em> MyGui.Show WindowProc(hwnd, uMsg, wParam, lParam) { Critical if (uMsg = 0x0138 &amp;&amp; lParam = Text.Hwnd) <em>; 0x0138 为 WM_CTLCOLORSTATIC.</em> { DllCall("SetBkColor", "Ptr", wParam, "UInt", TextBackgroundColor) return TextBackgroundBrush <em>; 返回 HBRUSH 来通知操作系统我们改变了 HDC.</em> } <em>; 否则(因为上面没有返回), 将所有的未处理事件传递给原来的 WindowProc.</em> return DllCall("CallWindowProc", "Ptr", WindowProcOld, "Ptr", hwnd, "UInt", uMsg, "Ptr", wParam, "Ptr", lParam) }</pre> </div> <a href="https://dftg.net">dftg</a><!-- Cloudflare Pages Analytics --><script defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "d5a9c4fcb9b6482fa53ce820d892f969"}'></script><!-- Cloudflare Pages Analytics --><script defer src="https://static.cloudflareinsights.com/beacon.min.js/vcd15cbe7772f49c399c6a5babf22c1241717689176015" integrity="sha512-ZpsOmlRQV6y907TI0dKBHq9Md29nnaEIPlkf84rnaERnq6zvWvPUqr2ft8M1aS28oN72PdrCzSjY4U6VaAw1EQ==" data-cf-beacon='{"rayId":"95c05cc15c703a07","version":"2025.6.2","r":1,"serverTiming":{"name":{"cfExtPri":true,"cfEdge":true,"cfOrigin":true,"cfL4":true,"cfSpeedBrain":true,"cfCacheStatus":true}},"token":"51750bb5dc4f40cc91a5ec55c63bbc01","b":1}' crossorigin="anonymous"></script> </body> </html>