Inno Setup .Net Wrapper

Emran Hussain,
Sep 18, 2014

NET_Wrapper_for_Inno

When you are thinking about an installer builder that can be configured by Scripting rather than GUI application so that you can save the script in your Source Control and maintain the script from Visual Studio, Inno Setup qualifies to be an excellent candidate to consider. You can configure the Installer by writing PASCAL scripts and store the Script in your source control same like you can store your C#/VB source code. This kind of installer builder is a critical requirement when you want to implement continuous integration for deploying your product. You must automate generating your installer as a part of your build process.

So far so good. But what if you are passionate .NET Programmer who loves the environment of Visual Studio and you do not like to learn PASCAL language, or you do not have the time or motivation to learn and maintain a different scripting language! You want to reuse your .NET Skill which should be not only productive but also exciting for you. In that case, you would need a .NET Wrapper for Inno Setup so that, you can target a .NET Interface and the wrapper will generate the PASCAL script for you. Yes, exactly that's what 'Inno Setup .NET Wrapper' is. It is a FREE and open source .NET Class Library is written in C#. You can add the reference to that class library to your Project and configure your installer by setting various properties of a .NET Settings class.

How does that work?

Ok, the wrapper class library has one public class named InnoSetupService. It will take various configuration info thru a .NET Class named SetupSettings in its constructor and based on setting, it will generate the necessary PASCAL script. If you just need the script what you would execute by yourself, then you can simply call a method named GenerateScripts(). That's all you need to generate scripts. Yes, you would need to specify the output folder path (in your Settings object) where you want your PASCAL script created. If you want the wrapper to execute the script and build the final installer (Setup.exe) file, you can call the other method named BuildSetupFile. This method takes the path of your Inno Setup Compiler as a parameter. Once you call this method, it will Generate the PASCAL script and then, execute the script using System.Diagnostics.Process class and capture the Console output for you. It is that simple.

Features

  • Open Source. The source code is open and easily customizable and extendable.
  • Highly reliable. No dependency on MSI.
  • Easy !! No steep learning curve like InstallShield.
  • Supports Uninstaller Creation automatically. When you build your installer using this wrapper, your installer will create an uninstaller automatically and place the uninstaller in the Program Files / Start Menu folder. The uninstaller generated by this wrapper will be smart enough to delete the User's App Data Directory created by this application. Not only that, if no another application has been installed from the same company (your company), then the Company App Data folder will be deleted too upon uninstallation.
  • Support Digital Signature. You can let this wrapper take care of signing all exe / dll (signable files) in your deployable folder. Not only that, it will sign your uninstaller too which is very rare to find in typical installer builders. If you have EV (Extended Validation) Code Sign Certificate that you want to use for signing your exe and dll files, you will find this solution extremely useful. Because most of the installer won't support EV Certificate to sign your files.
  • Support file extension association.
  • Support Context Menu Shell Integration.
  • An unlimited number of Prerequisite creation where each prerequisite download/install behavior can be programmed.
  • Support 32bit/64bit installation. If the user's PC is a 32bit machine, then the installer will deploy the application in C:\Program Files(x86) folder. If the user's machine is 64bit then the application will be deployed in C:\Program Files folder. You can configure to use 'AnyCpu' too so that the installer will check the user's machine and deploy in the correct folder.
  • You can add End User License Agreement file by setting a property easily which will be shown in the install time and ask the user if the user Agrees or Not.
  • And lots more properties to fine tune the install behavior.

How to use?

Using this class library is very simple.

  1. First, you instantiate a Settings class (named SetupSettings) which contains lots of properties to configure your Installer.
  2. Then, instantiate a class named InnoSetupService which takes an instance of the SetupSettings class as a constructor argument.
  3. Now, if you want just to generate the PASCAL scripts, call the method: GenerateScripts(). If you want to Generate the scripts and build the Scripts automatically, call the method named BuildSetupFile(string innoSetupCompilerExePath). Yes, when you call the BuildSetupFile method, you would need to pass the Inno Setup Compiler path as a parameter.

