![]() | ![]() | ![]() | ![]() | ![]() |
I wanted to be able to create MSI (Windows Installer) packages by defining a simple script and processing this via command line tools that could be run via a make file or simply a batch file.
I found that Wise installer supports OLE automation. I did a bit of research and created a header file which will generate a Visual Basic Script (VbScript) which when executed will create an MSI installer package from scratch (no template required).
The defined macros currently support (basically I believe anything you can do manually you should be able to do via this mechanism):
Other stuff not currently supported can be added via more macros or simply via inline VbScript. Unless you wish to add inline VbScript you do not need to have any knowledge of this language.
There is no need to ever use the wizard although it can be handly to confirm the approach you are using or for debugging your script. Another reason is to examine the tables that wise uses so you know how to update them from the script.
If you have many projects you will find that even though these scripts are simple, 90% of what they do is common between them and a bit of tweeking will quickly get your second, third or hundredth script going in no time. Nothing is hidden by the wizard, code and changes can be commented and PVCS and other standard difference like tools can be used.
I have created a de-compiler (disassembler) for WSI files so that you can take existing packages and create a script from this (it would probably need tweeking to make more readable but should work OK without the tweeking). This decompiler can also be used to work out the differences between two versions of the same package. I have also written a MSI dump program which can tell you a lot about the installer package.
Now the organisation I work for will have tens or hundreds of packages with a lot of common code and settings. For this reason I created the "BSD.WIH" header file (as a front end to the more generic "WISEINST.WIH"
This is an extremely simple script which produces an MSI that:
Most of the above is performed by my "organsations intermediate header file" which we have set up to ensure all packages we create follow set standards.
;---------------------------------------------------------------------------- ; ; MODULE NAME: BSDUTIL.WI ; ; $Author: USER "Dennis" $ ; $Revision: 1.0 $ ; $Date: 10 May 2001 17:33:28 $ ; $Logfile: C:/DBAREIS/Projects.PVCS/PpwAddOn/WiseInst/bsdutil.wi.pvcs $ ; ; DESCRIPTION: This is my attempt at setting up "BSD_UTIL" correctly. ; ; Wise Installer 3+ & PPWIZARD is required to build the ; WSI/MSI packages. The WSI dump utility is used to ; decompile packages. ; ; All known ANX standards are applied "behind the scenes", ; for example support contact information and the fact ; that MSI packages install without prompting. Note that ; as show a lot of information is defaulted and the version ; number has been autogenerated. ; ; Shortcuts/registry/feature/components etc also supported. ; Basically if it can be done manually I can do it in a ; script. ; ;---------------------------------------------------------------------------- ;--- Define Comment for BSDUTIL --------------------------------------------- #define ProductComments \ This product is a collection of a number of tools required \ by other products. \ It also contains BSD standard DLLs such as the LU 6.2 \ communications API. ;--- Start Package ---------------------------------------------------------- #define DEBUG_OUTPUT ;;Option: Macros display text (no trap handler - VB too stupid) #define ProductName BSD_UTIL #define BSD_NO_VER_FILE_EXISTS #include "BSD.WIH" ;--- Define all directories used -------------------------------------------- <$Directory Name="PgmFilesBSD" SubDir="BSD_UTIL"> ;--- Want directory to be at end of SYSTEM "PATH" --------------------------- <$Path PATH="<$PgmFilesBSD>"> ;--- Add files to "COMPLETE" ------------------------------------------------ <$AddFileWild \ SrcDir="P:\DEVELOP\Exported\BSD_UTIL" \ DestDir="<$PgmFilesBSD>" \ ; includes="cssapi.dll" ;;default is "*.*" \ >
The example shown above was quite a simple one, this example is a much more complex package.
The "BSD.WIH" header will by default look for a ".VER" file to match the ".WI" package being created, the example above told the header that this was not being supplied.
The ".VER" file contains header information (relevant to all "LIFE" releases) as well as every release distributed with at least a version number and a change history. There are a number of reasons for "splitting" the information, one of the most important is to prevent the actual package script from being modified just because you are creating a new release, we would like it to change only if the installation process does. It also allows someone without any real knowledge of the process to be able to safely update the version information.
The "BSD.WIH" header file reads the version file first extracting any heading information as well as the first (current) change record to pick up the version number that should be used for the current MSI build. It also picks up the change information for this version and adds it to the Generated MSI allowing batch files to scan MSI files for change information independent of any associated readme files etc (which may get lost etc).
The version file for the "LIFE" package looks like:
;---------------------------------------------------------------------------- ; ; MODULE NAME: LIFE.VER ; ; $Author: USER "Dennis" $ ; $Revision: 1.4 $ ; $Date: 28 May 2001 17:24:22 $ ; $Logfile: C:/DBAREIS/Projects.PVCS/PpwAddOn/WiseInst/life.ver.pvcs $ ; ; DESCRIPTION: Holds: ; ; (1) Product description & version history ; ; (2) Version history (current version at top!) ; ;---------------------------------------------------------------------------- ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; ProductName = ANX Quote Life ; DESCRIPTION = This holds the ANX Quote Life ; = quoting program. ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ;############################################################################ MAJ_MIN : 1.03 VERSION : 01.141 CHANGES : Contains the 1.0.3 version of the application. ;############################################################################ MAJ_MIN : 1.02 VERSION : 01.050 CHANGES : Contains the 1.0.2 version of the application.
The actual package script for "LIFE" looks like:
;---------------------------------------------------------------------------- ; ; MODULE NAME: LIFE.WI ; ; $Author: USER "Dennis" $ ; $Revision: 1.4 $ ; $Date: 24 May 2001 18:17:12 $ ; $Logfile: C:/DBAREIS/Projects.PVCS/PpwAddOn/WiseInst/life.wi.pvcs $ ; ; DESCRIPTION: Creates MSI for ANX Quote Life. ; ;---------------------------------------------------------------------------- ;--- Where are all the source files located? -------------------------------- #define SOURCE_QUOTE_LIFE_PGM <?=getenv("EXPDIR_ANX_QUOTE_LIFE_PGM", "Y")> #define SOURCE_QUOTE_LIFE_DATA <?=getenv("EXPDIR_ANX_QUOTE_LIFE_DATA", "Y")> #define ANX_QUOTE_LIFE_ICON .\ANXQLIFE.ICO ;--- Some constants --------------------------------------------------------- #define QL_SHORTDIR_TEXT <$BSD_AIS_STD_PGM_DIRNAME> #define QL_IDRIVE_DATA_DIR_TEXT I:\Program Files\ANX\<$QL_SHORTDIR_TEXT> ;--- Load "BSD" support ----------------------------------------------------- #define BSD_COMPLETE_FEATURE_DIR DIR_LIFE_PGM #define BSD_ALLUSERS 0 #include "BSD.WIH" ;--- Directory - PROGRAM in "\PROGRAM FILES\ANX\ANX Quote Life..." ---------- <$Directory Name="DIR_LIFE_PGM" Parent="DIR_ProgramFilesBSD" SubDir="<$QL_SHORTDIR_TEXT>"> ;--- Directory - DATA ------------------------------------------------------- <$Directory Name="DIR_LIFE_DATA" Parent="DIR_LIFE_PGM" SubDir="<$BsdIDriveSetupShortDirText>"> ;--- Take care of the data -------------------------------------------------- #( ;--- Create Component ---------------------------------------------------- <$AddComponent NAME="DIR_LIFE_DATA" DestDir="DIR_LIFE_DATA" ;;;; Attributes="<$CATTR_NOT_REMOVE_AT_UNINSTALL>+<$CATTR_NEVER_OVERWRITE>" > #) #( <$AddFileWild DestDir="<$DIR_LIFE_DATA>" SrcDir="<$SOURCE_QUOTE_LIFE_DATA>" Add2="DIR_LIFE_DATA" Add2Type="<$IS_COMPONENT>" KeyFile="*" > #) ;--- EXE Needs to go into it's own component -------------------------------- <$AddComponent NAME="QLIFE.EXE" DestDir="DIR_LIFE_PGM"> #( <$AddFileWild DestDir="<$DIR_LIFE_PGM>" SrcDir="<$SOURCE_QUOTE_LIFE_PGM>" Add2="QLIFE.EXE" Add2Type="<$IS_COMPONENT>" KeyFile="*" includes="ANXQuoteLife.exe" EXLIST="QLIFE_PGM" > #) ;--- These items need to be in seperate components (MS/AIS rules) ----------- #( <$AddFileWild DestDir="<$DIR_LIFE_PGM>" SrcDir="<$SOURCE_QUOTE_LIFE_PGM>" Add2="<$BSD_COMPLETE_FEATURE_NAME>" Add2Type="<$IS_FEATURE>" includes="*.dll" EXLIST="QLIFE_PGM" > #) #( <$AddFileWild DestDir="<$DIR_LIFE_PGM>" SrcDir="<$SOURCE_QUOTE_LIFE_PGM>" Add2="<$BSD_COMPLETE_FEATURE_NAME>" Add2Type="<$IS_FEATURE>" includes="*.ocx" EXLIST="QLIFE_PGM" > #) ;--- All other Program files can go into one component ---------------------- <$AddComponent NAME="DIR_LIFE_PGM" DestDir="DIR_LIFE_PGM"> #( <$AddFileWild DestDir="<$DIR_LIFE_PGM>" SrcDir="<$SOURCE_QUOTE_LIFE_PGM>" Add2="DIR_LIFE_PGM" Add2Type="<$IS_COMPONENT>" KeyFile="*" EXLIST="QLIFE_PGM" > #) ;--- Create the shortcut ---------------------------------------------------- <$AddIcon KEY="ICON_QLIFE.EXE" FILE="<$ANX_QUOTE_LIFE_ICON>"> #( <$AddShortCut RowKey="SC_ANX_QUOTE_LIFE_PGM" Title="ANX Quote Life <$MajVersion>" ScDir="SCDIR_BUSAPPS" ScTarget="anzquotelife.exe" ScWorkDir="DIR_LIFE_PGM" ScDesc="Life Insurance Quoting Application (BSD v<$BsdVersion>)" Add2="QLIFE.EXE" Add2Type="<$IS_COMPONENT>" ScIconNumb=0 ScIconKey="ICON_QLIFE.EXE" > #) ;--- Fix 2 Wise Installer BUGs ---------------------------------------------- <$HereWeAre "Working around 2 Wise Installer bugs (Icon Indexes 'adjusted' + Working directory bug)"> <$TABLE "Shortcut"> <$Row "SC_ANX_QUOTE_LIFE_PGM" WkDir="DIR_LIFE_PGM" IconIndex="0"> <$/TABLE> ;--- Add INI ---------------------------------------------------------------- <$IniFile FILE="ANXQUOTE.INI" SECTION="FileDetails" DIR="DIR_LIFE_PGM" ADD2="DIR_LIFE_PGM"> <$IniAdd Key="AgentDetailsFilePath" Value="<$QL_IDRIVE_DATA_DIR_TEXT>"> <$IniAdd Key="CustomerFilePath" Value="<$QL_IDRIVE_DATA_DIR_TEXT>"> <$IniAdd Key="QuoteFilePath" Value="<$QL_IDRIVE_DATA_DIR_TEXT>"> <$IniAdd Key="TempFilePath" Value="<$QL_IDRIVE_DATA_DIR_TEXT>"> <$IniAdd Key="CustomerQuoteKillDuration" Value="60"> <$IniAdd Key="CustomerQuoteKillDate" Value="30/12/2001"> <$IniAdd Key="Bank" Value="Y"> <$/IniFile> ;--- Add registry entries --------------------------------------------------- #define+ HKEY_DEFAULT <$CLASSES_ROOT> #define+ DEFAULT_ADD2 REG_CLASSES_ROOT #define+ DEFAULT_ADD2TYPE <$IS_COMPONENT> <$AddComponent NAME="<$DEFAULT_ADD2>" DestDir="DIR_LIFE_PGM"> #( <$AddRegValue KeyPath="Y" RowKey="KeyPathRegEntry" Key="CalculationXDll.clsCalculation" Value="CalculationXDll.clsCalculation" > #) #( <$AddRegValue Key="MedFinReqXDll.clsMedicalFinancialReq\Clsid" Value="{03AC0317-0756-11D3-9CDA-00C04F886CB8}" > #) #( <$AddRegValue Key="QuoteXDll.clsQuotes" Value="QuoteXDll.clsQuotes" > #) #( <$AddRegValue Key="CalculationXDll.clsCalculation\Clsid" Value="{C47325AD-0CF3-11D3-9CA9-00C04F886CB8}" > #) #( <$AddRegValue Key="DiscountBandXDll.clsDiscountBands" Value="DiscountBandXDll.clsDiscountBands" > #) #( <$AddRegValue Key="MedFinReqXDll.clsMedicalFinancialReqs\Clsid" Value="{03AC0319-0756-11D3-9CDA-00C04F886CB8}" > #) #( <$AddRegValue Key="ConstantXDll.clsConstant" Value="ConstantXDll.clsConstant" > #) #( <$AddRegValue Key="CalculationXDll.clsCalculationResults\Clsid" Value="{C47325AB-0CF3-11D3-9CA9-00C04F886CB8}" > #) #( <$AddRegValue Key="DiscountBandXDll.clsDiscountBand\Clsid" Value="{EBB4C949-0755-11D3-9CDA-00C04F886CB8}" > #) #( <$AddRegValue Key="CalculationXDll.clsCalculationResults" Value="CalculationXDll.clsCalculationResults" > #) #( <$AddRegValue Key="CustomerXDll.clsCustomer\Clsid" Value="{C06B9199-0CCF-11D3-9CA9-00C04F886CB8}" > #) #( <$AddRegValue Key="ProductXDll.clsProduct\Clsid" Value="{A76A95A1-0CF3-11D3-9CA9-00C04F886CB8}" > #) #( <$AddRegValue Key="CalculationXDll.clsCalculationResult" Value="CalculationXDll.clsCalculationResult" > #) #( <$AddRegValue Key="DiscountBandXDll.clsDiscountBands\Clsid" Value="{EBB4C947-0755-11D3-9CDA-00C04F886CB8}" > #) #( <$AddRegValue Key="MedFinReqXDll.clsMedicalFinancialReqs" Value="MedFinReqXDll.clsMedicalFinancialReqs" > #) #( <$AddRegValue Key="TrueDBGrid50.TDBDropDown" Value="True DBDropDown Control" > #) #( <$AddRegValue Key="DiscountBandXDll.clsDiscountBand" Value="DiscountBandXDll.clsDiscountBand" > #) #( <$AddRegValue Key="ProductXDll.clsProducts" Value="ProductXDll.clsProducts" > #) #( <$AddRegValue Key="TrueDBGrid50.TDBGrid" Value="True DBGrid Control" > #) #( <$AddRegValue Key="CustomerXDll.clsCustomers\Clsid" Value="{C06B9197-0CCF-11D3-9CA9-00C04F886CB8}" > #) #( <$AddRegValue Key="TrueDBGrid50.Style" Value="APEX True DBGrid Style Object" > #) #( <$AddRegValue Key="CalculationXDll.clsCalculationResult\Clsid" Value="{C47325A9-0CF3-11D3-9CA9-00C04F886CB8}" > #) #( <$AddRegValue Key="PrintQuoteXDll.clsPrint\Clsid" Value="{BDFAFAE8-0E58-11D3-9CAB-00C04F886CB8}" > #) #( <$AddRegValue Key="ProductXDll.clsProduct" Value="ProductXDll.clsProduct" > #) #( <$AddRegValue Key="ProductXDll.clsProducts\Clsid" Value="{A76A95A3-0CF3-11D3-9CA9-00C04F886CB8}" > #) #( <$AddRegValue Key="CalculationXDll.clsCalculations" Value="CalculationXDll.clsCalculations" > #) #( <$AddRegValue Key="TrueDBGrid50.ValueItem\CLSID" Value="{00028C65-0000-0000-0000-000000000046}" > #) #( <$AddRegValue Key="PrintQuoteXDll.clsPrint" Value="PrintQuoteXDll.clsPrint" > #) #( <$AddRegValue Key="QuoteXDll.clsQuotes\Clsid" Value="{8B14AE1E-0CCF-11D3-9CA9-00C04F886CB8}" > #) #( <$AddRegValue Key="CalculationXDll.clsCalculations\Clsid" Value="{C47325AF-0CF3-11D3-9CA9-00C04F886CB8}" > #) #( <$AddRegValue Key="CustomerXDll.clsCustomers" Value="CustomerXDll.clsCustomers" > #) #( <$AddRegValue Key="TrueDBGrid50.Style\CLSID" Value="{00028C6C-0000-0000-0000-000000000046}" > #) #( <$AddRegValue Key="TrueDBGrid50.TDBDropDown\CLSID" Value="{00028C70-0000-0000-0000-000000000046}" > #) #( <$AddRegValue Key="MedFinReqXDll.clsMedicalFinancialReq" Value="MedFinReqXDll.clsMedicalFinancialReq" > #) #( <$AddRegValue Key="QuoteXDll.clsQuote" Value="QuoteXDll.clsQuote" > #) #( <$AddRegValue Key="QuoteXDll.clsQuote\Clsid" Value="{8B14AE1C-0CCF-11D3-9CA9-00C04F886CB8}" > #) #( <$AddRegValue Key="CustomerXDll.clsCustomer" Value="CustomerXDll.clsCustomer" > #) #( <$AddRegValue Key="ConstantXDll.clsConstant\Clsid" Value="{3DCFDC55-0CF3-11D3-9CA9-00C04F886CB8}" > #) #( <$AddRegValue Key="TrueDBGrid50.TDBGrid\CLSID" Value="{00028C49-0000-0000-0000-000000000046}" > #) #( <$AddRegValue Key="TrueDBGrid50.ValueItem" Value="APEX True DBGrid Value Item Object" > #) ;--- Set up I: drive (VBS, INF(s) and registry) ----------------------------- #define IDRV_DIRKEY_VBS DIR_LIFE_DATA ;;Key into directory table (with only one dir INF/VBS in same dir as data) #define IDRV_DIRINFO_INF.1 ~<$ProductName>.INF ;;Name of INF file (typically shortname, but actually relative to VBS dir) #define IDRV_DIRINFO_DIRKEY.1 DIR_LIFE_DATA ;;Key to directory table #define IDRV_DIRINFO_SRC.1 <$SOURCE_QUOTE_LIFE_DATA>\*.*(CPYFLG_NeverOverwrite) #define IDRV_DIRINFO_ON_I.1 <$QL_IDRIVE_DATA_DIR_TEXT> ;;Full dir name on I drive #include "IDRIVESU.WIH"
![]() | ![]() | ![]() | ![]() | ![]() |