REQ: MSI command line parameters overview

M

mcsetech

Can someone direct me to a list/overview of command line parameters for
using MSI for deploying applications?

Thanks in advance
Claus
 
T

Torgeir Bakken (MVP)

mcsetech said:
Can someone direct me to a list/overview of command line parameters for
using MSI for deploying applications?

Hi

Attached is a copy (with some modifications) of an article I wrote 2001-08-01

Modifications are marked like this (e.g. some updates for MSI 2.0 vs. 1.x):

------------- UPDATE Start -------------

------------- UPDATE Stop -------------



HOWTO:
Check return values, boot status and (un)install status of MSI installations


I have not yet found/seen any good, comprehensibly description/solution
of this subject, so I will try to describe it the best I can based on
my experience and research. A lot of people asks for bits and pieces
around this subject, I will try to give you a overview the best I can
and also give you some code examples. The documentation from Microsoft
how to do this is scarce and spread around (and too often undocumented :-(.


KEYWORDS: MSI installations, Microsoft Installer, MSIEXEC.EXE, command
line, MIF, ISMIF32.DLL, check get return value, boot status, install
status, uninstall status, suppress reboot, restart, WSH, VBScript,
Installer object, silent install, modal dialogue box

OS TYPE: Windows 95, 98, ME, NT4, 2000 and XP

<<<<<<<<<<<<<<<<< >>>>>>>>>>>>>>>>>>>>>

There are two main "methods" of starting a MSI installation as I know
of:

A. Using the Installer object from a script/programming environment

OR

B. Run MSIEXEC.EXE from the command line (directly, or indirectly
through a setup.exe wrapper)

I will mainly focus on the command line options method, as I find it the
most easy, versatile, flexible and readable way of installing and
uninstalling a MSI package. Sometimes you also want to or have to use a
setup.exe wrapper, my description will apply to it as long you can pass
MSIEXEC command line parameters to it. Some of the things I mention
under the command line options method can be transferred to the
Installer object method (e.g.. input of property values to the
installation and some of the return values maybe. If you also can sent a
MIF file parameter to the installer object method, you can use that part
to)

<<<<<<<<<<<<<<<<< >>>>>>>>>>>>>>>>>>>>>

A. Using the Installer object from a script/programming environment

Pros: Direct access to "everything" if that is a prerequisite

Cons: You can drown in details

Object: WindowsInstaller.Installer
Method: InstallProduct
Method: MsiQueryProductState
Method: ConfigureProduct ?
Method: LastErrorRecord ?


To uninstall:
An Uninstall is simply the installer running with the REMOVE property
set.
It can either contain a individual component/feature, or "ALL"

object.InstallProduct(packagePath, propertyValues)
e.g. object.Installproduct("C:\Builds\Foo.msi", "REMOVE=ALL")

A good way of check if a install is successful, is to run
MsiQueryProductState after MsiInstallProduct.

Q: Can LastErrorRecord be used to get error status/messages after a
failed (un)installation or especially if the user canceled? When
looking at the designated functions that generate an error record
(listed in the MSI SDK) I didn't get overly optimistic.

When I played a little with this way of installing a MSI file, I just
got the error "Msi API Error 80004005" when pressing cancel.
If LastErrorRecord is usable for getting status after a failed
installation or especially if the user canceled, then this definitely
belongs under Pros!


<<<<<<<<<<<<<<<<< >>>>>>>>>>>>>>>>>>>>>

B. Run MSIEXEC.EXE from the command line (directly, or indirectly
through a setup.exe wrapper)

One prerequisite for the Pros list to be correct: ISMIF32.dll must be
present in the path (more details further down)

Pros: Full control over both MSIEXEC.EXE return codes and also MSI
"internal" errors.
Can detect that the user choose Cancel if you have a UI.
Consistent way of handling reboot/install status.
Command line parameters are documented and easy to understand.
Supports a setup wrapper if it can transfer command line parameters in
to MSIEXEC.EXE.
Supports UNinstall in the same way as install/reinstall.

Cons: No access to the "inner life" of MSI.

Example on a setup wrapper input parameter syntax (InstallShield):
' parameters after /v is passed to MSIEXEC.EXE
Setup.exe /s /v" /qb- /lv c:\install.log"


See also
http://msdn.microsoft.com/library/en-us/msi/setup/command_line_options.asp
for command line options to MSIEXEC.EXE.

<<<<<<<<<<<<<<<<< >>>>>>>>>>>>>>>>>>>>>


The rest of the document will refer to the command line method, but a
lot of it should be transferable to the Installer object method.

I will go through the following:

1. Reboot status and handling
2. Checking installation/uninstallation status.
3. List of errors returned by MSIEXEC.EXE and in a mif file
4. Code examples


<<<<<<<<<<<<<<<<< >>>>>>>>>>>>>>>>>>>>>


1. Reboot status and handling

Preventing reboot:
When running silent install (/q /qn /qb /qb-), MSI will default reboot
the PC without warning if it "wants" to. A "strong" reboot, bye bye to
any unsaved data if I remember correctly. This can be prevented (in most
cases anyway, there are exceptions e.g.. NAV 7.5 CE) by setting the REBOOT
property.

The REBOOT property can have three different values:
Suppress
Suppresses all reboots e.g.. caused "file in use". Will work in mostly
all situations

ReallySuppress
As Suppress but also suppresses a ForceReboot action (NB! When a MSI
installation uses the ForceReboot action the installation will always
continue after the reboot (using RunOnce in registry)

Force
If you really want to reboot every time, go ahead ;-)

Ex.: MSIEXEC.EXE /i <msifile> /qb- ALLUSERS=2 REBOOT=ReallySuppress
NB! always enter property names in CAPITAL letters.

Detecting reboot status:

Check the return value of MSIEXEC.EXE (1641 and 3010)

NB! If you use a setup.exe wrapper that doesn't relay the return values
of MSIEXEC.EXE (e.g.. InstallShield, the previous version anyway), bad for
you!
Consider to drop the wrapper if possible and use MSIEXEC.EXE directly.

----------------------------------------------------------------

1641 ERROR_SUCCESS_REBOOT_INITIATED ' The installer has started a
reboot (ForceReboot action).
What it is really saying: Going down "RIGHT" now. By by to all unsaved
data if my memory serves me right!. The MSI installation will continue
after the reboot (from RunOnce in registry). Suppress with
REBOOT=ReallySuppress (the boot that is, not the install that will
continue after the boot ;-)

TIP: If you have used REBOOT=Suppress and gets this return code, you
should quit your script (WSH: Wscript.Quit) because it can take some
time before the PC actually gets around to restart, this to avoid being
interrupted in the middle of something.

TIP: If it is important for you to do something after the MSI install is
finished in an 1641 situation (e.g.. after the reboot), put a script in
Run in registry before you start the MSI installation (and let the
script delete itself from Run as the first thing it does). Do NOT put it
in RunOnce, most likely your script will be run before the MSI
installation because the run sequence for values in RunOnce and Run is
the same as the creation sequence. Also everything in RunOnce is started
before anything is started in Run.

TIP: In RunOnce and Run: The alphabetically listed keys you see in
REGEDIT.EXE is not the actual run sequence. To see it, export the values
to a registry file. The order in the registry file reflects the run
order when the user logs on.

TIP: To be sure of the run sequence in RunOnce or Run, you can
(programmable) delete all necessary values, and then recreate them in
the desired run order.

Registry paths:
Run = HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
RunOnce = HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
----------------------------------------------------------------

3010 ERROR_SUCCESS_REBOOT_REQUIRED ' A reboot is required to complete
the install. This does not include installs where the
ForceReboot action is run (=1641)
----------------------------------------------------------------

You should at one point sooner or later give the user an option to
reboot, but it's all up to you ;-)


Example of a check on the return value of MSIEXEC.EXE

Set oShell = CreateObject("WScript.Shell")

strLaunchCmd = "MSIEXEC.EXE /i <msifile> /qb- ALLUSERS=2 REBOOT=Suppress"
intRetVal = oShell.Run(strLaunchCmd,1,True)

If intRetVal = "3010" Then
Set bolBootNeeded = "True"
MsgBox "TEST: Installation was successful, reboot needed"
ElseIf intRetVal = "1641" Then
Set bolBootNeeded = "True"
MsgBox "TEST: Installation will continue after boot, reboot started"
WScript.Quit
' If REBOOT=ReallySuppress, disable the two previous lines
' and enable the following line
' MsgBox "TEST: Installation will continue after boot, reboot needed"
ElseIf intRetVal = "0" Then
' A better solution: Check the mif file instead to be completely sure!
MsgBox "TEST: Installation was successful (probably!)"
Else
' A better solution: Check the mif file ALSO to be completely sure!
' If the MIF file has status "Success", it is no error!
' If the MIF file is missing, there is definitive a error!
MsgBox "TEST: Installation failed (probably!) - Error #" & intRetVal
End if

<<<<<<<<<<<<<<<<< >>>>>>>>>>>>>>>>>>>>>


2. Checking installation/uninstallation status.


Return values from MSIEXEC.EXE isn't always to useful here. 0 should
equal "Success", right. Nope! If you run some UI, like a progress bar
only (/qb-) the user can press Cancel and the installation is aborted.
MSIEXEC.EXE still returns 0. I guess that there are a lot of other
situations that give the same behavior.

But of course, you can (should?) test for the return code anyway.
Looking through the list of error codes (see link further down), it
looks like that you could trigger a error handling if return value is >
0, with an exception of 1641 and 3010 that indicates a reboot situation
and not an error!

------------- UPDATE Start -------------

The above issue is for 1.x only. With MSI 2.0, if the user pushes Cancel,
msiexec returns 1602 :)

Another annoying thing with 1.0 was that if you wanted an unattended install
with a progress bar to indicate that an install was ongoing (using e.g. /qb-),
there was not possible to disable the Cancel button in the progress bar.

If you have Windows Installer 2.0 installed, you can remove the Cancel button
by adding a !, like this: /qb-!

------------- UPDATE Stop -------------

But how can you test for the user cancellation and other more subtle
error situations? The answer is a MIF file, well known for most people
that have used Systems Management Server (SMS) I presume. I have never
seen/used SMS, but I think the MIF file originated from it.

But now you may say "But we do not use SMS, so this is not for me?"
Wrong!
One very overlooked feature of MSI is the capability to create a MIF
status file at the end of the installation (without SMS :). It gives
you a guaranteed way of testing the install/uninstall status and also
often a "detailed" message in case of error.

There are some rules that MUST be obeyed to get this to work!

A: ISMIF32.dll must be in the path

B: MIF filename must NOT be > 8 characters

C: MIF filename must NOT include a path

D: Do NOT enter a MIF filename extension

Is B -> D documented anywhere? I have not found it to be, it's the good
ool' trial and error method that applies ;-)

The MIF file will be placed in the %WinDir% folder (with a .mif filename
extension). I have seen some documentation that says that it can end up
in the %temp% folder too, but I think that applies maybe to a SMS
installation only and not to a MSI installation. But anyhow, it doesn't
hurt to check both places.

See the code section for a VBScript sub (MifStatus) that interprets
a MIF file and returns install status and error messages if any. This is
not so easy to implement as you should believe, the MIF file has a kind
of XML syntax.


TIP: You can use the MIF file to test if the MSI installation is
finished. The file is not created before the absolute end of the
installation. This is VERY useful if you have a setup.exe wrapper that
spawns a new install process and immediate "commits suicide" and let the
child to the dirty work. E.g.. the InstallShield version have this
"problem" (but I have seen rumors that the new version has a "wait"
switch on the command line?).


The following is an example of the command-line syntax used to generate
a status MIF file:

Install:
msiexec.exe /I <Package> /m MIFfilename

Uninstall:
msiexec.exe /x <Package|ProductCode> /m MIFfilename

When the /m command-line option is used to install software to a
computer with ISMIF32.dll present in the path (preferable in Windows
system folder?), Windows Installer generates an install status MIF file
that includes the following summary properties that correspond to the
MIF fields:


<quote>
Command line documentation on MSIEXEC.EXE for /m filename

Generates a Systems Management Server (SMS) status .mif file. Must be
used with the install (-i), remove (-x), administrative installation
(-a), or reinstall (-f) option. The Ismif32.dll is installed as part of
SMS and must be on the path.
The fields of the status .mif file are filled with the following
information:

----------------------------------------------------------------

Windows Installer MIF File Data
----------------------------------------------------------------

MIF Field --> Windows Installer Summary Property
----------------------------------------------------------------

Manufacturer --> Author (manufacturer)
Product --> Subject (product name)
Version --> Revision number (package code)
Locale --> Template
Serial Number --> Not set
Installation --> Set by Ismif32.dll to "DateTime"
InstallStatus --> "Success" or "Failed"
Description --> Blank if successful. Error message if failed. *)

