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:
- Detecting whether the target platform has .NET v2.0 already installed.
- Checking that the target platform has the necessary software to support .NET v2.0.
- Installing the support software required by .NET 2.0 – as needed.
- Installing .NET 2.0 – as needed.
- 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:
- 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.
- 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:
- Open the InstallShield IDE and create a new project in the usual way.
- Switch to the Installation Designer View and open the Behaviour and Logic folder.
- 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.
- 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.
- 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.
- 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'.
- Run the Release Wizard (Build | Release Wizard) to create your installation.
Listing 1: MainDetectDotNet.cpp
int _tmain(int argc, _TCHAR* args)
int rc = 0;
size_t matchLen = 0;
if ( argc > 1 )
matchLen = _tcslen(args);
for ( size_t x = 0; x < matchLen; x++ )
if ( args[x] == '*' )
matchLen = x;
args[x] = '\0';
cout << "searching for v" << args << endl;
rc = -1;
for(vector<STRING>::iterator it = CLRVersions.begin();
it < CLRVersions.end(); it++)
cout << *it << endl;
if ( rc == -1)
if ((*it).compare(0,matchLen,args) == 0)
cout << "matching CLR found" << endl;
rc = 0;
Listing 2: InstallShield script
#define CHECKPROGRAM SUPPORTDIR^"Detect.cmd"
#define BOOTPROGRAM SUPPORTDIR^"dotnetfx.exe"
export prototype DetectDotNet2(HWND);
BOOL bExit, bLaunchAndWait;
LaunchAppAndWait (CHECKPROGRAM, "", LAAW_OPTION_WAIT |
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
if (LaunchAppAndWait(BOOTPROGRAM, "", LAAW_OPTION_WAIT) < 0) then
Listing 3: Simple batch file
if %errorlevel% EQU 0 (echo found > found.txt)