November 21, 2010

How to create a Custom User friendly Log for InstallScript?

This article explains how to create your own logging for InstallScript / InstallScript MSI project or Custom Actions writted in InstallScript.

Use the following code-snippet to create Custom Logging for InstallScript

// Prototype Definition
prototype ISfn_CreateLogFile();
prototype BOOL ISfn_LogError(STRING, STRING, STRING);

// Global Declaration
STRING gLogFilePath,gLogFileName,sgLogfile, gsvProductName; // For Log file
BOOL IsLogCreated,IsLogWritten,IsTempLogPath; // For Log file

Add the following code in OnBegin event for InstallScript / InstallScript MSI project or just before invoking ISfn_LogError function.
// Read the ProductName to a variable
nvSize = 256;

//Create the Log File


How to invoke ISfn_LogError function for Custom Log?
For Example:
ISfn_LogError("Local system name", "From Wscript : '" + svLocalSystemName + "'" ,"Success")
- first parameter states that the particular step is perfomed while getting Local System Name,
- the second parameter logs the retrieved local system name, the third parameter
- the third parameter determines that the actions output is a Success
Function Definition:
Copy the following function definition to your .rul file:
// ISfn_CreateLogFile()
// Description: This function creates the installation log file in the format of
// PRODUCTNAME_Install_<InstallDate>__<InstallTime>.txt under
// Temp folder of the currently logged in user.

function ISfn_CreateLogFile()

STRING sInstallerLogFilePath,sProdName;
STRING sSysDate,sSysTime,sHhr,sMin,sSec,svString,sLogFileName;
NUMBER nvResult,nPos,nHhrLen,nMinLen;
STRING szErrMsg;
NUMBER nvFileHandle;

sInstallerLogFilePath = TempFolder;
if (ExistsDir(sInstallerLogFilePath)=0) then
         gLogFilePath = sInstallerLogFilePath;
         sInstallerLogFilePath = "";
         gLogFilePath = sInstallerLogFilePath + "\\";
         IsTempLogPath = TRUE;

// Creating the Log File Name
sProdName = gsvProductName;
//Frame the Log File Name
// Get the current date.
GetSystemInfo (DATE, nvResult, sSysDate);
// Get the current time.
GetSystemInfo (TIME, nvResult, sSysTime);
nPos = StrFind(sSysTime,":");
//Get the Hour part from the Date
StrSub ( sHhr, sSysTime, 0, nPos );
nHhrLen = StrLength(sHhr);
nHhrLen = nHhrLen + 1;
//Get the Minutes part from the Date
StrSub ( sMin, sSysTime, nHhrLen, 2 );
nMinLen = StrLength(sMin);
nMinLen = nHhrLen + nMinLen + 1;
//Get the Seconds part from the Date
StrSub ( sSec, sSysTime, nMinLen, 2 );
sSysTime = sHhr + "_" + sMin + "_" + sSec;
//Frame the final Log File name
if (!MAINTENANCE) then
          sLogFileName = sProdName + "_Install_" + sSysDate + "__" + sSysTime +".txt";
          sLogFileName = sProdName + "_UnInstall_" + sSysDate + "__" + sSysTime +".txt";
gLogFileName = sLogFileName;
// Set the file mode to append.
// Create a new file and leave it open.
if (CreateFile (nvFileHandle, gLogFilePath, sLogFileName) != 0) then
        // Report the error.
       MessageBox ("Error log file creation failed. Installation continues...", INFORMATION);
       IsLogCreated = FALSE;
       IsLogCreated = TRUE;endif;
// Set the message to write to the file.
// Append the message to the file.
// Write the Header Informations into the file
szErrMsg = "*******************************************************************************************************************";

if (WriteLine(nvFileHandle, szErrMsg) != 0) then
       // Report the error.
       MessageBox ("Errors couldnot be logged into the log file.     Installation continues...", INFORMATION);
       IsLogWritten = FALSE;
        IsLogWritten = TRUE;
        sgLogfile = gLogFilePath ^ sLogFileName;

if (IsLogWritten = TRUE)then
szErrMsg = "";
if (!MAINTENANCE) then
        szErrMsg = " "+ gsvProductName +" Installation Log ";
        szErrMsg = " "+ gsvProductName +" UnInstallation Log ";
