今天我发现我的安装程序无法再正确卸载。这意味着我的应用程序在从那里卸载后不再显示在控制面板中,但所有文件仍然存在。我查看了日志文件,看到很多“由于存在另一个客户端而不允许卸载组件”,这意味着我搞砸了..
那么清理我的电脑并防止将来再次发生这种情况的最佳方法是什么?是什么导致了这种情况?据我所知,我的应用程序的以前版本未完全卸载是此错误的原因吗?
不幸的是,由于各种原因无法使用VM..
仅供参考:出于开发和测试目的,我通常使用 1.0.xxxxx 测试和创建安装程序,其中 xxxxx 通常保持不变。我的升级代码始终相同。此外,我正在使用热量,并且尽可能让 wix 自动生成 GUID。此外,我有一个 CA 在安装后显示我的自述文件,还有一个用于执行批处理文件(使用 powercfg 修改注册表项)。卸载后,运行可执行文件以导入 .reg 文件以恢复修改的注册表项(因为它们将被 wix 卸载)。
我们最近遇到这样的情况:我们的一台开发机器在卸载时无法删除所有组件。然而,在其他机器上,
WiX
设置按预期工作。
因此,如果您错误地卸载了产品的早期版本并收到消息
Disallowing uninstallation of component: {GUID} since another client exists
,那么您的注册表中很可能存在孤立组件。
有一个更优雅的解决方案,可以使用 PowerShell
删除这些
hidden注册表项,而不是像其他人提到的那样使用第三方应用程序。
$productName = "Path\\YourProductName" # this should basically match against your previous
# installation path. Make sure that you don't mess with other components used
# by any other MSI package
$components = Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\
$count = 0
foreach ($c in $components)
{
foreach($p in $c.Property)
{
$propValue = (Get-ItemProperty "Registry::$($c.Name)" -Name "$($p)")."$($p)"
if ($propValue -match $productName)
{
Write-Output $propValue
$count++
Remove-Item "Registry::$($c.Name)" -Recurse
}
}
}
Write-Host "$($count) key(s) removed"
如果您想获得有关该消息原因的更详细解释
disallowing uninstallation...
请查看此处。
听起来您需要卸载安装了不需要的组件的功能(或整个产品)。 Windows Installer 有一个用于查询组件、功能和产品的 API。 WiX 工具集集成了一个名为 DTF 的 API 包装器。您可以使用它按组件查询功能。
因此,打开您最喜欢的 .NET 脚本运行程序(我的是 LINQPad)并运行查询。例如,要了解如何删除“candle.exe”:
// using System.Linq;
// using Microsoft.Deployment.WindowsInstaller;
// <ref>"C:\Program Files (x86)\WiX Toolset v3.8\SDK\
Microsoft.Deployment.WindowsInstaller.dll"</ref>
ComponentInstallation.AllComponents
.Where(c=>c.State == InstallState.Local)
.Where(c => c.Path.ToLowerInvariant().EndsWith(@"\candle.exe"))
.SelectMany(c => c.ClientProducts
.SelectMany(p => p.Features.Where(f => f.Usage.UseCount > 0)
.Select(f => new {
c.Path,
f.FeatureName,
p.LocalPackage,
p.UserSid,
p.ProductCode})))
然后,运行
msiexec /x <ProductCode>
以删除产品的所有功能
或
msiexec /i <LocalPackage> REMOVE=<FeatureName>
仅删除安装该组件的功能。
尽管这是一个有点旧的链接,但是发布我的发现可能会对面临同样情况的其他人有所帮助。
如果您在日志文件中发现
"Disallowing uninstallation of component: {Some GUID} since another client exists"
,那么原因可能是您之前的安装可能仍然引用此组件/GUID。
此外,如果您冒险尝试手动删除注册表项,您甚至可能在 regedit 中找不到 GUID。
但是这些注册表中可能很少,并且它们可能是隐藏的。在这种情况下,请使用 RegSeeker 工具 http://www.hoverdesk.net/ ,它可以帮助查找隐藏的注册表。在处理注册表时要保持警惕和谨慎。
根据 GUID,从您的解决方案/项目中获取组件的名称,并使用此工具的“在注册表中查找”选项找到它。仅验证并删除您认为不需要的条目。
诚然,我是通过下面的链接了解这个工具的
遵循此工作流程效果非常好:
创建这两个文件后,需要先删除它们的BOM,然后才能应用下一步。我使用过 Notepad++ (
Encoding
> Encode in UTF-8
)。
使用 Notepad++ 的多光标可以轻松为每行添加 /f 选项 - 使用
SHIFT
+ALT
+Arrows
激活它
对我来说以下方法有效:
从 MSI 日志文件中获取观察到以下类似错误的组件列表:
MSI (s) (08:10) [00:49:40:298]:禁止卸载 组件:{9F196C41-9C40-5728-84B5-4F93BD9EAC64} 来自另一个客户端 存在
现在从组件 {9F196C41-9C40-5728-84B5-4F93BD9EAC64} 尝试获取引用它的产品列表。
以下 win32 程序有帮助:
#include <iostream>
#include <Windows.h>
#include <msi.h>
#pragma comment(lib, "Msi.lib")
int main()
{
int nProductIndex = 0;
char szProductCode[255] = {0};
while (1)
{
auto ret = MsiEnumClientsA("{9F196C41-9C40-5728-84B5-4F93BD9EAC64}", nProductIndex, szProductCode);
if (ret == ERROR_SUCCESS)
{
std::cout << "Product code:" << szProductCode << std::endl;
nProductIndex++;
}
else
{
break;
}
}
}
当上述程序运行时,控制台中将看到产品代码列表。
现在使用 MsiZap.exe 使用以下命令删除产品:
msizap T {6088B31B-2A22-4477-B243-DEB2EDF26C9A}
请参阅 msizap 文档以从其他用户中删除。