*) Description - Error messages in the following order: 1) Error
messages generated by installer; 2) Resource from Msi.dll if
installation could not commence or user exits; 3) System error message
file; 4) Formatted message: "Installer error %i", where %i is the error
returned from Msi.dll
----------------------------------------------------------------

</quote>


Where can the ISMIF32.dll be found?

From MS DLL Help Database at
http://support.microsoft.com/servicedesks/FileVersion/default.asp :

PRODUCT SIZE MOD DATE CAB/IEXPRESSRELATIVE PATH
SMS 1.2 9,488 7/27/1996 \smssetup\mips\intnl
SMS 1.2 6,976 7/23/1996 \sp3\smssetup\x86\intnl
SMS 1.2 6,976 7/23/1996 \sp4\smssetup\x86\intnl
SMS 1.2 6,976 7/27/1996 \smssetup\x86\intnl
Visual Basic 6.0 6,976 7/26/1996 \disk 2\boffice
Visual J++ 6.0 6,976 7/26/1996 \boffice
Visual Studio 6.0 6,976 7/26/1996 \disk2\boffice



<<<<<<<<<<<<<<<<< >>>>>>>>>>>>>>>>>>>>>


3. List of errors returned by MSIEXEC.EXE and in a mif file


This one list error codes < 2000 and also 3010 =
ERROR_SUCCESS_REBOOT_REQUIRED (Success is an error ;-)
Platform SDK: Windows Installer, Error Codes
http://msdn.microsoft.com/library/en-us/msi/setup/error_codes.asp

