Minor and Major Upgrades Using IPWI
Version 1.0.0 of SampleApp may be on a users machine, and you want to update it to version 1.1.0 if its there, or just install 1.1.0 if its not. Heres an outline of some options that dont involve patching. (These notes may be useful even if you are creating a patch, since youll want to make sure the upgrade works as a standalone project before turning it into a patch.)
1: Upgrading by reinstalling
Build a 1.1.0 release that includes Setup.exe; after the build, open Setup.ini in the Disk1 folder and enter REINSTALLMODE=voums REINSTALL=ALL (no quotes) into the CmdLine key of the [Startup] section. This wont work, however, with the Network-Image/Compress-all-files combination (unless youat your own riskmodify the master Setup.ini in the Support folder of your IPWI distribution).
If we set REINSTALL=ALL at the command line or in Setup.ini, only those features that were already installed will be reinstalled, and therefore this technique wont work for a first-time installation (i.e., if version 1.0.0 isnt already on the target system). One way around this is to create a custom action that undefines REINSTALL if the user is running a first-time install. Create a Type-51 (set-a-property) custom action called fix_reinstall in the Action/Scripts view by right-clicking the Custom Actions icon and selecting New (dont use the wizard):
Next, go to the Sequences view and insert fix_reinstall in both the User Interface and Execute sequences before CostFinalize; making certain to attach the condition Not Installed to fix_reinstall. (Property names are case-sensitive; for example, the condition Not INSTALLED wont work.)
After that, first-time installations and reinstallations both behave correctly.
2: Upgrading by launching MsiExec
Uninstall any previous versions of the product by launching MsiExec in a custom action, as described in InstallShield KB article Q104221. Change the Product Code in the 1.1 project, write a custom action to detect the earlier version (perhaps using the MsiGetProductInfo API, setting a property FOUNDV100 to 1 if the product is installed), create a custom action uninstall100
insert uninstall100 into the UI sequence after CostFinalize, with a condition that evaluates to True if the earlier version is found (e.g., FOUNDV100=1; the condition should probably also include And Not Installed). Better yet, if FOUNDV100=1, display a custom dialog box that reads May I remove the earlier version ?, and then run uninstall100 if the user clicks Yes, and cancel the whole thing if the user clicks No.
A drawback is that this doesnt work if the user runs the 1.1 setup silently; it also goes against the philosophy of Windows Installer, since the User Interface sequence should only ask the user questions and query the target system, and not alter the target system.
3: Major upgrades
For major upgrades (e.g., updating SampleApp 1.1 to 2.0.0), theres a third option. Using IPWI 1.10 or later (and assuming the target system will have version 1.1 or later of the Windows Installer service), write information to the Upgrade table using Power Editor (youll have to change the Product Code for this to work). Heres a sample row:
(Note that Windows Installer uses only the first three fields of the ProductVersion property; the Upgrade table cant tell the difference between, say, versions 188.8.131.52 and 184.108.40.206 of a product. Also note that the Attributes field can accept flags that specify whether to include the VersionMin and VersionMax endpoints when performing version comparisons.)
In the Property Manager (in the Advanced Views section of the IDE), add a property called SecureCustomProperties with the value OLDPRODUCTS (no quotes). What happens is, the FindRelatedProducts action looks for installed products with the upgrade code we specified in the Upgrade table, and sets OLDPRODUCTS to the product codes of any installed products; later, the RemoveExistingProducts invokes a nested-(un)install action to remove any products whose codes are stored in OLDPRODUCTS. If no old versions of the product are installed, nothing special happens.
One thing to look out for is that the ALLUSERS property has to be the same for the installed product and the upgraded product; one can write a custom action that sets the appropriate value to ALLUSERS. (IPWI versions 1.5 and later automatically include such a custom action, a DLL custom action called ISSetAllUsers, which is described in the online help.)
A similar technique (adding a similar row to the Upgrade table, using the detect-only bit in the Attributes column; see the MSI help page How do I prevent an old package from installing over a newer version?) in the old product lets you prevent users from installing an older version of a product over a newer one.
What codes do I need to change?
Note that the Upgrade code never changes; having a constant upgrade code ensures a setup program will recognize both earlier and later versions of itself.
The Package Code appears under the Summary Information Stream icon; and the Product Version, Product Code, and Upgrade Code appear under the Product Properties icon, all under (1) Organize Your Setup > General Information in the IPWI IDE. Moreover, the Releases view of IPWI 2.x allows you to specify a perbuild configuration package code, product version, product code, and upgrade code.
To change these codes from an automated process, you can use the IPWI Automation interface. (See the IPWI help topics under Automation Interface.) Note that IPWI 2.x has a Generate Package Code setting for each product configuration in the Releases view, which performs this automatically.
For example, here is code for a Visual Basic executable that changes the package code for a project file (.ism file) passed as an argument on the command line.
Option Explicit Public Type GUID_t Data1 As Long Data2 As Integer Data3 As Integer Data4(7) As Byte End Type Public Const S_OK = 0 Private Declare Function CoCreateGuid Lib "OLE32.dll" (pGuid As GUID_t) As Long Private Declare Function StringFromGUID2 Lib "OLE32.dll" _ (ByRef rguid As GUID_t, ByVal lpsz As String, ByVal cchMax As Long) As Integer Sub Main( ) Dim oProject ' As ISWiAutomation.ISWiProject Dim newGuid As GUID_t Dim strIsmPath As String, strPackageCode As String Dim iChars As Integer, lReturn As Long On Error GoTo ErrorHandler If Command$ = "" Then MsgBox "Usage: ChangePkgCode <path-to-ISM>", vbInformation + vbOKOnly Exit Sub End If strIsmPath = Command$ ' in case user put ISM path in quotation marks... If Left$(strIsmPath, 1) = """" Then strIsmPath = Right$(strIsmPath, Len(strIsmPath) - 1) strIsmPath = Left$(strIsmPath, Len(strIsmPath) - 1) End If lReturn = CoCreateGuid(newGuid) If (lReturn <> S_OK) Then MsgBox "CoCreateGuid failed!" & vbCrLf & "(It's not your fault.)", vbExclamation + vbOKOnly Exit Sub End If strPackageCode = Space(100) iChars = StringFromGUID2(newGuid, strPackageCode, Len(strPackageCode)) strPackageCode = StrConv(strPackageCode, vbFromUnicode) strPackageCode = Left(strPackageCode, iChars - 1) strPackageCode = UCase(strPackageCode) Set oProject = CreateObject("ISWiAutomation.ISWiProject") oProject.OpenProject strIsmPath oProject.PackageCode = strPackageCode oProject.SaveProject oProject.CloseProject Set oProject = Nothing Exit Sub ErrorHandler: MsgBox "Something happened!" & vbCrLf & vbCrLf & _ "Error number: " & Err.Number & vbCrLf & _ "Error description:" & vbCrLf & Err.Description End Sub
(Note that we cant do this specific example from a VBScript file, since VBScript doesnt support calling Windows APIs.) Changing the Product Version or Product Code from a script or application follows similarly.
Last modified 19 February 2001.
Copyright © by InstallSite Stefan
Krueger. All rights reserved. Legal