The Problem
I have a metadata file that contains the installation path of my application (executable), and this metadata file is installed by my application installer (setup) in a different location than my application files. The purpose of this metadata file is that it would be used by other application to display my application info and also to know the install location of my application.
The problem is that the installation path is hard-coded (default path) in that metadata file, and when the user has changed the install location to a different path during the installation time, the installation path now becomes mismatched with the one in the metadata file. Thus, this has caused the other application cannot recognize the present of my application. My application installer is created using InstallShield 2016 Basic MSI project.
The Solution
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.
Here’s how I did it:
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.