The "RunCmd" Command |
This MAKEMSI command will execute any command line which can be executed on a Windows command prompt and can examines its return code to determine if it succeeded or not.
This command supports the definition of lines of text between this command and any terminating "/RunCmd". The meaning of the lines depends on the "@" parameter's value.
This command takes these parameters:
If "N" is used then you don't wish to create a file but have an easy way to supply extra command line parameters which will be appended to any that you have already supplied (on the "COMMAND" parameter).
If "Y" then a temporary file will be created at runtime and you will refer to it on your command line (the "COMMAND" parameter) as "{?}". A lot of programs would want the filename preceeded by a "@" symbol.
Note that this command does not accept normal MSI formatting but see the "DATA" parameter if you need to pass information.
It is possible to define "DEFAULT_RUNCMD_COMMAND", and not specify this parameter, but if not defined by you this parameter is mandatory.
The default for this parameter can be set via the DEFAULT_RUNCMD_KEEPINDENT macro. The initial value for this is "Y".
The default for this parameter can be set via the DEFAULT_RUNCMD_NEXTID macro. The initial value for this is "Y".
For example the "SQL" commands are defined as:
#define Sql <$RunCmd {$?} ALIAS="SQL"> #define /Sql <$/RunCmd {$?} ALIAS="/SQL">
The "VbsCaCadReplace" function will be run over both the command and the files contents.
There will typically be a lot of standard actions at known fixed locations (which may however differ in sequence number or even order relative to one another with different templates). Click here for the default "InstallExecuteSequence" and "InstallUISequence" sequencing details.
Normally you try not to duplicate sequence numbers but it is probably OK to do so as long as you don't care which one executes first! One possible exception would be deferred custom actions where the custom action data needs to be setup before execution. You should get a validation message to indicate a duplicated number (note that validation can't know whether custom action conditions are mutually exclusive etc).
The sequencing information (see some default sequencing) can be supplied in a number of formats:
Basically you describe a range of valid values and whether you prefer the value to be chosen from the lower (default) or higher end.
The range should be specified in the format "lower - higher". Either value can be omitted, be specified as an integer such as "1000" or the name of an action such as "InstallFiles". The default value for the lower end is "1" and for the higher "32767" and leading and trailing spaces for each are stripped. The minimum and maximum values can be returned as the range is inclusive.
By default MAKEMSI will choose a number as close as possible to the lower end, if you wish it to be as high as possible then begin the specification string with "<".
Some Examples:
You should of course be aware of what your script does, for example if it moves a custom action that you have already sequenced other actions relative to then that would probably be "bad"!
I have captured a large number of "sample properties" which could be used in any conditions. Note that the values of some properties may not be available at all times and in some cases are modified during processing.
You can also use any of the following predefined conditions:
#define? CONDITION_ALREADY_INSTALLED Installed ;;Repair, uninstall etc. #define? CONDITION_INSTALL_ONLY not Installed ;;Doesn't include a repair, uninstall etc! #define? CONDITION_UNINSTALL_ONLY Installed and REMOVE~="ALL" ;;Complete uninstall - HIGHLY RECOMMENDED at you read the "REMOVE" properties MAKEMSI doco! #define? CONDITION_EXCEPT_UNINSTALL not (<$CONDITION_UNINSTALL_ONLY>) ;;Install, Repair etc (all but complete uninstall) #define? CONDITION_IS_WIN2000 VersionNT = "500" ;;Is OS WIN2000? #define? CONDITION_IS_WINXP VersionNT = "501" ;;Is OS WINXP? #define? CONDITION_IS_VISTA VersionNT = "600" ;;Is OS WINDOWS Vista? #define? CONDITION_IS_WINDOWS_7 VersionNT = "601" ;;Is OS WINDOWS Windows7 #define? CONDITION_UI_NONE UILevel = 2 ;;Silent Install #define? CONDITION_UI_BASIC UILevel = 3 #define? CONDITION_UI_REDUCED UILevel = 4 #define? CONDITION_UI_FULL UILevel = 5 ;;"Normal" #define? CONDITION_UI_NO_DIALOGS UILevel <> 5 ;;Don't bother user with popup dialogs, opening readme files etc. #define CONDITION_PER_MACHINE Version9X or (ALLUSERS = 1) ;;True if per-machine install. #define CONDITION_PER_USER not (<$CONDITION_PER_MACHINE>) ;;True if per-user (not per-machine) install.
This parameter accepts one or more space separated attributes which are processed in left to right order. Available attributes are (not all may apply):
A deferred (in-script) custom action must be sequenced between the "InstallInitialize" and "InstallFinalize" actions (or you will get a 2762 error) and data is passed via the "CustomActionData" property.
All deferred actions are executed in a single pass by the server "MSIEXEC.EXE" service (a separate process) after being combined in a "script".
All immediate custom actions scheduled between the "InstallInitialize" and "InstallFinalize" actions (regardless of their sequence number) are executed before deferred ones.
This should only be used as a last resort when executing some poorly written piece of "cr*p" (or perhaps if it's success is not critical to the install).
Rollback custom actions (and so also the "worker" custom action) must be "deferred" and sequenced between "InstallInitialize" and "InstallFinalize".
There are circumstances where a custom action may still run with system privileges.
Lack of this attribute on deferred custom actions is a common reason for failures on Vista.
The CustomActionData property is also not logged when the installer executes the custom action.
Because the installer sets the value of CustomActionData from a property with the same name as the custom action, that property must be listed in the "MsiHiddenProperties" property to prevent its value from appearing in the log.
If the number begins with "0x" (case insensitive) then the number is being supplied as a hexadecimal value otherwise its interpreted as being in decimal.
You may need to supply a numeric value such as "+0x0001" if I haven't provided a suitable alias above...
RUNCMD Options/Defaults |
Please see the "options for commands" section of the manual.
#define? DEFAULT_RUNCMD_COMMAND #define? DEFAULT_RUNCMD_KEEPINDENT Y #define? DEFAULT_RUNCMD_NEXTID Y ;;"@@" etc will be converted #define? RUNCMD_CAD_SYMBOL_START (* ;;(*CadSymbolName*) #define? RUNCMD_CAD_SYMBOL_END *) ;;(*CadSymbolName*) #define? RUNCMD_FROM_SOURCE_FILE \ <$SourceFile FileName="{$FileName}" Html="Y"> ;;Add to HTML report #define? RUNCMD_SEQUENCE_NUMBER DuplicateFiles-InstallFinalize
EXAMPLE - RUNCMD via SQL Aliases |
The following example uses the "OSQL.EXE" which is expected to be available on the installed system. Note that "OSQL.EXE" uses the "-i" option to specify the name of an input file.
;--- Use existing ".SQL" file ----------------------------------------------- <$Sql file="Update.SQL" Command=^"osql.exe" -E -b -n -w512 -S . -d "DatabaseName" -i "{?}"^> ;--- .SQL inline ------------------------------------------------------------ #define MySqlCmd osql.exe -E -b -n -w512 -S . -d "DatabaseName" -i "{?}" <$Sql Command=^<$MySqlCmd>^> Delete from INF_Router Where ID_Process = 18 and ProcessName = 'GetAllDBUsers' GO Delete from INF_Error Where ID_Source = 'Operator.GetXMLForAllDBOperators' GO <$/Sql> <$Sql Command=^<$MySqlCmd>^> #include "SqlPart1.SQL" #include "SqlPart2.SQL" <$/Sql>
This example uses "MySql" an gets the database name from a Windows Installer property:
#data "CaSqlData" "Database" "[DBNAME]" ;;DBNAME comes from dialog (or command line if silent) #data #define MySqlCmd "mysql.exe" --user=root --password=PASSWORD1 < "{?}" <$Sql Command=^<$MySqlCmd>^ DATA=^CaSqlData^> CREATE DATABASE IF NOT EXISTS (*Database*); <$/Sql> #define MySqlCmd2 "mysql.exe" --user=root --password=PASSWORD1 < "{?}" <$Sql Command=^<$MySqlCmd2>^ DATA=^CaSqlData^> USE (*Database*); #include "query.sql" <$/Sql>
Example - Use CACLS.EXE |
The following example shows how some macros could be used to set up some comamnds which invoke "cacls.exe" to set or modify ACLs:
;--- Handy commands you may wish to use ------------------------------------- #define CACLS <$RunCmd {$?} ALIAS="CACLS" Command="CACLS.EXE" @="N"> ;;"CACLS.EXE" installed with WIN2000 & WINXP (at least on PRO) #define /CACLS <$/RunCmd {$?} ALIAS="/CACLS"> ;--- Update ACL on "fred.ini" ----------------------------------------------- <$CACLS CONDITION=^<$CONDITION_INSTALL_ONLY>^> ;--- Name of file we wish to modify ACL on ------------------------------ "%WinDir%\system32\fred.ini" ;--- Edit ACL instead of replacing it ----------------------------------- /E ;--- Deny administrators access ----------------------------------------- /D administrators <$/CACLS>
The example below makes use of the same macros used above but shows a how you can reference the installation directory (or msi properties in general):
#data '@@INSTALLDIR' 2 ;--- Install time name and location of SETACL tool ---------------------- "INSTALLDIR" "[INSTALLDIR]" #data <$CACLS CONDITION=^<$CONDITION_INSTALL_ONLY>^ DATA="@@INSTALLDIR"> "(*INSTALLDIR*)SomeFile.txt" /E /G Users:C <$/CACLS>
You could use the "ExeCa" command to execute the program without a script (which you may wish to do) however these are some of the disadvantages: