如何避免WiX/MSI部署解决方案中的常见设计缺陷?

Ste*_*mul 14 windows-installer installshield wix advanced-installer installshield-le

如何避免WiX/MSI部署解决方案中的常见设计缺陷?


部署是大多数开发的关键部分 - 部署失败意味着您的最终用户永远无法评估您的产品.这很容易成为软件开发中最昂贵的错误.请给这个内容一个机会.我坚信,通过应用程序设计的微小变化可以显着提高软件质量,使部署更加合理,更可靠 - 这就是"答案"的全部 - 软件开发.


这是一个Q/A风格的问题,答案只列出了MSI文件中不要做的一些事情,以避免最常见的设计缺陷.

Ste*_*mul 21

WiX/MSI部署反模式

WiX/MSI文件中经常会出现几种部署反模式.以下是一些最常见的粗略草案.

在讨论这些问题之前,另一方面,这里是一个快速提醒MSI取得全面成功的事情!(尽管有问题).

这个答案是一项正在进行的工作

你怎么知道我达到答案的最大尺寸.我想这已经足够了:-).有些部分需要澄清和改进.

如果您认识到其中一些问题,您可能需要继续阅读 - 这些都是众所周知的开发人员对 Windows Installer/MSI的厌恶和烦恼:

  • 无法使用最新设置可靠地覆盖较低版本的文件.
  • 无法可靠地覆盖非版本化文件(例如,对于IIS).
  • 尝试安装MSI升级后,文件神秘丢失.
  • 在(主要)升级方案期间数据被清除.例子包括:
    • 你的注册表存储许可证密钥.
    • 配置文件中的数据,如config.xml,settings.ini等......
    • 您未作为LocalSystem运行的服务的服务凭据.
  • 数据未更新:
    • 使用要强制执行的新设置在安装期间无法可靠地更新设置文件.
    • 更新每个用户(或HKCU)存储的数据文件中的设置时出现问题.您为安装用户更新,如何更新其他用户?
  • 你看到你的包裹意外地进行了自我修复.
  • 您的自定义操作会使设置炸弹出现神秘错误.
  • 这是一个很大的问题:您不必要对Windows Installer本身已经完全支持的事情使用自定义操作.这是一个巨大的部署反模式,也是部署失败的主要原因.
    • 您可以通过自定义操作安装Windows服务.使用内置结构在MSI本身中做得更好.
    • 您可以通过自定义操作将.NET程序集安装到GAC.Windows Installer本身完全支持这一点,而没有一行(有风险的)代码.
    • 您运行自定义.NET程序集安装程序类.这些都是用于开发和测试.它们永远不应该作为部署的一部分运行.相反,您的MSI应使用内置构造来部署和注册您的程序集.
    • 您可以通过自己的MSI中的自定义操作运行必备设置和运行时安装程序.这应该完全不同.见第6节.
  • 部署共享运行时文件时遇到问题.
  • 无提示安装 MSI似乎导致与以交互方式运行设置不同的安装状态.

以下部分根本没有特别的顺序 - 截至目前.

这些部分不断寻求改进.请添加有关不明确或有用的评论.

待补充:

  • 卸载不适用于MSI应用程序 - 错误1722
    • 服务控制:卸载前未能停止服务
    • 卸载CA:尝试在卸载期间运行不再位于磁盘上的批处理文件/脚本
    • 自定义操作:错误的调整因此意外运行自定义操作.通常在卸载或主要升级期间.

1. 自我修复问题

一个特别恼人的问题与经常为您安装的应用程序触发不必要的自我修复的构造有关.

  • 由于这个问题的多方面性质,我已经创建了一个单独的答案来描述要避免的设计结构,以防止在没有警告和意图的情况下进行自我修复:如何避免触发MSI自修复使用我的WiX/MSI包?.

  • 有时,自我修复会被用作使用应用程序设置填充HKCU的方法,或者将文件放在每个用户的用户配置文件中.这通常有效,但在我看来,这不是应用程序设计和部署的最佳实践 - 请参阅下面第9节中的更多详细信息.

