Office2024_0-2060_MSI_Advertised_排查记录
Microsoft Office 2024 安装失败 0-2060 (0) 排查记录
1. 问题概述
安装 Microsoft Office 2024 64 位版时,安装程序最初报错:
Couldn't install
Error Code: 0-2060 (0)
界面给出的提示主要是检查网络连接、磁盘空间并重试,但最终排查证明,这些都不是实际原因。
安装环境:
Windows 11
版本:10.0.26100.3194
安装介质:ISO
Office:Office LTSC Professional Plus 2024 Volume
架构:x64
Channel:PerpetualVL2024
最终根因是:
系统中残留了一个损坏的旧版 Office MSI Advertised(已播发)产品注册。
Office 2024 安装器能够检测到这个 32 位 MSI 产品,因此拒绝安装 64 位 Office;但 Windows Installer 又无法正常卸载它,从而产生1605,最终被 Office 安装程序包装成通用错误0-2060 (0)。
涉及的 ProductCode:
{90120000-0017-0000-0000-0000000FF1CE}
对应 Windows Installer packed GUID:
00002109710000000000000000F01FEC
2. 初期现象
最开始检查 Click-to-Run 服务:
sc query ClickToRunSvc
得到:
[SC] EnumQueryServicesStatus:OpenService 失败 1060:
指定的服务未安装。
同时:
dir "C:\Program Files\Common Files\Microsoft Shared\ClickToRun"
没有 Click-to-Run 文件;
reg query "HKLM\SOFTWARE\Microsoft\Office\ClickToRun"
也不存在对应注册表项。
这些结果只能说明当前系统没有正常部署 Click-to-Run Office,不能直接解释安装失败的具体原因。
系统中还存在一些旧 Office 注册表痕迹,例如:
HKLM\SOFTWARE\Microsoft\Office\15.0
HKLM\SOFTWARE\Microsoft\Office\Common
HKLM\SOFTWARE\Microsoft\Office\Delivery
HKLM\SOFTWARE\Microsoft\Office\PowerPoint
以及 32 位视图中的:
HKLM\SOFTWARE\WOW6432Node\Microsoft\Office\12.0
HKLM\SOFTWARE\WOW6432Node\Microsoft\Office\Common
HKLM\SOFTWARE\WOW6432Node\Microsoft\Office\Excel
HKLM\SOFTWARE\WOW6432Node\Microsoft\Office\PowerPoint
HKLM\SOFTWARE\WOW6432Node\Microsoft\Office\Word
但标准卸载列表中没有真正的旧版 Office,只看到例如 VSTO Runtime 等组件。
因此不能简单地把“存在 Office 注册表目录”等同于“Office 仍然完整安装”。
另外,PowerShell 启动时曾出现:
PSSecurityException
因为在此系统上禁止运行脚本
这最终也与 Office ODT 的 .exe 安装失败无关。
3. 找到真正的安装日志
从 %TEMP% 中找到:
DESKTOP-XZ001-20260907-1913.log
这是整个排查过程的第一个决定性证据。
日志表明安装程序使用:
Channel: PerpetualVL2024
OfficeClientEdition: 64
ProductsToAdd: ProPlus2024Volume
并且配置中启用了:
Data.RemoveAllMSI: True
也就是 XML 中的:
<RemoveMSI />
安装程序随后决定清理这个旧 MSI ProductCode:
{90120000-0017-0000-0000-0000000FF1CE}
日志中明确出现:
Non-standalone MSI product, not part of any Office SKU which will be kept, remove
并将其列入:
Data.MSIProductsToUninstall:
{{90120000-0017-0000-0000-0000000FF1CE}}
4. 0-2060 (0) 实际上只是外层错误
真正失败发生在 <RemoveMSI /> 阶段。
日志:
MSIScrub::MSIUninstaller::UninstallProducts
ErrorCode: 1605
ErrorType: 1605
Failed to configure:
{90120000-0017-0000-0000-0000000FF1CE}
Error: 0x645
Failed to uninstall MSI product
随后:
Failed to uninstall MSI skus as per config file instructions.
Installation failed
最后安装器才显示:
ErrorCode: 0-2060 (0)
因此:
0-2060 (0)
并不是底层真正错误。
真正的因果链是:
<RemoveMSI />
↓
检测到旧 MSI ProductCode
↓
尝试卸载
↓
Windows Installer 返回 1605 / 0x645
↓
RemoveMSI 阶段失败
↓
Office Bootstrapper 显示通用 0-2060 (0)
日志中还出现了一些:
OpenProcess failed
ErrorCode: 5
但这些记录明确标记:
OException has occured. Skip.
因此它们不是导致安装失败的错误。
5. 暂时关闭 <RemoveMSI />
因为 ISO 挂载盘是只读的,无法直接修改其中的:
configuration.xml
所以将:
G:\Office
完整复制到:
C:\Office2024
保持目录结构不变:
C:\Office2024\
├── install.exe
├── configuration.xml
└── Office\
└── Data\
然后将:
<RemoveMSI />
删除或注释。
重新执行:
cd /d C:\Office2024
install.exe /configure configuration.xml
6. 出现新的 32/64 位冲突错误
关闭 <RemoveMSI /> 后,不再出现 0-2060 (0),但安装器改为提示:
Microsoft 365 and Office (64-bit) couldn't be installed
because you have these 32-bit programs installed on your computer.
这非常重要,因为它证明:
<RemoveMSI />确实是原来0-2060的触发点;- 系统中仍存在能够被 Office 检测到的 32 位 MSI Office 产品;
- 需要清理这个残留,而不能单纯绕过
<RemoveMSI />。
新的日志:
DESKTOP-XZ001-20260907-1928c.log
直接点名:
BlockingApp:
{90120000-0017-0000-0000-0000000FF1CE}
紧接着:
Preexisting 32bit products detected.
32bit MSI Installation found and trying to install 64bit C2R.
所以不是出现了另一个产品。
从始至终阻塞安装的仍然是同一个 ProductCode。
7. 发现残缺的 Windows Installer 注册
ProductCode:
{90120000-0017-0000-0000-0000000FF1CE}
转换为 Windows Installer packed GUID:
00002109710000000000000000F01FEC
最初检查:
reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\00002109710000000000000000F01FEC" /s
确实找到残留:
...\Features
...\InstallProperties
LocalPackage REG_SZ C:\Windows\Installer\5ab4b.msi
WindowsInstaller REG_DWORD 0x1
但:
reg query "HKLM\SOFTWARE\Classes\Installer\Products\00002109710000000000000000F01FEC" /s
没有正常对应项。
这说明 MSI 注册已经处于不一致状态。
备份后删除了该 S-1-5-18\UserData 孤立产品键。
但重新运行 Office 后,新的日志仍然点名同一个 ProductCode。
因此该 UserData 项并不是唯一残留。
8. 若干注册表定位尝试
为寻找剩余 MSI 注册,曾依次检查:
Office\12.0\Registration- 当前 Administrator SID 下的
Installer\UserData Installer\UserData全树中对应 packed GUIDHKCU\Software\Microsoft\InstallerInstaller\ManagedHKCU\Software\Classes\Installer
这些位置都没有找到能解释当前状态的完整产品注册。
此时继续猜注册表路径已经没有效率,因此改为直接询问 Windows Installer API 自己如何看待该 ProductCode。
9. MsiEnumProductsExW:产品未被正常枚举
调用:
MsiEnumProductsExW()
按全部 MSI 安装上下文枚举:
{90120000-0017-0000-0000-0000000FF1CE}
结果:
NOT ENUMERATED BY WINDOWS INSTALLER
这个结果一开始容易被理解为“产品完全不存在”,但后续证明这是不充分的。
某些仅处于 Advertisement 状态的异常产品,并不一定会以预期方式出现在这样的产品枚举结果中。
因此需要继续直接查询产品状态。
10. MsiQueryProductStateW 找到关键状态
直接执行:
MsiQueryProductStateW(
"{90120000-0017-0000-0000-0000000FF1CE}"
)
返回:
1
即:
INSTALLSTATE_ADVERTISED
这是整个排查的关键突破。
它表示:
Windows Installer 仍然认为该 ProductCode 是一个 Advertised(已播发)产品,但产品并没有处于完整的正常安装状态。
这正好解释此前的矛盾:
Office 安装器:
“我能看到这个 32 位 MSI 产品。”
而正常卸载路径却:
Windows Installer:
“无法按正常已安装产品处理。”
因此才能同时出现:
Office SXS 检测 → 产品存在
和:
RemoveMSI / 卸载 → Error 1605
11. 正常 MSI 配置/卸载 API 同样失败
在确认产品状态为:
INSTALLSTATE_ADVERTISED
以后,尝试通过:
MsiConfigureProductExW(
ProductCode,
...,
INSTALLSTATE_ABSENT,
...
)
让 Windows Installer 自己把产品设置为卸载状态。
返回:
1605
于是形成了异常组合:
MsiQueryProductStateW → 1
MsiConfigureProductExW → 1605
即:
状态查询:它还存在(Advertised)
正常配置/卸载:却认为无法正常处理该产品
这说明 Advertisement 注册仍存在,但完整产品注册已经损坏。
12. 使用 Process Monitor 直接观察 MSI 实际读取位置
此时不再猜注册表。
使用 Microsoft Sysinternals:
Process Monitor / Procmon64.exe
只监控:
powershell.exe
然后再次调用:
MsiQueryProductStateW()
并将捕获结果导出:
Logfile.CSV
ProcMon 记录显示,Windows Installer 在查询该 ProductCode 时访问了多个 MSI 注册位置。
其中部分路径返回:
NAME NOT FOUND
但以下路径能够被成功打开:
HKCR\Installer\Products\
00002109710000000000000000F01FEC
结果:
SUCCESS
这成为最终定位依据。
前面已经删除的 S-1-5-18\UserData 项并不足以解除该 Advertisement 状态;ProcMon 证明 MSI 查询时仍然能通过 HKCR\Installer\Products 看到该 packed GUID。
13. 最终清理
针对 ProcMon 已经确认存在的单独产品键进行备份和删除:
HKCR\Installer\Products\
00002109710000000000000000F01FEC
注意这里只处理这个精确 ProductCode 对应的孤立产品项。
没有删除整个:
Installer
Products
Microsoft\Office
等注册表树。
删除后再次调用:
MsiQueryProductStateW(
"{90120000-0017-0000-0000-0000000FF1CE}"
)
返回:
-1
即:
INSTALLSTATE_UNKNOWN
这意味着 Windows Installer 已经不再认为该 ProductCode:
- 已安装;
- 已 Advertise;
- 或处于其他有效 MSI 产品状态。
14. 再次安装 Office
保持:
<RemoveMSI />
处于删除/注释状态。
执行:
cd /d C:\Office2024
install.exe /configure configuration.xml
结果:
Office 2024 安装成功
问题解决。
最终根因
整个故障的本质不是网络、磁盘空间、PowerShell ExecutionPolicy,也不是 Click-to-Run 服务本身损坏。
真正的问题是一个损坏的旧 Office 32 位 MSI Advertised 产品注册:
ProductCode:
{90120000-0017-0000-0000-0000000FF1CE}
Packed GUID:
00002109710000000000000000F01FEC
其状态异常:
MsiQueryProductStateW → INSTALLSTATE_ADVERTISED (1)
但:
MsiConfigureProductExW / Office RemoveMSI
→ ERROR_UNKNOWN_PRODUCT / 1605
因果链:
旧 MSI Advertised 残留
↓
Office 能检测到 32 位产品
↓
<RemoveMSI /> 尝试清理
↓
Windows Installer 返回 1605
↓
Office 显示 0-2060 (0)
关闭 <RemoveMSI /> 后则变成:
旧 MSI Advertised 残留
↓
不执行自动删除
↓
Office x64 前置检查发现 32 位 MSI
↓
64-bit / 32-bit conflict
最终通过 ProcMon 找到实际仍可访问的 Advertisement 注册:
HKCR\Installer\Products\
00002109710000000000000000F01FEC
清除该孤立产品项后:
MsiQueryProductStateW: 1 → -1
Office 2024 随后正常安装。
排查过程中确认不是根因的项目
| 项目 | 结论 |
|---|---|
| ClickToRunSvc 不存在 | 只能说明当前未部署 C2R Office,不是本次错误根因 |
0-2060 (0) |
Office Bootstrapper 的外层通用错误 |
| 网络 / 磁盘空间提示 | 通用 UI 文案,与实际日志不符 |
| PowerShell ExecutionPolicy 报错 | 与 ODT .exe 安装过程无直接关系 |
OpenProcess Error 5 |
日志明确标记为 Skip |
v32.cab not reachable |
在检测出 32 位 MSI 之后才发生,不是触发 32/64 冲突的原因 |
| VSTO Runtime | 不是被日志点名的 ProductCode |
Office\12.0 注册表目录本身 |
只能说明有旧 Office 痕迹,不能直接说明是哪一项阻塞 |
| 标准“程序和功能”卸载列表 | 无法可靠显示这种损坏的 Advertised MSI 残留 |
本次排查中走过的弯路
本次排查前半段做过一些价值较低或结论不足的检查,包括:
- 反复寻找不同 Office 注册表目录;
- 先根据普通卸载列表判断是否存在 Office;
- 分别猜测
HKCU、HKLM、不同 SID 下的 Installer 路径; - 一度把
MsiEnumProductsExW的“不枚举”理解得过强; - 一度根据
HKCR合并视图猜测底层 hive,而没有第一时间直接观察 API 的注册表访问。
后期更有效的方法是:
安装日志
↓
锁定具体 ProductCode
↓
Windows Installer API 查询实际产品状态
↓
ProcMon 观察 API 实际访问位置
↓
只清理已确认的孤立注册项
相比“大范围搜索 Office 注册表”,这个流程更快,也更安全。
推荐的类似问题排查顺序
以后如果再次遇到 Office 安装出现:
0-xxxx
或 32/64 位冲突,但“程序和功能”中又没有对应 Office,可以优先:
- 先看 ODT / Click-to-Run 日志,不以安装窗口的通用错误作为根因。
- 找:
ErrorCode MSIProductsToUninstall BlockingApp SXSMSIValidator - 如果已经获得具体 ProductCode,不再大范围清 Office 注册表。
- 调用:
查看 MSI 自己认为什么状态。MsiQueryProductStateW - 如果出现:
且正常卸载返回:INSTALLSTATE_ADVERTISED
则高度怀疑是损坏的 Advertisement 注册。1605 - 使用 ProcMon 捕获调用
MsiQueryProductStateW时的实际注册表访问。 - 只处理 ProcMon 确认的孤立 ProductCode 项。
- 删除后再次确认:
MsiQueryProductStateW → -1 - 再重新安装 Office。
关键文件
本次排查涉及:
DESKTOP-XZ001-20260907-1913.log
第一次安装失败日志,确认:
RemoveMSI
→ ProductCode {90120000-0017-...}
→ 1605
→ 0-2060 (0)
以及:
DESKTOP-XZ001-20260907-1928c.log
关闭 <RemoveMSI /> 后的日志,确认:
BlockingApp:
{90120000-0017-0000-0000-0000000FF1CE}
Preexisting 32bit products detected.
最后:
Logfile.CSV
为 Process Monitor 捕获结果,用于定位使 MSI 返回 INSTALLSTATE_ADVERTISED 的实际注册表来源。
备注
安装日志中包含 Office 产品密钥等敏感信息,因此本记录不保存 PIDKEY、MachineId、SessionID 等不必要的敏感字段。
注册表操作也只记录最终确定的 ProductCode 和对应孤立 MSI 项,不建议将“删除整个 Office/Installer 注册表树”作为通用处理办法。
附录 A:Windows Installer API PowerShell 代码
本次排查过程中,注册表检查只能看到部分 MSI 残留,最终是通过直接调用 Windows Installer 的 msi.dll API 才确认产品处于异常的 Advertised 状态。
目标 ProductCode:
{90120000-0017-0000-0000-0000000FF1CE}
Windows Installer packed GUID:
00002109710000000000000000F01FEC
以下代码均在 Windows PowerShell 中执行。
A.1 查询产品状态:MsiQueryProductStateW
这是本次排查中最关键的 API。
常用返回状态:
INSTALLSTATE_UNKNOWN = -1
INSTALLSTATE_ADVERTISED = 1
INSTALLSTATE_ABSENT = 2
INSTALLSTATE_DEFAULT = 5
其中:
-1:既未安装,也未 Advertise1:已 Advertise,但未处于正常完整安装状态2:Absent 状态5:默认/本地安装状态
完整 PowerShell 代码
Add-Type @'
using System.Runtime.InteropServices;
public static class MSIState
{
[DllImport("msi.dll", CharSet = CharSet.Unicode)]
public static extern int MsiQueryProductStateW(
string szProduct
);
}
'@
$productCode = "{90120000-0017-0000-0000-0000000FF1CE}"
$state = [MSIState]::MsiQueryProductStateW($productCode)
$stateName = switch ($state)
{
-1 { "INSTALLSTATE_UNKNOWN" }
1 { "INSTALLSTATE_ADVERTISED" }
2 { "INSTALLSTATE_ABSENT" }
5 { "INSTALLSTATE_DEFAULT" }
default { "UNKNOWN_VALUE" }
}
[PSCustomObject]@{
ProductCode = $productCode
State = $state
StateName = $stateName
}
本机异常状态下返回:
ProductCode : {90120000-0017-0000-0000-0000000FF1CE}
State : 1
StateName : INSTALLSTATE_ADVERTISED
最终清理完成后再次调用,返回:
State : -1
StateName : INSTALLSTATE_UNKNOWN
这也是确认残留已经真正消失的最终判据。
A.2 枚举产品上下文:MsiEnumProductsExW
为了确认该 ProductCode 是否存在于不同 MSI 安装上下文中,调用:
MsiEnumProductsExW
完整 PowerShell 代码
Add-Type @'
using System;
using System.Text;
using System.Runtime.InteropServices;
public static class MSIEnum
{
[DllImport("msi.dll", CharSet = CharSet.Unicode)]
public static extern uint MsiEnumProductsExW(
string szProductCode,
string szUserSid,
uint dwContext,
uint dwIndex,
StringBuilder szInstalledProductCode,
out uint pdwInstalledContext,
StringBuilder szSid,
ref uint pcchSid
);
}
'@
$productCode = "{90120000-0017-0000-0000-0000000FF1CE}"
# MSIINSTALLCONTEXT_USERMANAGED = 1
# MSIINSTALLCONTEXT_USERUNMANAGED = 2
# MSIINSTALLCONTEXT_MACHINE = 4
# MSIINSTALLCONTEXT_ALL = 7
$MSIINSTALLCONTEXT_ALL = 7
# S-1-1-0 = Everyone
$userSid = "S-1-1-0"
$index = 0
$found = $false
while ($true)
{
$installedProductCode =
New-Object System.Text.StringBuilder 39
$sidBuffer =
New-Object System.Text.StringBuilder 256
[uint32]$sidLength = 256
[uint32]$installedContext = 0
$result = [MSIEnum]::MsiEnumProductsExW(
$productCode,
$userSid,
$MSIINSTALLCONTEXT_ALL,
$index,
$installedProductCode,
[ref]$installedContext,
$sidBuffer,
[ref]$sidLength
)
# ERROR_NO_MORE_ITEMS = 259
if ($result -eq 259)
{
break
}
if ($result -ne 0)
{
Write-Host "MsiEnumProductsExW failed."
Write-Host "ErrorCode = $result"
break
}
$found = $true
$contextName = switch ($installedContext)
{
1 { "MSIINSTALLCONTEXT_USERMANAGED" }
2 { "MSIINSTALLCONTEXT_USERUNMANAGED" }
4 { "MSIINSTALLCONTEXT_MACHINE" }
default { "UNKNOWN_CONTEXT" }
}
[PSCustomObject]@{
ProductCode = $installedProductCode.ToString()
Context = $contextName
ContextID = $installedContext
SID = $sidBuffer.ToString()
}
$index++
}
if (-not $found)
{
Write-Host "NOT ENUMERATED BY WINDOWS INSTALLER"
}
本次实际返回:
NOT ENUMERATED BY WINDOWS INSTALLER
但之后:
MsiQueryProductStateW → 1
因此不能把:
MsiEnumProductsExW 没有枚举到
简单等价为:
Windows Installer 完全不知道该产品
实际状态应继续通过:
MsiQueryProductStateW
确认。
A.3 尝试使用 MSI API 正常卸载:MsiConfigureProductExW
在确认产品状态为:
INSTALLSTATE_ADVERTISED
以后,尝试让 Windows Installer 自己将产品设置为:
INSTALLSTATE_ABSENT
完整 PowerShell 代码
Add-Type @'
using System.Runtime.InteropServices;
public static class MSIConfigure
{
[DllImport("msi.dll", CharSet = CharSet.Unicode)]
public static extern uint MsiConfigureProductExW(
string szProduct,
int iInstallLevel,
int eInstallState,
string szCommandLine
);
[DllImport("msi.dll", CharSet = CharSet.Unicode)]
public static extern int MsiQueryProductStateW(
string szProduct
);
}
'@
$productCode =
"{90120000-0017-0000-0000-0000000FF1CE}"
# INSTALLSTATE_ABSENT = 2
$INSTALLSTATE_ABSENT = 2
$result = [MSIConfigure]::MsiConfigureProductExW(
$productCode,
0,
$INSTALLSTATE_ABSENT,
"REBOOT=ReallySuppress"
)
$stateAfter =
[MSIConfigure]::MsiQueryProductStateW(
$productCode
)
[PSCustomObject]@{
ProductCode = $productCode
RemoveResult = $result
StateAfter = $stateAfter
}
理想情况下,如果产品注册完整,正常结果应类似:
RemoveResult = 0
StateAfter = -1
但本机实际返回:
RemoveResult = 1605
而此前:
MsiQueryProductStateW = 1
因此出现异常组合:
QueryProductState:
INSTALLSTATE_ADVERTISED
↓
ConfigureProductEx:
ERROR_UNKNOWN_PRODUCT / 1605
说明 MSI Advertisement 注册仍然存在,但已经不具备一个可正常配置、修复或卸载的完整产品注册。
A.4 将查询与枚举合并成一个只读诊断脚本
下面的脚本:
- 查询产品状态;
- 枚举产品上下文;
- 输出解释。
不会修改系统。
Add-Type @'
using System;
using System.Text;
using System.Runtime.InteropServices;
public static class MSIProbe
{
[DllImport("msi.dll", CharSet = CharSet.Unicode)]
public static extern int MsiQueryProductStateW(
string szProduct
);
[DllImport("msi.dll", CharSet = CharSet.Unicode)]
public static extern uint MsiEnumProductsExW(
string szProductCode,
string szUserSid,
uint dwContext,
uint dwIndex,
StringBuilder szInstalledProductCode,
out uint pdwInstalledContext,
StringBuilder szSid,
ref uint pcchSid
);
}
'@
$productCode =
"{90120000-0017-0000-0000-0000000FF1CE}"
Write-Host "=== Windows Installer Product Probe ==="
Write-Host "ProductCode: $productCode"
Write-Host ""
# ------------------------------------------------------------
# 1. Query product state
# ------------------------------------------------------------
$state =
[MSIProbe]::MsiQueryProductStateW(
$productCode
)
$stateName = switch ($state)
{
-1 { "INSTALLSTATE_UNKNOWN" }
1 { "INSTALLSTATE_ADVERTISED" }
2 { "INSTALLSTATE_ABSENT" }
5 { "INSTALLSTATE_DEFAULT" }
default { "UNKNOWN_VALUE" }
}
Write-Host "MsiQueryProductStateW:"
Write-Host " State = $state"
Write-Host " StateName = $stateName"
Write-Host ""
# ------------------------------------------------------------
# 2. Enumerate MSI contexts
# ------------------------------------------------------------
$contextAll = 7
$everyoneSid = "S-1-1-0"
$index = 0
$found = $false
Write-Host "MsiEnumProductsExW:"
while ($true)
{
$productBuffer =
New-Object System.Text.StringBuilder 39
$sidBuffer =
New-Object System.Text.StringBuilder 256
[uint32]$sidLength = 256
[uint32]$context = 0
$result =
[MSIProbe]::MsiEnumProductsExW(
$productCode,
$everyoneSid,
$contextAll,
$index,
$productBuffer,
[ref]$context,
$sidBuffer,
[ref]$sidLength
)
if ($result -eq 259)
{
break
}
if ($result -ne 0)
{
Write-Host " API Error = $result"
break
}
$found = $true
$contextName = switch ($context)
{
1 { "USER_MANAGED" }
2 { "USER_UNMANAGED" }
4 { "MACHINE" }
default { "UNKNOWN" }
}
Write-Host " Product = $($productBuffer.ToString())"
Write-Host " Context = $contextName ($context)"
Write-Host " SID = $($sidBuffer.ToString())"
Write-Host ""
$index++
}
if (-not $found)
{
Write-Host " No instance enumerated."
}
Write-Host ""
Write-Host "=== Interpretation ==="
switch ($state)
{
-1 {
Write-Host "Product is neither installed nor advertised."
}
1 {
Write-Host "Product is ADVERTISED."
Write-Host "If normal configure/uninstall returns 1605, suspect broken MSI advertisement registration."
}
2 {
Write-Host "Product is in INSTALLSTATE_ABSENT."
}
5 {
Write-Host "Product is in INSTALLSTATE_DEFAULT."
}
default {
Write-Host "Unexpected MSI product state: $state"
}
}
对于本次故障,这个脚本的核心结果相当于:
=== Windows Installer Product Probe ===
ProductCode:
{90120000-0017-0000-0000-0000000FF1CE}
MsiQueryProductStateW:
State = 1
StateName = INSTALLSTATE_ADVERTISED
MsiEnumProductsExW:
No instance enumerated.
=== Interpretation ===
Product is ADVERTISED.
If normal configure/uninstall returns 1605,
suspect broken MSI advertisement registration.
A.5 清理完成后的验证
删除 ProcMon 已确认的孤立产品注册后,最终只需要再次执行:
[MSIState]::MsiQueryProductStateW(
"{90120000-0017-0000-0000-0000000FF1CE}"
)
最终结果:
-1
即:
INSTALLSTATE_UNKNOWN
这是本次问题真正解决的关键验证。
随后重新运行:
cd /d C:\Office2024
install.exe /configure configuration.xml
Office 2024 成功安装。
A.6 API 在本次排查中的作用
三个 API 的作用可以概括为:
MsiEnumProductsExW
│
├─ Windows Installer 能否正常枚举这个产品实例?
│
└─ 本次:没有枚举到
│
▼
MsiQueryProductStateW
│
├─ MSI 当前到底认为这个 ProductCode 是什么状态?
│
└─ 本次:INSTALLSTATE_ADVERTISED (1)
│
▼
MsiConfigureProductExW
│
├─ 能否让 MSI 自己正常移除它?
│
└─ 本次:1605 / ERROR_UNKNOWN_PRODUCT
│
▼
结论:
Advertisement 状态仍存在,
但完整产品注册已经损坏
│
▼
ProcMon 跟踪 MsiQueryProductStateW
│
▼
定位实际 Registry SUCCESS 项
│
▼
精确删除孤立产品注册
│
▼
MsiQueryProductStateW = -1
附录 B:关键命令速查
B.1 复制 ISO 中的 Office 安装目录
xcopy G:\Office C:\Office2024 /E /I /H
B.2 从本地目录安装
cd /d C:\Office2024
install.exe /configure configuration.xml
B.3 查询已知 MSI ProductCode 的状态
[MSIState]::MsiQueryProductStateW(
"{90120000-0017-0000-0000-0000000FF1CE}"
)
本次关键状态变化:
清理前:1
清理后:-1
B.4 最终定位到的孤立注册项
HKCR\Installer\Products\
00002109710000000000000000F01FEC
对类似问题,不应直接复制本记录中的 GUID 进行删除。
必须先从自己的 Office 日志中确定实际 BlockingApp / MSIProductsToUninstall ProductCode,再通过 MSI API 和 ProcMon 验证。

浙公网安备 33010602011771号