在MSI卸载过程中如何删除文件? [英] How exactly are files removed during MSI uninstall?

查看:163
本文介绍了在MSI卸载过程中如何删除文件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道在卸载过程中安装的文件/组件到底发生了什么.

I'd like to know what exactly happens to installed files / components during the uninstall procedure.

对于安装和升级过程,MSDN中提供可靠的文档(请参阅文件版本控制规则默认文件版本)

For the install and upgrade procedure, reliable documentation exists at MSDN (see File Versioning Rules and Default File Versioning, for example).

无论如何,我在MSDN或WiX的文档中找不到有关卸载删除逻辑的文档.

Anyways, I couldn't find a documentation of the uninstall remove logic at MSDN or at WiX´s documentation.

所以,我的问题很简单:我想知道何时从系统中确切删除了一个文件(情况并非总是如此-例如该文件是否存在/保留).

So, my question is simple: I would like to know when exactly a file is removed from the system (which isn't always the case - for example if a SharedDLLRefCount exists/remains for that file).

我找到的最接近的是以下MSDN 链接,给出了一些建议,但是基本上说:自己测试". 这对我来说并不令人满意,因为我想知道我是否可以依靠MSI的行为(也许是当前的行为),然后再向客户交付使用此行为的任何安装程序.

The closest I found was the following MSDN link, which gives some advice, but basically says: "Test it yourself". This isn't satisfying for me, because I would like to know if I can rely on a - maybe current - behaviour of MSI before I ship any installers using this behaviour to customers.

我正在寻找以下问题的可靠答案:

I'm searching for reliable answers to the following questions:

  • 在哪种情况下-除了明确的永久"定义或使用SharedDllRefCount-文件/组件能否在卸载操作中幸免?

  • Under which circumstances - aside from an explicit "permanent" definition or using SharedDllRefCount - will a file/component survive an uninstall Action?

如果DLL现在的版本比其安装时的版本高(由于热修补等),是否可以安全地将其删除?注意:我对此进行了测试,当前答案是是",但是我需要知道这是否是预期的行为,以及是否可以依靠它.

If a DLL has now a higher Version than at the time it was installed (because of hot patching or so) will it safely be removed? Note: I tested this and the current answer is yes, but I need to know if this is expected behaviour and if I can rely on it.

推荐答案

可用时间不多,但是我将尝试简要地进行总结. MSI文件的组件引用是在Windows Installer组件的基础上完成的,而不是基于在注册表中找到的旧的SharedDLL引用计数:HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDLLs.奇怪的是,有时还与MSI一起使用此SharedDLL引用计数器,但这仅是为了提供与旧版安装程序和部署技术的兼容性-我将在以后进行澄清.传统技术使用此SharedDLL计数器作为确定是否可以卸载文件的唯一方法.一旦ref-count降为0,该文件就可以删除.

Not much time available, but I'll try to summarize briefly. Component referencing for MSI files is done on a Windows Installer component basis - and not based on the old SharedDLL ref count found in the registry here: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDLLs. Strangely this SharedDLL ref-counter is sometimes also used with MSI, but this is only to provide compatibility with legacy installer and deployment technologies - I will clarify later. Legacy technologies used this SharedDLL counter as the sole way to determine if a file could be uninstalled. As soon as ref-count dropped to 0, the file could be removed.

Windows Installer的实际引用计数是基于Windows Installer组件而不是共享的dll ref-counters 完成的.这些组件是文件和注册表设置的基本安装包".它始终作为一个整体安装或卸载.组件基本上可以包含任何内容",但是在分解要部署到组件集合中的文件和注册表设置时,存在关于最佳实践的规则.我个人总是每个组件使用一个文件,因为这样可以避免Windows Installer升级期间出现的各种问题.

The actual reference counting for Windows Installer is done based on Windows installer components and not shared dll ref-counters. These components are "atomic installation bundles" of files and registry settings. It is always installed or uninstalled as a whole unit. A component can basically contain "anything", but there are rules with regards to best practice when decomposing the files and registry settings you want deployed into a collection of components. Personally I always use one file per component since this avoids all kinds of problems during Windows Installer Upgrades.

基本上,每个组件都有一个键路径"-一个文件或注册表项/值,用于确定是否已安装该组件. MSI的总体概念是,在此绝对组件密钥路径和唯一组件GUID之间存在一对一映射. GUID本质上是引用计数该绝对路径.几年前,我在一个答案中对此进行了解释,这似乎是对人们的一种易于理解的解释,也许可以快速阅读一下,以更详细地了解此组件的引用:

Essentially each component has a "key-path" - a single file or registry key / value which is used to determine if the component is installed or not. The overall concept of MSI is that there is a 1-to-1 mapping between this absolute component key path and a unique component GUID. The GUID essentially reference counts that absolute path. I explained this in an answer several years ago, and it seems to be a comprehensible explanation for people, perhaps give it a quick read to understand this component referencing in more detail: Change my component GUID in wix?

该组件GUID(用于特定的绝对磁盘或注册表位置)应由所有试图部署有问题的文件或组件的安装程序使用.允许安装的Window Installer机制称为合并模块".这是一个部分MSI数据库,可以在构建时合并到多个MSI文件中-允许在MSI文件之间共享相同的组件,并在所有文件中使用正确的组件GUID,从而可以进行引用计数. 这允许这些共享组件在每次由不同产品安装时增加引用计数,然后该组件将保留在系统上,直到将使用它的产品卸载到ref-count减少为0为止.顺序.应当注意,如果HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDLLs上的旧版引用计数器同时不是0,则将卸载该组件.如果将该组件设置为永久组件,或者使用空白组件GUID安装了该组件,则该组件也不会被卸载(安装并忘记"一个组件的特殊功能-不再处理).