2. 共享,供应商或Microsoft运行时文件的安装不正确

虽然在上面的链接(自修复问题)中对此进行了详细解释,但在此还应注意,任何设置中最常见的错误之一是包含共享运行时文件的"本地副本" - 有时也会全局注册在系统上,如果它们是COM文件.旧VB6应用程序的安装程序有时会针对它们所需的常见控件执行此操作,从而破坏了其他应用程序的系统.

  • 如果您需要特定版本的共享文件供COM使用,并且您无法更新应用程序以使用正确安装的共享组件,那么您可以使用无注册COM.基本上安装所需二进制文件的本地副本,并通过为二进制文件提供的清单文件强制加载共享文件.

  • 有关此主题的更多详细信息,请参阅上面第1项中的自我修复问题链接.

3. (您自己的)共享文件和数据的处理不正确

如果您创建一套MSI文件来部署不同的产品,他们可能会在它们之间共享某些文件.如果您从多个MSI文件(每个使用不同的组件GUID)定位相同的文件位置(绝对路径),那么每个安装程序都会将文件视为"拥有它" - 很高兴在卸载时将其卸载,或者再次将其放置到位通过自我修复.

  • 对此的正确解决方案是要意识到,对于您定位的每个绝对路径,必须有单个组件GUID.绝对路径是由组件GUID引用的 - 它必须在所有设置之间共享才能使其正常工作.

  • To achieve using the same component GUID in all your setups you should either create a merge-module to include in each setup, or use advanced constructs in WiX such as "include files" - with hard coded GUIDs for the components contained in them.

  • If the file in question is a data file that should never be uninstalled or replaced once updated, you should also consider installing it as a "permanent component" so that it is not uninstalled during major upgrades or manually run uninstalls.

4. Component creation errors - not following best practice

Not following best practice for component creation. MSI components are the basic installation units for files and registry settings.

  • There are best practice rules for how to "componentize" your application files. Breaking these rules can cause problems for patching and upgrades with mysterious symptoms such as missing files and settings after upgrades, or patches that bomb out with nonsensical errors.

  • To counter this problem the oversimplification is that you should use one file per component unless the number of files in your setup is truly enormous. This avoids all kinds of problems (read that link for a more thorough explanation of component ref-counting).

5. Upgrade problems relating to user data being overwritten or reset

This is no less than extremely common. I have answered several stackoverflow questions on this topic, and it keeps coming up.

  • Please read the section called "Overuse of per-user file and registry deployment" for a description of how to minimize the reliance on Windows Installer for user-data deployment in general. If you ask me this is the real answer to these persisting "data reversion" problems.

  • Since upgrades are complex in MSI, many standardize on major upgrades (the simplest form of upgrade). A major upgrade is essentially an uninstall and a reinstall of the same product (in different versions).

  • There are several ways to configure such a major upgrade, but if you uninstall the previous version completely before installing the new version, you could uninstall user data files that have been modified since installed. MSI does not check if data files have been modified since installed and will happily uninstall them without hesitation, unless you have marked the hosting component as "permanent" (it will never be uninstalled) or set a blank component GUID for the hosting component (a special feature to install the file and then ignore it completely).

  • A special case to be aware of is that even if you properly share such a file by using a merge module or WiX include file (to keep the installing component GUID stable)- it will likely still be uninstalled and reinstalled by a major upgrade if there is just one product on the box that has it referenced it at the time (reference count is 1).
  • After the major upgrade has completed it looks as if the data files have been overwritten or reverted, but in actual fact the modified data files were simply uninstalled and then reinstalled in their "fresh versions" (will update with some potential fixes for this soon).

  • In my opinion you should only install data files that are used read-only after installation. If the files should be written to, they should be generated by the application itself in my opinion, and stored in the user-profile. This is an example of how application design can be changed to make deployment more reliable. The "real solution" in my opinion.

  • If you do install the read/write data file with a component, set it permanent (or use blank GUID). The file overwrite rules will ensure that the file on disk isn't overwritten during installation (unless you do something stupid such as setting REINSTALLMODE to amus to force overwrite all files - this should never be allowed. It can downgrade shared files installed by merge modules as well - old-style DLL Hell). If you do want to wipe the file out and overwrite it, that is also possible using various methods, the best of which is probably to use a companion file. (more details will be added later).
  • Wix: Windows Service sometimes uninstalled when upgrading

