H 版里经常看到几个名字:AutoHotkey.dll、ahkdll、ahktextdll、ahkExec。新手容易把它们混在一起。其实先抓住一句话就够了:AutoHotkey.dll 是 DLL 形态的 AHK 解释器,后面这些名字大多是围绕它提供的导出函数和调用方式。
如果你只是在 H 版脚本内部创建线程,优先看 AhkThread()。如果你想让外部语言调用 AHK,或者自己用 DllCall 控制 AHK 解释器,就要理解 AutoHotkey.dll 这一层。
先加载 DLL
最小理解是:先把 DLL 加载进当前进程,再调用它导出的函数。
#Requires AutoHotkey v1.1
#NoEnv
SetBatchLines, -1
dllPath := A_ScriptDir "\AutoHotkey.dll"
hModule := DllCall("LoadLibrary", "Str", dllPath, "Ptr")
if (!hModule)
{
MsgBox, AutoHotkey.dll 加载失败
ExitApp
}
MsgBox, AutoHotkey.dll 已加载
这段代码只是证明 DLL 能加载。真正使用时,还要调用 DLL 里的导出函数,比如从文件启动脚本、从字符串启动脚本、执行代码、调用函数等。
ahkdll:从文件启动
ahkdll 适合脚本已经保存成文件的情况。你可以把工作脚本放在单独文件里,然后通过 DLL 启动它。
dllPath := A_ScriptDir "\AutoHotkey.dll"
scriptFile := A_ScriptDir "\Worker.ahk"
FileDelete, %scriptFile%
FileAppend, #Persistent`nMsgBox`, 来自 Worker.ahk, %scriptFile%, UTF-8
hThread := DllCall(dllPath "\ahkdll"
, "Str", scriptFile
, "Str", ""
, "Str", "FileThread"
, "CDecl Ptr")
文件方式的好处是代码可维护。外部语言调用时也一样,能把 AHK 逻辑放成独立脚本,就不要把大段脚本拼成字符串。
ahktextdll:从字符串启动
ahktextdll 适合临时脚本、动态生成脚本、测试片段。它不要求先写入文件。
dllPath := A_ScriptDir "\AutoHotkey.dll"
scriptText =
(
#Persistent
MsgBox, 来自字符串脚本
)
hThread := DllCall(dllPath "\ahktextdll"
, "Str", scriptText
, "Str", ""
, "Str", "TextThread"
, "CDecl Ptr")
字符串方式方便,但一长就难维护。尤其是外部语言拼接 AHK 脚本时,引号、换行、编码都容易出问题。
ahkExec:追加执行代码
ahkExec 更像是向已经存在的 AHK 环境里执行一段代码。它适合小段命令,不适合承载完整项目结构。
DllCall(dllPath "\ahkExec"
, "Str", "ToolTip, ahkExec 执行了一段代码"
, "CDecl Int")
如果你需要长期运行的逻辑,优先用文件脚本或明确的函数调用。不要把所有事情都塞给 ahkExec。
外部语言为什么要用 DLL
外部语言调用 AHK 有两种常见思路:一种是每次启动一个 exe,让脚本从命令行参数接收任务;另一种是加载 AutoHotkey.dll,让 AHK 解释器常驻在当前进程里。
前者简单,但每次启动进程都有成本,参数也多半只能按字符串处理。后者复杂一些,但可以长期保持解释器环境,更适合频繁调用、回调、复杂状态共享。
比如 Python、C#、易语言、VBS 这类语言,都可以围绕 DLL 做封装。真正要注意的是这些细节:
- 调用约定是否匹配,常见是
CDecl。 - 32 位程序要配 32 位 DLL,64 位程序要配 64 位 DLL。
- 字符串编码要统一,中文路径和中文脚本尤其要小心。
- 长期运行的线程要设计退出方式。
- 调用函数时要明确参数和返回值类型。
MemoryLoadLibrary 是另一种加载方式
普通 LoadLibrary 是从文件路径加载 DLL。H 版还提供过 MemoryLoadLibrary() 这类能力,可以从内存模块加载 DLL,并配合 MemoryGetProcAddress() 获取导出函数地址。
它适合更高级的发布和嵌入场景,比如资源内置、内存模块、多份独立模块状态。新手不用急着碰,先把普通 DLL 加载和导出函数调用理解清楚。
下面是一个最小结构:用 MemoryLoadLibrary() 加载 AutoHotkey.dll,再用 MemoryGetProcAddress() 找到 ahktextdll 和 ahkReady,最后通过函数指针调用。
dllPath := A_ScriptDir "\AutoHotkey.dll"
mod := MemoryLoadLibrary(dllPath)
if (!mod)
{
MsgBox, MemoryLoadLibrary 加载失败
ExitApp
}
pAhkTextDll := MemoryGetProcAddress(mod, "ahktextdll")
pAhkReady := MemoryGetProcAddress(mod, "ahkReady")
if (!pAhkTextDll || !pAhkReady)
{
MemoryFreeLibrary(mod)
MsgBox, 找不到需要的导出函数
ExitApp
}
code := "#Persistent`nMsgBox, 来自 MemoryLoadLibrary 加载的 AutoHotkey.dll"
DllCall(pAhkTextDll, "Str", code, "Str", "", "Str", "MemThread", "CDecl Ptr")
While DllCall(pAhkReady)
Sleep, 100
MemoryFreeLibrary(mod)
这个例子的重点不是推荐所有场景都改成内存加载,而是看清楚调用链路:先拿模块句柄,再取导出函数地址,用 DllCall 调用,最后释放模块。普通 DLL 调用都跑通以后,再考虑 Memory 系列会稳得多。
我建议的分工
- H 版脚本内部多线程:优先用
AhkThread()。 - 脚本文件已经存在:考虑
ahkdll。 - 临时代码片段:考虑
ahktextdll或ahkExec。 - 外部语言频繁调用 AHK:围绕 AutoHotkey.dll 封装。
- 正式发布和嵌入:再研究内存加载、资源释放、打包策略。
AutoHotkey.dll 最有价值的地方,不是让 AHK 代码写得更花,而是让 AHK 能进入别的程序结构里。理解这一点,H 版的 DLL 方向就不会乱。
站内相关链接
- DllCall 入门:AHK 调用 Windows API 的最小示例:先补 DllCall、参数类型和返回值基础。
- AhkThread 入门:AHK_H 为什么能多线程:如果只是 H 版脚本内部开线程,优先从这里入手。
- 易语言调用 ahkdll 示例:外部语言调用 AHK 的具体例子。
- VBS 隐藏调用 ahkdll 进程 COM:从 VBS 侧调用和隐藏运行的例子。
- AHK_L 版调用 H 版 dll 实现多线程函数:普通 v1 借助 H 版 DLL 的思路。
- AHK_H 编译发布修改:需要正式发布时,再看资源、版本信息和释放策略。

评论(0)