Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Installing .NET Framework 2.0 Applications with InstallShield

4.50/5 (11 votes)
13 Nov 20054 min read 3   903  
How to install .NET 2.0 Framework applications with InstallShield.

Image 1

Introduction

This article describes my attempts at deploying a .NET Framework 2.0 application using InstallShield X. I'm not really happy with the outcome, but hopefully it will help someone understand the issues and perhaps pave the way for something better.

InstallShield X provides a helpful deployment wizard that allows you to select the version of .NET Framework from a drop-down list box. This takes out of your hands all the messy business of detecting the .NET version on the target platform and installing the appropriate framework; you just make your selection and it's all done for you. Unfortunately, the list box doesn't support .NET v2.0 and after consulting the Macrovision website it became clear that there are no plans to address this issue in the near future; see this. It was for this reason that I decided to write this article.

The problem can be split into the following steps:

  1. Detecting whether the target platform has .NET v2.0 already installed.
  2. Checking that the target platform has the necessary software to support .NET v2.0.
  3. Installing the support software required by .NET 2.0 – as needed.
  4. Installing .NET 2.0 – as needed.
  5. Installing my application.

I found a program called DetectDotNet after searching CodeProject. This seemed to do most of what was required for the first step. However, it needed a bit of surgery before it could take the required .NET Framework as an input parameter and return a suitable exit code; see Listing 1. My plan was to create an InstallShield script and custom action that would launch DetectDotNet and run the necessary installation packages before continuing with the installation; see Listing 2. However, I ran into the following problems, which is why I'm not too happy with the overall result:

  1. InstallShield script doesn't recognize MS-DOS exit values so you have to run DetectDotNet from a batch file, test the %errorlevel% and then create a file for the InstallShield script to detect; see Listing 3.
  2. The .NET 2.0 Framework requires Windows Installer 3.0 (for NT), but unfortunately it doesn't seem that you can't install it from inside another installation program. I couldn't think of a way around this problem apart from writing a kind of bootstrap installer program. Ideas anyone?

The solution presented here doesn't check whether Windows Installer 3.0 has been installed on the target platform because I ran out of time. The best I can suggest for now is that you tell the user to install Windows Installer 3.0 before running your setup.exe (or App.msi). Installing this program multiple times doesn't seem to hurt.

The dependency on Windows Installer 3.0 (or 3.1) seems to imply that the only supported environments for .NET v2.0 are:

  • Windows 2000 sp3 and sp4
  • Windows Server 2003
  • Windows XP sp2

However, you can use Windows Installer 2.0 for non-NT platforms so this extends your reach to:

  • Windows 98
  • Windows 98 SE
  • Windows ME

[Thanks to David Kean for informing me about the above dependencies]. You can find the redistributables for Windows Installer 3.1 (WindowsInstaller-KB893803-v2-x86.exe) and .NET v2.0 Framework (dotnetfx.exe) in the Microsoft website.

I created the installation program and the script using InstallShield X, but other versions work in a similar fashion:

  1. Open the InstallShield IDE and create a new project in the usual way.
  2. Switch to the Installation Designer View and open the Behaviour and Logic folder.
  3. Click on 'Support Files' in the left view and right-click | Insert Files in the right view to add the files: detect.cmd, DetectDotNet.exe and dotnetfx.exe.
  4. Click on 'InstallScript' in the left view, select the 'Files' folder in the middle view and then right-click | New Script File. This opens a script template in the right view into which you should type Listing 2 as your custom function.
  5. Click on 'Custom Actions' in the left view, select the 'Custom Actions' item in the middle view and then right-click | Custom Action Wizard. Give your action a suitable name, select its type as 'Run InstallScript Code', select the name of your custom function as Source; see step 4, and use defaults for the remaining.
  6. Click on 'Sequences' in the left view and select the 'User Interface' item in the 'Installation' folder in the middle view. Right-click | Insert opens the Insert Action dialog where you will find your 'Custom Action' created in step 5. Select this item and press OK. Move your custom action until it is just after 'ISSetupFilesExact'.
  7. Run the Release Wizard (Build | Release Wizard) to create your installation.

Listing 1: MainDetectDotNet.cpp

C#
//*******************************************
//program takes an optional single parameter, 
//the version of CLR to find. The
//program returns 0 (success)if this version 
//of CLR is found, otherwise it
//returns -1 (fail). If no parameter is given 
//the program returns 0 (success).
//
// examples:
//  c:\>DetectDotNet 1.2.4455 //returns 0 if v 1.2.4455 is found
//  c:\>DetectDotNet 2.0*     //returns 0 if any v2.0 of CLR is found
//  c:\>DetectDotNet 1.*      //returns 0 if any v 1. of CLR is found
//  c:\>DetectDotNet *        //returns 0 if any version of CLR is found
//  c:\>DetectDotNet          //returns 0 whether CLR is found or not
//**********************************************************************
int _tmain(int argc, _TCHAR* args[])        
{
    int rc = 0;                
    size_t matchLen = 0;        
    CDetectDotNet detect;

    if ( argc > 1 )    
    {
        matchLen = _tcslen(args[1]);
        for ( size_t x = 0; x < matchLen; x++ )
        {
            if ( args[1][x] == '*' )
            {
                matchLen = x;
                args[1][x] = '\0';
            }
        }
        cout << "searching for v" << args[1] << endl;
        rc = -1;                                
    }

    vector<STRING> CLRVersions;

       // cout lines ommitted for clarity

    for(vector<STRING>::iterator it = CLRVersions.begin(); 
        it < CLRVersions.end(); it++)
    {
        cout << *it << endl;
        if ( rc == -1)                        
        {
            if ((*it).compare(0,matchLen,args[1]) == 0)
            {
                cout << "matching CLR found" << endl;
                rc = 0;
            }
        }
    }
        return rc;                            
}

Listing 2: InstallShield script

C#
#define CHECKPROGRAM   SUPPORTDIR^"Detect.cmd"
#define BOOTPROGRAM   SUPPORTDIR^"dotnetfx.exe"
        
#include "ifx.h"

export prototype DetectDotNet2(HWND);  

/////////////////////////////////////////////////////////////////////
//                                                                           
// Function:  MyFunction
//                                                                           
//  Purpose:  This function will be called by the script engine when 
//            Windows(TM) Installer executes your custom action 
//            (see the "To Do," above).
//                                                                           
/////////////////////////////////////////////////////////////////////
function DetectDotNet2(hMSI)  
    STRING svResult;
    BOOL bExit, bLaunchAndWait;
begin 
            
 LaunchAppAndWait (CHECKPROGRAM, "", LAAW_OPTION_WAIT |
                                     LAAW_OPTION_HIDDEN);
           
  if (FindFile (SUPPORTDIR, "Found.txt", svResult) < 0) then

     AskOptions (EXCLUSIVE, ".NET Framework 2.0 Deployment",
                 "Install .NET Framework 2.0", bLaunchAndWait,
                 "Abort installation", bExit);

     if bExit  then
         abort;
     endif;
               
     if (LaunchAppAndWait(BOOTPROGRAM, "", LAAW_OPTION_WAIT) < 0) then       
         abort;
     endif;
  endif;
end;

Listing 3: Simple batch file

C#
echo off
detectdotnet 2.*
if %errorlevel% EQU 0 (echo found > found.txt)

Acknowledgement

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here