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.
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”…
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 😉
Ha ha! That’s how I remember it, maybe my memory’s playing trick on me! 😉
Of course, if it was server side PHP you were writing, you wouldn’t need all this poncey Windows guff!
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?
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.
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
Cheers Scott. The best place is probably the NSIS wiki itself since that’s extremely useful code!
Many thanks Scott!!