Creating a .NET bootstrapped installer using NSIS

If you have ever deployed .NET windows applications, you have without doubt tried the Visual Studio Install project type. The Install project will create .MSI install applications for you, they're great for basic installations, but nothing more than that. The .NET bootstrapper is quite lacking, at times it won't be able to find the framework download file as it's changed it's location, at other times it's not able to download it. And finally, if it does determine that the user needs the framework, it's shown in an ugly uncustomizable GUI.

I've looked for an alternative, I'd prefer not to pay for using some of the well established installers such as Wise, InstallShield, ActiveInstall and so forth. What I found is a remnant of WinAmp, the NSIS (Nullsoft Scriptable Install System) project.

NSIS is both free and open source and it's very much community driven. There are a plethora of plugins available for all sorts of different tasks, and of course you can write your own plugins for special needs.

I won't delve too deeply into why you should choose NSIS over any of the competitors, instead I'll show you a step by step guide of how to create an NSIS installer that bootstraps the .NET 2.0 framework as well as running custom install and uninstall actions in .NET code.

You will need two tools. The first (and actually the only required tool) is NSIS itself: Download NSIS from nsis.sourceforge.net. While you can edit the install script in any text editor, using an IDE like HM NIS Edit (HMNE) makes it a whole lot easier: Download HMNE from hmne.sourceforge.net.

Let's first create the simple application that we will be installing. It doesn't really matter what it is, in this example I've made a single Windows Forms Application:

Image: nsis_1
Now let's start up HMNE. Click File -> New Script From Wizard. Fill in the relevant application date on the first page.

Image: nsis_2
You may choose a custom icon for the installer, the default icon is an NSIS standard icon. You can also choose the resulting installation files name as well as it's output location. If no location is specified, it will be outputted to the location that contains our script file. A great feature of NSIS is that it's got localized versions for a lot of different languages built in, simply select the languages that the user should be able to select and the installer will automatically be localized. You can choose a couple of different GUIs, I personally prefer the Modern one. As for compression, in my tests the LZMA compression works the best, though compression time and CPU usage might be a factor for very large projects.

Image: nsis_3
Now you specify where you want your application to be installed, notice the general use of variables like $PROGRAMFILES, $NSISDIR, $INSTDIR and so forth. You can also optionally choose a license file that the user must accept to continue the installation.

Image: nsis_4
Now you can setup the actual files that will be installed as part of the project. You cannot select a whole project output as you can in the Visual Studio Install project, instead you must manually select the files that should be installed, usually from the Debug/Release directories of your project(s).

Image: nsis_5
Optionally you can select which links you want to be placed in the application program group in the start menu, if such one should be created at all.

Image: nsis_6
When the installation is done, the user can choose to view the Readme file and/or start the application - that is, if you specify an application and/or a readme file.

Image: nsis_7
Finally we can choose to include an uninstaller, as well as specifying our custom uninstallation confirmation and report texts. If no custom text is specified, the default texts will be used.

When the wizard finishes, make sure to click "Save script" and "Convert file paths to relative paths".

Finally the install script is made and ready to run. Press Ctrl+9 to compile the script. If everything succeeds, you'll see the Setup.exe file in the same directory as the one where you saved the install script. I will not be going over the various commands and settings that are being set in the script, for that I strongly recommend the excellent built in help documents, as well as the WinAmp NSIS forums. Instead I'll focus on how to bootstrap the .NET Framework 2.0 and how to run custom install and uninstall actions.

Add these three lines to the top of your nsi file, they include some functions that we will need:

; Script generated by the HM NIS Edit Script Wizard.
!include WordFunc.nsh
!insertmacro VersionCompare
!include LogicLib.nsh

Add this line right above the .onInit function, it makes a variable (untyped) that'll contain a Yes/No value, depending on whether we need to install the .NET Framework or not.

Var InstallDotNET

Now modify the .onInit function so it matches the below, as well as adding the GetDotNETVersion function. First we ask the user what language they want to continue in (!insertmacro MUI_LANGDLL_DISPLAY). After that we initialize the InstallDotNET variable to "No". Depending on the result of the GetDotNETVersion we tell the user that we need to install the framework, either because the user does not have the framework at all, or because the version is less than 2.0. We won't actually install the framework yet, we'll just remember whether we have to or not.