This component GUID, for that particular absolute disk or registry location, should be used by all setups that seek to deploy the file or component in question. Window Installer's mechanism to allow this is referred to as a "merge module". This is a partial MSI database that can be merged into several MSI files at build time - allowing the same components to be shared between MSI files with the correct component GUID used in all of them so that reference counting is possible. This allows these shared components to increment the ref-count each time they are installed by different products, and then the component will remain on the system until the ref-count is reduced to 0 as the products that use it are uninstalled in sequence. It should be noted that if the legacy ref-counter at HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDLLs is not 0 at the same time, then the component will not be uninstalled. Nor will it be uninstalled if the component is set permanent or if it was installed with a blank component GUID (a special feature to "install and forget" a component - it is never dealt with again).

因此,请再重复一次,一个GUID表示一条绝对路径(一个GUID可以完全统治它们)-一个GUID具有关联的引用计数器,该计数器可以计算已注册组件的产品数量,以及与其关联的绝对键路径,以供使用.因此,作为示例,三个产品可能注册使用某个组件的GUID,使其引用计数为3,从而使其相关的密钥路径文件或注册表值始终存在,直到全部三个产品都被卸载为止.

So to repeat yet again, one GUID for one absolute path (one GUID to rule them all) - one GUID has an associated ref-counter which counts the number of products that have registered the component, with its associated absolute key-path, for its use. So, as an example three products might register use of a certain component GUID, making its reference count 3, and hence making its associated key-path file or registry value stick around until all 3 products are uninstalled.

请注意,不一定要为您的MSI组件启用旧版SharedDLL引用计数器.某些工具(例如 Installshield )启用一个标志来增加所有已安装文件的旧版共享DLL引用计数器,实际上,您必须为每个组件都将其关闭以摆脱此现象.这与其他工具(例如 WiX )形成对比,后者未将所有文件的共享dll引用计数器默认设置为打开(我不确定它们为哪些文件启用了该功能-如果有的话) ). 高级安装程序也未为所有组件启用SharedDLL ref-count标志(感谢 Bogdan Mitrache 进行验证-请在下面查看他的评论).

