Wix 工具集:“由于存在另一个客户端而禁止卸载组件”后完成清理

问题描述 投票:0回答:5

今天我发现我的安装程序无法再正确卸载。这意味着我的应用程序在从那里卸载后不再显示在控制面板中,但所有文件仍然存在。我查看了日志文件,看到很多“由于存在另一个客户端而不允许卸载组件”,这意味着我搞砸了..

那么清理我的电脑并防止将来再次发生这种情况的最佳方法是什么?是什么导致了这种情况?据我所知,我的应用程序的以前版本未完全卸载是此错误的原因吗?

不幸的是,由于各种原因无法使用VM..

仅供参考:出于开发和测试目的,我通常使用 1.0.xxxxx 测试和创建安装程序,其中 xxxxx 通常保持不变。我的升级代码始终相同。此外,我正在使用热量,并且尽可能让 wix 自动生成 GUID。此外,我有一个 CA 在安装后显示我的自述文件,还有一个用于执行批处理文件(使用 powercfg 修改注册表项)。卸载后,运行可执行文件以导入 .reg 文件以恢复修改的注册表项(因为它们将被 wix 卸载)。

wix installation windows-installer wix3.8 uninstallation
5个回答
19
投票

我们最近遇到这样的情况:我们的一台开发机器在卸载时无法删除所有组件。然而,在其他机器上,

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...
请查看此处


12
投票

听起来您需要卸载安装了不需要的组件的功能(或整个产品)。 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})))

LINQPad 即时分享

LINQPad Output

然后,运行

msiexec /x <ProductCode>
以删除产品的所有功能

msiexec /i <LocalPackage> REMOVE=<FeatureName>
仅删除安装该组件的功能。


10
投票

尽管这是一个有点旧的链接,但是发布我的发现可能会对面临同样情况的其他人有所帮助。

如果您在日志文件中发现

"Disallowing uninstallation of component: {Some GUID} since another client exists"
,那么原因可能是您之前的安装可能仍然引用此组件/GUID。

此外,如果您冒险尝试手动删除注册表项,您甚至可能在 regedit 中找不到 GUID。

但是这些注册表中可能很少,并且它们可能是隐藏的。在这种情况下,请使用 RegSeeker 工具 http://www.hoverdesk.net/ ,它可以帮助查找隐藏的注册表。在处理注册表时要保持警惕和谨慎。

根据 GUID,从您的解决方案/项目中获取组件的名称,并使用此工具的“在注册表中查找”选项找到它。仅验证并删除您认为不需要的条目。

诚然,我是通过下面的链接了解这个工具的

http://www.daviddeley.com/solutions/msiexec/index.htm


0
投票

遵循此工作流程效果非常好:

如何解决“由于存在另一个客户端而不允许卸载组件”?

创建这两个文件后,需要先删除它们的BOM,然后才能应用下一步。我使用过 Notepad++ (

Encoding
>
Encode in UTF-8
)。

使用 Notepad++ 的多光标可以轻松为每行添加 /f 选项 - 使用

SHIFT
+
ALT
+
Arrows

激活它

0
投票

对我来说以下方法有效:

从 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 文档以从其他用户中删除。

© www.soinside.com 2019 - 2024. All rights reserved.