WriteLine(nvFileHandle, szErrMsg);
szErrMsg = "";
szErrMsg = " Created on " + sSysDate + "__" + sSysTime + " ";
WriteLine(nvFileHandle, szErrMsg);
szErrMsg = "";
szErrMsg = "*******************************************************************************************************************";
WriteLine(nvFileHandle, szErrMsg);
szErrMsg = "";

// Close the file.
CloseFile (nvFileHandle);
end; // end of ISfn_CreateLogFile

// ISfn_LogError()
// Description: This function logs the installation actions in the Installation
// log file which was created using ISfn_CreateLogFile() function
// Input Parameters:
// ActionRequested: Action name/detail that is related to that particular action
// Parameters: Data that are passed to that particular function / action
// Status: Determines the output of that particular action(Information/Success/Error)
function BOOL ISfn_LogError(ActionRequested, Parameters, Status)

NUMBER nvFileHandle,nvResult;
STRING szErrMsg,sSysDate,sSysTime;
end; // end of ISfn_LogError
if (IsLogCreated=TRUE && IsLogWritten = TRUE) then
GetSystemInfo (DATE, nvResult, sSysDate);
GetSystemInfo (TIME, nvResult, sSysTime);
szErrMsg = "";
if Status = "Error" then
szErrMsg = "[" + sSysDate + "," + sSysTime + "] ERROR " + ActionRequested + " :: " + Parameters + " :: " + Status;
szErrMsg = "[" + sSysDate + "," + sSysTime + "] " + ActionRequested + " :: " + Parameters + " :: " + Status;

if (OpenFile (nvFileHandle, gLogFilePath, gLogFileName) = 0) then
WriteLine(nvFileHandle, szErrMsg);
CloseFile (nvFileHandle);
szErrMsg = "";

August 10, 2010

Adding a VBScript Custom Action to Installshield Merge Module

This article explains about adding a VBScript Custom Action to the Merge Module created using InstallShield 2010 SP1 and using the same in another InstallShield Project (Say Basic MSI Project).

Create a VBScript Custom Action (stored in binary table) in traditional way as you do in your Basic MSI or InstallScript MSI project. But the options to specify the Install UI / Execute Sequence and Conditions are not directly available as Custom Action properties in merge module project

So the conditions can be added to a custom action in a merge module by inserting the custom action into the sequence and modifying the condition column of the ModuleInstallUISequence or ModuleInstallExecuteSequence table. The following knowledge base article explains how to Insert a Custom Action into a Merge Module:

If you want the custom action to run only when a specific patch is being applied then you would possible use the PATCH property and the specific property name for the patch as part of the condition.
This is documented in the following msdn article:

To run the custom action during the uninstallation of a patch, you can set the "Run during Uninstall of Patch" property to yes. This can be changed under the custom action view and is described in the following helpnet article:

May 25, 2010

Where is the ICON for Add/Remove shortcut is stored?

Most of the product owners prefers to use Custom / Product specific icons for Add / Remove Programs (ARP) entry instead of using the default InstallShield or Windows Installer icon. Though we have an option to specify the ARP icon while creating the installer, we always wonder where this icon is stored after installation. This article details about that.

Once the product is installed, the following registry key is created.
  • HKEY_CLASSES_ROOT\Installer\Products\0CBC24CEC944140453282975376B5482   
ProductIcon - C:\WINDOWS\Installer\{EC42CBC0-449C-4041-3582-925773B64528}\ARPPRODUCTICON.exe
  • The Custom ARP icon is stored in the form of ARPPRODUCTION.exe under C:\WINDOWS\Installer\<ProductGUID>. This is just a few kb file which has the Custom icon set for it. Please note that C:\WINDOWS\Installer is a hidden folder.
  • Now you will wonder where does the key - 0CBC24CEC944140453282975376B5482 comes from. Being the developer you can easily conclude that this code is neither a Product Code nor a Package Code nor an Upgrade Code. There is a small trick involved in forming this code from Product Code and here is the steps to arrive the same
    • Remove the curly braces and write the Product Code
    • Reverse each piece of the Product Code
    • Remove the spaces and -'s between the above code and you will get
      0CBC24CEC944140453282975376B5482This is used under HKEY_CLASSES_ROOT\Installer\Products.

