Extending the Windows Installer (NSIS) with Custom Functionality
This guide explains how to add custom functionality to your application’s Windows installer using NSIS (Nullsoft Scriptable Install System). We’ll use a real-world example of adding CUDA detection and installation during setup.
Overview
NSIS is a professional open-source system to create Windows installers. When building with electron-builder, NSIS is used as the default Windows installer. You can extend its functionality by:
- Adding custom initialization code
- Including additional NSIS plugins
- Adding custom pages to the installer
- Running checks before/during installation
- Downloading and installing additional components
Basic Setup
First, configure your package.json to use custom NSIS scripts:
{
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true,
"include": "build/installer.nsh",
"perMachine": true,
"allowElevation": true
}
}
Adding Required NSIS Plugins
Add NSIS plugin packages as devDependencies in package.json:
{
"devDependencies": {
"nsisextra": "^1.0.0",
"nsis-wmi": "^1.0.0",
"nsis-inet-plugin": "^1.0.1"
}
}
Creating Custom NSIS Scripts
Create a file named installer.nsh in your build directory. This file will contain your custom NSIS code.
Basic structure of installer.nsh:
!macro customInit
; Your initialization code here
Function MyCustomFunction
; Function code
FunctionEnd
Function .onInit
; Code to run at installer startup
Call MyCustomFunction
FunctionEnd
!macroend
Real-World Example: CUDA Detection and Installation
Here’s a complete example that shows how to:
- Detect NVIDIA GPUs
- Check CUDA installation
- Download and install CUDA if needed
Step 1: Create Variables
!macro customInit
Var cudaVersion
Var driverVersion
Var cudaInstaller
Var cudaInstallerUrl
Step 2: Create Detection Functions
Function DetectNvidiaGPU
nsExec::ExecToStack 'wmic path win32_videocontroller get name'
Pop $0
Pop $1
${If} $1 contains "NVIDIA"
StrCpy $0 "1"
${Else}
StrCpy $0 "0"
${EndIf}
Push $0
FunctionEnd
Step 3: Check Driver Version
Function GetNvidiaDriverVersion
ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{B2FE1952-0186-46C3-BAEC-A80AA35AC5B8}_Display.Driver" "DisplayVersion"
Push $0
FunctionEnd
Step 4: Download and Install
Function InstallCuda
DetailPrint "Downloading CUDA installer..."
inetc::get "$cudaInstallerUrl" "$TEMP\cuda_installer.exe" /END
ExecWait '"$TEMP\cuda_installer.exe" /s /noreboot'
Delete "$TEMP\cuda_installer.exe"
FunctionEnd
Best Practices
- Always provide feedback to users about what’s happening
- Use DetailPrint for installer progress
- Use MessageBox for important decisions/information
- Handle errors gracefully
- Check return values from commands
- Provide fallback options
- Don’t abort installation unless absolutely necessary
- Clean up after yourself
- Remove temporary files
- Close handles and connections
- Don’t leave partial installations
- Respect user choice
- Ask before downloading large files
- Provide skip options for optional components
- Remember user preferences
Common NSIS Commands
MessageBox MB_YESNO|MB_ICONQUESTION "Question?" IDNO label
; Shows a message box with Yes/No buttons
DetailPrint "Message"
; Shows progress in the installer
ExecWait "command" $0
; Runs a command and waits for completion
ReadRegStr $0 HKLM "Registry\Path" "ValueName"
; Reads from Windows Registry
${If} condition
${ElseIf} condition
${EndIf}
; Conditional statements
Debugging Tips
- Use DetailPrint to log progress
- Test installer with logging enabled:
hello-world-setup.exe /LOG=installer.log
- Check Windows Event Viewer for errors
- Test on clean virtual machines
- Verify elevated privileges when needed
Resources
Need Help?
Reach out to me on X @alexheloai
About the Author: Alex Ash is an AI engineer focusing on automation and cross-platform development. Contact at alex@helosolutions.ai or follow @alexheloai for more tutorials.