6. Erroneous or unnecessary use of custom actions

The (over)-use of custom actions for MSI files is a huge topic and this section got too large and was split into a separate answer: Why is it a good idea to limit the use of custom actions in my WiX/MSI setups?.

Essentially custom actions are often unnecessary due to built-in support in MSI to achieve the same effect, or the availability of ready-made solutions in free frameworks such as WiX or commercial tools such as Advanced Installer or Installshield.

And custom actions are by their very nature error prone and the leading cause of deployment failures and errors. Please read the above link for details. Thousands of people, tens of thousands of people, even millions of people have tested these built-in constructs. Why on earth do you do it on your own?

Some "besserwissing" (advice that I should follow myself): Focus on what sets your product apart - what is new about it, and eliminate all other sources of errors. Good deployment won't make your product, but bad deployment can break it.

7. Failure to properly merge INI files

It is possible to install an INI file via the File table - as you would any other file. This allows no merging if there is an existing INI file at the target location.

  • If you import the INI entries into the appropriate MSI tables, you can update an existing INI file using "merging" with existing values, and not just do a file overwrite "wiping out" the existing entries, or not updating the file at all.

  • The "INI merging" is "auto-magic" which allows proper rollback support and "pin-pointed" updates to the values in any existing INI file. If the installer is aborted, the INI file is properly reverted to its initial state.

  • This is an excellent feature that really works great for almost any INI file I have ever seen. However, I have indeed seen a few cases where INI files have non-standard formatting. Sometimes they have large comments sections that you want to install (developer tools) or weird formatting that can't be supported by MSI's merging (comma delimited triple files and stuff like that). In these cases you have to install it as a file instead of as a "change transaction" to preserve the uniquely formatted INI file.

  • If you are developing and using a non-standard INI file, consider giving the file a different extension than*.INI in order to indicate its uniqueness and need for special handling. It is effectively no longer an INI file (key-value format). The opposite is also true: you have a unique extension, and you can change it to INI to handle it as a proper INI file if the file content is key-value pairs.

8. Erroneously using self-registration for COM files

Or install their registration via the Registry table. Use the appropriate COM advertisement tables. There are many reasons, as explained here: Self-registration considered harmful.

  • I have seen cases where self-registration performs other actions than actual COM registration on the system in question. This is generally horrible design from the developer in question, but I know of cases where people have chosen to use self-register rather than to re-implement what is done during self-registration as a proper custom action.

  • To allow a personal opinion: when I see network settings being affected by self-registration, I immediately want the software rejected for use altogether. That is how serious it is to do something so "hacky" in an standardized operation such as self-registration. The sane question to ask is "what else are they up to given that dodgy COM registration". It is just not a confidence builder to rely on non-standard, hacky stuff.

9. Overuse of per-user file and registry deployment

UPGRADE: new answer relating to this topic: Create folder and file on Current user profile, from Admin Profile.

This section got too large and was split into a separate answer: Why is it a good idea to limit deployment of files to the user-profile or HKCU when using MSI?

Essentially user-profile deployment of files or settings in HKCU are tolerable, but it might not be the best design, and it can be cumbersome to ensure that all settings and files make it into every user-profile and user registry on the box. The deployment problems that result and some proposed solutions are discussed in the linked answer above.