April 28, 2010

InstallScript - Reversing Input String

Here is the InstallScript function to reverse the input string and return the reversed string.


// StrReverse()
// Description: This function reverses the input string
function STRING StrReverse(svStr)
         string szReversedString;
         number nLen, nCnt;
         nLen = StrLength(svStr) - 1;
         for nCnt = 0 to nLen
                szReversedString[nCnt] = svStr[nLen - nCnt];
         return szReversedString;
end; // End of StrReverse

April 13, 2010

Deploying 32-bit Applications on 64-bit that requires 32-bit ASP.NET

This article focuses on how to deploy 32-bit .Net applications that are customized to work on 64-bit machine (NOT applicable for Native 64-bit application / Installer).

A 32-bit .Net Application would need 32-bit version ASP.NET 2.0 as a prerequisite and hence it might look for WOW64 registry entry (‘RootVer’ under HKLM\SOFTWARE\WOW6432node\Microsoft\ASP.NET)

• On Windows 2008 R2 64-bit machine, by default both 32-bit & 64-bit ASP.NET are registered and hence the application / installer that is looking for 32-bit registry path will work by default.

• On Windows 2003 64-bit machine, by default only 64-bit ASP.NET is registered and you will have to follow the Microsoft KB, to register 32-bit version of ASP.NET 2.0.

You will have to re-run the 32-bit installer after completing the steps in the Microsoft KB. We have tested the same on a Windows 2003 64-bit machine.

[Note: Though the MS KB talks about switching between the 32-bit versions of ASP.NET 1.1 and the 64-bit version of ASP.NET 2.0 on a 64-bit version of Windows, its applicable for 32-bit versions of ASP.NET 2.0 and 64-bit version of ASP.NET 2.0.]

Please refer here for more details on 32-bit version ASP.NET registration.

Recommended Reading:
How to switch between the 32-bit versions of ASP.NET 1.1 and the 64-bit version of ASP.NET 2.0 on a 64-bit version of Windows?

March 31, 2010

Creating New Custom Actions! Some tips to define Custom Error Messages...

Windows Installer has a long list of predefined error messages and there are many scenarios in which we would need to define Custom error messages for Custom Actions.

Windows Installer suggests the following Tips while creating Custom Error Messages.

  • The error number must be a non-negative integer.
  • The range from 25000 to 30000 is reserved for errors from custom actions. Authors of custom actions may use this range for their custom actions.
However InstallShield users have to be extra carefull to exclude the error codes ranging from 27500 to 27554, which is already being used by recent versions of InstallShield (verified in IS 12, 2009 & 2010) for IIS, Database, COM+, XML etc., related functionality.

March 10, 2010

How to register a .Net assembly into GAC using VBScript CA?

This article focuses on registering .Net assembly into GAC using VBScript Custom Action. We generally use the two approaches detailed here to register .Net Assembly into GAC. In case, if both the approach does not seem to work for your .Net DLL and you observe the following, then VBScript CA detailed below can be used to register the assembly.
  • When building the InstallShield project with .Net COM Interop property set to 'YES' (along with Destination Path to [GlobalAssemblyCache]), if you get the error =>
    Error -6210: An error occurred building COM .NET Interop information for Component [1].
    [1] indicates the part of your installation that is causing this error.
  • When .Net COM Interop property is set to 'NO', building the InstallShield project would succeed but running the installation does not register the .Net assembly into GAC.
Steps to Register .Net Assembly into GAC:
• Add the .Net Assembly to be registered under Support directory.
• Add a VBScript Custom Action (stored in binary table - MSI Type Number : 3078)– RegisterAsmToGAC with the below mentioned code snippet.

Function RegisterAsmToGAC
on error resume next
' Declaration
Dim szCommand, szCommand1, szCommand2, szCommand3
Dim PropArray, szSupportDir, sProductName,sAssemblyName
' Reading & Parsing the property values in Deferred CA
PropArray = Split(Session.Property("CustomActionData"), ";")
szSupportDir = PropArray(0)
sProductName = PropArray(1)
sAssemblyName = PropArray(2)
'Display Debug Message
'MsgBox szSupportDir, ,sProductName

