The problem that I had
Let’s say that I have my_app_installer
which contains the following files:
my_app_installer <-- created using InstallShield 2016
├── app_files <-- expected to be installed on "A location"
└── metadata_file <-- will be installed on "B location"
metadata_file
contains my app info and install location (“A location”) and the only reference used by other_apps
to get the metadata info.
The problem started when the user installed my_app_installer
into “C location” (where the app_files
are being extracted). Not in “A location” as I expected. This will cause other_apps
are unable to recognize the installation of my app because they only use my metadata_file
to acquire the metadata info and also install location.
The solution that I used to fix it
To solve this problem, I figured it out that I need to apply a deferred custom action using InstallScript. The reason for using the deferred custom action is because I need to make a change to the system and only the deferred custom action can be run in elevated context. Meaning after the installation of my app_files
is completed, the deferred custom action will find my metadata_file
and replace A location info with C location info.
Here’s how I did it in InstallShield 2016:
1. Created New Set Property custom action (type 51)
- Custom action name:
CA_PopulateCustomActionData
- Property Name:
CA_UpdateMetadataFile
- Property Value:
[INSTALLDIR]
- Install Exec Sequence:
After ScheduleReboot
- Install Exec Condition:
NOT REMOVE
The purpose of this type-51 custom action is to pass other property value to CustomActionData
property since the deferred custom action can only access to these built-in Windows Installer properties; CustomActionData
, ProductCode
and UserSID
. That is the limitation of the deferred custom action.
2. Created New InstallScript custom action as a deferred custom action
- Custom action name:
CA_UpdateMetadataFile
- Function Name:
MODIFY_METADATA
- In-Script Execution:
Deferred Execution
- Install Exec Sequence:
After CA_PopulateCustomActionData
- Install Exec Condition:
NOT REMOVE
Below is the InstallScript that I used to associate with my deferred custom action as defined above:
#include "ifx.h"
#include "isrt.h"
#include "iswi.h"
prototype FindAndReplace(STRING, STRING, STRING);
//Global Variables
STRING SrcDirFileName, SrchString, RplcString;
STRING firstPart;
NUMBER SrchLen, nvLineNumber;
function FindAndReplace(SrcDirFileName, SrchString, RplcString)
STRING svReturnLine,szString, secPart;
NUMBER nReturn, subPos;
begin
Disable(STATUSEX); //stop displaying the progress bar
ShowObjWizardPages(NEXT); //WARNING this may throw a user interface
SrchLen = StrLength(SrchString); //length of search string
nvLineNumber = 0; //pre-set file line number to 0
Din:
while (FileGrep (SrcDirFileName, SrchString, svReturnLine, nvLineNumber, RESTART)=0)
//subPos is the number where the first char of search string was found
subPos = StrFind(svReturnLine, SrchString);
//firstPart is the string upto search string but not including searchString
StrSub (firstPart, svReturnLine, 0, subPos);
//secPart is the string after search string
StrSub (secPart, svReturnLine, subPos+SrchLen, 50);
//new string is firstPart followed by replace string followed by secPart
TextSub.Value( "SUBBED" ) = RplcString;
szString = firstPart+"<SUBBED>"+secPart;
TextSub.Substitute( szString );
//write line replacing original
FileInsertLine (SrcDirFileName, szString, nvLineNumber, REPLACE);
//the code below examines the line written back for any other occurences
//systematically searching and re-writting back to file
//search first line again for search string
if (FileGrep (SrcDirFileName, SrchString, svReturnLine, nvLineNumber, RESTART)=0) then
goto Din; //another occurence found
else
//increment line number and start all over again
nvLineNumber = nvLineNumber + 1;
endif;
endwhile; //while loop exited when END_OF_FILE reached
end;
export prototype MODIFY_METADATA(HWND);
function MODIFY_METADATA(hMSI)
STRING installDirPath;
NUMBER installDirPathBuffer;
begin
installDirPathBuffer = MAX_PATH;
if (MsiGetProperty(hMSI, "CustomActionData", installDirPath, installDirPathBuffer) == ERROR_SUCCESS) then
StrReplace(installDirPath,"\\","\\\\",0);
endif;
if (SYSINFO.bIsWow64 != 0) then
if Is(FILE_EXISTS, "<PATH_TO_METADATA_FILE>") = TRUE then
FindAndReplace("<PATH_TO_METADATA_FILE>","<STRING_TO_FIND>", installDirPath);
endif;
else
if Is(FILE_EXISTS, "<PATH_TO_METADATA_FILE>") = TRUE then
FindAndReplace("<PATH_TO_METADATA_FILE>","<STRING_TO_FIND>", installDirPath);
endif;
endif;
end;
The script above should be self-explanatory. If you encountered the similar problem as mine, you may need to change <PATH_TO_METADATA_FILE>
and <STRING_TO_FIND>
to your own strings. Be aware of possible deadlock, if any, you may need to apply certain conditional statement (checking). Hopefully this method may help others who are in the similar situation.