Essentially user deployment can be supported using MSI self-repair, Microsoft Active Setup, or by logical design changes to the application or solution in question (the preferred option - see linked answer for details). In general deployment should not interfere with user data and settings since it is really user data and should not be deployed but generated at runtime by the application.

10. Silent installation fails to complete or is incomplete

A built-in feature of Windows Installer is that any MSI file can be installed in silent mode. This is a core feature of the technology intended to help corporate deployment - which is generally always run in silent mode. Making sure your MSI is capable of completing and successfully working after a silent install, is no less than exceptionally important. In my experience custom actions can often cause problems for silent install.

  • Never make changes to the computer from within the InstallUISequence (from your setup dialogs). This issue was described above. The custom actions used in the interactive GUI are immediate mode (without elevation for regular users) and should just gather and validate user input (read-only). All non-standard changes made to the computer should be done between InstallInitialize and InstallFinalize in the InstallExecuteSequence - the transacted, elevated operations where only deferred mode and elevated custom actions can run.

    • All changes made in the InstallUISequence will also be skipped entirely when you run in silent mode, and the install will then likely be incomplete. Silent installation is extremely important for corporate deployment - the GUI is generally always ignored and changes are enforced by using transforms and/or setting properties from the command line.

    • Here is a lengthy discussion of how silent and interactive installs and uninstalls can yield different results (and how it is a serious MSI design flaw): Uninstall from Control Panel is different from Remove from .msi

  • Never show dialogs from within your custom actions in InstallExecuteSequence. Doing so can cause silent install to fail completely since these dialogs won't automatically obey the UILevel setting of the running installation. When the setup is run in silent mode via deployment systems, a modal dialog can show up and block the completion of the setup, and there will be no user to dismiss the dialog of course. You can use the property UILevel to determine if the setup is run silently, and then suppress the display of your dialog - but showing a dialog like this is just wrong design.

11. You try to "force overwrite" files with your MSI installer

MSI features some pretty complex "file versioning rules" designed to minimize the impact of "DLL Hell". They typically cause files to not be overwritten as intended - a classic MSI issue. As a result, people feel they can't find a reliable way to always force overwrite files on disk during installation.

  • There are ways to force overwrite files, but not in the way that most people picture as logical. Frankly the file replacement design is often frowned upon even when understood.

  • Overwriting files works quite differently for versioned files and data files (text, images, anything without a version property). In essence higher versioned files overwrite lower version files when the files are versioned. Data files are not replaced if the create and modify dates are different for the file in question. It has then been modified since installed.

  • The file overwrite behavior can be slightly tweaked by custom settings for the the REINSTALLMODE property set at msiexec.exe command line level (overwrite older versions, overwrite equal versions, overwrite any version etc...). Setting the REINSTALLMODE property changes file replacement logic for all files in the whole setup - including files deployed with merge modules that might target files in shared locations. You could hence downgrade shared files and components - exactly what "DLL Hell" was about.

  • Nevertheless it is crucial to understand the "file overwrite rules" and how they can be affected by settings for but it is a setting that applies to all files in the whole install. There are also some "hacks" to overwrite specific files only.

  • Check this article for how you can force overwrite a file that won't upgrade.

  • This section is not finished yet.

12. You install services that run with user credentials

In my opinion this just isn't good practice, and typically people wipe out the credentials during major upgrade scenarios as well - and in some cases also settings files that the service uses.

  • To me this is a prime example of how application design changes are needed to make deployment reliable and sane.

  • In my experience people insist on using these solutions and end up with a lot of custom action hacking to get it sort of working.

  • Save yourself a lot of trouble and design your service to run as LocalSystem (or maybe better - another account that is intended for service use - do have a quick read of this linked content and talk to your development team about options. Here is another post that might be worth a skim: Is it safe to run a pool under NT AUTHORITY\NETWORK SERVICE?).

  • See next section on NT privileges for a common problem seen when using user credentials to run a service.


归档时间:

查看次数:

1790 次

最近记录:

6 年,3 月 前