' Using GACInstaller.exe utility downladed from
szCommand1 = Chr(34) & szSupportDir & "\GacInstaller.exe" & Chr(34)
szCommand2 = " i "
szCommand3 = Chr(34) & szSupportDir & sAssemblyName & Chr(34)
szCommand = szCommand1 & szCommand2 & szCommand3
'MsgBox szCommand, ,sProductName
Set wshShell = WScript.CreateObject ("WScript.Shell")
rc = wshshell.Run(szCommand, 0, True)
if rc <> 0 then
       MsgBox "Error Registering the Assembly", ,sProductName
end if

if Err.Number <> 0 then
       MsgBox Err.Number & " - " & Err.Description, ,sProductName
       RegisterAssemblyGAC = IDABORT
End If
End Function

• The RegisterAsmToGAC CA uses 3 MSI Properties (say SUPPORTDIR, ProductName and .Net Assembly to be  registered). By default the MSI properties will not be available to the Custom actions that are in Defered mode.
• To pass parameter to Deferred CA, create a Set Property CA (MSI Type Number: 51) as 'SetRegisterAsmToGAC', set the property name as RegisterAsmToGAC and set the semicolon separated property names to the property value as [SUPPORTDIR];[ProductName];[AssemblyNameToBeRegistered].
• Schedule the SetRegisterAsmToGAC as a Immediate CA after RegisterProduct.
• Schedule the RegisterAssemblyGAC as a Deferred in System Context after SetRegisterAsmToGAC.

February 25, 2010

How to Register a .Net assembly into GAC using WiX?

Ealier we have seen some articles to register a .Net assembly into GAC using either gacutil.exe or RegAsm.exe. In this article we will see the code snippet to register the assembly into GAC using WiX.

<Directory Id="ProgramFilesFolder">
<Directory Id="ProductDirectory" Name="$(var.ProductName)">
      <Directory Id="GAC" Name="GAC">
          <Component Id="MyGACControl" Guid="55857611-A13E-51ED-897B-A78830F68ADC" DiskId="1">
                <!-- Registering assembly in GAC -->
               <File Id="F_MyGACControl" Name="MyGACCtrl.dll" LongName="MyGACControl.dll" Source="$(var.SrcPath)

               \MyGACControl.dll" KeyPath="yes" Assembly=".net"/>

The highlighted text - Name="GAC" Assembly=".Net", in the above code drives the .Net assembly registration into GAC.

Read the following related articles:
  1. How to register a .Net assembly into GAC in a development environment?
  2. How to Register .Net assembly into GAC using InstallShield 2010?

February 16, 2010

How do you check whether the Patch is Installed already?

This article details on the approach to detect whether the patch is applied or not and prompt the user accordingly. This post applies to Basic MSI & InstallScript MSI Projects and is tested in InstallShield 2008, 2009 & 2010 with SP1. Refer here for the steps to create a Patch using InstallShiled.

• Add a VBScript Custom Action (stored in binary table)– CheckIfPatchInstalled
''' Function: CheckIfPatchInstalled
''' Purpose: Checks to see if a patch is installed and sets the value of the property
''' 'PATCHINSTALLED' to "true". This property value is used by the Custom action
''' 'CheckIfPatchInstalledError' to display an error.
Function IsPatchInstalled()
Dim sProductCode, sProductVersionHF, sProductVersionCurrent

             On Error Resume Next
              sProductCode = Session.Property("ProductCode")
sProductVersionCurrent = Session.Installer.ProductInfo(sProductCode, "VersionString")
sProductVersionBase = Session.Property("ProductVersion")
If (sProductVersionCurrent = sProductVersionBase) Then
     Session.Property("PATCHINSTALLED") = "true"
End If

End Function

• Schedule the CheckIfPatchInstalled as Immediate CA after ISSetupFilesExtract in both Install UI Sequence and Install Exec Sequence with the condition PATCH.
• Add a property PATCHINSTALLED in property manager and leave it empty.
• Add an Error Custom Action as Immediate CA after CheckIfPatchInstalled in both Install UI Sequence and Install Exec Sequence with the condition PATCH AND PATCHINSTALLED.
• Add the appropriate error message say "An existing upgrade is already installed. Please remove it before installing this upgrade." for the Error CA Or Add an entry (Error Number & Message) to the Error Table in the direct editor and use the Error Number in the CA

