Pages

Friday, July 6, 2012

Deploying Your .NET Application Using Inno Setup

Introduction
I deployed my application using Inno Setup. It is a free and very intuitive installation engine.
You can download it free here.

Basically you create a script that ends with an .iss extension using the Inno-setup editor, which is much like a Notepad, but colors the script words. The syntax of the script is very much like an ini file. There is a very good help.

In this article I will demonstrate how to create a good installation to your application.

The General Section
Use the Inno Setup help to figure out what each command do. But this is what I use:

[Setup]
AppName=My Application - Proffesional Edition
AppVerName=My Application - Proffesional Edition v0.95
OutputBaseFilename=My Application - Proffesional Edition
OutputDir=.\
AppComments=The Proffesional Edition is intended for computers in off-line environment.
AppContact=My Name and Phone
AppPublisher=My Company
AppPublisherURL=www.MyCompany.com
AppVersion=0.95
VersionInfoVersion = 1.0
DefaultDirName={pf}\My Application
DisableDirPage=yes
DefaultGroupName=My Application
AllowNoIcons=no
Compression=lzma
SolidCompression=yes
AllowUNCPath=no
UserInfoPage=no
AppCopyright=Copyright © 2007 My Company.
AlwaysRestart=yes

Of course replace your name, application, and company.
Notice the AlwaysRestart flag.
The {pf} is a short for Program Files.
It will create the installation file in the same folder as the iss file (you can change that of course).

The Tasks Section

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "DotNetFramework"; Description: ".NET Framework 1.1"; GroupDescription: "If .NET is NOT installed:";
Name: "MDAC"; Description: "Access, OleDB and Jet Providers"; GroupDescription: "Access, OleDB and Jet Providers";

During the installation the user will have a screen where he will be prompted to check various installation tasks. In this case he will be prompted to select a Desktop Icon, a Quick Launch Icon, a .NET Framework, and an MDAC.

You must install the .NET Framework on the client's computer, or he will not be able to run your .NET assembly. Here I show how to install .NET 1.1, but you might need to install .NET 2.0 as well. You also must install MDAC if you are using OleDb providers, Access databases and so on (In Windows XP you don't need to install this). You might decide to install those things without prompting the user (if they are already installed no harm can be done).

Of course you can add more tasks of your own.

The Dirs Section

[Dirs]
Name: "{app}\bin"
Name: "{app}\bin\DB"
Name: "{app}\bin\Config"
Name: "{app}\bin\Resources"
Name: "{app}\bin\DB\Xml"
Name: "{app}\DotNet"; Flags: deleteafterinstall; Tasks: DotNetFramework
Name: "{app}\MDAC"; Flags: deleteafterinstall; Tasks: MDAC
Name: "C:\Temp\"; Flags: uninsneveruninstall ;

Each directory will be created during the installation. {app} is a short for the installation folder, that is equivalent to te DefaultDirName attribute in the Setup section.

The Tasks flag is used to associate a line with a task. The task will be executed ONLY IF the task is selected by the user.
The deleteafterinstall flag makes the folder disappear after the installation. The user will not even be aware of this folder creation. I will this soon.

I also create the Temp folder just in case (some applications might depend on it).

The Files Section
This is probably the most important section. Here you list all the files you want to install.
Here are some examples:
First I install My application exes and dlls. After that I install the App.Config files. Then I install the Database and the Xml files. Then I install the resources. Last I install third-party dlls (simply copy the files).

[Files]
; MyApplication.exe
Source: "C:\Projects\My Application\bin\MyApplication.exe"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;
Source: "C:\Projects\My Application\bin\MyApplication.tlb"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly regtypelib;Attribs: readonly;
Source: "C:\Projects\My Application\bin\MyApplication.xml"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;
Source: "C:\Projects\My Application\bin\configuration.tbs"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly;
;Basic Software
Source: "C:\Projects\My Application\bin\Server.exe"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;
Source: "C:\Projects\My Application\bin\BusinessObjects.dll"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;
Source: "C:\Projects\My Application\bin\CommonControls.dll"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;
Source: "C:\Projects\My Application\bin\Forms.dll"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;
Source: "C:\Projects\My Application\bin\BasicForms.dll"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly; Attribs: readonly;
Source: "C:\Projects\My Application\bin\Grid.dll"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly; Attribs: readonly;

;Module1
Source: "C:\Projects\My Application\bin\Module1Manager.exe"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;
Source: "C:\Projects\My Application\bin\Module1Viewer.exe"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;

;Applications Configuration Files
Source: "C:\Projects\My Application\bin\MyApplication.exe.config"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;
Source: "C:\Projects\My Application\bin\Module1Manager.exe.config"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;
Source: "C:\Projects\My Application\bin\Module1Viewer.exe.config"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;
Source: "C:\Projects\My Application\bin\Server.exe.config"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;

;Additional files
Source: "C:\Projects\My Application\bin\DB\Database.mdb"; DestDir: "{app}\bin\DB"; Flags: ignoreversion overwritereadonly uninsremovereadonly
Source: "C:\Projects\My Application\bin\Config\ConfigurationFile.xml"; DestDir: "{app}\bin\Config"; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;
Source: "C:\Projects\My Application\bin\Resources\*.bmp"; DestDir: "{app}\bin\Resources"; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;

