MAKEMSI quickly and reliably creates MSI files in a non-programmatic way
Have your say! Join the MAKEMSI discussion list or view archive! Suggest improvements. No question too simple or too complex.
[Bottom][Contents][Prev]: Finding 'WSCRIPT.EXE' or 'CSCRIPT.EXE' Files[Next]: IIS Configuration
Have your say! Join the MAKEMSI discussion list or view archive! Suggest improvements. No question too simple or too complex.
\->Tips and Tricks->Automatically Uninstall Other Versions

Automatically Uninstall Other Versions

The method shown here completely uninstalls any version (older or newer) of the MSI being installed before the install continues (Applications to be uninstalled are identified by their "UpgradeCode" property). This means that you will not have to worry about what is "allowed" in "minor upgrades" etc, each of your builds can work exactly the same way without requiring any knowledge of what was done for the last install...

Most people will probably use the "Guid.UpgradeCode" keyword to assign a unique upgrade code to their product and use the "UpgradeCodes" keyword to uninstall other products.

Optionally Generating a Random Upgrade code

While I don't neccessarily recommend this the following code place into a common header file will modify the normal behavour so that you can still supply a fixed GUID (via version file or macro) but if not supplied a new unique GUID will be randomly generated for you:

;----------------------------------------------------------------------------
;--- If Upgrade GUID not supplied want a random one -------------------------
;----------------------------------------------------------------------------
#NextId
#( '<?NewLine>'
   #define COMPANY_SET_PROPERTY_UPGRADECODE

   ;--- Simple validation ---------------------------------------------------
   #if ['<$COMPANY_UPGRADE_CODE_QUALIFIER>' <> '']
       #error ^Currently don't support use of a "COMPANY_UPGRADE_CODE_QUALIFIER"...^
   #endif

   ;--- Generate a random GUID unless we already have one -------------------
   #RexxVar @@UcMacName = '<$VER_PRODINFO_GUID_PREFIX>UpgradeCode'
   dim UpgradeCode                                 ;;VBS variable
   #if  Defined(@@UcMacName) = 'N'
        ;--- Generate Random GUID -------------------------------------------
        #info "UpgradeCode GUID randomly generated..."
        UpgradeCode = GuidMake("UpgradeCode")      ;;VBS to generate random GUID
   #else
        ;--- Use value supplied (probably in ".VER" file --------------------
        #info "UpgradeCode: <$[@@UcMacName]>"
        UpgradeCode = "<$[@@UcMacName]>"           ;;GUID supplied as literal
        VbsReturnGuid "UpgradeCode", UpgradeCode   ;;Ensure HTML report can access this information
   #endif
   <$Property "UpgradeCode"   *Value="UpgradeCode">   ;;Use GUID "calculated" above
#)

Supporting Code in COMPANY.MMH

The following code is an extract from "COMPANY.MMH" shows the supporting code for the above GUID manipulations:

;----------------------------------------------------------------------------
;--- Set MSI guids ----------------------------------------------------------
;----------------------------------------------------------------------------
#define? COMPANY_UPGRADE_CODE_QUALIFIER              ;;Allow user scheme for having "sets" of upgrade codes
#if ['<$COMPANY_UPGRADE_CODE_QUALIFIER>' = '']
    ;--- Not part of a set (or at least using standard name) ----------------
    #define @@UPGRADE_CODE_NAME UpgradeCode
#elseif
    ;--- Upgrade code name qualified by name of a "set" ---------------------
    #option PUSH DefineMacroReplace=YES
    #define @@UPGRADE_CODE_NAME UpgradeCode.<$COMPANY_UPGRADE_CODE_QUALIFIER>
    #option POP

    ;--- Cludge so it can easily be referred to -----------------------------
    #define VBSRET.GUID.UpgradeCode <$VBSRET.GUID.[@@UPGRADE_CODE_NAME]>
#endif
#define  UpgradeCodeValue <$VBSRET.GUID.UpgradeCode>          ;;Info returned from VBSCRIPT pass1
#define? COMPANY_UPGRADECODE_HOOK_AFTER_SETTING               ;;Allow you to do something (perhaps validate value)
#( '<?NewLine>'
   #define? COMPANY_SET_PROPERTY_UPGRADECODE
   dim UpgradeCode                                    ;;Need a VBSCRIPT variable
   <$Guid '<$@@UPGRADE_CODE_NAME>' VB="UpgradeCode">  ;;Want same GUID every time!
   <$Property "UpgradeCode"   *Value="UpgradeCode">   ;;Use GUID "calculated" above
   <$COMPANY_UPGRADECODE_HOOK_AFTER_SETTING>          ;;Default is to do nothing (VBS variable "UpgradeCode" contains the value.
#)
#( '<?NewLine>'
   #define? COMPANY_SET_PROPERTY_PRODUCTCODE
   <$Property "ProductCode"   *Value='GuidMake("ProductCode")'> ;;Random GUID
#)
#( '<?NewLine>'
   #define? COMPANY_SET_PROPERTY_PACKAGECODE
   <$Summary  "PackageCode"   *Value='GuidMake("PackageCode")'> ;;Random GUID
#)
<$COMPANY_SET_PROPERTY_UPGRADECODE>       ;;User can override above macros to change behaviour...
<$COMPANY_SET_PROPERTY_PRODUCTCODE>
<$COMPANY_SET_PROPERTY_PACKAGECODE>

The following code is another "COMPANY.MMH" extract which performs these main functions:

  1. Installs an entry in the "Upgrade" table for any "UpgradeCode" we wish to process, the version numbers were left blank to look for any version. Note a validation message results from this which I filter out. A unique property name is specified for each entry.

  2. Each property must be specified in the "SecureCustomProperties" property.

  3. The "RemoveExistingProducts" action is moved to ensure that the found products are completely removed before the install continues.
#define? COMPANY_AUTO_UNINSTALL_VIA_UPGRADE_TABLE  Y
#if ['<$COMPANY_AUTO_UNINSTALL_VIA_UPGRADE_TABLE>' = 'Y']
   ;--- Look for older/newer versions of the same package (group) -----------
   #define UpgradeCode_PROPERTY    UNINSTALLTHIS   ;;Use this property in "Upgrade" table entry
   <$UpgradeTable "=UpgradeCode">                  ;;Use value in VB variable

   ;--- Any EXTRA upgrade codes to be handled? ------------------------------
   #if ['<$ProdInfo.UpgradeCodes>' <> '']
       <$UpgradeTable "<$ProdInfo.UpgradeCodes>">
   #endif

   ;--- Install MSI AFTER complete uninstallation of older ------------------
   #define? COMPANY_RemoveExistingProducts_HOOK_BEFORE_MOVING
   #define? COMPANY_RemoveExistingProducts_HOOK_AFTER_MOVING
   #define? COMPANY_RemoveExistingProducts_SEQ                 InstallValidate-InstallInitialize
   #(
        ;--- Allow easy overiding/removal of this step ----------------------
        #define? COMPANY_MOVE_RemoveExistingProducts
        <$Table "InstallExecuteSequence">
            ;--- Move to desired location -----------------------------------
            dim RepSeq : RepSeq = GetSeqNumber("InstallExecuteSequence", "<$COMPANY_RemoveExistingProducts_SEQ>", 1)
            <$Row Action="RemoveExistingProducts" *Sequence="RepSeq">
        <$/Table>
   #)
   <$COMPANY_RemoveExistingProducts_HOOK_BEFORE_MOVING>
        <$COMPANY_MOVE_RemoveExistingProducts>
   <$COMPANY_RemoveExistingProducts_HOOK_AFTER_MOVING>
#endif

UpgradeTable Command

The above code used The "UpgradeTable" command which I haven't documented separately. Note that this command generated debug output into the console file, this should help you understand where the values come from..

Its default configuration is:

#define? UPGRADETABLE_DEFAULT_VALIDATE            NEW           ;;Validation performed when row inserted (Don't allow overwrite of info).
#define? UPGRADETABLE_DEFAULT_REMOVE              ALL
#define? UPGRADETABLE_DEFAULT_VERSION_MIN
#define? UPGRADETABLE_DEFAULT_VERSION_MAX
#define? UPGRADETABLE_DEFAULT_LANGUAGE
#define? UPGRADETABLE_DEFAULT_PREFIX              UPGRADECODE.#.    ;;# = unique number
#define? UPGRADETABLE_DEFAULT_PREFIX_MACRO        <$UPGRADETABLE_DEFAULT_PREFIX>
#define? UPGRADETABLE_DEFAULT_PREFIX_VBVARIABLE   <$UPGRADETABLE_DEFAULT_PREFIX>
#define? UPGRADETABLE_DEFAULT_PREFIX_GUID         <$UPGRADETABLE_DEFAULT_PREFIX>
#(
    #define? UPGRADETABLE_DEFAULT_ATTRIBUTES

    (msidbUpgradeAttributesVersionMinInclusive
    or
    msidbUpgradeAttributesVersionMaxInclusive
    or
    msidbUpgradeAttributesLanguagesExclusive)
#)

It allows for "named" GUIDs which allow for GUID specific configuration as demonstrated by the following code:

#define ProductX.Version1               {11111111-1111-1111-1111-111111111111}  ;;Define UpgradeCode for product "ProductX" (major version 1)
#define ProductX.Version1_VERSION_MIN   1.00.0000
#define ProductX.Version1_VERSION_MAX   1.99.9999
#define ProductX.Version1_PROPERTY      MY_CONFIGURED_PROPERTY_NAME
#define ProductY                        {11111111-1111-1111-1111-333333333333}   ;;Define UpgradeCode for product "ProductY" (any version)


#define FromVbVariable_PROPERTY                          MY_UPGRADE_CODE_FROM_VB_VARIABLE         ;;Override property name for this GUID
#define {21AB9EAB-A161-45EA-9272-704EEC8175F9}_PROPERTY  MY_UPGRADE_CODE_21AB9EAB
dim FromVbVariable : FromVbVariable ="{99999999-9999-9999-9999-999999999999}"
<$UpgradeTable "{21AB9EAB-A161-45EA-9272-704EEC8175F9} ProductX1.00.0002 ProductX1.00.0003 {22222222-2222-2222-2222-222222222222} =FromVbVariable">

The UpgradeCodes (version file keyword) can be passed GUIDS in the same way.


Microsoft awarded me an MVP (Most Valuable Professional award) in 2004, 2005, 2006, 2007, 2008 & 2009 for the Windows SDK (Windows Installer) area.Please email me any feedback, additional information or corrections.
See this page online (look for updates)

[Top][Contents][Prev]: Finding 'WSCRIPT.EXE' or 'CSCRIPT.EXE' Files[Next]: IIS Configuration


MAKEMSI© is (C)opyright Dennis Bareis 2003-2008 (All rights reserved).
Sunday February 28 2016 at 3:45pm
Visit MAKEMSI's Home Page
Microsoft awarded me an MVP (Most Valuable Professional award) in 2004, 2005, 2006, 2007, 2008 & 2009 for the Windows SDK (Windows Installer) area.