标签:
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:
Here is how these items translates to Wix:
<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. |
<!-- Prevent downgrading --> <CustomAction Id="PreventDowngrading" Error="Newer version already installed." />
<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
For FindRelatedProducts to work correctly, the package author must be sure that the ProductLanguage property in the Property table 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 MigrateFeatureStates action and the RemoveExistingProducts action.
MigrateFeatureStates action runs through each record of the Upgrade table 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 CostFinalize action. MigrateFeatureStates must be sequenced in both the InstallUISequence table and the InstallExecuteSequence table.
The RemoveExistingProducts action must be scheduled in the action sequence in one of the following locations.
Windows Installer sets the UPGRADINGPRODUCTCODE Property when it runs this action.
Comments
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 ?
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.
Also, make sure that Upgrade/@Id has the same value as Product/@UpgradeCode and UpgradeVersion elements have correct values for Minimum and Maximum attributes.
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?
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
标签:
原文地址:http://www.cnblogs.com/cindy-hu-23/p/4810119.html