Creating an Installer

Sample Image

Introduction

There are many tools to create an Installer for our applications. I used two of them. The second one was NSIS and since I learned it, I did not go further. 

This article introduces NSIS and shows how to create a simple setup application using powerful, yet easy, NSIS scripts.

Lab Lab #0
Labs come at the end of each part of this article. In each lab we will examine a bit more than what we talked about in that part. In the final lab, we will have a complete setup application.

Table of contents

Ingredients

Before starting NSIS, we first need to get it's compiler. Beside the NSIS compiler, comes a lot of handy tools and plug-ins, mostly written by NSIS community. I put a link to those I used in this article here:

Download NSIS setup from: http://nsis.sourceforge.net/Download
Download HM NISEdit from: http://hmne.sourceforge.net/index.php#Download

The big picture

To create a simple installer with NSIS, we should start learning NSIS scripting language. The idea is that we create a single (or multiple) file(s) that contains the script instructions, then we ask NSIS compiler to compile the file and create a setup executable file.

Compiling a script

After downloading and installing NSIS, using the link provided above, all we need to do is to create a new text file called WhatEverName.nsi, type down our commands, right click on the file and select 'Compile NSIS Script'.

Compiling A Script

It's not quiet bad, but it's much better to have an IDE, isn't it? The second link above refers to a good yet small IDE for NSIS called "HM NIS Edit".

In NSIS Developer center you can download other IDE's or integrations to current Editors like Eclipse. Link to available text editors page is: http://nsis.sourceforge.net/Category:Development_Environmentsnmen

In HM editor we can also compile and run our installers, without the need to exit the IDE. We have a wizard, a page designer, and also F1 launches NSIS Script chm help file.

HM Editor

After running the IDE, you can either type in commands or use the wizard to craete a template for a simple installer. Personally I didn't learn using NSIS scripts until I started creating my own instructions, from scratch.

Scripts in detail


We got NSIS, an IDE and we know we shall write some instructions to create an installer. But what can we type in?
In NSIS each line is considered a new instruction. The instruction will be known by its name.

If we look at instructions of a sample NSIS script file, we will see some of these parts: Pages, Sections, Functions, variables and other compiler commands or attributes. Depending to their name, each of these instructions has special meanings to compiler. Below is a sample script that has three of the so called instructions.

Dividing A Script

Understanding pages and sections is quiet easy, since they represent visual equivalent in the final compiled installer. The above script, if compiled, will produce the following installer:

Dividing A Script
In the above example section created in a way that is hidden. We also had not any functions. Later on we will examine them in more details.

Attributes

Attributes are instructions that define behavior of the installer, it's look and feel or define behavior of the commands that will come after them. For example Name attribute defines the name of the installer that will be shown at title bar of the installer window.
    
        Name "MyProduct Version 1.0"
    
The following table lists some of the useful attributes that you can use in your script:

Attributes What it does
Name Sets the name of the installer.
OutFile Specifies the output file that the MakeNSIS should write the installer to. Name of the installer file(like MySetup.exe)
InstallDir Sets the default installation directory.
ShowInstDetails Sets whether or not the details of the install are shown.
ShowUnInstDetails Sets whether or not the details of the uninstall are shown.
SetCompressor Sets the compression algorithm used to compress files/data in the installer.
SetCompressorDictSize Sets the dictionary size in megabytes (MB) used by the LZMA compressor.

Lab Lab #1
Using HM NIS Edit create a new nsi script file. type the folloeing lines into it.

!include "LogicLib.nsh"
!include "MUI2.nsh"
 
!define PRODUCT_NAME "CP Lab"
!define SETUP_NAME "CPLabSetup.exe"
!define PRODUCT_VERSION "1.0"

 
OutFile ${SETUP_NAME}
Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"

 
;Default installation folder
InstallDir "$PROGRAMFILES\CPLab"
 
;Get installation folder from registry if available InstallDirRegKey HKLM "Software\CP Lab" ""
 
ShowInstDetails show
ShowUnInstDetails show
 
SetCompressor /SOLID lzma
SetCompressorDictSize 12
 
;Request application privileges for Windows Vista RequestExecutionLevel user ;Could be 'admin'

