Click here to Skip to main content
Email Password   helpLost your password?
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 its compiler. Beside the NSIS compiler comes a lot of handy tools and plug-ins, mostly written by the NSIS community. I put a link to those I used in this article here:

The Big Picture

To create a simple installer with NSIS, we should start learning the NSIS scripting language. The idea is that we create a single (or multiple) file(s) that contain(s) the script instructions. Then we ask the 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 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 quite 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 the NSIS Developer Center, you can download other IDEs or integrations to current Editors like Eclipse. Here's a link to the available text editors page. In the 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 the NSIS Script CHM help file.

HM Editor

After running the IDE, you can either type in commands or use the wizard to create a template for a simple installer. Personally, I didn't learn to use 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 the 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 names, each of these instructions has special meanings to the compiler. Below is a sample script that has three of the so-called instructions.

Dividing A Script

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

Dividing A Script

The above example section was created in a way that is hidden. We also did not have any functions. Later on, we will examine them in more detail.

Attributes

Attributes are instructions that define the behavior of the installer, its look and feel or define the behavior of the commands that will come after them. For example, the Name attribute defines the name of the installer that will be shown at the 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 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 to create a new NSI script file, type the following 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 other files, using the !include compile-time command.

*.nsh files contain 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 a *.nsh extension.

Please note that when using macros, we need to use both the $ sign and {} while in variables it is 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 the var command. To use it, we must use a $ before the variable name:

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

Notes when using variables:

Predefined variables:

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

Constants

Here's a good place to name some of the useful constants that NSIS defines for us. They also added these to their variable part of the help. Remember the InstallDir attribute? If you don't, it is the attribute that sets the installation directory for us. Unless you are sure about where exactly you want your installer files to be copied (like C:\MyFolder), you'll need to know some paths like the Windows folder path or the 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 the $PROGRAMFILES constant in front of the InstallDir attribute, which expected a string. $PROGRAMFILES will be replaced by the exact path string on the user's 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, the 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.
$WINDIR The Windows directory.
$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 that 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 well-known Microsoft Office that lets you chose to install, say, Powerpoint or not.

In NSIS, these options can be given to a user with the help of sections. In our example, Microsoft Powerpoint should have 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 and you can put it MySec if you want. We are free to put any amount of instruction we like inside a section block. I'm not going to describe sections in detail here; it makes this article too lengthy. However, there's a lot that you can find on sections, like how to disable a section, or re-enable it, how to create section groups, how to show section names 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 sections. We want the first one to be visible. The second section above will be called when uninstalling. The 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 application folder is really DANGEROUS. The reason is quite simple. Think about a beginner user that changes the default folder path to C:\Program Files. You save the path when installing and, upon calling RMDir, the 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 the user if he/she would like 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 currently over 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 the function section in NSIS help.

Lab Lab #3

Let's add two functions to our installer: a callback one 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 the pages being shown. Then we copy a message into the $0 variable that has already been defined for us by the compiler. Finally, we push it so that we can use it in a function later on and we call a function.

The user function tries to pop a string off the stack and into $R0 that again has already been defined by the compiler. We check to make sure it has a value. If it does 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 stacks), use variables and how easy it is to use logiclib, which we already included. If we were not to use logiclib, we would have to write a code similar to IBM x86 assembly instead of that of $IF.

Pages

"Each (non-silent) NSIS installer has a set of pages. Each page can be an NSIS built-in page or a custom page." At the time I wrote this article, there was a new version of NSIS available that gave better support for custom pages, but I was too lazy to read them. I leave that to the enthusiastic 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 the 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 the UI is to use a modern UI. It's not only simpler, but also much more familiar and nicer. Modern UI is included in NSIS after version 2.0. Documentation to the UI is available here. As I'm writing this article, the second version of the 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. The 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 is showed. It is, for example, useful when we want to ignore the next page or 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 a license page. The function checks a variable to see if the application already exists. If it exists, the license page will be ignored and the next page will be shown.

Lab Lab #4

If we run our installer right now, it will work! It uses sections, our attributes and also some default behavior to do required operations. The problem is that there is not any customization available to the 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 how we used 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 the section index and it is used to access a section.

The last 8 lines are there just to show "A Section" text when the 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 Executes 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 the 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 the existence of file(s) IfFileExists "$INSTDIR\${SQL_DATABASE_NAME}.mdf" 0 Goon_label
Delete Deletes 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 have an 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 be sure find what you need quickly. Good luck!

Acknowledgement and Disclaimer

Most of the text and codes here have been extracted from NSIS help and samples. I tried to put quotes around texts, but for codes, it would rather make it dirty. Thanks to the NSIS community, there are plenty of codes available to download here.

History

2008/03/04 : Tutorial created

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralHow to call external dll function
Dudi Daabul
0:23 1 Nov '09  
Hi all.

I try to call from NSIS to a function of a C++ dll I wrote. The dll is called GenUtils.dll, and the function has the following prototype:
int IsProcessRunning(std::string strExecName)

I tried calling the function by using the following command:
System::Call 'GenUtils::IsProcessRunning(t 'MyProcess.exe') i .r8'
As far as I understand, it means that the return value of the function, which is int, will be placed in NSIS variable, $8.
The problem is that this script compiles, but crushes at run time. Moreover, if I call, from the same dll, another function, that receives and returns no parameters,
,such as:
System::Call 'GenUtils::SayHello(v) v'
everything works just fine. Does anybody know what the problem here? It's an extremely urgent problem, that stops me from delibvering the product

Thanks,
David
GeneralThanks
mpino
1:30 22 May '09  
Thanks Hamed for sharing it with us. Your article is very clear and helpful.

