macOS 上同时运行两个微信实例的曲折之路
开篇:一个看似简单的需求
作为一个经常需要在工作和个人微信之间切换的 Mac 用户,我突发奇想:能不能在 macOS 上同时运行两个微信实例?这样就不用频繁切换账号了。
想法很美好,现实却很骨感。我简单地复制了 /Applications/WeChat.app 到桌面,改名为 WeChat2.app,并修改了 Info.plist 中的 bundle ID 为 com.eflabs.talk。然后,我满怀期待地双击了这个新应用...
砰! 系统无情地抛出了一个错误:
Launch failed. Launchd job spawn failed. (Error Code: 153)
第一个坑:签名问题
作为一个有经验的开发者,我立刻意识到这可能是签名问题。macOS 要求所有应用都必须经过代码签名才能运行,而我复制的应用还保留着原始开发者的签名。
于是我尝试重新签名:
codesign --force --deep --sign - ~/Desktop/WeChat2.app
结果,另一个错误接踵而至:
resource fork, Finder information, or similar detritus not allowed
第二个坑:Finder 的"礼物"
这个错误信息很明确:代码签名工具检测到了 Finder 留下的"垃圾数据"。当我们复制文件时,Finder 会顺手添加一些扩展属性(extended attributes),比如:
com.apple.FinderInfo:Finder 的元数据com.apple.fileprovider.fpfs#P:文件提供者的信息- Resource fork:传统 macOS 文件系统的资源分叉
这些"额外信息"在代码签名时是不被允许的,因为它们可能会影响签名的完整性验证。
漫长的探索:各种尝试
尝试 1:清理扩展属性
我的第一反应是清理这些扩展属性:
xattr -rc ~/Desktop/WeChat2.app
清理完成!再次签名... 还是失败。
尝试 2:删除 resource fork
既然扩展属性清理了,那可能是 resource fork 的问题。我检查了文件,发现确实有很多文件包含 resource fork:
find ~/Desktop/WeChat2.app -type f -exec sh -c 'test -e "$1/..namedfork/rsrc" && echo "$1 has resource fork"' _ {} \;
我尝试删除这些 resource fork,但依然无济于事。
尝试 3:使用 cp -RX 重新复制
cp -RX 可以排除 resource fork 进行复制。我重新复制了整个应用:
cp -RX /Applications/WeChat.app ~/Desktop/WeChat2.app
但问题依然存在。
尝试 4:使用 ditto 重新复制
ditto 命令据说可以更好地处理文件系统元数据。我尝试了:
ditto /Applications/WeChat.app ~/Desktop/WeChat2.app
还是没有解决问题。
关键时刻:找到真正的原因
在经历了无数次的失败后,我决定使用更详细的调试信息。我使用了 codesign 的详细输出模式:
codesign --force --sign - --verbose=4 ~/Desktop/WeChat2.app/Contents/PlugIns/WeChatMacShare.appex
这次,错误信息更具体了:
file with invalid attached data: Disallowed xattr com.apple.FinderInfo found on /Users/harvey/Desktop/WeChat2.app/Contents/PlugIns/WeChatMacShare.appex
啊哈! 问题出在目录本身的扩展属性,而不仅仅是文件。我之前的清理操作可能没有完全清理目录级别的扩展属性。
解决方案:系统化的清理和签名
找到了根本原因,解决方案就清晰了。关键是要:
- 彻底清理所有扩展属性:包括文件和目录
- 逐个签名组件:先签名所有子组件(.app、.appex、.framework),再签名主应用
- 使用 --preserve-metadata 选项:保留必要的元数据(entitlements、requirements 等)
完整的解决脚本
# 1. 重新复制应用(排除 resource fork)
cp -RX /Applications/WeChat.app ~/Desktop/WeChat2.app
# 2. 修改 bundle ID
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.eflabs.talk" ~/Desktop/WeChat2.app/Contents/Info.plist
# 3. 彻底清理所有扩展属性(文件和目录)
xattr -rc ~/Desktop/WeChat2.app
find ~/Desktop/WeChat2.app -type d -exec xattr -c {} \; 2>/dev/null
# 4. 移除所有现有签名
codesign --remove-signature ~/Desktop/WeChat2.app 2>/dev/null
find ~/Desktop/WeChat2.app -type d \( -name "*.app" -o -name "*.appex" -o -name "*.framework" \) -exec codesign --remove-signature {} \; 2>/dev/null
# 5. 逐个签名所有组件(从最内层开始)
find ~/Desktop/WeChat2.app -type d \( -name "*.app" -o -name "*.appex" -o -name "*.framework" \) | sort -r | while read dir; do
xattr -c "$dir" 2>/dev/null
codesign --force --sign - --preserve-metadata=entitlements,requirements,flags,runtime "$dir" 2>&1
done
# 6. 最后签名主应用
xattr -c ~/Desktop/WeChat2.app
codesign --force --sign - --preserve-metadata=entitlements,requirements,flags,runtime ~/Desktop/WeChat2.app
关键技巧
sort -r:确保从最内层的组件开始签名(依赖关系)--preserve-metadata:保留 entitlements 等信息,避免应用功能受限- 清理后立即签名:防止系统在签名过程中重新添加扩展属性
成功时刻:两个微信同时运行
执行完上述步骤后,我小心翼翼地尝试启动应用:
open -n ~/Desktop/WeChat2.app
成功了! 应用正常启动,并且我看到两个微信进程同时在运行:
- 原始微信:
com.tencent.xinWeChat(来自/Applications/WeChat.app) - 新微信:
com.eflabs.talk(来自~/Desktop/WeChat2.app)
它们使用不同的 bundle ID 和独立的数据目录,完全不会互相干扰。
经验总结
- macOS 的代码签名非常严格:不仅仅是文件内容,连文件系统元数据也会被检查
- Finder 会"好心"地添加元数据:复制文件时要注意清理扩展属性
- 签名顺序很重要:必须从最内层的依赖组件开始签名
- 使用详细输出模式调试:
--verbose=4可以帮助定位具体问题 --preserve-metadata选项很关键:避免丢失重要的应用配置信息
后记
这个看似简单的需求,实际上涉及了 macOS 的安全机制、文件系统特性、代码签名等多个方面。虽然过程曲折,但最终成功解决了问题,也让我对 macOS 的应用签名机制有了更深入的理解。
现在,我可以愉快地同时使用两个微信账号了!🎉
本文记录了在 macOS 15.6 上同时运行两个微信实例的完整解决方案。如果遇到类似问题,希望这篇文章能帮助你少走弯路。