In NSIS script, we can add macros (lines started with !define )and also we can use scripts in another files, using !include compile time command.

.nsh files contains normal NSIS Scripts, just like our script. The only difference is that we put .nsi to compile our script, but to include another one we have to give that file .nsh extension.

Please note that when using macros, we need to use both $ sign and {} while in variables it was just the $ sign.

Variables

They hold values for us, just like what we have in our programming languages. To define a variable we need to use var command. To use it, we must use a $ before variable name:

    
    var varName
    
    ...
    
    
    StrCpy $varName "example value"

    
Notes when using variables:

 

Predefined variables:

There are some variables that already defined in NSIS and we can use them without declaration. Also we must be careful and prevent from possible conflicts in our names with these:

For more information about variables refer to the variables section of the NSIS help.

 

Constants:

Here's a good place to name some of the useful constants that NSIS defined for us. They also added these to their variable part of the help. Remember InstallDir attribute? If you don't it was the attribute that mean to set installation directory for us. Unless you are sure about where you want your installer files to be copied exactly (like C:\MyFolder), you'll need to know some paths like Windows folder path or program files folder path. Constants help you here. At runtime they will be replaced with end user computer paths. Please look at the following example:
    
    InstallDir "$PROGRAMFILES\MyApp"
    
Here we used $PROGRAMFILES constant in front of the InstallDir which expected an string. $PROGRAMFILES will be replaced by the exact path string on users' computer. The following table shows a list of useful constants:

Constant What it means
$PROGRAMFILES Usually C:\ program files but may be different on different machines
$PROGRAMFILES32, $PROGRAMFILES64 On Windows X64, first one points to C:\Program Files (x86) while the second points to C:\Program Files
$DESKTOP The Windows desktop directory (usually C:\Windows\Desktop but detected at runtime).
$EXEDIR The directory containing the installer executable.
$EXEPATH The full path of the installer executable.
${NSISDIR} A symbol that contains the path where NSIS is installed. Useful if you want to call resources that are in NSIS directory e.g. Icons, UIs etc.
$WINDIR The Windows director
$SYSDIR The Windows system directory
$TEMP The system temporary directory
$STARTMENU The start menu folder (useful in adding start menu items using CreateShortCut).
$SMSTARTUP The start menu programs / startup folder. The context of this constant (All Users or Current user) depends on the SetShellVarContext setting. The default is the current user.
$DOCUMENTS The documents directory. The context of this constant (All Users or Current user) depends on the SetShellVarContext setting. The default is the current user.
$APPDATA The application data directory. The context of this constant (All Users or Current user) depends on the SetShellVarContext setting. The default is the current user.
$CDBURN_AREA A directory where files awaiting to be burned to CD are stored. This constant is available on Windows XP and above.

For a complete list of constants or requirements for them, please refer to the variables section of the NSIS help.

Sections

"Each NSIS installer contains one or more sections." That means, we need to have at least one section in our script. But what is the use of a section? You have certainly seen dozens of installers that let you choose what to install. Like the very known MS Office that let you chose to install say Powerpoint or not to do that.

In NSIS these options can be given to a user with the help of sections. In our example MS Powerpoint should has a particular section, and that section indeed has other sections for each feature you select to be installed.

It's logical to have at least one section, since we have at least one feature to install. Don't we?
Sections can be hidden, so the user will not see any feature to select one of them, which most probably, will be used in installers with only one section.
    
    Section "Installer"

        ; Instructions go here

    SectionEnd
    
In the above example, I created a new section and named that 'Installer' (Don't let the name fool you, it's just a name. you can put it MySec if you want.)
We are free to put any amount of instructions we like inside a section block.

I'm not going to describe sections in detail here. It makes this article too lengthy. But there are a lot that you can find in sections, like how to disable a section, or re enable it, how to create section groups, how to show section name in bold, make it optional or forced, etc.

Lab Lab #2
Reload previous file into your editor and add the following:

Section "Dummy Section" SecDummy
 
  SetOutPath "$INSTDIR"
 
  ;Store installation folder
  WriteRegStr HKCU "Software\CP Lab" "" $INSTDIR
 
  ;Create uninstaller
  WriteUninstaller "$INSTDIR\Uninstall.exe"
 
