Post Format

My Installer Fixation And NSIS

8 comments

I love installers. I’ve found that wherever I work I end up writing one for the software and I wouldn’t have it any other way. There’s something that gives me that warm, fuzzy feeling inside when just a few buttons clicks can make the process of putting software on a machine seem trivial and just as easily remove it when in fact there’s a whole load of clever stuff going on underneath. Still waters run deep.

Take my last company. Before I wrote an installer it would take a person around a day to properly deploy the software. It consisted of C++ COM dlls, C++ COM+ components, an ASP.NET web site and a SQL Server backend. For anyone who’s had to deploy this type of software you’ll know that there are a great number of things that can go wrong and add time to an install. Never mind the fact that different operating systems (such as the different editions of Windows 2000 and 2003) all have their own little nuances…

I’ve seen in a lot of places a “deployment document” written consisting of a list of steps to perform when installing and you have to keep it up to date when you encounter problems as you go along. You end up with a numbered list where each item consists of a bullet pointed list and this is why an install takes a day. I find it surprising that companies seem to consider writing an installer as one of those “we’ll do it if we ever get time” projects that they never get around to. It’s surprising because the gains you get for such a small amount of work are immense.

At my last place I probably took a few weeks to write the installer and it dropped the time to install our software from a full day to around 10 minutes – and it always worked (once the bugs were ironed out!). Finally, instead of a customer install being a case of “okay, what’s going to go wrong this time?” it was a simple task that took no time at all. It was stress-free and made us look like a real bunch of pros to the customers.

If a glitch came up on a certain operating system, I didn’t go and append another “if this happens do this…” bullet point to some 50 page Word document, I fixed the installer and promptly forgot about it – the installer would deal with it.

I’ve used most of the tools out there including Windows Installer, InstallShield, Wise and the free and rather good Inno Setup which I’ve used more and more recently. I came to realise that you spend most of your time with installers writing code to do custom, tricky things like set up a COM+ application or configure a web site and despite products like Installshield supporting this, they aren’t flexible enough and I’d end up rolling my own code in VBScript or in a C++ dll I’d call. Inno Setup has been just fine for this.

However I’d been aware of another free tool out there called NSIS but had never actually sat down and learned how to use it. Until last week. And I must say, it’s amazing. I can see why companies like Google are using it – you can write a highly sophisticated installer in no time at all.

Installers consist of a bunch of tasks you need to do in sequence and you undo them in an uninstaller. You may want to create a bunch of shortcuts, copy some files, register some libraries, create a web site and configure some configuration files. Inno Setup was fine for things like that but if I needed to find some existing shortcuts and delete them, then add some new ones, then check to see if something was installed and if not download it from the internet and install it, then copy some files and so on it wouldn’t support it and I’d have to jump through hoops to do it. The steps weren’t fine-grained enough. NSIS lets me do whatever I want in any order I want, both installing and uninstalling.

installerpage1

I’ve been writing the new installer for version 2 of my background switcher and I needed it to do things like check if MS.NET is installed and if not, download and install it. I wanted it to fix up an install of any previous versions of my app installed including removing shortcuts that were in the “All Users” start menu when they shouldn’t be (naughty Inno Setup). I also wanted more fine-grained control over the uninstall process which I couldn’t really do before but with NSIS it’s easy. I can control the order things happen (e.g. delete this shortcut, then copy these files, then launch this application, then copy another file) to a degree I couldn’t do before and it’s completely extensible – I can create custom pages and call the windows API or write my own custom dlls.

And I’ll tell you something else, you could spend £1000 on a commercial product like Installshield or Wise, or £0 on NSIS and believe it or not, there’s nothing Installshield or Wise can do that you can’t achieve with NSIS – and trust me you spend more time working around the weaknesses in the big products than you do writing code for an NSIS installer. Now, guess what product I’ll be using to write the installer at my new job? I’ll give you a clue – it’s a four-letter word. And it doesn’t start with “W”…

Posted by

Creator of John's Background Switcher. Scotsman, footballer, photographer, dog owner, risk taker, heart breaker, nice guy. Some of those are lies.

8 Comments Join the Conversation

  1. At my last place I probably took a few weeks to write the installer and it dropped the time to install our software from a full day to around 10 minutes – and it always worked.

    Really? Always worked? Is that a few ‘john’ weeks (which translates to 3 months in realtime?) And heres me thinking the original installer took on average 30 mins, with the record installation time being 12 minutes 😉

    Reply

  2. Of course, if it was server side PHP you were writing, you wouldn’t need all this poncey Windows guff!

    Reply

  3. I’m a .NET guy and mostly write apps using SQL Server 2005 Express for my client-side db (replicates with a server instance). I’ve scoured the internet looking for tutorials or tips on using NSIS for deploying .NET apps along with SQL 2005 Express, but haven’t come up with much. Could you recommend any resources to look at? Also, what do you feel is the best way to start learning NSIS? Just follow the user manual that it comes with?

    Reply

  4. I learned how to use NSIS just by trying to write my installer and finding sample code on the NSIS site to do what I was trying to do (with a small amount of reading the manual). Since I was only installing some files and downloading then installing the .NET framework if it wasn’t there, I just used the default template and went from there. You can modify the code here for the .NET 2 framework:

    http://nsis.sourceforge.net/Installing_the_Microsoft_.NET_Framework

    I’m not sure about deploying SQL 2005 Express though, I believe it uses the same installation technology as the .NET 2 framework so you might want to scour MSDN to see what the command line arguments for it are.

    If you’re still stuck then drop me an email on my contact page and I’ll see if I can help you out.

    Reply

  5. probably not the best place but here are some good macros for what you are doing.

    !include WinVer.nsh
    !include x64.nsh

    !macro CHECK_MIN_WIN_VER
    var /GLOBAL WIN_VER_PASSED ;Win98?
    var /GLOBAL WIN_SERVICEPACK_PASSED ;Win2kSP3
    var /GLOBAL SQL_SERVICEPACK_PASSED ;Win2kSP4,Win2k3SP1,WinXPSP2
    StrCpy $WIN_VER_PASSED “1”
    StrCpy $WIN_SERVICEPACK_PASSED “1”
    StrCpy $SQL_SERVICEPACK_PASSED “1”

    ReadRegDWORD $R0 HKLM “SystemCurrentControlSetControlWindows” “CSDVersion”
    IntOp $R0 $R0 / 256 ;get service pack major version

    ${If} ${IsWin2000}
    ${If} $R0 < “3” ; SP3
    StrCpy $WIN_SERVICEPACK_PASSED “0”
    ${ElseIf} $R0 < “4” ; SP4
    StrCpy $SQL_SERVICEPACK_PASSED “0”
    ${EndIf}
    ${ElseIf} ${IsWin2003}
    ${If} $R0 < “1” ; SP1
    StrCpy $SQL_SERVICEPACK_PASSED “0”
    ${EndIf}
    ${ElseIf} ${IsWinXP}
    ${If} $R0 < “2” ; SP2
    StrCpy $SQL_SERVICEPACK_PASSED “0”
    ${EndIf}
    ${ElseIf} ${AtMostWin2000}
    StrCpy $WIN_VER_PASSED “0”
    ${EndIf}
    !macroend

    !macro CHECK_ADMIN_RIGHTS
    var /GLOBAL USER_ADMIN_PASSED ;Administrator
    StrCpy $USER_ADMIN_PASSED “1”
    userInfo::getAccountType
    pop $0
    ${If} $0 != “Admin”
    StrCpy $USER_ADMIN_PASSED “0”
    ${EndIf}
    !macroend
    !macro MSI_INSTALL installer_path
    ;install msi version 3
    GetDllVersion “$SYSDIRmsi.dll” $1 $2
    IntOp $3 $1 / 0x00010000
    IntOp $4 $1 & 0x0000FFFF
    ;IntOp $5 $2 / 0x00010000
    ;IntOp $6 $2 & 0x0000FFFF
    StrCpy $0 ‘$3.$4’
    IntCmp $0 “3.1” MSI_NO_INSTALL MSI_INSTALL MSI_NO_INSTALL
    MSI_INSTALL: ;may be 3.0 or 3.1
    SetDetailsPrint both
    DetailPrint “Updating Windows Installer”
    SetDetailsPrint none
    !insertmacro EXEC_SUBINSTALLER “Windows Installer 3.1″
    ‘”${installer_path}WindowsInstaller-KB893803-v2-x86.exe” /quiet /norestart’
    MSI_NO_INSTALL:
    !macroend

    !macro MDAC_INSTALL installer_path
    StrCpy $1 “0”
    ReadRegStr $1 HKLM “SOFTWAREMicrosoftDataAccess” “Version”
    StrCpy $2 $1 3 ;e.g. $2 is now “2.5”

    ${If} $2 == “”
    Goto MDACNOTFOUND
    ${ElseIf} $2 == “2.1”
    Goto MDACNOTFOUND
    ${ElseIf} $2 == “2.5”
    Goto MDACNOTFOUND
    ${ElseIf} $2 == “2.6”
    Goto MDACNOTFOUND
    ${ElseIf} $2 == “2.7”
    Goto MDACNOTFOUND
    ${Else} ;must be greater
    Goto MDACCORRECT
    ${EndIf}
    MDACCORRECT:
    StrCpy $0 “0”
    Goto EXITFUNCTION
    MDACNOTFOUND:
    StrCpy $0 “1”
    Goto EXITFUNCTION
    EXITFUNCTION:
    ;is mdace same version
    ${If} $0 == “1” ;Less Than
    ;Install MDAC
    SetDetailsPrint both
    DetailPrint “Installing MDAC”
    SetDetailsPrint none
    !insertmacro EXEC_SUBINSTALLER “Microsoft Data Access Components 2.8″
    ‘”${installer_path}mdac_typ.exe” /Q:A /C:”dasetup /Q:D /N”‘
    ;SetRebootFlag True not needed after runonce test
    ${EndIf}
    !macroend

    !macro NET_INSTALL installer_path
    ;Check for framework 2.0————————————–
    StrCpy $1 “0”
    ReadRegDword $1 HKLM “SOFTWAREMicrosoftNET Framework SetupNDPv2.0.50727” “Install”
    ${If} $1 != “1”
    ${If} ${RunningX64}
    StrCpy $9 ‘”${installer_path}netfx64.exe” /q /c:”install.exe /qb /noaspupgrade”‘
    ${Else}
    StrCpy $9 ‘”${installer_path}dotnetfx2.0.exe” /q /c:”install.exe /qb /noaspupgrade”‘
    ${EndIf}
    SetDetailsPrint both
    DetailPrint “Installing .Net Framework”
    SetDetailsPrint none
    !insertmacro EXEC_SUBINSTALLER “Microsoft .Net Framework 2.0” $9
    ${EndIf}
    !macroend

    !macro IS_SQL_INSTALLED instance_name return
    strcpy ${return} “0”
    ReadRegStr $0 HKLM “SOFTWAREMicrosoftMicrosoft SQL Server${instance_name}MSSQLServerCurrentVersion” CurrentVersion
    ${If} $0 != “” ;should = 9.if 2005 8. if 2000
    strcpy ${return} “1”
    ${EndIf}
    !macroend

    !macro SQL_INSTALL installer_path instance_name password install_dir settings_file
    ;Check for existing instance
    AddSize 358400
    ReadRegStr $0 HKLM “SOFTWAREMicrosoftMicrosoft SQL Server${instance_name}MSSQLServerCurrentVersion” CurrentVersion
    ${If} $0 == “” ;should = 9.if 2005 8. if 2000
    !insertmacro EXEC_SUBINSTALLER “Microsoft SQL Server 2005 Express Edition”
    ‘”${installer_path}SQLEXPR.EXE” /settings ${settings_file} /qb INSTANCENAME=${instance_name} SAPWD=${password} INSTALLSQLDIR=”${install_dir}”‘
    ${EndIf}
    !macroend

    Reply

Leave a Reply

Required fields are marked *.

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s