This one also list error codes > 2000
http://msdn.microsoft.com/library/en-us/msi/setup/windows_installer_error_messages.asp

"The error codes above 2000 are internal errors and do not have authored
strings, but these can occur if the installation package has been
incorrectly authored." (minus error 3010 if I am not mistaken ;-)


Interesting return codes/message in MIF file when user cancels:

User cancel:
1602 Are you sure you want to cancel?
(2322 User canceled)
(2367 User Abort. )


<<<<<<<<<<<<<<<<< >>>>>>>>>>>>>>>>>>>>>


4. Code examples

****************************************************************

' Example on how to use the function MifStatus


Set oFSO = CreateObject("Scripting.FileSystemObject")

sMyMifFile = "<path to some MIF file>"

If oFSO.FileExists(sMyMifFile) Then
bMifSuccess = MifStatus(sMyMifFile, sMifErrMsg, sMifMsiMsg)

If Not TypeName(bMifStatus) = "Boolean" Then
' There is something fishy here
' (eg. missing MIF file or invalid data in MIF file)
MsgBox bMifSuccess

ElseIf bMifSuccess Then
' (un)Installation succeded
MsgBox "(un)Installation succeded!" & vbCrLf & sMifMsiMsg

Else
' (un)Installation failed
MsgBox sMifErrMsg & vbCrLf & sMifMsiMsg
End if
End if