Function .onInit
  !insertmacro MUI_LANGDLL_DISPLAY
  
  ; Check .NET version
  StrCpy $InstallDotNET "No"
  Call GetDotNETVersion
  Pop $0
  
  ${If} $0 == "not found"
        StrCpy $InstallDotNET "Yes"
  	MessageBox MB_OK|MB_ICONINFORMATION "${PRODUCT_NAME} requires that the .NET Framework 2.0 is installed. The .NET Framework will be downloaded and installed automatically during installation of ${PRODUCT_NAME}."
   	Return
  ${EndIf}

  StrCpy $0 $0 "" 1 # skip "v"

  ${VersionCompare} $0 "2.0" $1
  ${If} $1 == 2
        StrCpy $InstallDotNET "Yes"
  	MessageBox MB_OK|MB_ICONINFORMATION "${PRODUCT_NAME} requires that the .NET Framework 2.0 is installed. The .NET Framework will be downloaded and installed automatically during installation of ${PRODUCT_NAME}."
   	Return
  ${EndIf}
FunctionEnd

Function GetDotNETVersion
	Push $0
	Push $1

	System::Call "mscoree::GetCORVersion(w .r0, i ${NSIS_MAX_STRLEN}, *i) i .r1"
	StrCmp $1 "error" 0 +2
	StrCpy $0 "not found"

	Pop $1
	Exch $0
FunctionEnd

Before we continue, you'll have to install the InetC plugin.

Now find the "MainSection" section (depending on what you called it in the wizard).

Modify the section so it looks like the below. Your file names and amount may vary, the primary part of our concern is the first part. It will test whether the $InstallDotNET variable implies that we have to install the framework. If it does, it'll hide the usual GUI elements of the installer and start the download of the .NET Framework from any URL you specify, this could be the official download URL or a location you host yourself. If the user cancels the download we'll delete the half-finished file and abort. Otherwise we'll execute the dotnetfx.exe file and wait for it to complete (hence we'll now have the .NET Framework 2.0). After having installed the framework we delete the dotnetfx.exe file again. Finally we show the GUI again.

Section "MainSection" SEC01
  SetOutPath "$INSTDIR"
  SetOverwrite ifnewer

  ; Get .NET if required
  ${If} $InstallDotNET == "Yes"
     SetDetailsView hide
     inetc::get /caption "Downloading .NET Framework 2.0" /canceltext "Cancel" "http://www.url_of_the_dotnetfx.exe_file" "$INSTDIR\dotnetfx.exe" /end
     Pop $1

     ${If} $1 != "OK"
           Delete "$INSTDIR\dotnetfx.exe"
           Abort "Installation cancelled."
     ${EndIf}

     ExecWait "$INSTDIR\dotnetfx.exe"
     Delete "$INSTDIR\dotnetfx.exe"

     SetDetailsView show
  ${EndIf} 
 
  File "Install\bin\Debug\Install.exe"

  File "Install\bin\Debug\Uninstall.exe"

  File "MyApplication\bin\Debug\MyApplication.exe"
  CreateDirectory "$SMPROGRAMS\My application"
  CreateShortCut "$SMPROGRAMS\My application\My application.lnk" "$INSTDIR\MyApplication.exe"
  CreateShortCut "$DESKTOP\My application.lnk" "$INSTDIR\MyApplication.exe"
SectionEnd

Now comes the part where I'll introduce our custom .NET install and uninstall actions. Create two new Console Application projects in the solution called Uninstall and Install, like the following:

Image: nsis_9
Add the below function to your code, it'll run the Install.exe file after the installation has successfully completed:

Function .onInstSuccess
         ExecWait "$InstDir\Install.exe"
FunctionEnd

Locate the "Section Uninstall" part and add the following line as the very first:

ExecWait "$InstDir\Uninstall.exe"

Make sure to add both Install.exe and Uninstall.exe to the list of files that will be installed, in the Main Section. It will run the Uninstall.exe application before anything else, and wait for it to finish before continuing. After it's done we'll delete all the installed files, including the Install and Uninstall.exe applications - remember to add those to file deletions manually, following the syntax of the other file deletions.

Now press Ctrl+9 to build the installer, and look at it run in all of its awesomeness:

Image: nsis_10

Image: nsis_11
After having downloaded the .NET Framework 2.0, it'll start the .NET installer and run it through as usual, the installation will continue as soon as the .NET installer finishes. There is currently no check for whether the user cancels the .NET installation midways or if it fails. A simple check could be made right afterwards by simple calling the GetDotNETVersion function again like we did just before, if it fails, the user hasn't installed .NET for some unknown reason and we'll have to abort.

You can see my complete install script here. Download and rename to *.nsi to compile it.

kick it on DotNetKicks.com


Comments

tt | Jun 16th, 2007, 11:38 AM

Thanks for the tutorial, it worked great! Currently using it for the install of my latest project :)

Check it out - http://www.thetechtray.net/2007/06/13/clipmon-10/

Zophar | Jun 27th, 2007, 6:34 AM

Great Tutorial ... Helped solve my .NET installation problems.

Webmaster | Jul 1st, 2007, 6:50 AM

