Blogus Maximus

Rubbing people the wrong way since 1970...

  Home  |   Contact  |   Syndication    |   Login
  1366 Posts | 10 Stories | 2225 Comments | 1336 Trackbacks

News


Google My Blog

Catch me at: The List!


My InstallScript Utility Belt My Amazon Wishlist
My Standard Disclaimer

Tag Cloud


Archives

Post Categories

Image Galleries

Blogs

Code Camps

CTown Geeks

Geeky Webcomics

High Geek

Magenic Blogs

Microsoft Blogs

My Articles

My Sites

PodCasts

UG

XNA

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.

posted on Thursday, March 31, 2005 12:15 PM

Feedback

# re: InstallShield Utility Belt 8/16/2007 10:26 AM Dave
The site is great!
Question: How do you assign a 'String table' value from a script function?

Post A Comment
Title:
Name:
Email:
Comment:
Verification: