Wix 工具集:“由于存在另一个客户端,因此不允许卸载组件”后完成清理

Posted

技术标签:

【中文标题】Wix 工具集:“由于存在另一个客户端,因此不允许卸载组件”后完成清理【英文标题】:Wix toolset: complete cleanup after "disallowing uninstallation of component since another client exists" 【发布时间】:2014-12-31 14:26:20 【问题描述】:

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

那么清理我的电脑并防止它在未来发生的最佳方法是什么?是什么原因造成的? afaik 未完全卸载我的应用程序的早期版本是导致此错误的原因吗?

很遗憾,由于各种原因,无法使用 VM..

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

【问题讨论】:

不,这不是错误;这是信息性的。由于您是在一个接一个地安装共享一个组件的产品,因此在卸载所有产品之前不应删除该组件。您可能只想确保在安装下一个版本生成的产品之前卸载产品。 【参考方案1】:

听起来您需要卸载安装了不需要的组件的功能(或整个产品)。 Windows Installer 具有用于查询组件、功能和产品的 API。 WiX 工具集在 API 周围集成了一个称为 DTF 的包装器。您可以使用它按组件查询功能。

所以,打开你最喜欢的 .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 Instant Share

然后,运行msiexec /x &lt;ProductCode&gt; 删除产品的所有功能

msiexec /i &lt;LocalPackage&gt; REMOVE=&lt;FeatureName&gt; 仅删除安装该组件的功能。

【讨论】:

【参考方案2】:

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

如果您在日志文件中找到了"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

【讨论】:

一点更正:注册表项本身没有隐藏(可以通过regedit或任何其他注册表查看/编辑实用程序正常查看);但它们不包含组件的 GUID。相反,GUID 值被转换为字节,字节被转换为十六进制字符串,这就是注册表路径的一部分。【参考方案3】:

我们最近遇到了这样一个案例,即我们的一台开发机器在卸载时无法删除所有组件。然而,在其他机器上,WiX 设置按预期工作。

因此,如果您错误地卸载了产品的早期版本并收到消息 Disallowing uninstallation of component: GUID since another client exists,则很有可能您的注册表中存在孤立组件。

有一个更优雅的解决方案可以使用PowerShell 删除那些隐藏注册表项,而不是像其他人提到的那样使用第三方应用程序。

$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... 的原因的更详细说明,请查看here。

【讨论】:

谢谢!我能够对此进行修改,并纠正了我正在构建的 WixInstaller 应用程序的卸载问题。【参考方案4】:

遵循此工作流程完美:

How to fix "disallowing uninstallation of component since another client exists"?

在那里创建两个文件后,在应用下一步之前有必要为它们删除 BOM。我用过记事本++ (Encoding > Encode in UTF-8)。

使用 Notepad++ 的多光标可以轻松地为每一行添加 /f 选项 - 使用 SHIFT+ALT+Arrows 激活它

【讨论】:

以上是关于Wix 工具集:“由于存在另一个客户端,因此不允许卸载组件”后完成清理的主要内容,如果未能解决你的问题,请参考以下文章

使用 wix 工具集安装多个 exe

将 MsiPackage 安装为 /passive(WIX 工具集)

用于包装的 WiX 工具

Wix 工具集 - 参考二进制文件 - 单独的 wxs 文件

WiX工具集:安装具有特定权限的文件

SQLite的相对路径不适用于WIX工具集