Create the Patch and apply the patch where the main installer is installed. Running the patch again on the same machine should display the message "An existing upgrade is already installed. Please remove it before installing this upgrade." abort the installation.

January 29, 2010

Windows Installer 4.5 Support - InstallShield 2010 SP1

This post focuses on modifying the installer for Windows Installer (MSI) 4.5 Support for Basic MSI and InstallShield MSI Projects.

  • Open the existing InstallShield project in InstallShield 2010 SP1 IDE.
  • Modify the  Schema under General Information - Summary Information Stream to specify the minimum Windows Installer version that is required for your Installation package. Say 405 to support Windows Installer 4.5
  • If the selected minimun Windows Installer version is not available on the target system, then the installer will prompt a message and abort the installation.
  • Also, you can select the Windows Installer 4.5 pre-requisite for the supported operating system from Application Data - Redistributable.
    For Pre-requisite integration, you can choose either of the following 3 options based on various factors
Extract From Setup.exe - The pre-requisites will be bundled with the installer. This is applicable if the Installation Package size is not a constraint / if you want to distribute the installation package as a single compressed file.
Copy From Source Media - The pre-requisite will be stored in a folder 'ISSetupPrerequisites' parallel to the installer.
Download from the Web - At the time of installation, the pre-requisite will be downloaded on the fly from the web and installed. Internet connection is required during installation.
  • If you have not downloaded the pre-requisites already to your installer development machine, right click the Pre-req and click "Dowload the selected Item".
Refer here for dependency errors and solution related to Windows Installer 4.5 Integration.

January 13, 2010

Did you get these MSI 4.5 PreReq Integration Errors in InstallShield 2010 SP1

This post focuses on the compilation error's, - 1007 and 5054, when the pre-requisites that has other dependency is include for the installer. This article is validated against InstallShield 2010 SP1

For a requirement to support Windows Installer 4.5, for both Windows XP SP3 and Windows Server 2003 SP2 Operating System, the following Pre-Requisites were selected from Application Data -> Reditributables.
  • Windows Installer 4.5 Update for Windows Server 2003 SP2 and later (x86)
  • Windows Installer 4.5 Update for Windows XP SP3 and later (x86)
and during compilation (either from IDE or Stand Alone Builder), the following error messages were displayed.
  1. ISDEV : error -1007: Cannot copy source '<ISPRODUCT>\SetupPrerequisites\Windows Installer\4.5\x86\Pre-Vista\WindowsServer2003-KB942288-v4-x86.exe' to target '<ISPROJECT>\Product Configuration 1\Release\DiskImages\DISK1\ISSetupPrerequisites\{a0f7f2eb-ed14-4351-a30e-ef802db4b38f}\WindowsServer2003-KB942288-v4-x86.exe'
  2. ISDEV : error -5054: Could not determine the size of the file "<ISPROJECT>\Product Configuration 1\Release 1\DiskImages\DISK1\ISSetupPrerequisites\{a0f7f2eb-ed14-4351-a30e-ef802db4b38f}\WindowsServer2003-KB942288-v4-x86.exe"
  3. ISDEV : error -1007: Cannot copy source '<ISPRODUCT>\SetupPrerequisites\Windows Installer\4.5\x86\Pre-Vista\WindowsXP-KB942288-v3-x86.exe' to target '<ISPROJECT>\Product Configuration 1\Release 1\DiskImages\DISK1\ISSetupPrerequisites\{22aa129a-8e5d-45ae-a3e4-d110703ef141}\WindowsXP-KB942288-v3-x86.exe'
  4. ISDEV : error -5054: Could not determine the size of the file "<ISPROJECT>\Product Configuration 1\Release 1\DiskImages\DISK1\ISSetupPrerequisites\{22aa129a-8e5d-45ae-a3e4-d110703ef141}\WindowsXP-KB942288-v3-x86.exe"
 The reason for the error is, the two selected pre-requisites has dependency on the other pre-requisites -
Windows Installer 4.5 Update for Windows Server 2003 SP1 and later (x86)
Windows Installer 4.5 Update for Windows XP SP2 and later (x86)
Selecting these pre-requisites did help to go past the errors.

Hint :- For the compilation -1007 & -5054, open the selected pre-requisite in edit mode to view the dependencies, if any, then include the dependency file too.