November 28, 2009

How to register a .Net assembly into GAC in a development environment

This article focuses on ways to register .Net assembly into GAC in a development / build environment and NOT applicable for deployment hence should NOT be bundled with the installer.
1. Using Gobal Assembly Cache Tool (gacutil.exe)
  • gacutil allows you to view and manipulate Global Assembly Cache.
  • GACUTIL doesn't come with the .NET Runtime, but with the .NET SDK - also a free download, but not part of the basic redistributable. So it should NOT be used for deployment.
  • Regiser -> Use the property variable to invoke gacutil.exe
    Syntax - gacutil.exe -i
  • UnRegister -> Use the property variable to invoke gacutil.exe
    Syntax - gacutil -u
2. You can also install your assembly into GAC, just by copy it into C:\WINDOWS\Assembly directory in explorer.
3. You can also use System.EnterpriseServices.Internal.Publish.GacInstall to register assembly into GAC.

Click Here to read more on how to register a .Net assembly into GAC using InstallShield or RegAsm.exe

November 27, 2009

How to write into MSI Log File from Custom Actions using Installshield

Writing into MSI Log file from Custom Actions

InstallShield, by default, handles logging the start / end & success or failure of the Custom Action into MSI Log and here is the code snippet, from an InstallShield document, for writing detailed information into MSI Log file from the Custom Actions. The custom action can be written in InstallScript, VBScript or DLL.
InstallScript Sample
In an InstallScript custom action, you can call SprintfMsiLog to write a string to the MSI log file. For example, the following InstallScript code prototypes and defines an InstallScript custom action called LoggingTestInstallScript.

#include "ifx.h"
// standard custom action prototype
export prototype LoggingTestInstallScript(HWND);
// custom action definition
function LoggingTestInstallScript(hInstall)
begin
     SprintfMsiLog("Calling LoggingTestInstallScript...");
     // return success to MSI
     return ERROR_SUCCESS;
end;
In order to be called, the custom action must be scheduled in the sequences. For this example, open the Custom Actions view and create an immediate-mode InstallScript custom action called callLoggingTest that calls the LoggingTestInstallScript function, and schedule it to run after LaunchConditions.

Click here for the full article from InstallShield which details on writing to MSI Log file from VBScript as well as DLL Custom Actions.

November 26, 2009

Windows Installer Overview

Windows Installer Service:
Windows Installer Service helps to install, modify and remove applications provided as MSI Package. The first Windows Installer version was 2.0 and it was released with Windows XP. The latest one is Windows Installer 5.0 released with Windows Server 2008 R2 & Windows 7.
Installation Mechanism
There are two phases to a successful installation process: acquisition and execution. If the installation is unsuccessful, a rollback phase may occur.
  • Acquisition: At the beginning of the acquisition phase, an application or a user instructs the installer to install a feature or an application. The installer then progresses through the actions specified in the sequence tables of the installation database. These actions query the installation database and generate a script that gives a step-by-step procedure for performing the installation. This is user-interface part of the installation
  • Execution: During the execution phase, the installer passes the information to a process with elevated privileges and runs the script. This is the execution part of the installation which actually modifies the system.
Features of Windows Installer:

  • Rollback: WIS includes the computers’ pre-installation state in its database, so it can support complete installation rollbacks in the case of a problem during installation, returning the target system to the same state it was in before the installation began.
  • Repair: WIS stores the software configuration in its database and hence it can automatically repair an installation, when a problem occurs with the program. This repair mode can be run through a maintenance mode, but it is also automatically run each time a user launches a program through its shortcuts or through opening of a document generated by the program.
  • Elevation: In secured environments like Vista / Windows 7, the WIS, which is always running in the background, can automatically elevate a user’s privilege during installation to insure that the installation will occur properly in locked-down environments.
  • Resiliency: Resiliency is a program's ability to recover gracefully from situations in which a vital component is missing. By authoring an installation package and by using the Installer functions, developers can use Windows Installer to produce resilient programs that can recover from such situations.
    Resiliency can be turned ON or OFF for a file / registry key by specifying / removing the KeyPath value for each component.
    Click here for more information on resiliency.
  • Advertise: Advertised application can create shortcuts, registry keys, file association etc., without actually copying files to the target computer. The product will be installed by triggering the entry point (say by means of a Start menu shortcut, by opening a document that the product is configured to handle, or by invoking an advertised COM class). When advertised, the application will be installed as explained in Install on demand.
    Two types of advertising viz- Assigning & Publishing
    For assigned application, the user has an entry point like shortcut to activate the application on-demand.
    For published application, the user does not have an entry point to activate installation-on-demand and the application can be installed only if another application activates the published application.
  • Install on demand: Installation-on-demand makes it possible to offer functionality to users and applications in the absence of the files themselves. This notion is known as advertisement. The Windows Installer has the capability of advertising functionality and to make installation-on-demand of application features or entire products possible. When a user or application activates an advertised feature or product, the installer proceeds with installation of the needed components. This shortens the configuration process because additional functionality can be accessed without having to exit and rerun another setup procedure.
  • Remove / Retire: WIS can completely remove the application from system, as all the configurations are tracked in its database
Detect Windows Installer Version Installed:
There are three different ways in which you can detect the version of Windows Installer installed on your system.

  1. Using MSI.DLL
    Search MSI.DLL in Windows Folder, Locate the file, RightClick –> Properties -> Version Tab -> Product Version. 
  2. Getting Help on MSIEXEC
    Type msiexec /? Or msiexec /help on command prompt
  3. Scripting
    Create a VBS File – DisplayMSIVersion.vbs and type in the following code
    set installer = createobject("windowsinstaller.installer")
    msgbox "MSI Version - " & installer.version
    Save & Run to see the MSI Version
Windows Installer Architecture:


MSI consists of Installation instructions and Software parts to be installed i.e. usually .cab files within or along with MSI
MSI contains an installation database (relational) that WIS uses to perform installation
Product
  • highest level in hierarchy
  • Identified by Product code or Product GUID
Feature
  • Basic building block from end user’s perspective and it can have sub-features too.
  • Each item that can be selected or unselected for installation is a feature or sub-feature.
  • Features can be shared across applications.
  • WIS does not remove the shared feature until all the application using the shared feature is removed.
  • Example: Spell checker in Office, which is shared across Excel, Word Applications.
Component
  • Features are made up of components and it the basic building block from developer’s perspective.
  • Component has a unique GUID, which WIS uses to identify it within the installation database.
  • A component is a collection of files, registry keys, shortcuts, create / manage NT Services which are generally called as resources.
  • Component Rules: Component rules ensure that the authors of installation packages create packages that do not damage the components of other installations or leave resources after uninstall.
    • All files in a given component must be installed to the same directory. Conversely, files installed to separate folders must belong to separate components.
    • There can be only one key path per component
    • Any two resources that might ship separately in subsequent versions should exist in separate components. Resources should be grouped into the same component only when you are certain that these resources will never ship separately. Versioned resource should not be shipped in more than one component.
    • All primary resources (DLLs, EXEs, for example) always exist in separate.
    • Same file cannot be the key file in two different components which will go into a common feature.
Key Paths:
  • A key path is a specific file, registry key, or ODBC data source that the package author specifies as critical for a given component. Because a file is the most common type of key path, the term key file is commonly used.
  • A component can contain only one key path; if a component has no explicit key path, the component's destination directory is taken to be the key path. 
  • When an MSI-based application is launched, Windows Installer checks the existence of these critical files or registry keys (that is, the key paths). If there is a mismatch between the current system state and the value specified in the MSI package (e.g., a key file is missing), then the related feature is re-installed. This process is also known as self-healing or self-repair. 
  • No two components should use the same key path.

November 18, 2009

Reading ComputerName - VBScript & InstallScript Samples

