Friday, October 27, 2006

WIN32: Build a basic Windows installer with NSIS

Its not that hard to write a program that copies a bunch of files to another folder, but sometimes it is nice to have a program that takes care of the license agreement, registry keys, start menu creation, copying to a program files folder and uninstaller all in one self-extracting archive.

There are many installers available, I decided to go with the Nullsoft Scriptable Install System (NSIS) as it is open source. I used the following settings in my script, I should have provided enough that you should do find-replaces on the provided script to customize for your basic needs. For anything else go look at the NSIS documentation, its pretty good.

Settings-
Language: English
Program Name: Popcorn Maker
Program .EXE: popcorn.exe
Program .DLLs: popcorn1.dll, popcorn2.dll
EULA File (for installer): TermsOfUse.txt
Installer .EXE: pm_installer.exe
Splash bitmap: res/splash3.bmp (res is relative to your NSIS script)
Installer Icon: res/installer.ico
Uninstaller Icon: res/uninstaller.ico

[1] Go to http://nsis.sourceforge.net, download and install the Nullsoft Scriptable install system.

[2] Look at their example scripts and get a feel for how you want to design your installer. They provide 20+ example scripts from basic, to complex, even including the source for the installer that they used to install the NSIS that you downloaded.

[3] Use the following script. Save it as a .NSI filetype and put it in the same folder as your .EXE file and res folder (setup as shown above).

;--------------------------------
;Include Modern UI

  !
include "MUI.nsh"

;--------------------------------
;Splash screen
XPStyle on

Function .onInit
  ; the plugins dir is automatically deleted when the installer exits
  
InitPluginsDir
  File /oname=$PLUGINSDIR\splash3.bmp ".\res\splash3.bmp"

  
advsplash::show 1500 600 0 -1 $PLUGINSDIR\splash3
  ;splash3.bmp is a splash screen that fades in for
  ;1500 ms holds for 600 ms and fades out for 0 ms

  
Pop $0
  
; $0 has '1' if the user closed the splash screen early,
  ; '0' if everything closed normally, and '-1' if some error occurred.
FunctionEnd

;--------------------------------
;General

  ;Name and file
  
Name "Popcorn Maker"
  
OutFile "pm_installer.exe"

  
;Default installation folder
  
InstallDir "$PROGRAMFILES\Popcorn Maker"

  
!define MUI_ICON ".\res\installer.ico"
  
!define MUI_UNICON ".\res\uninstaller.ico"

  
!define MUI_FINISHPAGE_NOAUTOCLOSE
  !define MUI_UNFINISHPAGE_NOAUTOCLOSE
;--------------------------------
;Variables
  
Var STARTMENU_FOLDER
  Var MUI_TEMP
;--------------------------------
;Interface Settings
  !
define MUI_ABORTWARNING
  !define MUI_STARTMENUPAGE_DEFAULTFOLDER "Popcorn Maker"
;--------------------------------
;Pages
  !
insertmacro MUI_PAGE_LICENSE ".\TermsOfUse.txt"
  
!insertmacro MUI_PAGE_COMPONENTS
  !insertmacro MUI_PAGE_DIRECTORY

  ;Start Menu Folder Page Configuration
  !
define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKCU"
  
!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\Popcorn Maker"
  
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
  
!define MUI_UN_REG "Software\Microsoft\Windows\CurrentVersion\Uninstall"

  
!define MUI_FINISHPAGE_RUN "$INSTDIR\popcorn.exe"
  
!define MUI_FINISHPAGE_NOREBOOTSUPPORT

  !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER

  !insertmacro MUI_PAGE_INSTFILES

  !insertmacro MUI_PAGE_FINISH

  !insertmacro MUI_UNPAGE_CONFIRM
  !insertmacro MUI_UNPAGE_INSTFILES

;--------------------------------
;Languages

  !
insertmacro MUI_LANGUAGE "English"

;--------------------------------
;Installer Sections

Section "Popcorn Maker (Core)" SecMain
  SectionIn RO

  SetOutPath "$INSTDIR"

  
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application

  File .\popcorn.exe
  File .\popcorn1.dll
  File .\popcorn2.dll

  ; Write the installation path into the registry
  
WriteRegStr HKCU "Software\Popcorn" "Install_Dir" "$INSTDIR"

  
; Write the uninstall keys for Windows
  
WriteRegStr HKCU "MUI_UN_REG\Popcorn" "DisplayName" "Popcorn Uninstaller"
  
WriteRegStr HKCU "MUI_UN_REG\Popcorn" "UninstallString" '"$INSTDIR\un.exe"'
  
WriteRegDWORD HKCU "MUI_UN_REG\Popcorn" "NoModify" 1
  
WriteRegDWORD HKCU "MUI_UN_REG\Popcorn" "NoRepair" 1
  
WriteUninstaller "un.exe"

  
;Create shortcuts
  
CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER"
  
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Popcorn Maker.lnk" "$INSTDIR\Popcorn.exe"
  
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\Un.exe"

  
!insertmacro MUI_STARTMENU_WRITE_END

SectionEnd

;--------------------------------
;Descriptions

  ;Language strings
  
LangString DESC_SecMain ${LANG_ENGLISH} "Popcorn Maker, a program"

  
;Assign language strings to sections
  !
insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
    !insertmacro MUI_DESCRIPTION_TEXT ${SecMain} $(DESC_SecMain)
  !
insertmacro MUI_FUNCTION_DESCRIPTION_END

;--------------------------------
;Uninstaller Section
Section "Uninstall"

  
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP

  Delete "$SMPROGRAMS\$MUI_TEMP\Popcorn Maker.lnk"
  
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"

  
;Delete empty start menu parent diretories
  
StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP"

  
smDeleteLoop:
    
ClearErrors
    RMDir $MUI_TEMP
    GetFullPathName $MUI_TEMP "$MUI_TEMP\.."

    
IfErrors smDeleteLoopDone

    StrCmp $MUI_TEMP $SMPROGRAMS smDeleteLoopDone smDeleteLoop
  smDeleteLoopDone:

  
Delete "$INSTDIR\popcorn.exe"
  
Delete "$INSTDIR\popcorn1.dll"
  
Delete "$INSTDIR\popcorn1.dll"
  
Delete "$INSTDIR\Un.exe"
  
RMDir "$INSTDIR"

  
DeleteRegValue HKCU "MUI_UN_REG\Popcorn" "DisplayName"
  
DeleteRegValue HKCU "MUI_UN_REG\Popcorn" "UninstallString"
  
DeleteRegValue HKCU "MUI_UN_REG\Popcorn" "NoModify"
  
DeleteRegValue HKCU "MUI_UN_REG\Popcorn" "NoRepair"
  
DeleteRegKey   HKCU "MUI_UN_REG\Popcorn"
  
DeleteRegValue HKCU "Software\Popcorn" "Install_Dir"
  
DeleteRegKey   HKCU "Software\Popcorn"

SectionEnd




Note: I'm always fighting with the width of blogspot, so i changed the font to Arial to fit more in. Sometimes the commands wrapped to the next line, but you can kind of tell. When you actually run it you'll have to put the wrapped text back on the original line.