来源说明:本文参考 AutoHotkey 论坛 tic 发布的 GDI+ standard library 1.45 by tic 主题,并结合本站 GDI+系列教程目录 做补充整理。本站已有完整的 GDI+ 中文教程路线,这篇不重复讲画笔、画刷、图片绘制,而是专门说库版本、兼容性和常见坑。

很多 GDI+ 示例本身并不难,真正让新手卡住的往往是:下载了不合适的 Gdip 库、AHK 位数不匹配、Unicode/ANSI 不一致、反复启动关闭 GDI+、透明窗口在高 DPI 下显示异常。把这些基础问题排掉,后面的绘图、截图、保存图片、异型界面才会顺很多。

一、优先使用 Gdip_All.ahk

原帖里推荐的是 Gdip_All.ahk,它的意义不是名字更“全”,而是为了兼容不同 AHK 环境:ANSI、Unicode、x86、x64。很多老教程里可能引用的是较早版本的 Gdip.ahk,在旧环境能跑,但换到新版 AHK 或 64 位系统后就可能出现问题。

对现在的 AHKv1 用户来说,建议优先确认三件事:

  • 脚本包含的是当前可用的 Gdip_All.ahk,不是很早以前下载的旧版。
  • AHK 解释器位数和库的兼容性没有冲突,尤其是 x64 环境。
  • 不要在多个目录里放不同版本的 Gdip 库,避免 #Include 实际加载的不是你以为的那个文件。
#Requires AutoHotkey v1.1
#NoEnv
#SingleInstance Force
#Include <Gdip_All>

if !pToken := Gdip_Startup()
{
    MsgBox, 48, GDI+错误, GDI+ 启动失败,请检查 Gdip_All.ahk 和系统环境。
    ExitApp
}

; 这里写你的 GDI+ 绘图、截图、保存图片逻辑

Gdip_Shutdown(pToken)
ExitApp

如果你使用的是放在脚本同目录的库,也可以写成 #Include %A_ScriptDir%\Gdip_All.ahk,这样最清楚,不容易被全局 Lib 目录里的旧版本干扰。

二、Gdip_Startup 和 Gdip_Shutdown 不要乱放

一个很常见的错误是:把 Gdip_Startup()Gdip_Shutdown() 写进每个函数里,用一次图片就启动一次,用完马上关闭一次。看起来很整洁,实际很容易造成重复关闭、句柄失效、脚本崩溃或者图片对象莫名失效。

更稳的写法是:脚本开始时启动一次,脚本退出前关闭一次。中间的函数只负责创建、使用和释放具体的 bitmap、graphics、brush、pen 等对象。

#NoEnv
#SingleInstance Force
#Include %A_ScriptDir%\Gdip_All.ahk

if !pToken := Gdip_Startup()
{
    MsgBox, 48, GDI+错误, GDI+ 启动失败。
    ExitApp
}

OnExit, HandleExit

; 主逻辑
Gosub, Demo
return

Demo:
    ; 在这里使用 GDI+,不要每次都重新 Startup / Shutdown
return

HandleExit:
    Gdip_Shutdown(pToken)
ExitApp

如果你的脚本是常驻型工具,比如截图工具、放大镜、悬浮提示、动态 GUI,更应该保持这种结构。GDI+ 初始化属于脚本生命周期级别,不要当成普通函数内部的临时变量处理。

三、x64、Unicode 和参数类型问题

论坛回帖里多次提到旧版 Gdip 在 x64 或 Unicode 环境下的问题。根本原因通常不是绘图逻辑错了,而是底层 DllCall 参数类型不适配。例如指针相关参数在 32 位和 64 位下不能简单都写成 UInt,很多时候需要 PtrUPtr

如果遇到下面这些情况,先怀疑库版本和位数兼容性:

  • 同一份脚本在 32 位 AHK 能跑,64 位 AHK 不显示。
  • 更新 AHK 后,原来的透明窗口、图层窗口突然不可见。
  • Gdip_Startup() 能成功,但 UpdateLayeredWindow、保存图片、加载图片异常。
  • 老示例里大量使用 UInt 传窗口句柄、HDC、bitmap 指针。

排查时不要急着改业务代码,先换成兼容性更好的 Gdip_All.ahk,再确认运行的是 32 位还是 64 位 AHK。

MsgBox, % "A_PtrSize = " A_PtrSize "`n"
    . "AHK位数 = " (A_PtrSize = 8 ? "64位" : "32位") "`n"
    . "Unicode = " (A_IsUnicode ? "是" : "否")