Often we have a requirement to read the target computer name to update INI / config file etc., and this article discuss the different ways to read the Computer Name in Script, which can be used within any installer.
VBScript
 Function GetCompName
          Set objNetwork = CreateObject("WScript.Network")
          ComputerName = objNetwork.ComputerName
          GetCompName = ComputerName
 End Function

InstallScript
function STRING  GetCompName()
       STRING szKey, szName, svValue;
       NUMBER nvSize, nvType;
begin
      szKey = "System\\CurrentControlSet\\Control\\ComputerName\\ComputerName";
      szName = "ComputerName";
      // Set the default root.
      RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
      // Retrieve the registry key value.
      RegDBGetKeyValueEx(szKey, szName, nvType, svValue, nvSize);
   return svValue;
end;

November 17, 2009

How to configure user & local group on target computer using InstallShield

Net.exe is a process, which belongs Windows OS can be used to create / delete users, local groups and add domain users to local group

  • Checking the user in existing domain
           net user <user>/<domain>
  • Add / delete Users to existing local group
           net localgroup <localgroup> <domain>\<user> /add
  • Create / delete a local user
           net user<user> <pass> /add
           net user <user> / delete
  • Create / delete a local group
           net localgroup <localgroup Name> /add /comment:”….”
           net localgroup <localgroup Name> /delete
The following functions are InstallScript implementation to create / delete the specified Local group which can be directly used in InstallScript / InstallScript MSI Projects. The same can be written as Custom Action for Basic MSI Projects
These functions are compatible IS 12.0 & above, further it might work on previous versions of InstallShield too.

Function Declaration:
          Prototype CreateLocalGroups(STRING);
          Prototype DeleteLocalGroups(STRING);

Function Definition:
//---------------------------------------------------------------------------
//CreateLocalGroups()
// Description: This function creates the specified local group
//INPUT:
//               szLocalGroupName – Local group to be created
// OUTPUT:
//               N/A - No return value
//---------------------------------------------------------------------------
function CreateLocalGroups(szLocalGroupName)
       STRING szProgram, szCmdLine, svDummy, svBatchFilePath, svBatchFileName;
       NUMBER nResult, nvResult, nvFileHandle;
begin
        svBatchFilePath = SUPPORTDIR;
        svBatchFileName = "createlocalgroups.bat";
       // Set the file mode to append.
       OpenFileMode (FILE_MODE_APPEND);
       // Create a new file and leave it open.
       if (CreateFile (nvFileHandle, svBatchFilePath, svBatchFileName) != 0) then
                   // Report the error.
       else
                  // Batch file create successfully
                 szProgram = "net.exe";
                 szCmdLine = "localgroup “ + szLocalGroupName + “ /add /comment:\"Members in this group are granted the administration rights\">createlocalgroups.log";
                 WriteLine(nvFileHandle, szProgram + " " + szCmdLine);
      endif;
      CloseFile ( nvFileHandle );
      ChangeDirectory(SUPPORTDIR);
      nResult = LaunchAppAndWait(svBatchFilePath ^ svBatchFileName,"",LAAW_OPTION_WAIT
LAAW_OPTION_HIDDEN);

      if (nResult = 0) then
                GetFileInfo ( SUPPORTDIR ^ "createlocalgroups.log" , FILE_SIZE , nvResult , svDummy );
                if (nvResult = 0) then
                          //Local Groups create successfully
               else
                          MessageBox ("Failed to create Local Groups. Installation Continues..", INFORMATION);
               endif;
      else
               MessageBox ("Failed to create Local Groups. Installation Continues..", INFORMATION);
      endif;
end; // End of Createlocalgroups


//---------------------------------------------------------------------------
//DeleteLocalGroups()
// Description: This function deletes the specified local group
//INPUT:
//           szLocalGroupName – Local group to be deleted
// OUTPUT:
//           N/A - No return value
//---------------------------------------------------------------------------
function DeleteLocalGroups(szLocalGroupName)
              STRING szProgram, szCmdLine, svDummy, svBatchFilePath, svBatchFileName;
              NUMBER nResult, nvResult, nvFileHandle;
