如何在纯MSI安装包内可靠检测Word进程并阻止安装(无需额外引导程序)
我完全懂你的烦恼——用bootstrapper虽然能解决问题,但多了一个文件、额外的构建步骤,确实有点累赘。好消息是,纯MSI里完全可以干净、可靠地检测Word是否运行,还能避开讨厌的Error 1001,关键是选对自定义动作的时机和类型,别踩之前的坑。
核心思路:用「即时UI序列自定义动作 + Launch Condition」组合
你之前踩的1001错误,大多是因为用了Visual Studio Installer类自定义动作(就是那种引用.NET DLL、继承自Installer的)——这类动作的错误处理机制特别严格,很容易触发1001。我们换个思路,用更轻量、更原生的方式:
1. 别用.NET Installer类,改用原生脚本或C++自定义动作
放弃那些继承Installer的DLL,改用VBScript/JScript或者原生C++ DLL做自定义动作。它们更轻量,权限要求更低,也不会触发1001错误。
比如用VBScript写个简单的进程检测(放在UI序列早期):
Function CheckForRunningWord() Dim wmi, processes Set wmi = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2") Set processes = wmi.ExecQuery("SELECT * FROM Win32_Process WHERE Name = 'WINWORD.EXE'") ' 设置MSI属性供后续判断 If processes.Count > 0 Then Session.Property("WORDRUNNING") = "1" CheckForRunningWord = 1 Else Session.Property("WORDRUNNING") = "0" CheckForRunningWord = 0 End If End Function
2. 把自定义动作放在UI序列的正确位置
在MSI的UI序列里,找一个早于InstallValidate的节点(比如CostInitialize之后),添加这个即时执行的自定义动作:
- 为什么选UI序列+即时动作:
- 即时动作在安装开始修改系统前运行,出错了能优雅终止,不会触发回滚。
- UI序列的动作在交互安装时有用户上下文,能正确检测当前用户的Word进程;静默安装时也能正常运行(只要不是UI专属动作)。
- 这个阶段还没开始执行安装操作,终止安装不会留下烂摊子。
3. 用Launch Condition阻止安装
在自定义动作检测到Word运行后,会设置WORDRUNNING=1这个MSI属性。接下来添加一个Launch Condition:
- 条件:
WORDRUNNING <> 1 - 错误消息:
请先关闭Microsoft Word,再安装StyleGuard Pro。
这样,只要检测到Word在运行,MSI就会弹出你自定义的错误提示,然后优雅终止安装,完全不会触发1001。
更优雅的方案:用WiX替代Visual Studio Installer Project
如果你的项目可以迁移到WiX(比VS Installer Project灵活得多),那事情会更简单——WiX直接内置了进程检测的支持,连自定义动作都不用写:
<!-- 检测WinWord.exe进程,设置WORDRUNNING属性 --> <Property Id="WORDRUNNING"> <ProcessSearch Id="DetectWordProcess" Name="WINWORD.EXE" /> </Property> <!-- 用Launch Condition阻止安装 --> <Condition Message="请先关闭Microsoft Word,再安装StyleGuard Pro。"> NOT WORDRUNNING </Condition>
WiX会自动把进程检测封装成MSI属性,完全原生支持,可靠性拉满,没有自定义动作的稳定性问题。
关键注意事项(避坑指南)
- 别用延迟自定义动作:延迟动作是在系统上下文(System Account)运行的,可能看不到当前用户的Word进程(尤其是用户级安装的Office Add-in),会导致误判。
- 静默安装也能生效:即时自定义动作和Launch Condition在静默安装(
msiexec /i setup.msi /qn)下同样有效,MSI会返回错误代码1603,日志里会有对应的错误消息,不会静默失败。 - 权限问题:检测进程只需要普通用户权限(能看到自己的进程就行),管理员权限下也能正常检测其他用户的Word进程(如果是机器级安装)。
- 为什么之前的Install/Commit阶段动作不行:那些属于执行序列,MSI已经开始准备修改系统了,出错会触发回滚,而且Install类的错误处理机制容易出1001。
对比Bootstrapper的优劣
纯MSI方案的优势:
- 不需要额外的EXE,只发一个MSI文件,简化分发和构建。
- 完全遵循MSI原生机制,稳定性更高,没有额外的启动逻辑。
- 支持所有MSI的标准参数(静默、日志、修复等)。
如果你的项目必须用Visual Studio Installer Project,上面的VBScript+Launch Condition方案完全可行;如果能转WiX,那是最干净的解决方案。
内容来源于stack exchange