Let's demonstrate a very basic scenario. I have provided a code snippet which contains the explanation for all properties in comments. I am assuming that you have a button named btnBuildInstaller in your Windows Form and the click event handler of that button may look like this:

    private void btnBuildInstaller_Click(object sender, RoutedEventArgs e)
    {
        Guid productGuid = Guid.NewGuid();

        SetupSettings innoSetupSetting = new SetupSettings
        {
            CompanyName = "Your Company Name",
            // A folder with this company name will be created in the Start Menu / Program Files folder
            //and your Product short cut will be placed within this Company Name folder.

            ProductName = "your Product Name", 

            ProductId = productGuid,
            // You should keep your product Guid Unique so that you can publish update easily with same Guid.

            ProductVersion = new Version("1.0.0.0"), // Your product version


            DeployableFolderPath = "C:\\My Project\\Deployable",
            // This folder contains all of the exe, dll etc whatever you want to publish to your user's computer. 
            // You do not need to specify each file name. Whatever file placed in this deployable folder will be 
            // published to your user's computer.

            StartUpApplictionFileName = "My Exciting Calculator.exe",
            // This is the main executive file name which will be placed in the Start Menu

            ShortCutName = "Exciting Calculator", // Well, this shortcut name will be shown in the start menu,
            //if  your product name is very big, it is better to use a short cut name to identify your product 
            // quickly and this property holds that short cut name.

            CompanyUrl = "http://your-company-website.com",

            SupportUrl = "http://your-company-website.com/ContactUs",

            IconFilePath = "C:\\My Project\\Resources\\my-icon.ico", // This icon will be shown in Desktop and Start Menu.

            EULAFilePath = "C:\\My Project\\Resources\\End-User-License-Agreements.txt",
            // If you set this property, only then a End User License Agreement Screen will be shown, otherwise no EULA screen will be shown.

            Platform = PlatformNames.AnyCPU_Prefer32Bit,
            // This property is self explanatory, you can set PlatformNames.AnyCPU, PlatformNames.x64, PlatformNames.x32 etc.
            // If you set value PlatformNames.x64 then your application will be installed in "C:\Program Files" folder. 
            // If you set value PlatformNames.x32, then your application will be installed in "C:\Program Files (x86)" folder. 
            // If you set value PlatformNames.AnyCPU, then, if your user's OS is Windows 64 bit, then your application will be
            // installed in "C:\Program Files" folder otherwise it will be installed in "C:\Program Files (x86)" folder.


            UninstallerFeedbackUrl = "http://your-company-website.com/uninstall-feedback",
            // If you set this property, then after uninstall, this url will be opened where you can ask your user to provide
            // additional feedback.

            GeneratedSetupScriptFolderPath = "C:\\My Project\\Temporary Scripts",
            // This folder is the place where InnoSetup Intermediate scripts will be generated. 
            // As you understand that, your C# code will generate Pascal Scripts for InnoSetup and 
            // finally that Pascal script will be executed to build your installer file. So, this 
            // folder will be used to store the auto generated Pascal Scripts. You will never need to 
            // look insude this folder. Everytime you call the BuildSetupFile(), old scripts will be 
            // deleted and new scripts will be written.


            OutPutSetupFilePath = "C:\\My Project\\Publish\\Setup.exe",
            // This is the path where your final Installer file will be created.
            // If a file already exists in this path then the existing file will be replaced.


            Prerequisites = new List<Prerequisite> // This is a collection of prerequisites. 
            // Prerequisites can be .NET Framework, SQL Express Local Db etc.
            // Every prerequisite can be configured to either install directly from Installer 
            // or Download from Web. IF you prefer to download from Web, then you can super configure
            // to define if the required prerequisite file should be downloaded automatically from a 
            // remote URL or it will simply navigate to a Download page and user can download the necessary
            // prerequisite from that page. For example, if you want to define .NET Framework 4.5 is a 
            // prerequisite for your application, then, you can navigate your user to the download page of 
            // microsoft .net framework 4.5. 
            {
                new DotNetFrameworkPrerequisite(DotNetVersions.V4_5)
                // This example is a very basic common usage. Here we did not set any extra property of the DotNetFrameworkPrerequisite.
                // Therefore, by default, the installer will check if the .NET framework version 4.5 is installed in user machine or not.
                // If not, then, the installer will take the user to the official Microsoft .NET framework 4.5 download page.

                // You can set many properties of this class to customize the behavior. For example, you can host the .NET Framework in your 
                // own server and silently download and install to user's machine so that if the user wont be aware about what is .NET framework,
                // you wont risk missing the user's installation. Please check the documentation for details.
            },

            FileExtensionAssociation = ".abc",
            // Yes, if you want to associate any file that has extension *.abc with the start up application,
            // then, you set this property. That means, if a file with extension *.abc is double clicked in user's PC,
            // then the exe file defined in the property StartUpApplictionFileName = "My Exciting Calculator.exe", will be 
            // invoked and the full file path of the double clicked file will be passed to this exe file as command argument.
            // So, you can program your application to receive this file path as command Argument.


            SignToolInfo = new SignToolInfo("C:\\Program Files (x86)\\Windows Kits\\8.0\\bin\\x64\\signtool.exe") 
            // The constructor takes the path to your SignTool.exe file.

            // Yes, you can program to Sign All Signable files (like exe and dll files) including the Uninstaller.
            // You read right. Yes, your uninstaller will be signed too, which is really an exciting and rare feature
            // in a installer software. If you do not want to digitally sign your files, then, do not set this SignToolInfo property.
            {
                PfxFilePath = "C:\\your-pfx-file-path.pfx",
                TimeStampServerUrl = "http://digicert.timestampserver.com", // a TimeStamp Server Url
                CertificateSubject = "Certificate Subject"
            },

            ShellContextMenuItem = new WindowsShellContextMenuItem
            // Another exciting feature is Windows Context Menu Shell Integration.
            // That means, you can right click on a file or folder and invoke your application.
            // If you do not want the Shell Integration, then do not set this property.
            {
                DisplayName = "Resize Image", // This text will be shown in the Context Menu

                // Say for example, your application is an Image Resizer application, then, you may want to 
                // allow the user to right click on an image type file to invoke the application.
                // If the user right click on an Image file and click the menu item 'Resize Image', 
                // then your startup exe application defined in the property StartUpApplictionFileName = "My Exciting Calculator.exe", will be 
                // invoked and the full file path of the double clicked file will be passed to this exe file as command argument.
                // So, you can program your application to receive this file path as command Argument.

                TargetType = WindowsShellContextMenuItem.TargetTypes.FileWithExtensionConstraint,
                // This Target type can be WindowsShellContextMenuItem.TargetTypes.FileWithExtensionConstraint, 
                // WindowsShellContextMenuItem.TargetTypes.File or WindowsShellContextMenuItem.TargetTypes.Folder.
                // If you set WindowsShellContextMenuItem.TargetTypes.FileWithExtensionConstraint, that means,
                // The context menu will be shown on File but if the file has extensions defined in the array type 
                // property named ExtensionConstraints. IF you set WindowsShellContextMenuItem.TargetTypes.Folder
                // for this TargetType, then the ContextMenu will be shown on Folders only. If you set TargetType
                // = WindowsShellContextMenuItem.TargetTypes.File then the Context Menu will be shown on Any File type.

                ExtensionConstraints = new[] {  ".jpg", ".png"}
            },

        };


        // Ok, once we have setup various properties for the object  SetupSettings innoSetupSetting,
        // we can instantiate the  InnoSetupService and pass this Settings object to the constructor.

        InnoSetupService generator = new InnoSetupService(innoSetupSetting);

        // And it should work on future versions too. If this library stops to work on future version, then, as this wrapper library source code is open,
        // hopefully someone will come up with an upgrade which will work on the future version.


        // finally call the BuildSetupFile() method of InnoSetupService Class. This method will return a log string which 
        // is captured from the Console output of InnoSetup.


        string result = generator.BuildSetupFile(innoSetupCompilerExePath: "C:\\Program Files (x86)\\Inno Setup 5\\iscc.exe");


        // Yes, you need to pass your InnoSetup Compiler File Path. At the time of this writing, This wrapper library is working fine on Inno Setup 5. 

        // If all goes good, then, your Setup.exe file is built by this time and stored in the path you defined in the property named OutPutSetupFilePath.

        // If you just need to generate the PASCAL Scripts without building the installer automatically, you can call the method : GenerateScripts().

    }

