The "ExeCa" Command |
This command is used to define an EXE based custom action. It also sets up the "Action Text" which gets displayed in the progress bar. You may need to read the "Hide the DOS Command Window" section of this manual.
EXE based custom actions have many issues (see the blog "Integration Hurdles for EXE Custom Actions"), for this reason you'd never write one, however sometimes you have to use what is available! If you were starting from scratch then you'd probably write a "DLL" based one instead (perhaps using the "DllCa-C" command).
As batch files are executed via the operating system command processor, this command can also execute them, please see the "Batch File Custom Actions" section for more information.
This command takes these parameters:
A non-blank value indicates the EXE runs from the "Binary" table. A value of "?" means allow this command to choose the key into the "Binary" table, any other value is the key itself.
To refer to an existing "Binary" key you should pass the key name in this parameter and the "EXE" parameter should be blank (as a non-blank "EXE" parameter means add the binary to the table).
This could be an already installed file or one you install yourself as part of this package (this will of course affect sequencing).
Depending on how the executable was built a windows command prompt window may appear "behind" your program, if this happens you may want to try invoking the command processor and letting it start your program (you may see a minor "flash").
Note that "MSIEXEC.EXE" (version 4+, in Vista) is build with the "NXCOMPAT" option, this will be an issue if any of your code (or the libraries it uses) is not NX compliant. See "KB929710" for more information.
It is used to supply the working (or "current") directory for the executable. The value supplied can be a directory key (see the "Directory" table) or you can supply the directory name.
The default for this parameter can be set via the DEFAULT_EXECA_WORKDIR macro. The initial value for this is "SystemFolder".
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"!
Note that you can supply an empty list (no tables) if you just want to define the custom action but not sequence it, however you must confirm this by supplying a sequence number of 0 (zero) on the "SEQ" parameter can you should also supply "" for the "CONDITION" parameter (value ignored).
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. If it uses or runs files installed by this MSI then the custom action must be run deferred and sequenced after the file installation!
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). Note that even batch files can be made to return decent return codes.
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...
EXAMPLE - Execute Installed File at End of Install |
This example executes a program installed by this package. If you wish to execute an already installed executable file you would use "FileFind" to find it first.
;--- Load MAKEMSI (via my personal branding and configuration file) -------- #include "ME.MMH" ;--- Define installation directory and install file to this location -------- <$DirectoryTree Key="INSTALLDIR" Dir="[ProgramFilesFolder]\MyTestDir" CHANGE="\" PrimaryFolder="Y"> <$Component "Simple" Directory_="INSTALLDIR"> <$File Source="D:\DBAREIS\tools\MsgBox.exe" RowKey="MsgBox.EXE" KeyPath="Y"> <$/Component> ;--- Invoke the install executable ------------------------------------------ #( ;--- Run after install, ignore return code and don't wait for completion --- <$ExeCa EXE="[INSTALLDIR]MsgBox.exe" Args=^"MsgBox Title" "MsgBox text..."^ ;EXE="[SystemFolder]notepad.exe" Args="c:\tmp\1.x" WorkDir="INSTALLDIR" SEQ="InstallFinalize-" Type="immediate ASync AnyRc" Condition="<$CONDITION_INSTALL_ONLY>" > #)
EXAMPLE - Execute Program in Binary Table (not installed) |
The following adds "msgbox.exe" to the "Binary" table and executes it during install:
#( <$ExeCa Binary="MSGBOX.EXE" EXE="D:\DBAREIS\tools\msgbox.exe" Args=^"ExeCa Test Title" "From BINARY table"^ CONDITION=^<$VBSCA_CONDITION_INSTALL_ONLY>^ Rc0="N" ;;Can't trust the return code of this program (so can't check if worked) Seq="InstallFiles-" > #)
EXAMPLE - Regression Test for "ExeCa" |
;---------------------------------------------------------------------------- ; MODULE NAME: RegressionTests-ExeCa.MMH ; ; $Author: USER "Dennis" $ ; $Revision: 1.0 $ ; $Date: 07 Oct 2004 18:09:02 $ ; $Logfile: C:/DBAREIS/Projects.PVCS/Win32/MakeMsi/RegressionTests-ExeCa.mm.pvcs $ ; COPYRIGHT: (C)opyright Dennis Bareis, Australia, 2003 ; All rights reserved. ; ; Note that I had to pick a "EXE" that already existed for this demo... ;---------------------------------------------------------------------------- ;---------------------------------------------------------------------------- ;--- Include common "regression test" logic and MAKEMSI support ------------- ;---------------------------------------------------------------------------- #include "RegressionTests.MMH" ;--- During Build Create some files ----------------------------------------- #( '' #define BuildFile #define SRC_{$ID} <$MAKEMSI_OTHER_DIR>\{$ID}.TXT <$FileMake "<$SRC_{$ID}>"> ;--- Generate contents ---------------------------------------------- {$ShowUser} <$/FileMake> #) <$BuildFile ID="CostFinalize" ShowUser=^ExeCa: Early in INSTALL: After 'CostFinalize' action<?NewLine>No files from MSI installed yet.<?NewLine>Install paused until program completes (notepad exited).^> <$BuildFile ID="InstallFiles" ShowUser=^ExeCa: After 'InstallFiles' action<?NewLine>Any MSI files are now installed.<?NewLine>Install paused until program completes (notepad exited).^> <$BuildFile ID="InstallFinalize" ShowUser=^ExeCa: late in INSTALL: Just before 'InstallFinalize' action<?NewLine>Any MSI files are installed.<?NewLine>Install continues (does not wait for program to exit).^> ;--- Install some files ----------------------------------------------------- <$Files "<$SRC_CostFinalize>;<$SRC_InstallFiles>;<$SRC_InstallFinalize>" DESTDIR="INSTALLDIR"> ;--- Pretty Early (install waits) ------------------------------------------- #( <$ExeCa EXE="[SystemFolder]notepad.exe" Args=^"<$SRC_CostFinalize>"^ ;;Files not yet installed so can only access previously installed files (source) CONDITION=^<$VBSCA_CONDITION_INSTALL_ONLY>^ Seq="CostFinalize-" Type="AnyRc Immediate" ;;Ignore return code. Deferred (the default) custom actions can't be sequenced here! > #) ;--- After files installed (install waits) ---------------------------------- #( <$ExeCa EXE="notepad.exe" ;;Its in the path so "[SystemFolder]" not required. Args=^"[INSTALLDIR]InstallFiles.TXT"^ ;;This is NOT the source file but the installed file CONDITION=^<$VBSCA_CONDITION_INSTALL_ONLY>^ Type="AnyRc" Seq="InstallFiles-" > #) ;--- Run program neart end of install, don't wait! -------------------------- #( <$ExeCa EXE="[SystemFolder]notepad.exe" Args=^"[INSTALLDIR]InstallFinalize"^ ;;This is NOT the source file but the installed file CONDITION=^<$VBSCA_CONDITION_INSTALL_ONLY>^ Seq="<-InstallFinalize" Type="AnyRc Async" ;;Async = don't wait (also requires AnyRc!) > #)