Note that the legacy SharedDLL ref-counter is not necessarily enabled for your MSI components. Some tools, such as Installshield, enable a flag to increment the legacy, shared DLL ref-counter for all files installed and you actually have to turn it off for each component to get rid of this behavior. This is in contrast to other tools, such as WiX, which does not default the shared dll ref-counter to be on for all files (I am not sure for what files they do enable it - if any). Advanced Installer also does not enable the SharedDLL ref-count flag for all components (thanks to Bogdan Mitrache for verifying this - see his comment below).

弥补旧版参考计数器(在开发和测试安装过程中可能会发生)可能会导致应卸载的Windows Installer组件意外留在磁盘上.如果看到这种情况,请检查系统是否干净,以确定主机上的问题是否使旧的引用计数器混乱.然后,您需要手动调整注册表以修复开发计算机的引用计数.这将涉及此键下的所有适用项目:HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDLLs.这不是一件有趣的工作-我曾经在白天使用Installshield Developer 7时遇到过这种情况.

Messed up legacy reference counters - which can happen during development and test installation - may cause a Windows Installer component that should be uninstalled to be left on disk unexpectedly. If you see this, check on a clean system to determine if messed up legacy ref-counters is the problem on your main machine. You then need to manually tweak the registry to fix the ref-count for your development machine. That will involve all applicable items under this key: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDLLs. Not a fun job - I had it happen when using Installshield Developer 7 way back in the day.

未能为每个绝对键路径保持一致的组件GUID将导致神秘且不可预测的问题,例如MSI卸载会删除仍与其他产品共享的文件,但是引用计数已被弄乱了. strong> MSI文件错误地认为它们拥有"共享组件并愉快地将其删除.身份错误的情况(同一绝对路径具有指向它的多个组件GUID-每个引用计数为1).这是人们使用Windows Installer面临的关键问题之一-因此建议每个组件都保留一个文件.

Failing to keep a consistent component GUID for each absolute key path will cause mysterious and unpredictable problems such as an MSI uninstall removing files that are still shared with other products, but the reference counting has been messed up. The MSI files mistakenly believe they "own" the shared component and happily deletes them. A case of mistaken identity (the same absolute path has multiple component GUIDs pointing to it - each one reference counted to 1). This is one of the key problems people face with Windows Installer - hence the advice to stick with one file per component.

我现在将发布此答案以保留它,然后在稍后对其进行调整.

I will post this answer now to persist it, and then tune it up a little afterwards.

更新:具体说明您的具体问题.

Update: Let's be concrete about your specific questions.

  1. 您已经回答了很多问题.如果您的MSI文件在卸载时递减了其注册",然后该组件的引用计数(对于组件GUID)大于0,则该文件将保留在磁盘上.如果它的MSI组件设置为永久组件,或者它具有空白组件GUID,或者为该组件启用了旧版SharedDLL引用计数(可能不是),并且此处的引用计数更高,它也将保留.大于0:HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDLLs.这些都是我所知道的条件.我想还有其他方面,例如广告产品,但是老实说,我不确定它们将如何影响卸载.广告产品不是真正安装的,而是由用户根据需要安装"的.阅读Phil的答案,我还记得传递组件也可以按照他在回答中描述的方式进行卸载-通过在重新安装过程中将关联条件评估为false.

  1. You have already pretty much answered the question. The file will remain on disk if the component's ref-count (for the component GUID) is higher than 0 after your MSI file has decremented its "registration" of it on uninstall. It will also remain if its MSI component is set to be permanent, or if it had a blank component GUID, or if the legacy SharedDLL ref-count was enabled for the component (it may not be) and the ref-count here is higher than 0: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDLLs. Those are all the conditions I know about. I suppose there are other aspects such as advertised products, but I am honestly not sure how they will affect uninstall. Advertised products are not really installed, but "installable" by the user on demand. Reading Phil's answer I also recall that transitive components can also be uninstalled in the fashion he describes in his answer - by having the associated condition evaluate to false during reinstall.

