windows用assocquerystring(assocstr_executable)查mailto关联程序,macos用lsgetapplicationforurl获取bundle路径,linux用xdg-mime query default x-scheme-handler/mailto解析desktop文件;跨平台应优先调用原生open api而非自行解析路径。

Windows 上用 AssocQueryString 查 mailto 关联程序
Windows 默认不暴露“系统邮件客户端路径”这个概念,而是靠协议关联(mailto)间接定位。直接读注册表(比如 HKEY_CLASSES_ROOT\mailto\shell\open\command)容易出错——很多现代邮件客户端(如 Outlook、Mailbird)会注册为“应用协议处理程序”,走的是 AssocQueryString 这套 API。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 调用
AssocQueryString时,iAssoc必须传ASSOCSTR_EXECUTABLE,不是ASSOCSTR_COMMAND或ASSOCSTR_FRIENDLYAPPNAME - 缓冲区要先调一次获取长度,再分配内存重试;否则返回
ERROR_MORE_DATA是常态 - 结果路径可能带引号和参数(如
"C:\Program Files\Outlook\outlook.exe" /mailto:%1),需用CommandLineToArgvW解析首项,或简单提取双引号内内容 - 注意权限:UWP 邮件应用(如 Windows Mail)返回的可能是
AppX...协议 URI,不是可执行路径,这种无法直接 launch
macOS 上用 LSGetApplicationForURL 获取 mailto: 处理器
macOS 没有全局“默认邮件客户端”设置入口,它按 URL scheme 绑定到具体 app bundle。不能靠读 ~/Library/Preferences/com.apple.LaunchServices.plist —— 该文件结构私有、易过期,且不反映当前生效状态。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 构造一个
CFURLRef指向mailto:test@example.com,传给LSGetApplicationForURL,它会返回实际处理该 scheme 的 app bundle 路径 - 返回值是 bundle path(如
/Applications/Mail.app),不是可执行文件路径;需拼接Contents/MacOS/Mail才能得到二进制路径(但多数场景只需 bundle 路径即可 open) - 如果用户没设默认 handler,函数可能返回
errLSApplicationNotFound,这时应 fallback 到open -b com.apple.mail或提示手动设置 - 注意沙盒限制:在 App Sandbox 下,该 API 可能被拒,需加
com.apple.security.automation.apple-events权限并声明LSHandlers
Linux 上解析 xdg-mime 和 mimeapps.list
Linux 没有统一接口,依赖 XDG 标准。核心是查 mailto 对应的 MIME type(x-scheme-handler/mailto),再找其默认应用。不能只看 /usr/share/applications/mimeinfo.cache —— 它是只读缓存,不反映用户级覆盖。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 优先执行
xdg-mime query default x-scheme-handler/mailto,它会按顺序检查$HOME/.config/mimeapps.list→/usr/local/share/applications/defaults.list→/usr/share/applications/defaults.list - 返回的是 desktop 文件名(如
thunderbird.desktop),需在$XDG_DATA_DIRS/applications/下搜索该文件,并解析Exec=行(注意处理 %u、%U 占位符) - 某些桌面环境(GNOME)会绕过 XDG 直接调用 GIO 的
g_app_info_get_default_for_type,C++ 中可用 glibmm 封装,但引入依赖较重 - 别硬编码路径如
/usr/bin/thunderbird—— Flatpak、Snap 版本会注册独立 desktop 文件,路径完全不同
跨平台封装要注意的三个现实约束
没有“一个函数返回所有平台的可执行路径”这种银弹。每个系统对“默认邮件客户端”的定义不同:Windows 认为是协议 handler,macOS 认为是 bundle,Linux 认为是 desktop entry。强行统一抽象反而导致行为不一致。
关键点:
- 不要试图 launch 后再判断是否成功 —— 用户可能取消授权(macOS)、弹出选择框(Linux)、或触发 UAC(Windows),这些都不是错误,而是流程一部分
- 避免缓存结果:用户随时可能改默认程序,每次调用都应重新查询(尤其 macOS 的 LS API 是实时的)
- 真正需要的是“打开 mailto 链接”,而不是“拿到路径再自己 fork”;绝大多数场景下,直接调用平台原生 open(
ShellExecute/NSWorkspace::openURL:/xdg-open)更可靠、更安全
最常被忽略的一点:你其实不需要知道邮件客户端是什么,只需要确保 mailto:xxx 能被正确分发。查路径只是为了 fallback 或 UI 显示,而显示又极易过时 —— 很多用户根本不知道自己默认用的是什么。