begin
             svBatchFilePath = SUPPORTDIR;
             svBatchFileName = "deletelocalgroups.bat";
             // Set the file mode to append.
             OpenFileMode (FILE_MODE_APPEND);
             // Create a new file and leave it open.
             if (CreateFile (nvFileHandle, svBatchFilePath, svBatchFileName) != 0) then
                        // Report the error.
             else
                       // Batch file create successfully
                       szProgram = "net.exe";
                       szCmdLine = "localgroup “ + szLocalGroupName + “ /delete\">deletelocalgroups.log";
                       WriteLine(nvFileHandle, szProgram + " " + szCmdLine);
             endif;
             CloseFile ( nvFileHandle );
             ChangeDirectory(SUPPORTDIR);
             nResult = LaunchAppAndWait(svBatchFilePath ^ svBatchFileName,"",LAAW_OPTION_WAIT
LAAW_OPTION_HIDDEN);
             if (nResult = 0) then
                         GetFileInfo ( SUPPORTDIR ^ "deletelocalgroups.log" , FILE_SIZE , nvResult , svDummy );
                         if (nvResult = 0) then

                                       //Local Groups deleted successfully
                         else
                                        MessageBox ("Failed to delete Local Groups. Installation Continues..", INFORMATION);
                         endif;
             else
                          MessageBox ("Failed to delete Local Groups. Installation Continues..", INFORMATION);
             endif;
end; // End of Deletelocalgroups

November 16, 2009

Issue in Minor Upgrade - Files not copied or overwritten

The Requirement for minor upgrade and the steps to create it in InstallShield was discussed already in the previous article. As a continuation to that we will discuss the most common issue with Minor Upgrade and the solutions for the same in this article.

Issue in Minor Upgrade - Files not copied / overwritten during upgrade
If the files are bundled with the installer by linking the folders dynamically with include subfolders enabled, then upgrade completes without any errors but further exploring would reveal that none of the folders / files were copied / replace during upgrade mode.

A closer look at Community discussions and Knowledge base in Macrovision / Acresso provides enough information to conclude that this issue persists in InstallShield Developer 7.04, 8.0, 11.0 & 12.0.

[Note: In fact the problem may or may not exist with other versions of InstallShield but I didn’t have an opportunity to check them.]

Explanation:
In your previous release (say for eg. v1.0),
    a) if the same component was already existing.
    b) if that component was set to pick files using "Dynamic File Linking" option with "Include all subfolders" option checked.
    c) if that component was really had at least one subfolder in it

If all of the above are TRUE in your previous release that you are trying to minor upgrade, then the files in this component will not be upgraded during upgrade.

Solution 1:
This solution is applicable for the setup authors, who have release the base version and working on the minor upgrade:
Base Installer: Assume the base installer is released with “Dynamic File Linking” and the “Include all subfolders” option is checked.
Minor Upgrade: Integrate each folder (main folder / subfolder) with dynamic linking option but UNCHECK “Include all subfolders”.

Example:
  • Base release had F1 (feature) -> C1 (component) -> 20 files, Config & Help sub folders.
  • During minor upgrade, because of subfolders (Config & Help) in dynamic link, modified files may not be copied to the target machine.
  • In IDE, I just renamed the F1 to "F15" and also renamed the component from "C1" to "C15" and then set to include *only* the files dynamically and unchecked the "Include all Subfolders" option for not to include subfolders.
  • For the each of other subfolders "Config" and "Help", I created a separate component and set to include *only* the files dynamically.
  • Modified the component settings in the old Feature "F1" to not to include subfolders and included just files for "C1". I did not try to remove this component. I am not sure that would that give me an error or not. You can try modifying this first and then try removing it forever.
  • To the build command-line, you *must* specify REINSTALLMODE=voums REINSTALL=WebFiles ADDLOCAL=WebFiles15
    FYI:
    REINSTALLMODE - please refer HELP; there is a big explanation available.
    REINSTALL takes the name of the Features (separate multiple features by comma) that you had in the previous release and wanted to upgrade now.
    ADDLOCAL take the name of the Features (separate multiple features by comma) that are newly added in this release.
