This is a little collection of routines I've written to make my life easier when using InstallShield. Use at your own risk. Modify to your heart's content. No warranties expressed or implied. Etc...
//////////////////////////////////////////////////////////////////////////////
// File Name: UsefulScripts.rul
// Description: Reusable Installshield Scripts
// Comments: Scripts that may prove to be handy in other installs
// would go in this file.
//
//////////////////////////////////////////////////////////////////////////////
//////////////////// installation declarations ///////////////////
// ---- script function prototypes -----
// these update the Web.Config
prototype NUMBER ReadKey(HWND, STRING, STRING, byref STRING);
prototype UpdateKey(HWND, STRING, STRING, STRING);
prototype AddKey(HWND, STRING, STRING, STRING);
prototype UpdateCustomErrors(HWND, STRING, STRING);
// these help working with legacy (orphaned) installs
prototype STRING CheckRegistryForPath(STRING);
prototype STRING CheckRegistryForMSI(STRING);
prototype NUMBER RemoveLegacyInstall(STRING, STRING, STRING);
// these help working with Virtual Dirs
prototype NUMBER CheckIISExists(HWND);
prototype NUMBER DeleteVirtualDir(HWND, STRING);
prototype NUMBER CheckVirtualDirExists(HWND, STRING);
// these help with file manipulation
prototype NUMBER ClearReadOnly(HWND, STRING);
////////////////////////////////////////////////////////////////////////
// Function: ClearReadOnly
// Purpose: Clear the Read Only attribute on all files in or below the
// directory passed in
////////////////////////////////////////////////////////////////////////
function NUMBER ClearReadOnly(hmsi, svDirectory)
NUMBER nFilesResult;
STRING svMatchingFileName;
begin
nFilesResult = FindAllFiles(svDirectory, "*.*", svMatchingFileName, RESET);
while(nFilesResult = 0)
SetFileInfo(svMatchingFileName, FILE_ATTRIBUTE, FILE_ATTR_NORMAL, "");
nFilesResult = FindAllFiles(svDirectory,"*.*", svMatchingFileName, CONTINUE);
endwhile;
return nFilesResult;
end;
////////////////////////////////////////////////////////////////////////
// Function: ReadKey
// Purpose: Read specific keys in Web.Config, using FileGrep
////////////////////////////////////////////////////////////////////////
function NUMBER ReadKey(hmsi, svFilePath, svFindEntry, svReturnValue)
NUMBER nResult, nvLineNumber;
STRING WebConfigFile, svReturnLine;
begin
// build the path to the Web.Config
WebConfigFile = svFilePath ^ "web.config";
// search the Web.Config for the entry we specify
nResult = FileGrep( WebConfigFile, svFindEntry, svReturnLine, nvLineNumber, RESTART );
switch(nResult)
case 0:
// Since this line normally appears twice in our web.config file, we're
// checking for the existence of a 2nd match and reading it instead.
// If there is no 2nd match, go ahead and read the one we find.
if ( svFindEntry = "ConnectionString" ) then
nResult = FileGrep( WebConfigFile, " if ( nResult < 0 ) then
FileGrep( WebConfigFile, " endif;
endif;
svReturnValue = svReturnLine;
return 0;
case -2:
// File Not Found
MessageBox( "Web.Config file not found.", SEVERE );
return nResult;
case -4:
// EOF reached
MessageBox( svFindEntry + " key not found.", SEVERE );
return nResult;
default:
//unknown error
MessageBox( "An unknown error has occurred. The Web.Config file could NOT be read.", SEVERE );
return nResult;
endswitch;
end;
////////////////////////////////////////////////////////////////////////
// Function: AddKey
// Purpose: Add a key to the Web.Config, using FileGrep
////////////////////////////////////////////////////////////////////////
function AddKey(hmsi, svFilePath, svType, svNewEntry)
NUMBER nResult, nvLineNumber;
STRING WebConfigFile, svReturnLine;
begin
// build the path to the Web.Config
WebConfigFile = svFilePath ^ "web.config";
if svType = "VERB" then
// search the file for "" and insert ours immediately before it
nResult = FileGrep( WebConfigFile, "", svReturnLine, nvLineNumber, RESTART );
elseif svType = "KEY" then
// search the file for "" and insert ours immediately before it
nResult = FileGrep( WebConfigFile, "", svReturnLine, nvLineNumber, RESTART );
endif;
switch(nResult)
case 0:
// found it, now put our new key just before it
if ( FileInsertLine( WebConfigFile, svNewEntry, nvLineNumber, BEFORE ) < 0 ) then
// hmmm... we couldn't insert the new key
MessageBox( "Unable to update Web.Config file.", SEVERE );
endif;
case -2:
// File Not Found
MessageBox( "Web.Config file not found.", SEVERE );
case -4:
// EOF reached
MessageBox( "Relevant section not found, unable to insert new entry.", SEVERE );
default:
//unknown error
MessageBox( "An unknown error has occurred. The Web.Config file has NOT been updated.", SEVERE );
endswitch;
end;
////////////////////////////////////////////////////////////////////////
// Function: UpdateKey
// Purpose: Update specific keys in Web.Config, using FileGrep
////////////////////////////////////////////////////////////////////////
function UpdateKey(hmsi, svFilePath, svFindEntry, svNewEntry)
NUMBER nResult, nvLineNumber;
STRING WebConfigFile, svReturnLine;
begin
// build the path to the Web.Config
WebConfigFile = svFilePath ^ "web.config";
// search the file for the key we specify
nResult = FileGrep( WebConfigFile, " switch(nResult)
case 0:
// Since this line normally appears twice in our web.config file, we're
// checking for the existence of a 2nd match and updating it instead.
// If there is no 2nd match, go ahead and update the one we find.
if ( svFindEntry = "ConnectionString" ) then
nResult = FileGrep( WebConfigFile, " if ( nResult < 0 ) then
FileGrep( WebConfigFile, " endif;
endif;
// once we find the key, update (replace) it with our new values
if ( FileInsertLine( WebConfigFile, svNewEntry, nvLineNumber, REPLACE ) < 0 ) then
// hmmm... we couldn't update the value of the key
MessageBox( "Unable to update Web.Config file.", SEVERE );
endif;
case -2:
// File Not Found
MessageBox( "Web.Config file not found.", SEVERE );
case -4:
// EOF reached
MessageBox( svFindEntry + " key not found.", SEVERE );
default:
//unknown error
MessageBox( "An unknown error has occurred. The Web.Config file has NOT been updated.", SEVERE );
endswitch;
end;
////////////////////////////////////////////////////////////////////////
// Function: UpdateCustomErrors
// Purpose: Update customErrors in Web.Config, using FileGrep
////////////////////////////////////////////////////////////////////////
function UpdateCustomErrors(hmsi, svFilePath, svNewEntry)
NUMBER nResult, nvLineNumber;
STRING svResult, WebConfigFile, svReturnLine;
begin
//build the path to the Web.Config
WebConfigFile = svFilePath ^ "web.config";
// search the file for the key we specify
nResult = FileGrep( WebConfigFile, "", nvLineNumber, REPLACE ) < 0 ) then
// hmmm... we couldn't update it
MessageBox( "Unable to update Web.Config file.", SEVERE );
endif;
case -2:
// File Not Found
MessageBox( "Web.Config file not found.", SEVERE );
case -4:
// EOF reached
MessageBox( "customErrors setting not found.", SEVERE );
default:
//unknown error
MessageBox( "An unknown error has occurred. The Web.Config file has NOT been updated.", SEVERE );
endswitch;
end;
////////////////////////////////////////////////////////////////////////
// Function: CheckRegistryForPath
// Purpose: look in the HKEY_LOCAL_MACHINE\SOFTWARE\etc... for a
// key matching the GUID passed in as a parameter. Returns
// the location of the installation.
////////////////////////////////////////////////////////////////////////
function STRING CheckRegistryForPath(szProductGUID)
NUMBER nSize, nResult, nType;
STRING szKey, szProductName, szReturn, szInstallLocation;
begin
RegDBSetDefaultRoot ( HKEY_LOCAL_MACHINE );
szReturn = "";
szInstallLocation = "";
nSize = -1;
nType = REGDB_STRING;
szKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Products\\" + szProductGUID + "\\InstallProperties";
nResult = RegDBKeyExist (szKey);
if (nResult = 1) then
nResult = RegDBGetKeyValueEx (szKey, "InstallLocation", nType, szInstallLocation, nSize);
if (nResult = 0) then
szReturn = szInstallLocation;
endif;
endif;
RegDBSetDefaultRoot ( HKEY_CLASSES_ROOT );
return szReturn;
end;
////////////////////////////////////////////////////////////////////////
// Function: CheckRegistryForMSI
// Purpose: look in the HKEY_LOCAL_MACHINE\SOFTWARE\etc... for a
// key matching the GUID passed in as a parameter. Returns
// the location of the msi file.
////////////////////////////////////////////////////////////////////////
function STRING CheckRegistryForMSI(szProductGUID)
NUMBER nSize, nResult, nType;
STRING szKey, szProductName, szLocalPackage;
begin
RegDBSetDefaultRoot ( HKEY_LOCAL_MACHINE );
szLocalPackage = "";
nSize = 256;
nType = REGDB_STRING;
szKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Products\\" + szProductGUID + "\\InstallProperties";
nResult = RegDBKeyExist (szKey);
if (nResult = 1) then
nResult = RegDBGetKeyValueEx (szKey, "LocalPackage", nType, szLocalPackage, nSize);
if (nResult != 0) then
MessageBox("Unable to retrieve MSI location for " + szProductGUID, INFORMATION);
endif;
endif;
RegDBSetDefaultRoot ( HKEY_CLASSES_ROOT );
return szLocalPackage;
end;
////////////////////////////////////////////////////////////////////////
// Function: RemoveLegacyInstall
// Purpose: look in the HKEY_LOCAL_MACHINE\SOFTWARE\etc... for a
// key matching the GUID passed in as a parameter. If found
// delete the key. This should remove it from the
// Add/Remove Programs applet
////////////////////////////////////////////////////////////////////////
function NUMBER RemoveLegacyInstall(szUninstallGUID, szInstallerGUID, szMSILocation)
NUMBER nSize, nReturn1, nReturn2, nReturn3, nReturn4, nResult, nType, nLocation;
STRING szKey, szDir;
begin
RegDBSetDefaultRoot ( HKEY_LOCAL_MACHINE );
// remove the Uninstall entry (this should clear it out of Add/Remove Programs)
nReturn1 = FALSE;
nSize = -1;
nType = REGDB_STRING;
szKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + szUninstallGUID;
nResult = RegDBKeyExist (szKey);
if (nResult = 1) then
nResult = RegDBDeleteKey (szKey);
if (nResult = 0) then
nReturn1 = TRUE;
else
MessageBox("Unable to Remove Uninstall Key for " + szUninstallGUID + ".",INFORMATION);
endif;
else
MessageBox("Unable to Find Uninstall Key for " + szUninstallGUID + ".",INFORMATION);
endif;
// remove the Installer entry (this should keep it from being detected again)
nReturn2 = FALSE;
nSize = -1;
nType = REGDB_STRING;
szKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Products\\" + szInstallerGUID;
nResult = RegDBKeyExist (szKey);
if (nResult = 1) then
nResult = RegDBDeleteKey (szKey);
if (nResult = 0) then
nReturn2 = TRUE;
else
MessageBox("Unable to Remove Installer Key for " + szInstallerGUID + ".",INFORMATION);
endif;
else
MessageBox("Unable to Find Installer Key for " + szInstallerGUID + ".",INFORMATION);
endif;
// remove the key under HKLM\SOFTWARE\Classes\Installer\Products
nReturn3 = FALSE;
nSize = -1;
nType = REGDB_STRING;
szKey = "SOFTWARE\\Classes\\Installer\\Products\\" + szInstallerGUID;
nResult = RegDBKeyExist (szKey);
if (nResult = 1) then
nResult = RegDBDeleteKey (szKey);
if (nResult = 0) then
nReturn3 = TRUE;
else
MessageBox("Unable to Remove Classes\Installer Key for " + szInstallerGUID + ".",INFORMATION);
endif;
else
MessageBox("Unable to Find Classes\Installer Key for " + szInstallerGUID + ".",INFORMATION);
endif;
RegDBSetDefaultRoot ( HKEY_CLASSES_ROOT );
// remove the key (if any) under HKCR\Installer\Products
nSize = -1;
nType = REGDB_STRING;
szKey = "Installer\\Products\\" + szInstallerGUID;
nResult = RegDBKeyExist (szKey);
if (nResult = 1) then
nResult = RegDBDeleteKey (szKey);
endif;
// delete the appropriate MSI file since we don't need it anymore
nResult = DeleteFile(szMSILocation);
if (nResult = 0) then
nReturn4 = TRUE;
else
nReturn4 = FALSE;
MessageBox("Unable to delete MSI file for " + szMSILocation + ".",INFORMATION);
endif;
// only return TRUE if all results are TRUE
if ((nReturn1 = TRUE) && (nReturn2 = TRUE) && (nReturn3 = TRUE) && (nReturn4 = TRUE)) then
return TRUE;
else
return FALSE;
endif;
end;
////////////////////////////////////////////////////////////////////////
// Function: DeleteVirtualDir
// Purpose: Pass in the name of a virtual directory and this function
// will delete it and return TRUE. If it can't delete it, or
// the virtual directory isn't there, function returns FALSE
////////////////////////////////////////////////////////////////////////
function number DeleteVirtualDir(hmsi, sVDirName)
NUMBER nReturn;
OBJECT objIIS_Root;
begin
set objIIS_Root = CoGetObject("IIS://localhost/W3SVC/1/Root", "");
if (IsObject(objIIS_Root)) then
try
objIIS_Root.Delete("IISWebVirtualDir", sVDirName);
nReturn = TRUE;
catch
// VDir doesn't exist.
nReturn = FALSE;
endcatch;
else
// objIIS_Root has not been assigned a reference to a valid object.
nReturn = FALSE;
endif;
return nReturn;
end;
////////////////////////////////////////////////////////////////////////
// Function: CheckIISExists
// Purpose: Checks for the existence (and major version) of IIS
// Returns -1 if not found OR major version number if found
// Example: Server 2003 would return 6 if IIS is installed
////////////////////////////////////////////////////////////////////////
function number CheckIISExists(hmsi)
NUMBER nvType, nResult, nvIISVer, nvSize;
STRING szKey, szIISVersion;
begin
RegDBSetDefaultRoot ( HKEY_LOCAL_MACHINE );
nvIISVer = -1;
nvType = REGDB_NUMBER;
szKey = "\\SOFTWARE\\Microsoft\\InetStp";
nResult = RegDBKeyExist(szKey);
if (nResult = 1) then
nResult = RegDBGetKeyValueEx(szKey, "MajorVersion", nvType, szIISVersion, nvSize);
if (nResult = 0) && (szIISVersion != "") then
StrToNum(nvIISVer, szIISVersion);
endif;
endif;
RegDBSetDefaultRoot (HKEY_CLASSES_ROOT);
return nvIISVer;
end;
////////////////////////////////////////////////////////////////////////
// Function: CheckVirtualDirExists
// Purpose: Checks for the existence of a specific virtual directory
//
// NOTE: THIS FUNCTION DOES NOT WORK ON SERVER 2003. XP ONLY!!
// DeleteVirtualDir does not need this function to be called.
////////////////////////////////////////////////////////////////////////
function number CheckVirtualDirExists(hmsi, sVDirName)
NUMBER nType, nvSize, nReturn, nResult;
STRING svReturn, VDir;
begin
VDir = "/" + sVDirName;
nType = REGDB_STRING;
nvSize = MAX_PATH;
RegDBSetDefaultRoot ( HKEY_LOCAL_MACHINE );
nResult = RegDBGetKeyValueEx("\\SYSTEM\\CurrentControlSet\\Services\\W3SVC\\Parameters\\Virtual Roots\\", VDir, nType, svReturn, nvSize );
RegDBSetDefaultRoot ( HKEY_CLASSES_ROOT );
if (nResult < 0) then
// The Virtual Directory does not exist
nReturn = FALSE;
MessageBox("VirtualDir " + sVDirName + " not found, or does not exist.", INFORMATION);
else
// The Virtual Directory does exist
nReturn = TRUE;
MessageBox("VirtualDir " + sVDirName + " exists.", INFORMATION);
endif;
return nReturn;
end;
In addition to the handy stuff up above, here's another VERY useful script, although this one should go in your Setup.rul file:
function OnResumeUIBefore()
STRING szMsg;
NUMBER nResult;
begin
// change the interior-dialog banner
DialogSetInfo(DLG_INFO_ALTIMAGE, SUPPORTDIR ^ "newbanner.bmp", TRUE);
Dlg_Start:
nResult = SdWelcome( "Upgrading your product", "The InstallShield Wizard will now upgrade your existing your product installation." );
if (nResult = BACK) goto Dlg_Start;
Enable(STATUSEX);
end;
When you are working with an InstallScript MSI Project, you will find that Minor Upgrades are treated as Resumed Installations by the UI. The support community is loaded with people asking how to change the text of the Welcome dialog. Directly editing the string table doesn't seem to work either. If you manually add the above function, it will override the built in version of the same and you can control your “resume” behavior. This has been an ongoing problem since at least version 7 of IS. You won't find this anywhere in the help documentation either.