SectionEnd
 

 
Section "Uninstall"
 
  Delete "$INSTDIR\Uninstall.exe"
 
  RMDir "$INSTDIR"
 
  DeleteRegKey /ifempty HKCU "Software\CP Lab"
 
SectionEnd

The above code creates two section. We want the first one to be visible. The Second section above will be called when uninstalling. NSIS Compiler realizes that because of the name. If a section name is 'UnInstall' or it starts with 'un.' it will be called when uninstalling.

WARNING:Please note that calling RMDir to remove our app folder is really DANGEROUS. The reason is quiet simple. Think about a beginner user that changes default folder path to C:\Program Files. You save path when installing and uppon calling RMDir installer tries removing the whole program files folder.

Functions

"Functions are similar to Sections in that they contain zero or more instructions." There are two types of functions. User functions and Callback functions. User functions will be called manually by using the Call instruction. "Callback functions will be called by the installer when a certain event occurs."

"Functions must be declared outside of Sections or other Functions."
    
    Function func
      ; some commands
    FunctionEnd

    Section
      Call func
    SectionEnd
    

The above code shows how to create a user function. There is also another type of function called callback. Callback functions are known by their names. The names are unique and the NSIS compiler can recognize them. These functions will be called upon special events and you're free to put your favorite instructions in them, so that upon the event, they'll run.
    
     Function .onInit
       MessageBox MB_YESNO "This will install. Continue?" IDYES NoAbort
         Abort ; causes installer to quit.
       NoAbort:
     FunctionEnd
    
The above code shows how to write .onInit so that as soon as the installer starts, a message box asks user if he/she is interested to continue.

The following table lists some of the common Callback functions.
Callback function name When it's called
.onInit Will be called when the installer is nearly finished initializing. If the '.onInit' function calls Abort, the installer will quit instantly.
.onUserAbort Is called when the user hits the 'cancel' button, and the install hasn't already failed. If this function calls Abort, the install will not be aborted.
.onInstFailed Is called when the user hits the 'cancel' button after the install has failed.
.onMouseOverSection is called whenever the mouse position over the sections tree has changed. This allows you to set a description for each section for example. The section id on which the mouse is over currently is stored, temporarily, in $0.
un.onInit Will be called when the uninstaller is nearly finished initializing. If the 'un.onInit' function calls Abort, the uninstaller will quit instantly.

For more information on functions, refer to function section in NSIS help.

Lab Lab #3
Let's add two functions to our installer. A callback and a user one.

Function .OnInit
 
  StrCpy $0 "Welcome to my first setup wizard"
 
  push $0
 
  Call ShowWelcome
 
FunctionEnd
 

 
Function ShowWelcome
 
  pop $R0
 
  ${If} $R0 == ''
    StrCpy $R0 "Message from function"
  ${EndIf}
 
  MessageBox MB_OK $R0
FunctionEnd

Here we create a callback function .onInit that will be called whenever the installer starts up, before pages being shown. Then we copy a message into $0 variable that has already been defined for us by the compiler, and finally push it, so that we can use it in a function later on. Finally we call a function.

The user function tries to pop a string off the stack and in to $R0 that again has been defined already by the compiler. We check to make sure it has a value, and if it has not we put another value into $R0 and we finally show a message box.

This shows how we create functions, pass arguments (using built-in stack), use variables and how easy it is to use logiclib that we already included. If we were not to use logiclib we had to write a code similar to IBM x86 assembly instead of that $IF.

Pages

"Each (non-silent) NSIS installer has a set of pages. Each page can be a NSIS built-in page or a custom page".

At the time I wrote this article there's a new version of NSIS available that gives better support for custom pages, but I was too lazy to read them. I leave that to the enthusiast reader.

The page instruction is simple:
    
    Page license
    
The above code shows a simple empty license page, to force it to show your license file, you should use page options. In fact not only the license page, but also all other default pages have options that can be provided by the help of attributes.

Another way of coding UI is to use modern UI. It's not only simpler but also much familiar and nicer. Modern UI is included to the NSIS after version 2.0. Documentation to the UI is available here: http://nsis.sourceforge.net/Docs/Modern%20UI/Readme.html