;Additional Dlls
Source: "C:\Projects\IOS\STU\bin\AxInterop.SHDocVw.dll"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;
Source: "C:\Projects\IOS\STU\bin\Interop.SHDocVw.dll"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;
Source: "C:\Projects\IOS\STU\bin\Nini.dll"; DestDir: "{app}\bin"; Flags: ignoreversion overwritereadonly uninsremovereadonly;Attribs: readonly;

In addition I need to Install several MFC dlls, because one of my apps is from MFC in VC6. In that case I add the following section:

;System Files
Source: "C:\WINNT\system32\MFC80d.dll"; DestDir: "{sys}"; Flags: allowunsafefiles
Source: "C:\WINNT\system32\msvcr80d.dll"; DestDir: "{sys}"; Flags: allowunsafefiles
Source: "C:\WINNT\system32\MFC42d.dll"; DestDir: "{sys}"; Flags: allowunsafefiles
Source: "C:\WINNT\system32\MSVCRTD.DLL"; DestDir: "{sys}"; Flags: allowunsafefiles
Source: "C:\WINNT\system32\MFCO42D.DLL"; DestDir: "{sys}"; Flags: allowunsafefiles
Source: "C:\WINNT\system32\MFCN42D.DLL"; DestDir: "{sys}"; Flags: allowunsafefiles

The {sys} folder is the System32 folder on the client's machine. It will not replace the files if newer versions exist.

Next, we need to prepare the .NET Framework and MDAC installations. We will put the files in the folder of the iss file, pack the files to the installation file. When the client will run the setup, we will unpack the installation files into the folder we marked earlier to remove after installtion. This is simply to copy the installation file. It will NOT run the setup files:

;.NET Framework
Source: "C:\Share\installs\Developer\ Installation\dotnetfx.exe"; DestDir: {app}\DotNet; Tasks: DotNetFramework; Flags: deleteafterinstall;

;MDAC
Source: "C:\Projects\IOS\STU\ Installation\MDAC_TYP2.8.EXE"; DestDir: "{app}\MDAC"; Tasks: MDAC; Flags: deleteafterinstall;

Notice the Tasks attribute - we only unpack the files if the user asked us to. The installation files will be deleted after the installation as well. You can download those executables free from the internet.

Last, note that if you need to register a COM dll, or an OCX, you should use the regserver flag.

Icons Section
This will create the relevant shortcuts and icons on the client's computer.

[Icons]
Name: "{group}\My Application - Proffesional Edition"; Filename: "{app}\bin\MyApplication .exe"; WorkingDir: "{app}\bin"
Name: "{group}\{cm:UninstallProgram,My Application - Proffesional Edition}"; Filename: "{uninstallexe}"
Name: "{userdesktop}\My Application - Proffesional Edition"; Filename: "{app}\bin\MyApplication .exe"; Tasks: desktopicon ; WorkingDir: "{app}\bin"
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\My Application - Proffesional Edition"; Filename: "{app}\bin\MyApplication .exe"; Tasks: quicklaunchicon ; WorkingDir: "{app}\bin"

Registry and Environment Variables
Here is an example of how to install an environment variable on the client's machine. The same way applies to all registry keys.

[Registry]
Root: HKLM ; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; ValueName: "VARNAME"; ValueData: "VALUE"; Flags: uninsdeletevalue

Note that the running application will not be notified of the new environment variables, and will need to restart (there is a way to solve this issue).

Installing the .NET Framework, MDAC, and other installations
The Run section allows you to run additional installations. We need to run the installation of the .NET Framework, and MDAC. We use certain flags to make this a Silent Installation.
[Run]
;.NET Framework 1.1
Filename: "{app}\DotNet\dotnetfx.exe"; Parameters:"/q:a /c:""install /q""" ;StatusMsg: "Installing Microsoft .NET Framework (this may take a few minutes)"; Flags: skipifdoesntexist; Tasks: DotNetFramework; WorkingDir: {app}\DotNet; Description: Installs the DotNET Framework 

;MDAC
Filename: "{app}\MDAC\MDAC_TYP2.8.exe"; Parameters:"/Q:A /C:""dasetup /Q:D /N"""; WorkingDir:"{app}\MDAC"; Flags: skipifdoesntexist; Tasks: MDAC; StatusMsg: "Installing Access, OleDb and JET Providers (MDAC) (This may take a few minutes)"; Description: Installs MDAC
Testing your install file
After the script is done, we can compile it using Inno Setup. A setup executable will be generated for us, which we should ship to the client and he should be able to install and run your application.

Great, huh? Well, imagine a client installing your application only to find out that it doesn't work because you forgot a dll? What would happen if you by mistake ruined the Client's registry, or deleted his Environment Variable? What about when uninstalling you by mistake deleted the Windows folder?

Don't send it anywhere until you test it. The best way to test it is by using a Virtual PC. Create yourself one Virtual PC of Windows 2000, and one for Windows XP. Create a backup before you play with it. Then run the installation on the Virtual PC. Don't forget to uninstall and see the results. You get several benefits by applying this method:
1. You can be 100% sure that you cannot do any damage, and that everything work.
2. If something went wrong you can fix it, delete the VPC, and try again (don't forget to backup the original VPC).

No comments:

Post a Comment