Limitation:
      The above solution would be very tedious if the number of folders and subfolders are more. Click here for the related Installshield community link.

Solution 2:
The solution is applicable to the Setup Authors, who are in design phase of Base Installer and foresee a minor upgrade for the same.
Base Installer: Include one of the files as a static file (instead of as a dynamically linked file), set it to be the key file of the component C1, and select the Always Overwrite check box in that file's properties. Then, for the dynamic file link for the same component C1, exclude that one static file specifically.
Minor Upgrade: With the above setting, all of the dynamically linked files are overwritten because the component's key file--the static file--is set to always be overwritten.

Example:
I just did a quick test to confirm this. My base installation had one component with several dynamically linked HTML files and one static HTML file. I marked the static file as the key file and selected the Always Overwrite check box in the file's properties. For my minor upgrade, I deleted one of the dynamically linked files in the source folder and added a new HTML file. When I ran the base installation and then the minor upgrade, the deleted file was removed and the new file was added.

[Note: Basic MSI projects and InstallScript MSI projects let you mix static and dynamic files in the same component. (InstallScript projects do not; each component in an InstallScript project contains only dynamic or static files.)]

Warning!:
Dynamic file linking should be used with caution. If you inadvertently delete a dynamically linked file from the source folder that your dynamic link references, InstallShield does not display any build warnings or errors. Your product may install without any issues, but it may not work as expected, without the dynamically linked file that was inadvertently deleted.

In most cases, it is best to avoid using dynamic file links for critical executable files—such as .exe, .dll, or .ocx files—especially if your product requires them in order to run successfully.

About Author

Bhuvana is into Configuration Management, Specialized in Application Packaging, Source Control Management & Build Automation. This blog mostly revolves around these three areas.

How to Register .Net assembly into GAC using InstallShield 2010

Deploying .Net assemblies into Global Assembly Cache can be done using either regasm.exe tool  in .Net SDK or using InstallShield IDE. This article discusses them in detail