Function MifStatus (sMifFile, sMifErrMsg, sMifMsiMsg)

' Checks (un)install status from a MIF file

' Function returns:
' True ==> (boolean) Successful (un)installation
' False ==> (boolean) Failed (un)installation
' "Missing MIF input file: " & sMifFile ==> (string) Selfdocumenting ;-)
' "Invalid data in MIF file " & sMifFile ==> (string) Selfdocumenting ;-)

' Input parameters:
' sMifFile ==> (string) Full Path/Filename to input MIF file

' Output parameters:
' Set at both success and failure:
' sMifMsiMsg ==> (string) <msg from MSI> ' mostly "" when success

' Set only at failure:
' sMifErrMsg ==> (string) "Installation of <sMsiProd> failed"

Dim oFSO, bInstFound, bFailedFound, bDescrFound, bProductFound
Dim sMsiProd, fMifFile, sMifLine, iPos, iMyLen

Set oFSO = CreateObject("Scripting.FileSystemObject")
bInstFound = False
bFailedFound = False
bDescrFound = False
bProductFound = False

If oFSO.FileExists(sMifFile) Then
' Init value
MifStatus = "Invalid data in MIF file " & sMifFile
Set fMifFile = oFSO.OpenTextFile(sMifFile)
Do Until fMifFile.atEndOfStream
sMifLine = fMifFile.ReadLine
If InStr(1, sMifLine, "VALUE = ", vbTextCompare) > 0 Then
If bInstFound Then
bInstFound = False
If InStr(1, sMifLine, "Success", vbTextCompare) > 0 Then
MifStatus = True
End If
If InStr(1, sMifLine, "Failed", vbTextCompare) > 0 Then
MifStatus = False
bFailedFound = True
End If
End If
If bProductFound Then
bProductFound = False
iPos = InStr(1, sMifLine, "=", vbTextCompare)
iMyLen = Len(sMifLine)
iMyLen = iMyLen - iPos
sMsiProd = trim (right(sMifLine, iMyLen))
' getting rid of the "" that is read from the file
sMsiProd = Replace(sMsiProd, """", "", 1, -1, vbTextCompare)
End If
If bDescrFound Then
bDescrFound = False
iPos = InStr(1, sMifLine, "=", vbTextCompare)
iMyLen = Len(sMifLine)
iMyLen = iMyLen - iPos
sMifMsiMsg = trim (right(sMifLine, iMyLen))
' getting rid of the "" that is read from the file
sMifMsiMsg = Replace(sMifMsiMsg, """", "", 1, -1, vbTextCompare)
If bFailedFound Then
sMifErrMsg = "Installation of " & sMsiProd & " failed"
End If
End If
End If
If InStr(1, sMifLine, "Product", vbTextCompare) > 0 Then
If InStr(1, sMifLine, "NAME = ", vbTextCompare) > 0 Then
bProductFound = True
End If
End If
If InStr(1, sMifLine, "InstallStatus", vbTextCompare) > 0 Then
If InStr(1, sMifLine, "NAME = ", vbTextCompare) > 0 Then
bInstFound = True
End If
End If
If InStr(1, sMifLine, "Description", vbTextCompare) > 0 Then
If InStr(1, sMifLine, "NAME = ", vbTextCompare) > 0 Then
bDescrFound = True
End If
End If
Loop
Else
MifStatus = "Missing MIF input file: " & sMifFile
End If
End Function


****************************************************************

Custom action that can be used in a MSI file:

Function ChkBoot
Set oShell = CreateObject("WScript.Shell")
If (Session.Mode(6)) Then
oShell.RegWrite "HKLM\SOFTWARE\<somewhere>\Bootneeded","True"
Else
oShell.RegWrite "HKLM\SOFTWARE\<somewhere>\Bootneeded","False"
End If
End Function


****************************************************************


An example on how to create a MSIEXEC command that uninstalls
applications silently and uninstall applications on a per-user basis,
see MSIREMOV.vbs at
http://www.microsoft.com/technet/prodtechnol/sms/deploy/depovg/deplymsi.asp
(Deploying Windows Installer Setup Packages with Systems Management
Server 2.0). Here is an extract of the description:

<quote>
The MSIREMOV.vbs script extracts the package code from a specified
Windows Installer package and automatically creates a registry file
(.reg) that includes the necessary registry key entries to both
uninstall applications silently and uninstall applications on a per-user
basis.
</quote>


****************************************************************

Puhhh, finished, and I hope someone will find this
information useful :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top