Best regards
Manuel
GeneralRe: Thanks
Hamed Mosavi
23:28 22 May '09  
It's my pleasure Manuel. Glad you like it.

Thank you too for kind comments. Smile


"I hope you live a life you're proud of. If you find that you're not, I hope you have the strength to start all over again."    
 - I wish I knew who is this quote from
 

GeneralNSIS sample
BoscoBill
15:38 30 Oct '08  
Very nice.
I was going through the NSIS stuff concurrent with reading this.
This article is well done.
Thanks, Hamed.
GeneralRe: NSIS sample
Hamed Mosavi
18:53 30 Oct '08  
Thanks for kind words.

Glad you like it. Smile

"In the end it's a little boy expressing himself."    Yanni


GeneralConfusing....
rittjc
17:15 10 Mar '08  
In the intro, I see a modern day (> the year 1985) installation dialog from a normal installer. But when I see the installer screen in the example, it is a cheesmo (American term for crappy) Dos-window-like box that is logging the installation results as a installer log. This would make the user think he or she had made a major mistake either buying or getting software from developers that would expose them to such techno-uninteresting stuff. What happened to the screen at the top of the article. You can't have a cheesmo installation and expect users to take you seriously.

Not trying to be a butt-hole, but am I missing something?
GeneralRe: Confusing.... [modified]
Hamed Mosavi
19:51 10 Mar '08  
Hi.

If you download the demo setup executable file ([modified] or compile the sample included [/modified] ) and compare it to the first snapshot at the top of the article, you notice the only difference is the image on the left. However in next pages, you also notice the difference at the top.

We can easily ask NSIS to change setup images. I downloaded a free theme(for NSIS a set of images and icons) called: MUIOrangeVistaTheme.

Then in my NSIS script:

; Icons, this changes icon of the exe file
!define MUI_ICON "${OMUI_THEME_PATH}\installer-nopng.ico"
!define MUI_UNICON "${OMUI_THEME_PATH}\uninstaller-nopng.ico"

;Header behavior: show header image, it's right to left and text background should be transparent
!define MUI_HEADERIMAGE
!define MUI_HEADERIMAGE_RIGHT
!define MUI_HEADER_TRANSPARENT_TEXT

;Images: change big welcome image in both installer an un installer and change header image
!define MUI_WELCOMEFINISHPAGE_BITMAP "${OMUI_THEME_PATH}\wizard.bmp"
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "${OMUI_THEME_PATH}\wizard-un.bmp"
!define MUI_HEADERIMAGE_BITMAP "${OMUI_THEME_PATH}\header-r.bmp"
!define MUI_HEADERIMAGE_UNBITMAP "${OMUI_THEME_PATH}\header-r-un.bmp"

; We can also change the branding text that normally shows Nullsoft insta...
BrandingText "My Trademark™"


Please note that ${OMUI_THEME_PATH} points to the folder I copied the theme.


[modified] About the example inside the article, it's because it's using normal pages. I stated that it's more nice to use modern UI. That's why in first step of labs I included: !include "MUI.nsh"[/modified]

Did this help? Smile

// "In the end it's a little boy expressing himself." Yanni
while (I_am_alive)
{
    cout<<"I love to do more than just programming.";
}

modified on Tuesday, March 11, 2008 1:16 AM

GeneralRe: Confusing.... [modified]
rittjc
13:07 11 Mar '08  
I get it now. I looked at the site you referenced. Looks like an excellent solution. Thanks for taking the time to post a tutorial here. I give you a 5 for the work!

Regards,
Jim

modified on Tuesday, March 11, 2008 6:15 PM

GeneralRe: Confusing....
Hamed Mosavi
21:02 11 Mar '08  
I think, now I understand you. You didn't like that log window and I thought you didn't like the page layout.

Let me solve a few confusions:

To hide the log window use these attributes: ShowInstDetails, ShowUnInstDetails. In my article I added them and also in a lab I used them. Usage:

ShowInstDetails hide ; this hides the log window, but the button to show it exists so user can click it to show details

ShowInstDetails nevershow ;prevent user from seeing log.



To see old style page layout like my example, use page command. To use new look and feel use modern UI. like first snapshot of the article.

To customize default behavior of pages in modern UI, read modern UI documentation(link is provided in the article.) In last post I sent some of them.

// "In the end it's a little boy expressing himself." Yanni
while (I_am_alive)
{
    cout<<"I love to do more than just programming.";
}

GeneralRe: Confusing....
Hamed Mosavi
21:12 11 Mar '08  
rittjc wrote:
Thanks for taking the time


It's my pleasure

rittjc wrote:
I give you a 5


That's kind of you Jim. I appreciate it.


I'm happy that someone found this article useful. Big Grin

// "In the end it's a little boy expressing himself." Yanni
while (I_am_alive)
{
    cout<<"I love to do more than just programming.";
}

GeneralVery nice
Nick Butler
6:13 8 Mar '08  
A quality article - thanks Hamed Cool

----------------------------------
Be excellent to each other Smile

GeneralRe: Very nice
Hamed Mosavi
6:30 8 Mar '08  
Nick Butler wrote:
A quality article - thanks Hamed


You're welcome. Smile

// "In the end it's a little boy expressing himself." Yanni
while (I_am_alive)
{
    cout<<"I love to do more than just programming.";
}

GeneralAuthor - Article update
Hamed Mosavi
20:18 7 Mar '08  
I keep an updated copy of this article here: http://www.sarvsoft.com/nsis[^]

It's easier to update my articles in my own host, so it might be updated sooner than the above article.

// "In the end it's a little boy expressing himself." Yanni
while (I_am_alive)
{
    cout<<"I love to do more than just programming.";
}

modified on Wednesday, March 12, 2008 2:16 AM


Last Updated 7 Mar 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010