四、透明窗口和高 DPI:UpdateLayeredWindow 要注意宽高

GDI+ 常用于透明悬浮窗、异型界面、无边框 GUI。论坛讨论里提到过一个高 DPI 相关问题:某些旧写法只调用 UpdateLayeredWindow(hwnd, hdc),在非标准 DPI 或某些系统环境下可能显示异常。

更稳的写法通常会把窗口宽高也传进去,例如:

UpdateLayeredWindow(hwnd1, hdc, 0, 0, WinWidth, WinHeight)

如果你做的是圆角窗口、透明贴图、悬浮按钮、放大镜、屏幕标注工具,遇到“脚本在运行但界面不显示”“换电脑后位置或大小不对”“缩放比例不是 100% 时异常”,就要把 DPI 和 UpdateLayeredWindow 参数一起检查。

五、对象释放比想象中重要

GDI+ 里很多东西都不是普通变量,而是系统资源句柄。比如 bitmap、graphics、brush、pen、font 等,用完后应该释放。短脚本可能看不出问题,常驻脚本或循环截图脚本就很容易内存上涨、句柄增加,最后卡顿或崩溃。

pBitmap := Gdip_CreateBitmap(400, 200)
G := Gdip_GraphicsFromImage(pBitmap)
pBrush := Gdip_BrushCreateSolid(0xffffcc00)

Gdip_FillRectangle(G, pBrush, 20, 20, 160, 80)
Gdip_SaveBitmapToFile(pBitmap, A_ScriptDir "\test.png")

Gdip_DeleteBrush(pBrush)
Gdip_DeleteGraphics(G)
Gdip_DisposeImage(pBitmap)

记住一个简单原则:凡是 CreateFromCloneBitmapGraphics 这类创建出来的对象,都要留意有没有对应的删除或释放函数。

六、AHKv2-Gdip 可以了解,但本站示例仍以 v1 为主

论坛后续讨论里提到了 AHKv2-Gdip,它主要面向 AHK v2,同时也兼容 v1.1。对于同时维护 v1/v2 脚本的人,这是一个值得了解的分支。

不过本站目前主要以 AHKv1 为主,如果你只是学习和运行本站 GDI+ 系列教程,优先跟着 v1 示例和 Gdip_All.ahk 走即可。不要一边看 v1 教程,一边混用 v2 风格库和 v2 语法,这样排错成本会明显增加。

七、官方示例和本站教程怎么对应

tic 原帖列出了 12 个官方示例,它们和本站 GDI+ 系列教程有不少对应关系。你可以把原帖示例当成“英文原版路线”,把本站教程当成“中文实战路线”。

  • 绘制形状、轮廓:对应本站画笔、画刷基础。
  • 从图片创建 GUI:对应异型界面、透明窗口。
  • 创建 bitmap 并保存 PNG:对应画布内容保存为文件。
  • 图片编辑:对应截图、缩放、裁剪、转换等应用。
  • 可拖动圆角矩形、绘制文字:对应自定义 GUI 和控件美化。
  • 进度条:对应模块化进度条。
  • 旋转、翻转、镜像:对应图像旋转与镜像。
  • BRA 动画、LockBits + MCode:属于更进阶的资源打包和高速像素处理。

如果你是新手,建议先按本站 GDI+系列教程目录 学;如果你已经会基本绘制,再回头看原帖官方示例,会更容易理解每个例子在练什么。

八、排错清单

遇到 GDI+ 示例跑不起来,可以按这个顺序检查:

  • 确认脚本加载的是正确的 Gdip_All.ahk
  • 确认 AHK 位数:32 位还是 64 位。
  • 确认是否 Unicode 版本。
  • 确认 Gdip_Startup() 是否成功。
  • 确认没有重复或过早 Gdip_Shutdown()
  • 确认 bitmap、graphics、brush、pen 等对象没有提前释放。
  • 透明窗口不显示时,检查 UpdateLayeredWindow 参数和 DPI 缩放。
  • 保存图片失败时,检查路径权限、文件扩展名和图片对象是否有效。
  • 老代码迁移时,重点看 DllCall 里的 UIntPtrUPtr

GDI+ 的难点不只在“画什么”,还在“资源和环境是否正确”。库版本选对、启动关闭放对、对象释放做对,很多看似玄学的问题都会少一大半。

站内延伸

声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。