Prerequisites Explained

You may need to know little more about the Prerequisite feature. Prerequisites can be .NET Framework, SQL Express Local Db etc. This library will allow you to add as many prerequisites as you want using a List<Prerequisite> type property in the Settings. The installer will check for all prerequisites before installing the application to your user's machine. This Prerequisite class is an abstract class so that you can implement various Specific Prerequisite like DotNetFrameworkPrerequisite, SQLExpressPrerequisite etc.

Every prerequisite can be configured to either install directly from Installer or Download from the Web. IF you prefer to download from the Web, then you can super configure to define if the required prerequisite file should be downloaded automatically from a remote URL or it will simply navigate to a Download page and user can download the necessary prerequisite from that page. For example, if you want to define .NET Framework 4.5 is a prerequisite for your application, then, you can let your user navigate to the download page of Microsoft .net framework 4.5.

Anyway, for your convenience, I have already coded 2 prerequisites for you. 1. DotNetFrameworkPrerequisite2. SQLExpress2012LocalDbPrerequisite

You can see the source code of these prerequisite classes and create your prerequisite subclass very easily.

1. DotNetFrameworkPrerequisite

This prerequisite will check for a required .NET Framework version in your user's computer. If the correct .NET Framework version is not found in your user's computer, then, some actions will be executed. If you want to navigate your user to the Microsoft's official download page for that specific .NET Framework, then, you may not need to configure the DotNetFrameworkPrerequisite much. A code snippet like this will be enough.

