windows用enumprinters需设dwlevel=2并两次调用获取缓冲区大小;linux/macos用cups时应优先cupsgetdests,注意权限、socket路径及默认打印机需单独查询。

Windows 下用 EnumPrinters 获取本地打印机列表
直接调用 Windows API 是最轻量、最可靠的方式,不需要额外依赖或服务运行。前提是目标机器是 Windows,且程序有基础权限(普通用户即可)。
常见错误现象:EnumPrinters 返回 0 个打印机,或调用失败返回 ERROR_INSUFFICIENT_BUFFER —— 这不是没打印机,而是你没给够缓冲区,或者没正确设置 dwLevel。
-
dwLevel必须设为2(PRINTER_INFO_2),才能拿到名称、状态、是否默认等关键字段;设成 1 只能拿到名字,但很多字段是空的 - 第一次调用要传
NULL和0,靠返回值确定所需缓冲区大小,再分配内存重试 —— 跳过这步直接 malloc 固定大小,大概率崩或漏数据 - 返回的
PRINTER_INFO_2结构里,pPrinterName是有效指针,但必须用LocalFree释放整个缓冲区,不能单独 free 某个字段 - 如果只想要启用的本地打印机,过滤时检查
status & PRINTER_STATUS_ERROR == 0和Attributes & PRINTER_ATTRIBUTE_LOCAL
CUPS 环境下用 cupsGetPrinters 列出可用打印机
Linux/macOS 上走 CUPS 是标准路径,但要注意:它不等于“系统所有打印机”,而是 CUPS 自己管理的队列列表 —— 如果打印机没被添加进 CUPS(比如纯 USB 直连未配置),就不会出现。
常见错误现象:cupsGetPrinters 返回空数组,但 lpstat -p 却能看到 —— 很可能因为没调用 cupsSetServer 或环境变量 CUPS_SERVER 指向了错误地址(默认是 /run/cups/cups.sock 或 localhost:631)。
立即学习“C++免费学习笔记(深入)”;
- 调用前建议先执行
cupsGetDests,它比cupsGetPrinters更健壮,能兼容 IPP Everywhere 打印机,且返回结构更扁平 -
cupsGetPrinters返回的是cups_dest_t *数组,每个元素的name字段才是打印机名,instance通常为空 - 记得在用完后调用
cupsFreeDests,否则内存泄漏 —— CUPS 的 dest 数组不是简单 malloc 出来的,内部有引用计数 - 如果你的程序跑在容器里,确保挂载了
/run/cups/cups.sock,或配置了正确的CUPS_SERVER和CUPS_ENCRYPTION=IfRequested
跨平台?别硬凑,按部署环境选路径
没有“一个函数通吃 Windows/Linux/macOS”的方案。强行封装抽象层,反而会让错误更难定位,比如把 cupsGetPrinters 的空结果误判为“无打印机”,而实际是 CUPS 服务根本没起来。
性能与兼容性影响很实际:Windows 下调 EnumPrinters 是毫秒级;CUPS 调用则涉及 socket 连接、HTTP 请求解析,首次调用可能卡几十毫秒,尤其在 CUPS 配置混乱时。
- 构建时用
#ifdef _WIN32分支,而不是试图用 libusb 或 dbus 直读底层 —— 那些路径权限高、不稳定、且不同发行版行为不一 - macOS 本质也走 CUPS,但部分 AirPrint 打印机可能只出现在
bonjour发现结果里,cupsGetPrinters不会返回它们;真要支持,得另接dns_sd.h - 不要在主线程同步调用这些 API,特别是 CUPS —— 网络超时或套接字卡住会导致界面冻结;至少设个 3 秒超时,或扔进线程池
默认打印机怎么拿?别只看列表顺序
很多人以为第一个就是默认,其实 Windows 和 CUPS 都有独立的“默认”标记位,必须显式查。
Windows 下用 GetDefaultPrinter(注意:它只返回名字字符串,不带其他信息,得再用 OpenPrinter 查详情);CUPS 下得调 cupsGetNamedDest 并传 NULL 作为 printer name,它才会返回默认那个。
-
GetDefaultPrinter在 Windows 10 1809+ 支持多用户上下文,但如果你的服务跑在 Session 0,它会返回空 —— 这不是 bug,是 Session 隔离设计 - CUPS 的默认打印机可能属于另一个用户(比如 root 添加的队列),普通用户调
cupsGetNamedDest可能拿不到,得检查返回值是否为NULL并 fallback 到第一个可用项 - 别缓存“默认打印机名”太久,用户随时可能在控制面板里改 —— 如果你的应用需要持续感知,默认状态应定期(比如每分钟)轮询一次