Thanks for a great script! A real time saver.

How could we change it to check .NET 3.0 installed, so that the user doesn't have to download .NET 2.0 if he has .NET 3.0 installed?

Thank you,

Andrei

Mark S. Rasmussen | Jul 1st, 2007, 12:58 PM

@Andrei

Although I haven't tested it, I'd suppose you could add a third version check like this:

${VersionCompare} $0 "3.0" $1
${If} $1 == 2
StrCpy $InstallDotNET "No"
Return
${EndIf}

And then nest the other version checks inside this one in the case 3.0 isn't installed.

Let me know if you get it working, I'd like to add it to the article as well :)

Thanks,
Mark

Webmaster | Jul 2nd, 2007, 2:49 AM

Thank you, Mark.

I don't have Vista installed on my computer, therefore I can't test your code immediately.

To all: If somebody tests this 3.0 check, please, post.

Thank you,

Andrei

Webmaster | Jul 5th, 2007, 2:37 PM

Hello Mark,

I found out a better way to check .NET 2.0 or 3.0 installation - that is to see for a certain registry value:

; Check .NET version
ReadRegDWORD $0 HKLM 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727' Install
${If} $0 == ''
StrCpy $InstallDotNET "Yes"
MessageBox MB_OK|MB_ICONINFORMATION "${PRODUCT_NAME} requires that the .NET Framework 2.0 is installed. The .NET Framework will be downloaded and installed automatically during installation of ${PRODUCT_NAME}."
Return
${EndIf}

We check for .NET 2.0 installation in registry. If .NET 3.0 is installed, the .NET 2.0 record will be there as well.

This should work better than mscoree::GetCORVersion check, because GetCORVersion returns the version number of the CLR that is running in the current process.

Credits: NSIS Quick Start article by Jared James Sullivan
(http://www.codeproject.com/useritems/NSIS.asp?msg=2107949)

zamnut6 | Jul 11th, 2007, 3:33 AM

Very nice tutorial. I am a new user to NSIS and ran into a small problem. The installer needed a plugin to recognize inetc::get. I got the plugin at:

http://nsis.sourceforge.net/Inetc_plug-in

and put the inetc.dll file in:

C:\Program Files\NSIS\Plugins

I am sure that most readers had done this long ago but I put in this detail in case it might help other new users like me.

Indika Perera | Oct 25th, 2007, 8:32 PM

Works Great!! Thanks Mark!!

zamnut6's advise too was valuable...

:-)
Cheers!

Ramesh Vagh | Dec 21st, 2007, 1:52 PM

Hello...!

Can any one help me to call web service with parameters and from the NSIS installer and get response from the web service.

based on the response i want to continue installation and also want to check at the end of the installation over the web service.

Or

If any one can tell me to use .Net dll functions in the NSIS script.

Or

Any way to get my gole without opening any explorer, i should be able to pass parameters to the web service or .Net dll and get response from that.

if anyone can give me the solution then i'll be thankfull.

Ramesh

SLK | Jan 3rd, 2008, 4:51 AM

This solution looks great :-> But I have a few questions...

Could you explain the purpose of the console apps (install.exe and uninstall.exe)? You wrote:
"Make sure to add both Install.exe and Uninstall.exe to the list of files that will be installed, in the Main Section. It will run the Uninstall.exe application before anything else, and wait for it to finish before continuing. After it's done we'll delete all the installed files, including the Install and Uninstall.exe applications - remember to add those to file deletions manually, following the syntax of the other file deletions."

I'm confused because I don't see those lines in the Main Section of your script.

Also, the .NET Framework installer (dotnetfx.exe) seems to execute with its GUI and not silently. Is there anyway to make it silent?

And finally, since Microsoft Installer 3.0 is a prerequisite for the .NET Framework 2.0 installer, can you recommend a method for checking for and installing it? This only seems to be an issue on Windows 2000 machines.

Thanks!

Dhaval Bhatt | Jan 12th, 2008, 11:57 AM

Ramesh
This is Not Possible from NSIS.
Althouh you can do it by using Commandline Programe
(Which you had apply in your Service already)
JA ja ja

zee | Feb 18th, 2008, 5:02 PM

Thanks for the great tutorial it was a great help for me :)
i have a question.
is there a way to check the setup folder for dotnetfx.exe first and if it does not exist then jump to the download process??

thank you very much

Anand | Mar 26th, 2008, 1:39 PM

Hi,

How to create a Application Icon...I have created the setup with Icon...after the application installed ...my appication having Windows default icon for shorcuts....
pls help

Karl | Apr 18th, 2008, 12:59 PM

Great article! I love NSIS but when I switched to .Net programming I didn't find any easy ways to check if the framework was installed, now I can finally toss out the Setup Project crap from Microsoft!

