From MSI to WiX, Part 8 - Major Upgrade, by Alex Shevchuk
Following content is reprinted from here, please go to the original website for more information.
Author: Alex Shevchuk
Introduction
A typical Major Upgrade removes a previous version of an application and installs a new version. This blog will guide you through the process of creating Major Upgrade.
Source code for RTM version
Let's create a C# solution with two projects in it: library and console application. Here is the code for the library:
using System;
using System.Collections.Generic;
using System.Text; namespace TestLib
{
public class TestClass
{
private string greeting; public TestClass()
{
greeting = "Hello, World!";
} public TestClass(string greeting)
{
this.greeting = greeting;
} public void WriteLine()
{
Console.WriteLine(greeting);
}
}
}
Here is the code for the console application:
using System;
using System.Collections.Generic;
using System.Text;
using TestLib; [assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")] namespace TestApp
{
class Program
{
static void Main(string[] args)
{
TestClass test = new TestClass();
test.WriteLine();
}
}
}
Source code for V2 version
For version 2 of our product we will change the console application to use a non-default constructor to pass the greeting string the the TestClass instance:
using System;
using System.Collections.Generic;
using System.Text;
using TestLib; [assembly: AssemblyVersion("1.0.1.0")]
[assembly: AssemblyFileVersion("1.0.1.0")] namespace TestApp
{
class Program
{
static void Main(string[] args)
{
TestClass test = new TestClass("QFE version");
test.WriteLine();
}
}
}
Our updated version of application will print out "QFE version" message instead of default "Hello, World!". Notice also that AssemblyVersion and AssemblyFileversion have changed.
Installation Wix script with no Major Upgrade support
Typical installation script for such program would be something like this:
<?xml version='1.0' encoding='windows-1252'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2003/01/wi'> <?define SkuName = "TestApp"?> <?define ProductVersion="1.0.0" ?>
<?define UpgradeCode="{3485E6A2-A1F3-4329-8BB5-ED8FFCF283D4}"?>
<?define Manufacturer="Acme Corp."?>
<?define ProductCode="{5C32A3BD-3BA3-43AF-951F-1077E84B00DC}"?>
<?define PackageCode="{????????-????-????-????-????????????}"?> <Product Id='$(var.ProductCode)'
Name='ProductName'
Language='1033'
Version='$(var.ProductVersion)'
Codepage='1252'
Manufacturer='$(var.Manufacturer)'
UpgradeCode='$(var.UpgradeCode)'> <Package Id='$(var.PackageCode)'
Description="PackageDescription"
Comments='Comments'
Manufacturer='$(var.Manufacturer)'
InstallerVersion='200'
Languages='1033'
SummaryCodepage='1252'
Compressed='yes'
AdminImage='no'
Platforms='Intel'
ReadOnly='yes'
ShortNames='no'
Keywords='Installer,MSI,Database' /> <Media Id="1" Cabinet="$(var.SkuName).cab" EmbedCab="yes" /> <Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='LocalAppDataFolder'>
<Directory Id='INSTALLDIR' Name='TestApp'> <Component Id='$(var.SkuName)' Guid='{835A4136-B01E-4F8B-8EA7-5D6F69B07A83}'>
<File Id='TestAppExe' DiskId='1' KeyPath='yes' Checksum='yes'
Vital='yes' Name='TestApp.exe' Assembly='.net'
AssemblyManifest='TestAppExe'
AssemblyApplication='TestAppExe'
Source='TestApp.exe' />
</Component> <Component Id='TestLibDll_Component' Guid='{5BC55186-170E-475C-B77A-D80581FC88EC}'>
<File Id='TestLibDll' Name='TestLib.dll' DiskId='1' KeyPath='yes'
Vital='yes' Assembly='.net' AssemblyManifest='TestLibDll'
AssemblyApplication='TestLibDll' Source='TestLib.dll' />
</Component> </Directory>
</Directory>
</Directory> <Feature Id='Complete' Level='1'>
<ComponentRef Id='$(var.SkuName)' />
<ComponentRef Id='TestLibDll_Component' />
</Feature> </Product>
</Wix>
Aside from UpgradeCode that script does not have any support for major upgrades.
Preparing an application for future major upgrades
Here is what needs to be done from purely MSI standpoint in order to support major upgrades:
- Add a record to the Upgrade table to detect if installed on the system application has older version than current installation package. We need this in case we need to apply conditions for upgrade only. For example, we may update some configuration file during regular install and skip updating during upgrade thus preserving changes users might add to the configuration file after the original install. This record should have the following values for columns:
- UpgradeCode is set to the UpgradeCode property value
- VersionMin is set to RTM version of the product
- VersionMax is set to the current product version
- Language can be set to a product language or empty
- Attributes is set to msidbUpgradeAttributesVersionMinInclusive
- Remove is empty
- ActionProperty is set to the name of the public property which FindRelatedProducts action will set to the product code of already installed related product. Suggested name for this property is UPGRADEFOUND.
- Add a record to the Upgrade table to detect if current installation package contains an older version of an application. We need this to prevent downgrading installed application to older version. This record should have the following values for columns:
- UpgradeCode is set to the UpgradeCode property value
- VersionMin is set to current product version
- VersionMax is empty
- Language can be set to a product language or empty
- Attributes is set to msidbUpgradeAttributesOnlyDetect
- Remove is empty
- ActionProperty is set to the name of the public property which FindRelatedProducts action will set to the product code of already installed related product. Suggested name for this property is NEWPRODUCTFOUND.
- FindRelatedProducts action should be scheduled in both InstallExecuteSequence and InstallUISequence tables.
- Custom action Type 19 should be scheduled in both InstallExecuteSequence and InstallUISequence tables. This action should be conditioned on NEWPRODUCTFOUND property.
- MigrateFeatureStates action should be scheduled in both InstallExecuteSequence and InstallUISequence tables. This action sets the feature states on installed application to reflect the feature states of already installed application.
- RemoveExistingProducts action should be scheduled in InstallExecuteSequence table. This action removes the installed product or only updated files depending on where this action is scheduled in the InstallExecuteSequence table.
- Both UPGRADEFOUND and NEWPRODUCTFOUND public properties must be added to the SecureCustomProperties property.
- Change ProductCode, PackageCode, and ProductVersion properties.
- Important: Product language defined in the Language attribute of <Product> element must be one of the languages listed in the Languages attribute of the <Package> element.
Here is how these items translates to Wix:
- You add records to the Upgrade table:
<Upgrade Id="$(var.UpgradeCode)">
<UpgradeVersion Minimum="$(var.ProductVersion)"
IncludeMinimum="no"
OnlyDetect="yes"
Language="1033"
Property="NEWPRODUCTFOUND" />
<UpgradeVersion Minimum="$(var.RTMProductVersion)"
IncludeMinimum="yes"
Maximum="$(var.ProductVersion)"
IncludeMaximum="no"
Language="1033"
Property="UPGRADEFOUND" />
</Upgrade>
Id attribute of the <Upgrade> element is set to a value of UpgradeCode property (UpgradeCode attribute of <Product> element) and will be added to the UpgradeCode column of every record in the Upgrade table.
Every <UpgradeVersion> element adds a new record to the Upgrade table.
Minimum attribute of the <UpgradeVersion> element sets the value of VersionMin column.
Maximum attribute of the <UpgradeVersion> element sets the value of VersionMax column.
Property attribute of the <UpgradeVersion> element sets the value of ActionProperty column.
Here is the relationships between flag bits in Attributes column of the Upgrade table and attributes of the <UpgradeVersion> element:
Flag |
Attribute |
Description |
msidbUpgradeAttributesMigrateFeatures |
MigrateFeatures |
Enables the logic of MigrateFeatureStates action of migrating feature states. |
msidbUpgradeAttributesOnlyDetect |
OnlyDetect |
Detects products and applications but does not remove. |
msidbUpgradeAttributesIgnoreRemoveFailure |
IgnoreremoveFailure |
Continues installation upon failure to remove a product or application. |
msidbUpgradeAttributesVersionMinInclusive |
IncludeMinimum |
Detects the range of versions including the value in VersionMin. |
msidbUpgradeAttributesVersionMaxInclusive |
IncludeMaximum |
Detects the range of versions including the value in VersionMax. |
msidbUpgradeAttributesLanguagesExclusive |
ExcludeLanguages |
Detects all languages, excluding the languages listed in the Language column. |
- Wix will add FindRelatedProducts and MigrateFeatureStates actions to sequence tables.
- Wix will add UPGRADEFOUND and NEWPRODUCTFOUND public properties to the SecureCustomProperties property.
- You create Custom action Type 19:
<!-- Prevent downgrading -->
<CustomAction Id="PreventDowngrading"
Error="Newer version already installed." />
- You schedule custom action in both InstallExecuteSequence and InstallUISequence tables and RemoveExistingProducts action in InstallExecuteSequence table:
<InstallExecuteSequence>
<Custom Action="PreventDowngrading"
After="FindRelatedProducts">NEWPRODUCTFOUND</Custom>
<RemoveExistingProducts After="InstallFinalize" />
</InstallExecuteSequence> <InstallUISequence>
<Custom Action="PreventDowngrading"
After="FindRelatedProducts">NEWPRODUCTFOUND</Custom>
</InstallUISequence>
Here is an updated installation script:
<?xml version='1.0' encoding='windows-1252'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2003/01/wi'> <?define SkuName = "TestApp"?>
<?define RTMProductVersion="1.0.0" ?>
<?define ProductVersion="2.0.0" ?>
<?define UpgradeCode="{3485E6A2-A1F3-4329-8BB5-ED8FFCF283D4}"?>
<?define Manufacturer="Acme Corp."?>
<?define PackageCode="{????????-????-????-????-????????????}"?> <Product Id='{8EEB7D19-F7F4-4218-93B9-BBEAAA4C2E2D}'
Name='ProductName'
Language='1033'
Version='$(var.ProductVersion)'
Codepage='1252'
Manufacturer='$(var.Manufacturer)'
UpgradeCode='$(var.UpgradeCode)'> <Package Id='$(var.PackageCode)'
Description="PackageDescription"
Comments='Comments'
Manufacturer='$(var.Manufacturer)'
InstallerVersion='200'
Languages='1033'
SummaryCodepage='1252'
Compressed='yes'
AdminImage='no'
Platforms='Intel'
ReadOnly='yes'
ShortNames='no'
Keywords='Installer,MSI,Database' /> <Media Id="1" Cabinet="$(var.SkuName).cab" EmbedCab="yes" /> <Upgrade Id="$(var.UpgradeCode)">
<UpgradeVersion Minimum="$(var.ProductVersion)"
IncludeMinimum="no"
OnlyDetect="yes"
Language="1033"
Property="NEWPRODUCTFOUND" />
<UpgradeVersion Minimum="$(var.RTMProductVersion)"
IncludeMinimum="yes"
Maximum="$(var.ProductVersion)"
IncludeMaximum="no"
Language="1033"
Property="UPGRADEFOUND" />
</Upgrade> <Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='LocalAppDataFolder'>
<Directory Id='INSTALLDIR' Name='TestApp'> <Component Id='$(var.SkuName)' Guid='{835A4136-B01E-4F8B-8EA7-5D6F69B07A83}'>
<File Id='TestAppExe' DiskId='1' KeyPath='yes' Checksum='yes'
Vital='yes' Name='TestApp.exe' Assembly='.net'
AssemblyManifest='TestAppExe'
AssemblyApplication='TestAppExe'
Source='TestApp.exe' />
</Component> <Component Id='TestLibDll_Component' Guid='{5BC55186-170E-475C-B77A-D80581FC88EC}'>
<File Id='TestLibDll' Name='TestLib.dll' DiskId='1' KeyPath='yes'
Vital='yes' Assembly='.net' AssemblyManifest='TestLibDll'
AssemblyApplication='TestLibDll' Source='TestLib.dll' />
</Component> </Directory>
</Directory>
</Directory> <Feature Id='Complete' Level='1'>
<ComponentRef Id='$(var.SkuName)' />
<ComponentRef Id='TestLibDll_Component' />
</Feature> <!-- Prevent downgrading -->
<CustomAction Id="PreventDowngrading" Error="Newer version already installed." /> <!-- Sequences -->
<InstallExecuteSequence>
<Custom Action="PreventDowngrading" After="FindRelatedProducts">NEWPRODUCTFOUND</Custom>
<RemoveExistingProducts After="InstallFinalize" />
</InstallExecuteSequence> <InstallUISequence>
<Custom Action="PreventDowngrading" After="FindRelatedProducts">NEWPRODUCTFOUND</Custom>
</InstallUISequence>
</Product>
</Wix>
How upgrade works
- The FindRelatedProducts action runs through each record of the Upgrade table in sequence and compares the upgrade code, product version, and language in each row to products installed on the system. When FindRelatedProducts detects a correspondence between the upgrade information and an installed product, it appends the product code to the property specified in the ActionProperty column of the Upgrade table (Property attribute of the <UpgradeVersion> element).
For FindRelatedProducts to work correctly, the package author must be sure that the ProductLanguage property in the Propertytable is set to a language that is also listed in the Template Summary Property.
FindRelatedProducts should be authored into the InstallUISequence table and InstallExecuteSequence tables. The FindRelatedProducts action must come before the MigrateFeatureStatesaction and the RemoveExistingProductsaction.
- MigrateFeatureStates reads the feature states in the existing application and then sets these feature states in the pending installation. The method is only useful when the new feature tree has not greatly changed from the original.
MigrateFeatureStates action runs through each record of the Upgradetable in sequence and compares the upgrade code, product version, and language in each row to all products installed on the system. If MigrateFeatureStates action detects a correspondence, and if the msidbUpgradeAttributesMigrateFeatures bit flag is set in the Attributes column of the Upgrade table, the installer queries the existing feature states for the product and sets these states for the same features in the new application. The action only migrates the feature states if the Preselected property is not set.
The MigrateFeatureStates action should come immediately after the CostFinalizeaction. MigrateFeatureStates must be sequenced in both the InstallUISequencetable and the InstallExecuteSequencetable.
- The RemoveExistingProducts action goes through the product codes listed in the ActionProperty column of the Upgradetable and removes the products in sequence by invoking concurrent installations. For each concurrent installation the installer sets the ProductCode property to the product code and sets the REMOVE property to the value in the Remove field of the Upgrade table. If the Remove field is blank, its value defaults to ALL and the installer removes the entire product.
The RemoveExistingProducts action must be scheduled in the action sequence in one of the following locations.
- Between the InstallValidateaction and the InstallInitializeaction. In this case, the installer removes the old applications entirely before installing the new applications. This is an inefficient placement for the action because all reused files have to be recopied.
- After the InstallInitializeaction and before any actions that generate execution script.
- Between the InstallExecuteaction, or the InstallExecuteAgainaction, and the InstallFinalizeaction. Generally the last three actions are scheduled right after one another: InstallExecute, RemoveExistingProducts, and InstallFinalize. In this case the updated files are installed first and then the old files are removed. However, if the removal of the old application fails, then the installer rolls back both the removal of the old application and the install of the new application.
- After the InstallFinalize action. This is the most efficient placement for the action. In this case, the installer updates files before removing the old applications. Only the files being updated get installed during the installation. If the removal of the old application fails, then the installer only rolls back the uninstallation of the old application.
Windows Installer sets the UPGRADINGPRODUCTCODE Property when it runs this action.
Comments
- Alex: Regarding Ramon's question on missing UpgradeCode in Version 1. Unfortunately, I don't think anything can be done to allow automatic major upgrade.
To enable major upgrade Windows Installer requires UpgradeCode, ProductVersion and ProductLanguage properties identified in the installation package. That is why the Best Practice with any installation package is to always assign values for these properties.
Q: Hello ,I followed your blog and I am able to sucessfully upgrade with the new MSI. ( major upgrade). Now when I use the new msi to uninstall the product, it fails with error ( this action is only valid for currently installed products.)
Can you provide some insights on what could be done to fix this ?
- Alex: Make sure that new version has both Product and Package codes different from original package.
Q: How to prevent a custom action from running during a upgrade ?
i have this : <Custom Action='CreateScheduledTask' After='InstallFinalize'>NOT Installed</Custom>
But this runs during the upgrade.
- Alex: During upgrade Windows Installer sets the UPGRADINGPRODUCTCODE property. You can use it in conditions.
- Alex: Make sure you change Product Code (Product/@Id), Package Code (Package/@Id), and Product Version (Product/@Version). During upgrade only first three fields of Product Version are recognized. Fourth field is ignored and if two versions are different in fourth field only, they will be considered as the same version by Windows Installer.
Also, make sure that Upgrade/@Id has the same value as Product/@UpgradeCode and UpgradeVersion elements have correct values for Minimum and Maximum attributes.
- Alex: Major upgrade is by far the easiest of upgrades/updates. It is even more so for pre-release development because it allows changing feature tree layout (move or delete feature, add subfeature), move or delete components, change the component code of existing component, change keypath of the component, etc. As far as major upgrade implementation, all it needs is upgrade code and version min/max. You still will need to keep track of product codes in some cases. For example, installation is done by using bootstrapper and you want to have some extra processing based on which version is being updated.
I don't have lot of experience with InstallShield, but I'll be surprised if their MSI's don't have UpgradeCode. For first WiX-based version of installer which upgrades InstallShield installer, you may want to schedule RemoveExistingProducts earlier, preferably between InstallValidate and InstallInitialize.
Q:Hi Alex, I have two questions for you if you please.
1. As far as we need a RemoveExistingProducts action only when we install product, don't we need some condition on it? Like "NOT REMOVE=ALL" or something like this?
2. You say that location of RemoveExistingProducts action before InstallInitialize action is inefficient and that most efficient placement for this action is after InstallFinalize action. Right? But where I must to place it if some action which deals with uninstallation(I placed them before the UnpublishComponents action) removes files, stops service, etc. I am afraid if I place RemoveExistingProducts after InstallFinalize there will be some comflicts. Can you suggest me something?
- Alex: Hi Cody,
For your first question - quote from msdn.microsoft.com/.../aa371197(VS.85).aspx:
"The installer only runs the RemoveExistingProducts action the first time it installs a product. It does not run the action during a maintenance installation or uninstallation."
So, the answer is: there is no need to do any additional scheduling, MSI will take care of it.
As far as scheduling, it is very tricky. Even though scheduling REP after InstallFinalize is the most efficient, it may not always possible to schedule REP at this point. Sometimes you have to schedule it before InstallInitialize if there are special CA's on uninstall. Also, keep in mind that when REP uninstalls the old product, it sets UPGRADINGPRODUCTCODE property which you can use to schedule CA to run only during uninstall or during old product uninstall. This may help you in scheduling REP after InstallFinalize by splitting your CA to two separate: one for product uninstall and another one for old product uninstall during upgrade.
Hope this helps,
From MSI to WiX, Part 8 - Major Upgrade, by Alex Shevchuk的更多相关文章
- From MSI to WiX, Part 1 - Required properties, by Alex Shevchuk
Following content is directly reprinted from From MSI to WiX, Part 1 - Required properties Author: A ...
- From MSI to WiX, Part 2 - ARP support, by Alex Shevchuk
Following content is directly reprinted from From MSI to WiX, Part 2 - ARP support Author: Alex Shev ...
- How To: Implement a Major Upgrade In Your Installer
When creating an .msi-based installer, you are strongly encouraged to include logic that supports Wi ...
- From MSI to WiX, Part 4 - Features and Components by Alex Shevchuk
Following content is directly reprinted from : http://blogs.technet.com/b/alexshev/archive/2008/08/2 ...
- Installshield Major upgrade
Major upagrade: delete old version firstly, then install new version. need to change [product code] ...
- WIX Custom Action (immediate, deffered, rollback)
Following content is directly reprinted from From MSI to WiX, Part 19 - The Art of Custom Action, Pa ...
- What are the differences between small, minor, and major updates?
Following contents are excerpted from the this website and only used for knowledge sharing: Install ...
- Installshield 打包安装包心得
制作简单的安装软件 声明:下面的教程,是把读者当做完全没接触过IS的角度来制作的. 1. 启动InstallShield 12.建立一个InstallShield MSI Project,如图: 2 ...
- Paying for upgrades, by Bob Arnson
Following content is reprinted from here, please go to the original website for more information. Au ...
随机推荐
- Mac OS X 10.10 Yosemite下安装java、jdk、mysql、maven、idea
Mac OS X Yosemite已经在2014年10月17日正式发布了. 作为一个java开发者,尝鲜第一时间安装了最新版本. 和之前的OS X系统还是有很多不同的.下面主要在java开发环境方面做 ...
- android143 360 短信电话拦截
package com.itheima.mobileguard.services; import java.lang.reflect.Method; import android.app.Servic ...
- C++中的inline函数
内联函数: () 内联函数定义和作用: 将一个函数声明为inline,那么函数就成为内联函数.内联函数通常就是它在程序中每个调用点上“内联地”展开.从定义上看,内联函数跟一般函数不一样,一般函数调用的 ...
- 如何将一个 ASP.NET MVC 4 和 Web API 项目升级到 ASP.NET MVC 5 和 Web API 2
----转自微软官网www.asp.net/mvc/ ASP.NET MVC 5 和 Web API 2 带来的新功能,包括属性路由. 身份验证筛选器,以及更多的主机.请参阅http://www.as ...
- 使用Areas(区域)分离ASP.NET MVC 项目
在使用Areas区域时,如果使用默认路由表,将造成路由表冲突,这种情况需要修改一下区域内<区域名称>AreaRegistration.cs和/App_Start/RouteConfig.a ...
- 跳表SkipList
原文:http://www.cnblogs.com/xuqiang/archive/2011/05/22/2053516.html 跳表SkipList 1.聊一聊跳表作者的其人其事 2. 言归正 ...
- jsp自定义标签分页
第一步:建立分页实体page类 package com.soda.util; /** * @description 分页实体类 * @author line * @time 2016年8月28日11: ...
- html笔记03:表单
1.表单是用来收集用户填写的信息,可以说表单就是一个容器,里面的元素的类型可以不一样,所表示的功能也不同. 表单基本语法: <html> <head> <title> ...
- xmpp搭建服务器
二.环境配置1.安装mysql2.修改mysql的帐户的密码>sqlite(移动平台) ,是没有密码直接连接数据库>mysql sqlServer (服务端的数据库) 是有帐户和密码 默 ...
- linux云计算集群架构学习笔记:用户管理和root用户密码重置
RHEL7用户管理 本节所讲内容: 用户和组的相关配置文件 管理用户和组 RHEL7破解root密码 与windows 相比 LINUX中的用户和账号的作用是一样的. 都是基于用户对访问的资源做控制, ...