Prerequisites = new List { new DotNetFrameworkPrerequisite(DotNetVersions.V4_5) }

If you want to host the .NET Framework installer file in your server and download and installed automatically to your user's machine if the framework is not present in your user's machine, you can use the code snippet like this: (All sub-properties are explained in the comments).

Prerequisites = new List
{ 

    new DotNetFrameworkPrerequisite(DotNetVersions.V4_5) {

                        FileUrlIfInstallSourceIsWebAuto = new Dictionary<Prerequisite.Architecture, string> 
                        {
                            {
                                Prerequisite.Architecture.X86, 
                                // The value could be Prerequisite.Architecture.X64, Prerequisite.Architecture.X86, Prerequisite.Architecture.Any etc
                                // When you set this value to Prerequisite.Architecture.X86, the installer will check if the user's machine
                                // is 32 bit, only then, this prerequisite will be downloaded. You can add another entry to this dictionary
                                // for 64 bit, so that if the user's machine is 64 bit then the url from other entry will be used.
                                // if you set the Prerequisite.Architecture.Any, then the url you set in this entry will be used 
                                // as prerequisite no matter 32 or 64 bit version of windows the user is using.

                                "http://my-company.com/my-dotnet-repo/framework45.exe" 
                                // this is the url from where the .NET Framework will be downloaded.
                            }
                        }, 
                        AlertPrerequisiteInstallation = false, // Silently download and install. 
                        MessageWhileInstallationInProgress = "Please wait while setup install prerequisite: .net framework..."
                    },
                    // We just added a prerequisite for .NET Framework 4.5. We configured to download the .net framework 
                    // automatically from the url http://my-company.com/my-dotnet-repo/framework45.exe silently without 
                    // asking the user. But yes, a message will be shown from the property value MessageWhileInstallationInProgress.
                },
}
2. SQLExpress2012LocalDbPrerequisite

SQL Server 2012 Express Localdb has been an outstanding choice for developers when a lightweight embedded sql server is required for a desktop application. Therefore, I have implemented another Prerequisite for that too. Same like DotNetFrameworkPrerequisite you can configure this prerequisite to navigate to Microsoft Website for downloading (if the localdb is not present in user's machine), or you can configure to silently download and install from your hosted repository to your user's machine. I am providing some sample snippets where the comments are explaining the properties in details.

This snippet shows how to download and install the correct version of SQL Server LocalDb to users machine based on user's machine's 32bit/64bit architecture.

                    Prerequisites = new List<Prerequisite>
                    {
                        new SQLExpress2012LocalDbPrerequisite
                        {
                            FileUrlIfInstallSourceIsWebAuto = new Dictionary<Prerequisite.Architecture, string>
                            {
                                { Prerequisite.Architecture.X64, "http://your-hosted-repository/SqlLocalDB.x64.MSI" }, 
                                { Prerequisite.Architecture.X86, "http://your-hosted-repository/SqlLocaLDB.x86.MSI" }
                            },
                            MessageWhileInstallationInProgress = "Please wait while setup install prerequisite: SqlLocalDB..."
                        }
                    }

If you want to embed the SQL Server LocalDb in your Installer (Setup.exe) file so that if the correct SQLServerExpress LocalDb is missing on your user's computer, the LocalDb Installer will be extracted from your installer and being installed on your user's machine, then you can use similar to the following snippet.

                    Prerequisites = new List<Prerequisite>
                    {
                        new SQLExpress2012LocalDbPrerequisite
                        {
                            FilePathIfInstallSourceIsEmbedded = new Dictionary<Prerequisite.Architecture, string>
                            {
                                {Prerequisite.Architecture.X64, @"C:\SqlServer\SqlLocalDB.x64.MSI"},
                                {Prerequisite.Architecture.X86, @"C:\SqlServer\SqlLocalDB.x86.MSI"}
                            }
                        }
                    }

So, that's pretty much everything about this Wrapper class library.

Download

Ok, now it is downloading time. I have attached Inno Setup Compiler (version 5.5.3) and the Source Code that works on version 5.5.3 (as of today) here so that in the future, if the new source code or new Inno Setup version does not work with this Wrapper, then, if you would love to use this wrapper, you can use this exact Inno Setup version and the wrapper together. You can download the most current source code from BitBucket too. Here is the repository link: https://bitbucket.org/spicelogic-team/dotnetwraperforinnosetup

Last Modified at Sep 25, 2018
© All content, photographs, graphs and images in this article are copyright protected by SpiceLogic Inc. and may not, without prior written authorization, in whole or in part, be copied, altered, reproduced or published without exclusive permission of its owner. Unauthorized use is prohibited.

Please feel free to share your comment.