When I'm writing this article, the second version of modern UI is available with even better features.

    
    !insertmacro MUI_PAGE_LICENSE "License.rtf"
    
The above code adds a license page and loads the specified file to be signed by the user.

last important thing in the pages is that they all have events (remember callbacks?) With these callback handlers you can simply modify the way they behave or decide what to do after or before a page showed. It's for example, useful when we want to ignore next page or to enable/disable some options according to previous pages.
    
    !define MUI_PAGE_CUSTOMFUNCTION_PRE PAGE_LICENSE
    !insertmacro MUI_PAGE_LICENSE "License.rtf" "" PreLicense
    
    ...
    
    Function PreLicense

        ; If app installed already, license signed, ignore it
        ${If} $bAppExists == '1'
        Abort
        ;${Else}
        ${EndIf}

    FunctionEnd
    
    
The above code calls a function before displaying license page. The function checks a variable to see if application already exists. If it exists, the license page will be ignored and next page will be shown.

Lab Lab #4
If we run our installer right now, it will work! It uses sections and our attributes and also some default behavior to do required operations. The problem is that there is not any customization available to user. Let's get user some choices using pages of the modern UI.

!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "License.txt"
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
 
!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH
 
!insertmacro MUI_LANGUAGE"English"
 
;--------------------------------
;Descriptions
 
;Language strings
LangString DESC_SecDummy ${LANG_ENGLISH} "A section"
 
;Assign language strings to sections
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${SecDummy} $(DESC_SecDummy)
!insertmacro MUI_FUNCTION_DESCRIPTION_END

Just add some pages, and we're done. Before we finish this lab, please note that how we used the SecDummy to add a description to the 'select feature' page. If you return back and take a look at sections, you'll notice that other than the name, there's a SecDummy at the end of the line. In fact this is called section index and it is used to access a section.
The last 8 lines are there just to show "A section" text when mouse is over the "Dummy Section".

Useful instructions

The following table lists some of the instructions that you will probably need.

Instruction What it does Usage
File Adds file(s) to be extracted to the current output path ($OUTDIR). File "Bin\7z\*.*"
Quit Causes the installer to exit as soon as possible.
ExecWait Execute the specified program and wait for the executed process to quit. ExecWait 'c:\SomeProgram.exe' $0
DetailPrint Adds the string "message" to the details view of the installer. DetailPrint "message"
Strlen Sets user variable $x with the length of str. Strlen $0 ${SETUP_NAME}
ReadRegStr Reads from the registry into the user variable. ReadRegStr $0 HKLM Software\NSIS ""
CopyFiles Copies files from the source to the destination on the installing system. CopyFiles "$0\cid.dll" "$INSTDIR"
CreateDirectory Creates (recursively if necessary) the specified directory. CreateDirectory "7z"
CreateShortCut Creates a shortcut 'link.lnk' that links to 'target.file', with optional parameters 'parameters'. CreateShortCut "$SMPROGRAMS\$STMenuDirectory\Help.lnk" "$INSTDIR\Yas.chm"
IfFileExists Checks for existence of file(s). IfFileExists "$INSTDIR\${SQL_DATABASE_NAME}.mdf" 0 Goon_label
Delete Delete file (which can be a file or wildcard, but should be specified with a full path) from the target system. Delete '"$INSTDIR\${SQL_DATABASE_NAME}.mdf"'
DeleteRegKey Deletes a registry key. DeleteRegKey[/ifempty] root_key subkey
DeleteRegValue Deletes a registry value. Valid values for root_key are listed under WriteRegStr. DeleteRegValue root_key subkey key_name

Final note

Please note that the installer included in this article will create a folder in your program files named CPLab. Inside the folder you can Uninstall.exe that will clean up everything for you.

There are still plenty features or needed tips to mention here but if I do that, then I'm putting the NSIS help in another order here. I think this is enough to start creating your installers and as I did when creating my own you'll sure find what you need quickly. Good lock!

Acknowledgement and Disclaimer

Most of the text and codes here had been extracted from NSIS help and samples. I tried to put quote around texts, but for codes, it would rather make it dirty.
Thanks to NSIS community, there are plenty of codes available to download here: http://nsis.sourceforge.net/Category:Code_Examples

History

2008/03/04 : Tutorial created