VB.NET Tips / Tricks / Examples and Help

.NET : Install your app and the .NET framework using INNO Setup

Today we are going to look at an alternative to using ClickOnce or Windows Installer MSI to install an application written in .NET.

This article is written for installing an application written in Microsoft .NET 2.0 (does not have to be VB specifically) and including the .NET Framework as part of your installation.  The concepts here can be applied to do the same for a .NET 1.1 or .NET 3.x installation, however some specific values will change, and some logic will change (like the fact that .NET 3.x will not run on any Windows below XP SP2, and .NET 2.0 does).

Some History
One of the biggest problems with using an MSI based installer, or ClickOnce to deploy your .NET application, is the need to have multiple installation files. At the least, you generally need 2, a setup.exe bootstrapper to kick things off, and the MSI file which contains the actual files to install. This can be fine when dealing with a CD based installation, but how about internet downloads? Some make use of self extracting zip type files to extract the setup files and launch the bootstrapper, others simply zip the files up and expect you to unzip them and run the setup.exe. There are also 3rd party apps like InstallShield and Wise, however I was looking for a "free" approach to his.

Depending on your target audience, you may want to have a nice, easy to use, single setup.exe that people can download and run without any extra hoops to jump through.

INNO Setup is a free open source installer program written and maintained by Jordan Russell. It has the ability to package everything into a single setup.exe for easy deployment.

Here are some links of importance:

Make sure you always download and install the QuickStart pack installation of Inno versus just the standard one, as it includes a preprocessor
(similar to conditional compiler statements in VB with the # sign). More on this later.

One thing to keep in mind as you start to use INNO Setup, is that it is NOT a .NET installer. It is a generic installer for installing anything from a full blown app, to possibly just a directory of pictures. It is very flexible, albeit with a slight learning curve if you are used to VB programming. I use VB for 95% of my programming, and I found INNO's Pascal Scripting to be pretty easy to pickup, and not required in all cases of use.

Inno setup files have .iss (Inno Setup Script) extensions, and they are nothing more than plain text files. You can write them in notepad if you wish, however using the INNO program gives you syntax highlighting (plus it is free, and we need it to compile the script when we are done).

This article does not require you have existing knowledge of INNO Setup, however if you do, you will likely understand much of this article a bit better than those who do not. For those of you who are new to INNO Setup, this is not going to teach you everything you will want to know about it, so I suggest you visit the INNO website and look over some of the docs, tutorials, and newsgroups there. That being said, you can take this script and mostly change around values of the fields to fit your own application and have a working installer.

This article is also not a tutorial on how to use INNO Setup. It is an article on how to use INNO Setup to install the .NET Framework. Yes there is a difference, and that difference is I am not going to go into great detail about what each property and method of the setup does. That would take a book, not a blog posting. Wink


Time to get down to business

Ok, so now that we have gotten those facts out of the way, and you have downloaded and installed either the specific version I used for this example (5.1.9), or the newest version.
(Important note as of this article, the newest version of INNO 5.2.3 works fine with this script)

So we are going to go through the entire setup file, section by section, and talk about what is going on.

    So open up INNO setup, and select "Create a new empty script file", or just click cancel on the welcome dialog. You will be at the script editor window, which looks similar to a standard text editor like notepad.

    Lets add some comments to the top of the script:

    ;Example .NET Setup Script
    ;written in Inno Setup 5.1.9 (ISPP
    ;2008 Matthew Kleinwaks
    ;change these comments as needed for your own app

    Now we will define some preprocessor variables

    #define SourceFileDir "c:\work\myapp"
    #define IncludeFramework true
    #define IsExternal ""

    Basically, these preprocessor variables are generated BEFORE the script is compiled. This is useful to do things like set options. Perhaps you want to have a demo version as well as the full version of your product. You could use a preprocessor directive to indicate if its the demo or not, which can allow for boolean logic (if statements) to include one file or another based on if its a demo or not. That is just one example, but you get the idea.

    • SourceFileDir is a directory on my machine where the release version files of my actual application are. This would be the exe, dll files, any support files that are part of the application itself, like pictures, data files, eula, readme, etc.
    • IncludeFramework is simply a boolean to indicate if you want the framework packaged and installed. Sometimes it can be convenient to NOT include it.
    • IsExternal is also a directive that we will use to determine if in fact we want to package the app files with the setup.exe, or keep them separate. Obviously for a download we want them all to be part of one setup.exe file, however sometimes for distribution on CD, it is better to package the files loosely instead of all packed into one file. If you do want the files external, you should replace the "" in this line with "external" (including the quotes). More on why it works like that further down in the [files] section.

    So now lets write out the sections of the setup file. After that, we will create the code section to do all of our custom .NET installer coding.

    The first section is [Setup]. It defines many of the properties that make up the installer's look, feel, and many textual attributes.

    ;name of your application
    ;repeat name of application. (otherwise you get
    ;multiple entries in add/remove programs)

    ;app publisher name

    ;app publisher website URL

    ;app publisher support URL

    ;app publisher updates URL

    ;default directory {pf} is a constant for
    ;program files. See INNO help for all constants

    ;default group name in the programs
    ; section of the start menu

    ;Boolean to disable allowing user to customize
    ;start menu entry during installation

    ;Boolean to warn if directory user picks
    ;already exists

    ;directory where uninstaller exe will be

    ;this will be where our app is
    ;the constant we use is {app}

    ;Location of the license file

    ;file to show before install (I show sys requirements)

    ;file to show after install (I show readme)

    ={#SourceFileDir} eadme.txt
    ;Custom image to show on left side of installer

    ;Icon for uninstall in add/remove programs
    ;I use whatever my apps icon is

    ;Version number of your installer (not your app)

    ;If IncludeFramework, append _FW to end of compiled setup
    I do this to make it easy to compile a version with and
    ;without the framework included

    #if IncludeFramework



    ;Directory where setup.exe will be compiled to


    So there you have it. The comments should explain what is going on for you. Some of these fields are optional and can be removed if not needed. Likewise there are some others that I do not use. See the INNO help file for a full list of what is available to the [setup] section.

    The next section is the [files] section. This section defines what files should be compiled into the setup.exe file. Our example will simply consist of an exe, a readme.txt and the .NET framework redistributable file.

    Source: {#SourceFileDir}\MyApp.exe;      DestDir: {app}; Flags: ignoreversion {#IsExternal}
    source: {#SourceFileDir}readme.txt;      DestDir: {app}; Flags: ignoreversion {#IsExternal}
    #if IncludeFramework

    : {#SourceFileDir}\dotnetfx.exe; DestDir: {tmp}; Flags: ignoreversion {#IsExternal} ; Check: NeedsFramework

    So what we have here are 3 lines indicating our source files, where they will go on the target machine, and any flags we need to associate. Again see INNO help for all possible flags and properties here. (I will mention to refer to INNO Help a lot because it is too robust to cover every aspect in one article, however you will end up with a fully working script by the end of this one). Notice that while we install myapp.exe and readme.txt to the {app} directory, we unpack the dotnetfx.exe (framework redist file) to a temp directory. The constant {app} actually translates at install time to whatever directory the user selects for installation. While we always like to assume they will install to c:\program files\ that will never be the case 100% of the time. IgnoreVersion just means we are going to copy the file no matter what, you can put restrictions here to only copy the file if its a newer version, etc. Also notice our {#IsExternal} directive here. I mentioned earlier that its value should be either "" or "external". That is because these preprocessor directives are compiled into literal strings for use when the setup is compiled. What that means is, when the value of #IsExternal is "" the line looks like this once compiled:

      Source: {#SourceFileDir}\MyApp.exe;      DestDir: {app}; Flags: ignoreversion

    however if the value is "external" then the line compiles like this:

      Source: {#SourceFileDir}\MyApp.exe;      DestDir: {app}; Flags: ignoreversion external

    Which adds the external flag to this file. The external flag tells the setup that the file to install is actually not packed into the installer, but a loose file included along with it. This makes preprocessor directives very powerful to customize your installer when used in this way. The Check: NeedsFramework line for the .NET redist file will call up a custom function to check and see if the machine even needs the .NET framework or not. We will define that function when we get to the code section of the script.

    Now lets specify the shortcuts we want made.

    Name: {group}\MyApp;        Filename: {app}\MyApp.exe; WorkingDir: {app}
    Name: {group}\Remove MyApp; Filename: {uninstallexe};  WorkingDir: {app}
    Name: {userdesktop}\MyApp;  Filename: {app}\MyApp.exe; WorkingDir: {app}

    So we are putting a shortcut in the programs section of the start menu, as well as the desktop. We also put an uninstall shortcut in the start menu folder. Everything that is in curly braces { } is a constant that INNO translates at install time into a real string value. Where can you find all these constants? Yup, you guessed it, in the INNO Help file.

    On to the [Run] section. This section defines what processed should be run as part of the installation.


    #if IncludeFramework
    : {tmp}\dotnetfx.exe; Parameters: "/q:a /c:""install /l /q"""; WorkingDir: {tmp}; Flags: skipifdoesntexist; StatusMsg: "Installing .NET Framework if needed"

    Filename: {win}\Microsoft.NET\Framework\v2.0.50727\CasPol.exe; Parameters: "-q -machine -remgroup ""MyApp"""; WorkingDir: {tmp}; Flags: skipifdoesntexist runhidden; StatusMsg: "Setting Program Access Permissions..."

    Filename: {win}\Microsoft.NET\Framework\v2.0.50727\CasPol.exe; Parameters: "-q -machine -addgroup 1.2 -url ""file://{app}/*"" FullTrust -name ""MyApp"""; WorkingDir: {tmp}; Flags: skipifdoesntexist runhidden; StatusMsg: "Setting Program Access Permissions..."

    Lets go over each of these 3 lines here. The first one installs the .NET framework. The Parameters we pass along with this file are simply those to make the installation silent (show no UI while installing). You may or may not want this, it is up to you. For full details on the exact meaning of the command line switch used for the framework installation, visit this MSDN Page on the subject. The StatusMsg field lets you specify a message that INNO displays while this process is running. I left it off for space saving here, but I often also put "this will take several minutes..." because lets face it, installing the .NET framework on an average computer does take a few minutes. The skipifdcoesntexist flag is important because remember that we only extracted the framework to the temp directory if the system didn't have the framework installed, so if it doesn't exist, then chances are the user already had the framework (or is out of disk space!).

    The other 2 lines simply run caspol.exe to set permissions using a little "url" trick. If you are not familiar with CasPol, it is the Code Access Security Policy utility. .NET code has an extra layer of code security hooked into the runtime, and by default your local drives run with full trust, and your network drives run with partial trust. Long story short, if your customer installs your program to a network mapped drive, there is a good chance they will start getting weird security exceptions because you did not design your app to run at partial trust (or your app simply NEEDS full trust to do whatever it is your app does). So we have INNO set permissions to give full trust to wherever the user installed the app. If its the local drive, they already had the permissions, and if its a network drive, this will give them the permissions to the installation folder. Its a win/win scenario. You may wonder why there are 2 entries though, and that is because we first try to remove an existing entry if it is there, to avoid duplicate entries in caspol. If there was no entry to begin with, the method simply completes without doing anything.

    The skipifdoesntexist flag just tells INNO that if the file we are working with doesn't exist for some reason, don't try to launch it. The only time it won't exist is in weird scenarios of framework installation failure, etc.

    Like the [run] section, there is also an [uninstallrun] section. This section defines what will run when the program is uninstalled. The only thing I put here, is to remove the caspol entry, just like we did in the run section. It is better to do it here, the entry in the run section is just a backup.


    Filename: {win}\Microsoft.NET\Framework\v2.0.50727\CasPol.exe; Parameters: "-q -machine -remgroup ""MyApp"""; Flags: skipifdoesntexist runhidden;

    So up to this point, we have finished our entire INNO script file, with the exception of the code section. INNO implemented a pascal scripting code section at some point in its life to allow you to extend and customize the installation experience. There is little you can not do, but this flexibility comes at the cost of knowing how to write the code. The Inno Setup IDE is not nearly as advanced as the Visual Studio IDE, so writing code can be a bit difficult at times, however I am going to take you through my standard setup code, and explain what it does.

    The code section starts with, yes you guessed it, a [code] heading.

    First we are going to define some functions that we will use to aid our setup process.

    I am not going to go over every single line here in detail. The functions are pretty short, and do specifically only what they say. Most of them are just checking the state of the system for a given value, like if the framework exists, if a service pack is installed, etc. Most of them query a registry key, check a file version, or something similar to see what is already on the system.


    // Indicates whether .NET Framework 2.0 is installed.
    IsDotNET20Detected(): boolean;
        success: boolean;
        install: cardinal;
        success := RegQueryDWordValue(HKLM, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727', 'Install', install);
        Result := success and (install = 1);

    //Remember this method from the Files section above

    NeedsFramework(): Boolean;

    Result := (IsDotNET20Detected = false);


    IsWin95 : boolean;

    Result := (InstallOnThisVersion('4.0,0', '4.1.1998,0') = irInstall);


    IsWinNT : boolean;

    Result := (InstallOnThisVersion('0,4.0.1381', '0,4.0.1381') = irInstall);


    GetIEVersion : String;
      IE_VER: String;

    {First check if Internet Explorer is installed}
      if RegQueryStringValue(HKLM,'SOFTWARE\Microsoft\Internet Explorer','Version',IE_VER) then
    Result := IE_VER
        {No Internet Explorer at all}
        result := '';

    GetMSIVersion(): String;

    GetVersionNumbersString(GetSystemDir+'\msi.dll', Result);


    ErrorCode: Integer;


    if (MsgBox('Would you like to go to the Windows Update site now?' + chr(13) + chr(13) + '(Requires Internet Connection)'
    , mbConfirmation, MB_YESNO) = IDYES) then
    ShellExec('open', 'http://windowsupdate.microsoft.com','', '', SW_SHOW, ewNoWait, ErrorCode);


    Those are all of our helper functions. Now lets define a few functions that are built into INNO setup. When I say built in, I mean these are functions that INNO already knows the name of, and will run at a certain point if you define them.

    There are many, but the ones we are going to use are

    • InitializeSetup()
    • GetCustomSetupExitCode()

    InitializeSetup() is the first thing that runs when the installer starts up, so we will add in our custom logic to this routine to make the installer do the checks we want to perform.

    GetCustomSetupExitCode() is something that runs when setup is complete. We use this method to check for the .NET Framework (because at this point it should be installed) and give an error message if we don't find it. There are any number of reasons why the installation could fail, so all we really do here is check and alert the user if it did fail.

    I will define GetCustomSetupExitCode() first because it is shorter and self explanatory.


    GetCustomSetupExitCode(): Integer;

    if (IsDotNET20Detected = false) then
    MsgBox('.NET Framework was NOT installed successfully!',mbError, MB_OK);
    result := -1


    Now on to the InitializeSetup. This routine is pretty long, because it contains all the checks we want to do before allowing the user to install. Returning False from this routine aborts the setup.

    We perform the following checks

    • If Windows 2000, require at least SP3
    • If WIndows XP, require at least SP2
    • If Windows 95 or NT, don't install at all
    • If NT based (2000, XP, 2003, Vista), require MSI Installer 3 or higher
    • If 9x based (98, ME), require MSI Installer 2 or higher
    • If IE version is less than version 5.01, don't install
    • If admin is not logged on, don't install (.NET Framework install requires admin rights)

    So lets get to the code shall we:

    function InitializeSetup: Boolean;

    Version: TWindowsVersion;
    IE_VER: String;
    MSI_VER: String;
    NL: Char;
    NL2: String;


      NL := Chr(13);
    NL2 := NL + NL;

    // Get Version of Windows from API Call

      // On Windows 2000, check for SP3

      if Version.NTPlatform and
    (Version.Major = 5) and
    (Version.Minor = 0) and
    (Version.ServicePackMajor < 3) then
    SuppressibleMsgBox('When running on Windows 2000, Service Pack 3 is required.' + NL +
    'Visit' + NL2 +
    ' *** http://windowsupdate.microsoft.com ***' + NL2 +
                'to get the needed Windows Updates,' + NL +
    'and then reinstall this program',
    mbCriticalError, MB_OK, MB_OK);
    Result := False;

      // On Windows XP, check for SP2
    if Version.NTPlatform and
    (Version.Major = 5) and
    (Version.Minor = 1) and
    (Version.ServicePackMajor < 2) then
    SuppressibleMsgBox('When running on Windows XP, Service Pack 2 is required.' + NL +
    'Visit' + NL2 + ' *** http://windowsupdate.microsoft.com ***' + NL2 +
    'to get the needed Windows Updates,' + NL +
    'and then reinstall this program',
    mbCriticalError, MB_OK, MB_OK);
    Result := False;

    if (IsWin95) or (IsWinNT) then
    SuppressibleMsgBox('This program can not run on Windows 95 or Windows NT.',
    mbCriticalError, MB_OK, MB_OK);
    Result := False;

    MSI_VER := GetMSIVersion
    if ((Version.NTPlatform) and (MSI_VER < '3')) or ((Not Version.NTPlatform) and (MSI_VER < '2')) then
    SuppressibleMsgBox('You do not have all the required Windows Updates to install this program.' + NL +
    'Visit *** http://windowsupdate.microsoft.com *** to get the needed Windows Updates,' + NL +
    'and then reinstall this program',
    mbCriticalError, MB_OK, MB_OK);
    Result := False;

    IE_VER := GetIEVersion;
    if IE_VER < '5.01' then
    if IE_VER = '' then
    MsgBox('Microsoft Internet Explorer 5.01 or higher is required to run this program.' + NL2 +
    'You do not currently have Microsoft Internet Explorer installed, or it is not working correctly.' + NL +
    'Obtain a newer version at www.microsoft.com and then run setup again.', mbInformation, MB_OK);
    MsgBox('Microsoft Internet Explorer 5.01 or higher is required to run this program.' + NL2 +
    'You are using version ' + IE_VER + '.' + NL2 +
    'Obtain a newer version at www.microsoft.com and then run setup again.', mbInformation, MB_OK);

    result := false;

      if IsAdminLoggedOn then
    result := true
    MsgBox('You must have admin rights to perform this installation.' + NL +
    'Please log on with an account that has administrative rights,' + NL +
    'and run this installation again.', mbInformation, MB_OK);
    result := false;


    So there you have it. The completed code section of the script. You now have a fully functional INNO setup script that will package the .NET 2.0 framework with your program into a single setup.exe, extract it at install time if needed, and install it silently. The script also checks to make sure the user has admin rights, has the needed service pack levels for certain operating systems, has the needed versions of things like MSI installer and Internet Explorer. There may be other checks you want to do (or even some you want to omit) so I hope my script is a good jumping off point for you to get into customizing your own. My code also offers in several failure spots due to system requirements to launch the windows update page so users can get the updates they need. You would be surprised how many people are not even running SP2 on Windows XP even though it has been out a long time now and we are already up to SP3.

    Next time, I plan to show you how to associate a file type with your program at install time. For example, maybe your app is a text editor, and you want to associate *.txt files with your program when it is installed. There are a few registry keys involved, and we will go over them in detail. I will also show you how to install a font file, as well as adding some custom functionality to the UI of the installer, to perform even more custom actions. Then I will go into detail about detecting if the user has the .NET Framework installed from an ASP, PHP, or ASP.NET Page and Internet Explorer (Sorry FireFox users, it doesn't work for you). This can be useful when you want to offer your user a download with or without the framework (to save the 20+MB) based on if they need it or not.

    You can download the complete script file as it appears in this article from the media download page.


    Posted Jun 23 2008, 08:53 PM by Matthew Kleinwaks
    Filed under: , , , ,


    Lawrence wrote re: .NET : Install your app and the .NET framework using INNO Setup
    on Sun, Jun 29 2008 1:37 AM

    Good bit of info on this page for setup and deployment.

    I have been messing around with the vs msi setup.  Nothing in particular, but just to see how it works. I will have a look at the web links you included.  Good web sight Matt, and good info to boot.

    Regards Zeldacat


    xancholy wrote re: .NET : Install your app and the .NET framework using INNO Setup
    on Wed, Jul 2 2008 11:39 AM

    kleinma, I am a great fan of innosetup and have used it extensively for deployment in the past.

    How can I use innosetup to check for website based program updates  like clickonce does ?

    Matthew Kleinwaks wrote re: .NET : Install your app and the .NET framework using INNO Setup
    on Wed, Jul 2 2008 4:42 PM

    There is a plugin for INNO Setup that allows you to perform downloads during install time. This can be useful when doing things like downloading the latest version of your program when the user goes to install it from an old version installer, or even doing something like using my example code above and only downloading and running the .NET framework setup if the user doesn't have it, which means there is no need to package the framework with the setup.

    That being said though, I don't really think INNO or any installer is really the right tool for doing "after installed" update checks. This is something that would be better for your own app to do. It can be as easy as keeping a text file on the server that just has the latest version number listed, and your app reading that text file to compare against its own version, and downloading update files if needed. This generally requires 2 exe files. Your main application, and also an updater exe that can replace your applications file with the new downloaded ones, and then restart the main application. This is the method I use for my apps, and it works rather well for the most part.

    I am actually writing up an article I plan to publish in the next few days on a downloader class I wrote for downloading files from the web using VB.NET which is more robust than any of the built in file download methods of the framework.

    DV wrote re: .NET : Install your app and the .NET framework using INNO Setup
    on Sat, Jan 31 2009 8:09 PM

    Thanks a ton. I had been looking for some example like this for  while with no luck. I eventually only found it after installing the Inno Setup Preprocessor for some reason - a tool I had been missing out on for some time.

    Great post, great information and examples... Many thanks!

    Thoms31 wrote re: .NET : Install your app and the .NET framework using INNO Setup
    on Fri, Apr 24 2009 8:57 PM

    Matthew, you saved me many hours of investigate this issue. Thanks a lot from Denmark

    DennisD wrote re: .NET : Install your app and the .NET framework using INNO Setup
    on Tue, Aug 25 2009 1:46 PM

    Is there a way to use the preprocessor variables you defined inside the [code] section?

    Matthew Kleinwaks wrote re: .NET : Install your app and the .NET framework using INNO Setup
    on Tue, Aug 25 2009 2:21 PM


    Yes you just need to make sure there is a # in front of the variable so that it gets processed by the preprocessor prior to compile.

    So if you have a preprocessor variable of #IsDemo with a value of 1 or 0 (yes or no) you could write code like this in the code section:

    if {#IsDemo} = 1 then


       //do stuff here


    millag wrote re: .NET : Install your app and the .NET framework using INNO Setup
    on Fri, Aug 28 2009 10:30 AM


    thanks for the nace tutorial. I was wondering if there is a way to check the exit code of a program runned in [Run] section?

    (for example:



    Filename: "{tmp}MyProgram.exe";Parameters:"-silent;Flags: waituntilterminated


    Matthew Kleinwaks wrote re: .NET : Install your app and the .NET framework using INNO Setup
    on Fri, Aug 28 2009 11:08 AM


    I am not aware of anything in the run section to return a usable exit code to you. I think the issue is even if you got a valid exit code, what would you do with it in the run section?

    Probably the best thing for this would be to put something in the code section and use the Exec function(look in INNO help for the Exec function) This function takes a variable by reference that after the function runs, will contain the exit code of the process you launched. This way you can evaluate the result code and take appropriate action.

    Armin wrote re: .NET : Install your app and the .NET framework using INNO Setup
    on Tue, Oct 20 2009 7:59 AM

    Matthew, many thanks for the tutorial.

    This was exaxtly what I was looking for. I had no clue about Innosetup before and it would have taken me months to find it out by myself.

    Thanks, Armin

    Victor wrote re: .NET : Install your app and the .NET framework using INNO Setup
    on Sun, Oct 25 2009 9:30 AM

    Man, you saved my and more ten people's week! Thanks a lot!

    David wrote re: .NET : Install your app and the .NET framework using INNO Setup
    on Tue, Nov 17 2009 12:11 PM

    Thx Mate, great work!!

    © 2014 - ZerosAndTheOne.com - Hosting by Orcsweb (http://www.orcsweb.com/)
    Powered by Community Server (Non-Commercial Edition), by Telligent Systems