Creating an MSI installation package from scratch
Published: 2004 - Last update: 2004 (c) Nicolai Kjaer / Spinner Software B.V.
This article describes how to create your own MSI file (software installation package) using the free MSI SDK tools.
Tip: Microsoft has now released WiX, an XML based tool to create MSIs, as open source :) ! You can pick it up at http://sourceforge.net/project/showfiles.php?group_id=105970&package_id=114109&release_id=228957.
Tip: This is recommended reading for scripters and MSI packaging experts. It takes about 30 minutes to complete a small setup file using the techniques described in this article, and it enables you to better troubleshoot and change MSI distributions in the future.
I've yet to see a "Setup" authoring tool that consistently works ; being this intimate with the MSI databases allow you to fix just about anything. It's also good practice.
If you do not already have the Microsoft Windows Installer SDK (MSI SDK) installed, download it from Microsofts website and perform a complete installation. This will by default install to C:\Program Files\MSIntel.SDK\. After the installation, browse to the C:\Program Files\MSIntel.SDK\Tools folder and run the Orca.msi installation. Perform a complete installation of Orca (you use Orca to modify .MSI files).
Tip: Add the C:\Program Files\MSIntel.SDK\Tools subfolder to your PATH system environment variable, so you can run the MSI tools from a command line.
Tip: There's a help file for the tools available in C:\Program Files\MSIntel.SDK\Help\msitool.hlp. The full MSI table reference is also available in the Msi.chm help file. I recommend creating shortcuts for both files and keeping them in the same location as your shortcut to Orca.
Tip: Have the MSI.chm help file opened while you're working in Orca. It helps. Look up the table you're working in, so you can see all the values and options for that table.
Create a basic MSI for your company's software packages, with all the data that is "static".
First, let's create a very basic MSI file that installs a single registry key.
Create a new directory to hold your MSI project, then check the C:\Program Files\MsiIntel.SDK\Samples\Scripts folder for valuable vbscripts. WiReadMe.txt has a list of the scripts and their primary purpose. You will need the WiFilVer.vbs later for updating your MSI package with new files, and WiSumInf.vbs for updating the summary stream. Copy those two scripts (at least) to your new project folder.
The syntax is for WiFilVer is: CScript //nologo WiFilVer.vbs D:\MySoftware\Update\NewMSI.msi This command will list the file information found in the specified MSI file. CScript //nologo WiFilVer.vbs D:\MySoftware\Update\NewMSI.msi D:\MySoftware\Update\NewFiles\ /U This command will update the file information in the specified MSI file, with the files in the NewFiles subdirectory. According to the WiFilVer script sourcecode: The 1st argument is the path to MSI database, at the source file root The 2nd argument can optionally specify separate source location from the MSI The following options may be specified at any point on the command line /U to update the MSI database with the file sizes, versions, and languages Notes: If source type set to compressed, all files will be opened at the root Using CSCRIPT.EXE without the /U option, the file info will be displayed Copyright (C) Microsoft Corporation, 1999-2000. All rights reserved.
Another useful script is the WiSumInf.vbs, which can be used to update the summary information (Add / Remove Progams). Just specify the location of the MSI file, and the properties you want to update.
Copy the 3 msi files from the C:\Program Files\MSIntel.SDK\Database subfolder to your project folder. These MSI files contain the basic schema, the basic sequencing, and an example UI. All you need to do is merge them. Open a command prompt, go to your project folder, then type in:
copy schema.msi mymsi.msi msimerg mymsi.msi sequence.msi msimerg mymsi.msi uisample.msi exit (to quit the command prompt)
You can delete the schema,sequence,uisample MSI files from your project folder after completing this step.
You now have a single MSI file of roughly 280k with most of the basic logic and sequencing. You need to open the mymsi.msi file with Orca (right-click on the file and select Edit with Orca), and change the basic properties / summary information to suit your needs. When you're finished, copy the mymsi.msi file and keep it as a very basic MSI for your company.
Finalizing the basic MSI
Tip: Use a consistent ID naming convention, for example "it must always be 8 characters". Your keys can then have a short prefix followed by a number which is generally large enough - for example REG00001 and FILE0001.
Before the MSI file can be used for anything, you must make a few changes. The following describes the basic steps needed to install a single registry key (and thereby having a fully featured MSI file that does next to nothing ;).
Tip: You need to run GUIDGen to generate special codes named "GUID"s. This program is part of Microsoft Visual C++, but other alternative tools or scripts can be used. If you use GUIDGEN, use option 4 "Registry format", then click on New GUID to see the next number. If you're happy with it, click on Copy to copy it to the clipboard. You can then paste it into the necessary field in Orca. You do need to check the key for lowercase characters and then manually make them uppercase, otherwise they WILL NOT WORK.
Click on the View | Summary Information menu in Orca to review the Summary Information stream. Change it into something relevant for your company or product, and select the platforms and languages.
Each MSI you create must have a unique ProductID. Run GUIDGEN (standard tool) to get the GUID for the ProductID for this MSI package, and make sure it's all in UPPERCASE!
Go to the Properties table, and make the following changes:
Set the ShowUserRegistration property to 0 to avoid the company / user / cd key dialog. Change the ARPHELPLINK property to use your corporate website. Drop the ComponentDownload row. Add a new row (record) for each of the following properties (case sensitive): ALLUSERS = 1 for machine-only installations. ProductName = Your product name Manufacturer = Your company name ProductCode = Use GUIDGEN to get a GUID for this product ProductVersion = Your product version You can also add a property named ARPNOMODIFY with a value of 1 to disable the "Modify" button in Add/Remove Programs. ARPNOREMOVE=1 to disable the Remove button. You need to create at least 1 feature and 1 component before you can run/use the MSI: Go to the Features table and create a new record: Feature = Complete Parent = Title = Complete Description = Complete installation Display = 1 (=displayed expanded in the featurelist) Level = 1 Directory = TARGETDIR Attributes = 0 Now we need to add the registry key we'd like to install. Go to the Registry table, and create a new record: Registry = REG00001 Root = -1 (= Depend on the value of the ALLUSERS property. You can also use 0=HKCR, 1=HKCU, 2=HKLM, 3=HKEY_USERS) Key = SOFTWARE\[Manufacturer]\[ProductName] Name = ProductName Value = [ProductName] [ProductVersion] Component = COMP0001 Note: Using [propertyname] means it should use the named property's value. Now we need to create the COMP0001 component we just referenced: Go to the Components table and create a new record: Component = COMP0001 ComponentID = (Run GUIDGEN to get a GUID - make sure it's in UPPERCASE) Directory = TARGETDIR Attribute = 4 (4= registry, 0 = files to be installed locally) Condition = Keypath = REG00001 Now we can connect this component to the "Complete" feature: Go to the FeatureComponents table and create a new record: Feature = Complete Component = COMP0001 Now, we need to have a single record in the Directory table before the installation can be done. This is because of the special handling of the TARGETDIR property, which has to be resolved. Create a new record in the Directory table: Directory = TARGETDIR Directory_parent = (leave blank (null) or use TARGETDIR) Value = SourceDir
Tip: Change the LicenseAgreementDlg record (text field) in the Controls table to include your license text.
Tip: Use the Tools | Validate menu option in Orca to make sure you have entered all the data properly.
You should now be able to install your MSI package (only a registry key, but a good start :). Make sure the installation and removal can be completed, and that the ARP (Add / Remove Programs) info is as expected. Use RegEdit to see that the registry key was installed properly, and is removed when uninstalling.
Installing MSI packages silently using command lines
You can create a small command file to install the MSI package silently (not going through all the dialogs) ; this makes it easier and faster to perform tests of the actual installations once the user interface has been tested.
Create the following two small text files in your project folder: To install silently, no prompts, no logging: Install.cmd: MsiExec /i "MyMSI.msi" /Qb-! Uninstall.cmd: MsiExec /X "MyMSI.msi" /Qb-! To install silently with a success/failed dialog in the end, and logging enabled: Install.cmd: MsiExec /i "MyMSI.msi" /L*v "c:\temp\MyMSI_i.log" /Qb+ Uninstall.cmd: MsiExec /X "MyMSI.msi" /L*v "c:\temp\MyMSI_x.log" /Qb+ Please make sure you have a C:\temp directory, and have write access to it.
You can now doubleclick on the install.cmd or uninstall.cmd to quickly install or remove your software package.
Tip: Make sure you include the redistributable setup files for MSI for Win9x / NT on your website, some people might now have the Windows Installer technology installed. They can be found in C:\Program Files\MsiIntel.SDK\Redist\.
Expanding the installation package
Naturally, installing a registry is not as important as installing files - but it's vital to have the basics in order. If your MSI is now fully working, you can continue with adding files to be installed.
For each of your following MSI projects, remember to copy your basic MSI and then open it with Orca and change the ProductID in the summary information and the ProductCode property to some unique UPPERCASE GUID numbers for this package.
Adding file(s) to the installer
Create a directory structure under your project source directory that matches the target folder structure. For each directory you have listed, a subdirectory with the same name must exist in the source tree. In most cases this means you would have a Program Files\Your Company Name\Your Product Name\ subfolder in the root of the source tree.
The files you want to install must be found in the directory they should be installed to (by default relative to the source root, and the installation target base path). So if you install YourApplication.exe to C:\Program Files\MyCompany\MyProduct\, you should have a Program Files\MyCompany\MyProduct\YourApplication.exe under the root of your source tree.
Create the following properties in the directory table:
(Use Directory_Parent=TARGETDIR, DefaultDir=.) SystemFolder ProgramMenuFolder PersonalFolder ProgramFilesFolder DesktopFolder StartMenuFolder
These are automatically resolved during install. See "System Folder Properties" in the help file (link from under Directory table help).
Create the target installation directory records:
DIR00001, parent=ProgramFilesFolder, DefaultDir=.:PROGRA~1|Program Files DIR00002, parent=DIR00001, Defaultdir=Spinne~1|Spinner Software
Tip: The Defaultdir uses the format targetdir:sourcedir, optionally short | long filenames. The "." means the value used in the directory_parent field is the resolved target path (so no subdirectory is created with the defaultvalue name). So while DIR00001 resolves to the ProgramFilesFolder value (and expects a Program Files subdirectory in the source tree), DIR00001 creates a Spinner Software subdirectory under the resolved DIR00001 parent (and expects one in the source tree as well). This essentially allows you to have multiple source directories that all resolve to the same target during installation.
Go to the Files table, and create a new entry record. In this example we'll install Notepad.exe:
File=FILE0001, Component=COMP0002, Filename=Notepad.exe, Filesize=0, Attributes=0, Sequence=1.
If a file is Vital to the proper functioning of the application, use Attributes = 512.
Create a new component to hold your file. Go to the Components table and add a new record:
COMP0002, attributes = 0, directory = DIR00002, Keypath=FILE0001. Use GUIDGEN to get the component ID.
When you set the directory field for Component COMP0002 to use DIR00002, Notepad.exe (FILE0001 pointed to by the keypath) file will be installed into C:\Program Files\Spinner Software\.
Create a new record in the FeatureComponents table:
Create a record in the Media table with DiskID=1, LastSequence=1 (highest Sequence number in File table).
Save the MSI, then open a command prompt and run the following command in the project folder:
CScript //nologo wifilver.vbs mymsi.msi
This will show you the information found in the File table.
Now update it with the latest file information (found from the source tree):
CScript //nologo wifilver.vbs mymsi.msi /U
Your MSI file is now ready to install both registry keys and files :).
Adding a shortcut
Create new records in the Directory table for the shortcuts:
DIR00003, parent=StartMenuFolder or ProgramMenuFolder, defaultdir=. DIR00004, parent=DIR00003, defaultdir=Spinne~1|Spinner Software (or whatever name you want in your start menu, in short | long file name format) DIR00005, parent=DesktopFolder, defaultdir=.
Create a new record in the Icon table:
Name=ICON0001.exe, Data = full path to the exe file in the source tree (this will be read in automatically and the icon stored).
Create new records in the Shortcuts table:
Shortcut=SCUT0001, Directory=DIR00004, Name=Notepad, Component=COMP0002, Target=Complete, Description=Notepad, Icon=ICON0001.exe, IconIndex=0 or 1, ShowCmd=1. For the Desktop shortcut, copy/paste the row, then change the keyname and the directory from DIR00004 to DIR00005: Shortcut=SCUT0002, Directory=DIR00005, Name=Notepad, Component=COMP0002, Target=Complete, Description=Notepad, Icon=ICON0001.exe, IconIndex=0 or 1, ShowCmd=1.
Note that there's a bug, so the extension (file type) of the Icon field must match the target file extension. Hence, name it ICON0001.exe. Please note the value of this field is Case Sensitive.
Validate your project, then run a test. You should now have a shortcut on the users desktop, and under the Spinner Software folder in the Start Menu | Programs menu.
Compressing the MSI into a single file
For this you need the WiMakCab.vbs script from C:\Program Files\MsiIntel.SDK\Samples\Scripts. Copy it to your project folder.
Run the following command to create a cabinet file with all the files listed in MyMSI.MSI :
cscript //nologo wimakcab.vbs mymsi.msi MyCabinet /C
The most useful option for this script is the ability to create a single MSI file that contains everything. This is the kind of file you want to distribute.
Run the following command to create a cabinet file containing all your files, sequence the File table, update the MyMSI.MSI file to include the new cabinet file, and enable the "Compressed" option:
cscript //nologo wimakcab.vbs mymsi.msi MyCabinet /C /U /E /S
Note that 4 MyCabinet.* files are created: The actual cabinet file (.CAB), the .DDF is a definition of the content of the CAB file, and the .INF file lists the files in the cabinet. The .RPT file is just a text report on the success of the cabinet creation. All 4 files can be deleted after you've used the /C /U /E /S options, since your files are now streamed inside the single MSI file.
Tip: If you have problems creating the cab file, check the File Table records. The WiMakCab.vbs script only handles File Table records where the NonCompressed bit is cleared in the Attributes field. There is also a problem with MakeCab.exe under Windows 2000 : if a file has the NonCompressed bit set and you update the MSI to use a single compressed file (with a built in cabinet), this flag is not removed, and the package will not install properly. You'll get an error that the file to install could not be found. You can fix this in the WiMakCab script by adding the following lines :
' In the top of WiMakCab, add the msidbFileAttributesCompressed flag Const msidbFileAttributescompressed = &h00004000 ' Right before the statement (search wimakcab for it): ' If (attributes And msidbFileAttributesNoncompressed) = 0 Then ' you can insert the following block of code: ' MakeCab does not change the NonCompressed / Compressed flag!! If (attributes And msidbFileAttributesNoncompressed) <> 0 Then WScript.Echo "File " & filename & " is marked non-compressed. Changing attribute." attributes = (attributes - msidbFileAttributesNonCompressed) ' To be really nice, you could set it to the proper value: attributes = (attributes + msidbFileAttributesCompressed) ' Update the field (the record will be updated later) record.IntegerData(5) = attributes End If
I found it useful to create a little command script to create the compressed MSI. I update the files in the source tree, then run this command file to create a new, single (compressed) MSI for distribution. It expects a file named Input.msi, and creates the file Output.msi.
CreateMSI.cmd: @ECHO OFF @ECHO. @ECHO Updating MSI file with new files @ECHO. @ECHO Remove target output file DEL /Q "Output.msi" @ECHO Update MSI information from source tree CScript //nologo WiFilVer.vbs Input.msi CScript //nologo WiFilVer.vbs Input.msi /U @ECHO Copy original file to new output file COPY /Y "Input.msi" "Output.msi" REM @ECHO Create a single cabinet file with all your files REM CScript //nologo WiMakCab.vbs "Output.msi" Files /C @ECHO Create and embed the file cabinet CScript //nologo WiMakCab.vbs "Output.msi" Files /C /U /E /S DEL /Q "Files.CAB" DEL /Q "Files.INF" DEL /Q "Files.RPT" DEL /Q "Files.DDF" @ECHO Done PAUSE
Part 2 - Custom Actions:
Continue to -> How to create MSI Custom Actions
(c) 2004 Nicolai Kjaer, Spinner Software B.V.