是的,只要在特定的绝对路径(文件的完整安装路径)的生存期内组件GUID保持稳定,则该文件可以进行任何数量的更新,并且引用仅当另一个带有其他产品代码的MSI也安装了count时,该计数才会增加.换句话说:如果您已对原始MSI进行了4次更新,并且为特定文件维护了一致的组件GUID,并且每次使用新版本进行更新,则该文件的组件引用计数仍为1-只要没有其他MSI也没有安装该组件-在这种情况下,它将是2个或更多,并且卸载您的产品将卸载它,但是将引用计数减少1.

Yes, as long as the component GUID has remained stable throught the lifetime of a specific absolute path (the full installation path for a file), then that file can have gone through any number of updates, and the reference count is only incremented if another MSI with another product code also installs it. In other words: if you have delivered 4 updates to your original MSI, and you have maintained a consistent component GUID for a specific file and updated it each time with a new version, then the component ref-count for this file is still 1 - as long as no other MSI has also installed the component - in which case it will be 2 or more, and an uninstall of your product will not uninstall it, but reduce the ref-count by 1.

请尝试阅读此答案,因为它似乎已经为其他人澄清了一些问题:

Please do try to read this answer since it seems to have clarified things for other people: Change my component GUID in wix? (same as recommended above).

最后我应该注意,也可以通过 WiX包含文件安装共享的组件-WiX完全引入了一个新概念.这些就像C ++中的常规包含文件一样,只需定义一次即可,并且可以在编译时将它们包含在多个WiX源文件中.老实说,我从来没有使用过它,但是从概念上讲,它类似于合并模块-内置Windows Installer概念来处理共享组件.但是,有一个重要的区别:合并模块是整体版本化的,而WiX包含文件则包含源文件夹中的动态文件.我觉得这更好,但这当然是一个很大的讨论,而且是一个偏爱的问题.我不会在这里详细说明.

Finally I should note that shared components can also be installed via WiX include files - a new concept entirely introduced with WiX. These are like regular include files in C++, you simply define them once and they can be included in several WiX source files at compile time. I have honestly never made use of this, but conceptually it is similar to merge modules - the built-in Windows Installer concept to deal with shared components. There is one important difference though: merge modules are versioned as a whole, whereas WiX include files include files dynamically from your source folder. I find this better, but this is certainly a large discussion and a matter of preference. I will not elaborate this here.

如果您使用的是WiX,我建议您尝试使用WiX包含文件来管理共享组件.在我看来,它们似乎是合并模块的一种更灵活的实现.从主观上讲,我从来都不是合并模块的忠实拥护者,尽管如果您有很多要与其他产品一起安装的共享文件,则必须使用合并模块.为什么我不喜欢合并模块?它们看起来像是一个额外的复杂问题,并且需要额外的维护.实际上,它们相当于一种奇怪的静态链接形式-带有我们从常规静态链接中了解到的所有问题.这可能太主观了,所以我将以该注释为结尾,但是对于共享文件和组件,请使用合并模块或WiX包含文件或存在的任何其他构造来实现我所不知道的相同功能.

If you are using WiX I would recommend you try to use WiX include files to manage your shared components. They seem - to me - to be a more flexible implementation of merge modules. On a subjective note I have never been a huge fan of merge modules, though they are essential to use if you have lots of shared files to install with different products. Why do I not like merge modules? They seem like an extra complication and a binary blob that needs extra maintenance. In effect they amount to a weird form of static linking - with all the problems we know from regular static linking. This may be getting too subjective, so I will end on that note, but for shared files and components use either merge modules or WiX include files or whatever other construct exist to achieve the same that I am not aware of.

这篇关于在MSI卸载过程中如何删除文件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