Using Assembly Regstration Tool (regasm.exe) - Basic MSI, Installscript MSI & InstallScript Projects
  • User 'System Search' inInstallShield IDE or FindAllFiles() in script to locate the regasm.exe from 'C:\WINDOWS\Microsoft.Net\Framework', which will set the path to a property variable (already defined in property manager) 
  • Register -> Use the property variable to invoke regasm.exe
    Syntax -
    regasm <Assembly file name with path>  /codebase /tlb:<AssemblyName>.tlb
    [Note: The above command registers all public classes contained in <AssemblyName>.dll, and generates and registers the type library <AssemblyName>.tlb, which contains definitions of all the public types defined in <AssemblyName>.dll.] 
     
  • For InstallScript Project, 'System Search' is not available in IDE, so we can use InstallScript to reigster assembly using regasm.exe
    if ( FindAllFiles ( WindowsFolder ^ "Microsoft.NET" ^ "Framework", "regasm.exe", svResult, RESET ) == 0 ) then
             svPath = INSTALLDIR ^ "<AssemblyName>.dll /codebase /tlb";
             LongPathToQuote ( svPath, TRUE );
             if (LaunchAppAndWait ( svResult, svPath, WAIT ) < 0) then
                    MessageBox (" Unable to launch " + svPath + ".", SEVERE);
             endif;
    endif;
  • UnRegister-> Use the property variable to invoke regasm.exe
    Syntax - regasm.exe -u <Assembly file Path>
 Using Installshield IDE - Basic MSI & Installscript MSI Projects
  • Open the new or existing Installshield project from which you need to register an assembly into cache.
  • Go to Tools -> Options -> Preference Tab ->  Self Registration and keep the following two items selected.
    • Com Extraction will occur during the build time
             [Note: If this item is not selected, then you would get the following error at runtime
               'Error 1935 An error occurred during the installation of assembly component  <Component GUID> HRESEULT 0x800700B07]

  • Create a component and add the assembly to be registered into GAC to the new component and set it as a key file.
  • Set .Net COM Interop property to 'YES'
  • For Windows Installer projects (Basic MSI and InstallScript MSI), each component contained in the project has a .NET COM Interop setting. Setting this option to Yes will run regasm.exe against the component's keyfile at build and import the registry information into the built installation. If the destination for the component is [GlobalAssemblyCache], the /codebase parameter is passed as needed to regasm.exe at build time.
  • InstallShield 2009 / 2010 uses the regasm assembly from the path stored in 'DotNetRegasmPath' under HKEY_LOCAL_MACHINE\SOFTWARE\InstallShield\15.0\Professional
  • Build & test the installer to see the new file registered into GAC.

November 12, 2009

How to create Custom Dialog Themes for Basic MSI Project in InstallShield 2010

InstallShield provides some good default Themes and if you wonder how to create your own Custom Themes, which displays your product specific images on the Installation GUI, then this article is for you. This article explains the steps to create / use Custom dialog Themes in Basic MSI Project, which is an undocumented feature of InstallShield.
Steps to create Custom Dialog Themes
1. Go to <Installshield Installation Directory>\Support\Themes
2. Create a .Theme file say “My Custom.Theme” with following content
<?xml version="1.0" encoding="UTF-8"?>
<Theme Name="My Custom Theme" Path="My Custom Theme" Preview="preview.jpg" SetupImage="setup.gif">
<Transform id="welcome">
         <Add Control="Image" Type="Bitmap" Region="0 0 374 234" Attributes="1" Source="welcome.jpg"/>
         <Add Control="DlgLine" Type="Line" Region="0 234 374 0" Attributes="1"/>
</Transform>
<Transform id="interior">
         <Add Control="Banner" Type="Bitmap" Region="0 0 374 44" Attributes="1" Source="banner.jpg"/>
         <Add Control="BannerLine" Type="Line" Region="0 44 374 0" Attributes="1"/>
</Transform>
<Apply Transforms="welcome">
        <Match Size="374 266"/>
        <Include>
                <Name>AdminWelcome</Name>
                <Name>SetupCompleteError</Name>
                <Name>SetupCompleteSuccess</Name>
                <Name>SetupInitialization</Name>
                <Name>SetupInterrupted</Name>
                <Name>InstallWelcome</Name>
               <Name>MaintenanceWelcome</Name>
               <Name>PatchWelcome</Name>
               <Name>SetupResume</Name>
               <Name>SplashBitmap</Name>
               <Name>Exterior</Name>
        </Include>
</Apply>
<Apply Transforms="interior">
         <Match Size="374 266"/>
         <Exclude>
                  <Name>AdminWelcome</Name>
                  <Name>SetupCompleteError</Name>
                  <Name>SetupCompleteSuccess</Name>
                  <Name>SetupInitialization</Name>
                  <Name>SetupInterrupted</Name>
                  <Name>InstallWelcome</Name>
                  <Name>MaintenanceWelcome</Name>
                  <Name>PatchWelcome</Name>
                  <Name>SetupResume</Name>
                  <Name>SplashBitmap</Name>
                  <Name>Exterior</Name>
        </Exclude>
</Apply>
</Theme>
3. Create a folder say “My Custom Theme” along with “My Custom.Themes” file
4. Create the following images carrying your logo / brand / trademark with the below mentioned specification and copy them to
“<Installshield Installation Directory>\Support\Themes\My Custom Theme” directory

5. Launch InstallShield IDE, Open the existing project / Create New project
6. Go to User Interface --> Dialogs --> Themes, select the newly created theme ‘My Custom Theme’
Now all the dialogs either default or custom will have the New Theme.