Geetha | Jul 28th, 2008, 6:58 AM

Hi, plz let me knw sooooon how to create virtual directory usng NSIS in .Net Applications

shraddha | Jul 31st, 2008, 11:21 AM

Hi...thanks for these posts...it helped me alot as a beginner in NSIS. I am not able to solve a small problem. my .nsi script calls a powershell script. i want that my nsi script should call this script from the same folder where the installer exe is placed. i dont want to hard code the path for my ps1 script. please can anyone give mea command which can fetch my ps1 file from the same path where installer is placed.
thanks in advance :)

Andrei | Jun 19th, 2009, 11:53 AM

Hi,
Nice tutorial!
I need something more than this. I want to include .net fr in my project, not to download it on intallation. My setup will be x MB + 25 MB (.net fr).
How to do that ?

Andrew

Alexey | Jul 23rd, 2009, 1:03 PM

Hi! Thanks for the great explanations, but I have a little problem - GUI with downloading status is not appeared though I don't have "silent" mode... What can I do in order to see this GUI?

Diego Sanchez | Sep 21st, 2009, 4:15 PM

I've read your tutorial but when I execute the next instruction- I didn't get any result.

The instruction is 'ExecWait "$INSTDIR\dotnetfx.exe"'.

After call this function We should view .NET install page. But I didn't see that.

Why didn't ExecWait show nothing?

Thanks
Good gay ahead!

Jamie | Mar 9th, 2010, 7:22 PM

How could I modify this for .NET 4.0 RC

I have tried changing
${VersionCompare} $0 "4" $1
${VersionCompare} $0 "4.0" $1

etc but still asks me on my development machine to install .net 4 (which is installed)

Thanks

Batcha | Mar 28th, 2010, 7:29 AM

Oh what a nice tutorial! But still i could not use it for my app.
My app.pre requirements are Windows installer 3.1,.NetFramework 3.5 and cards.dll. And i dont want users download them from net instead i want to include them in my setup how do you do that?
thanks in advance :)

Aaron | Mar 29th, 2010, 6:22 AM

Thanks so much for this script its helped heaps!

hair salon software | Jun 21st, 2010, 2:27 PM

I am planning to have a salon management software developed in .net. What is your opinion about maintaining its database.

ab circle pro reviews | Jul 12th, 2010, 12:13 PM

Your blog keeps getting better and better! Your older articles are not as good as newer ones you have a lot more creativity and originality now keep it up!

injury lawyer | Jul 13th, 2010, 12:32 PM

The websites that understand the value of providing a quality resource for free. It is the old what goes around comes around routine,.its very interesting community and sure i will connect to the my flickers follow and get involved in the whole community and enjoy a lot.Thank you

simulation assurance auto | Aug 6th, 2010, 9:38 AM

Information like the one you mentioned here will be very useful to me!.I love books and used to visit bookshops regularly.I just visit your blog.The articles are quite good and I have already used to visit this site regularly.A nice one...

SEO Los Angeles | Aug 15th, 2010, 5:23 PM

You can also optionally choose a license file that the user must accept to continue the installation.

travel | Aug 17th, 2010, 9:53 PM

Th4t be an epic da shizzi4 post, th4nkie 4it & in da futures we'll be seeing more of it

cruises | Aug 17th, 2010, 9:54 PM

We7ll I8be dat9 ogr6e speekie da speekie, gratz & than4x

flight center | Aug 17th, 2010, 9:55 PM

heb7e sh8at be th34nkie 4it on da posting left & righ8ty

Life Cover | Aug 19th, 2010, 1:45 PM

the Visual Studio Install project, instead you must manually select the files that should be installed, usually from the Debug/Release directories

window cleaning herdforshire | Aug 20th, 2010, 11:39 AM

You can also optionally choose a license file that the user must accept to continue the installation.....

Chicago mover | Aug 20th, 2010, 1:54 PM

I am a novice in .NET and happy to see a new topic.

Insurance | Aug 20th, 2010, 8:49 PM

the user can choose to view the Readme file and/or start the application - that is, if you specify an application and/or a readme file.

phentermine without prescription | Aug 23rd, 2010, 2:12 PM

it makes a variable (untyped) that'll contain a Yes/No value, depending on whether we need to install the .NET Framework or not.

Short poems | Aug 31st, 2010, 1:08 PM

I am planning to have a salon management software developed in net.

Add comment

After you have posted a comment, an email will be sent to the provided email address. Before your comment is activated, you will have to click the confirmation link within the email.

Name:

Email (only used for validation):

Website (optional):

Message:

Notify me when new comments are added:

Please type the following letters into the box below:  

Post!