Signing an Uninstaller

From NSIS Wiki

Especially under Windows Vista, installer/uninstaller binaries need to be signed to avoid alarming looking dialog boxes with dire warnings about "unknown publishers" etc.

This presents a difficulty in that the uninstaller binary would normally never be present on your development/packaging machine, only being written onto the target machine at install time. So how can you sign it?

The answer is to run the installer on the development machine in a special mode which only writes the uninstaller to some known location, then sign that binary in the usual way, and finally package the signed uninstaller using a normal File command rather than WriteUninstaller.

!ifdef INNER
  !echo "Inner invocation"                  ; just to see what's going on
  OutFile "$%TEMP%\tempinstaller.exe"       ; not really important where this is
  SetCompress off                           ; for speed
  !echo "Outer invocation"
  ; Call makensis again against current file, defining INNER.  This writes an installer for us which, when
  ; it is invoked, will just write the uninstaller to some location, and then exit.
  !makensis '/DINNER "${__FILE__}"' = 0
  ; So now run that installer we just created as %TEMP%\tempinstaller.exe.  Since it
  ; calls quit the return value isn't zero.
  !system "$%TEMP%\tempinstaller.exe" = 2
  ; That will have written an uninstaller binary for us.  Now we sign it with your
  ; favorite code signing tool.
  !system "SIGNCODE <signing options> $%TEMP%\uninstaller.exe" = 0
  ; Good.  Now we can carry on writing the real installer.
  OutFile "my_real_installer.exe"
  SetCompressor /SOLID lzma
Function .onInit
!ifdef INNER
  ; If INNER is defined, then we aren't supposed to do anything except write out
  ; the installer.  This is better than processing a command line option as it means
  ; this entire code path is not present in the final (real) installer.
  WriteUninstaller "$%TEMP%\uninstaller.exe"
  Quit  ; just bail out quickly when running the "inner" installer
...[the rest of your normal .onInit]...
Section "Files" ; or whatever
  ; where you would normally put WriteUninstaller ${INSTDIR}\uninstaller.exe put instead:
!ifndef INNER
  SetOutPath $INSTDIR
  ; this packages the signed uninstaller
  File $%TEMP%\uninstaller.exe
!ifdef INNER
Section "Uninstall"
  ; your normal uninstaller section or sections (they're not needed in the "outer"
  ; installer and will just cause warnings because there is no WriteInstaller command)

This script should just be compiled in the usual way. It will invoke makensis a second time with the additional symbol INNER defined to generate a slightly unusual installer that does nothing except write the uninstaller binary to a specified location (%TEMP%/uninstaller.exe). Next it actually invokes the generated installer and sure enough it writes %TEMP%/uninstaller.exe with no user interaction needed. Now we can sign the binary with our usual tools (I use SIGNCODE because I have a rather arcane development environment, but SIGNTOOL would be more usual I guess). Once that is done, we run through the rest of the normal stuff, except instead of calling WriteUninstaller again we just package the already-created, signed uninstaller instead. Job done.

Hope you find this helpful.

Davida 07:26, 3 December 2009 (UTC): If you are using the Modern UI, you also need to add the conditionals as follows:

!ifdef INNER  
  !insertmacro MUI_UNPAGE_CONFIRM

This prevents yet another warning message.

Personal tools