Auto-uninstall old before installing new: Difference between revisions

From NSIS Wiki
Jump to navigationJump to search
(Suggested code that does not assume previous install was in the default directory)
(Empty string protection)
 
(One intermediate revision by the same user not shown)
Line 1: Line 1:
{{PageAuthor|codehistorian}}
{{PageAuthor|Anders}}
[[Category:Code Examples]]
[[Category:Functions_%26_Macros]]


This is a code snippet you can put into your .onInit function that will detect whether your software is already installed and, if so, allows the user to uninstall it first.
This code can be used to uninstall a existing installation. The previous installation must also be a NSIS based install.


In the source below, we use the define ${PROGRAM_NAME} to be the name of the product as well as the name of the installer/uninstaller.  You can either define this define or search/replace it. If you name your uninstaller or registry location differently, you should change those strings accordingly.
<highlight-nsis>
RequestExecutionLevel User
InstallDir "$LocalAppData\Programs\NSISTest"
!define UninstId "NSISTest" ; You might want to use a GUID here


<highlight-nsis>
!include LogicLib.nsh
Function .onInit


  ReadRegStr $R0 HKLM \
  "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROGRAM_NAME}" \
  "UninstallString"
  StrCmp $R0 "" done


  MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \
!macro UninstallExisting exitcode uninstcommand
  "${PROGRAM_NAME} is already installed. $\n$\nClick `OK` to remove the \
Push `${uninstcommand}`
  previous version or `Cancel` to cancel this upgrade." \
Call UninstallExisting
  IDOK uninst
Pop ${exitcode}
  Abort
!macroend
 
Function UninstallExisting
;Run the uninstaller
Exch $1 ; uninstcommand
uninst:
Push $2 ; Uninstaller
  ClearErrors
Push $3 ; Len
  ExecWait '$R0 _?=$INSTDIR' ;Do not copy the uninstaller to a temp file
StrCpy $3 ""
StrCpy $2 $1 1
StrCmp $2 '"' qloop sloop
sloop:
StrCpy $2 $1 1 $3
IntOp $3 $3 + 1
StrCmp $2 "" +2
StrCmp $2 ' ' 0 sloop
IntOp $3 $3 - 1
Goto run
qloop:
StrCmp $3 "" 0 +2
StrCpy $1 $1 "" 1 ; Remove initial quote
IntOp $3 $3 + 1
StrCpy $2 $1 1 $3
StrCmp $2 "" +2
StrCmp $2 '"' 0 qloop
run:
StrCpy $2 $1 $3 ; Path to uninstaller
StrCpy $1 161 ; ERROR_BAD_PATHNAME
GetFullPathName $3 "$2\.." ; $InstDir
IfFileExists "$2" 0 +4
ExecWait '"$2" /S _?=$3' $1 ; This assumes the existing uninstaller is a NSIS uninstaller, other uninstallers don't support /S nor _?=
IntCmp $1 0 "" +2 +2 ; Don't delete the installer if it was aborted
Delete "$2" ; Delete the uninstaller
RMDir "$3" ; Try to delete $InstDir
RMDir "$3\.." ; (Optional) Try to delete the parent of $InstDir
Pop $3
Pop $2
Exch $1 ; exitcode
FunctionEnd


  IfErrors no_remove_uninstaller done
    ;You can either use Delete /REBOOTOK in the uninstaller or add some code
    ;here to remove the uninstaller. Use a registry key to check
    ;whether the user has chosen to uninstall. If you are using an uninstaller
    ;components page, make sure all sections are uninstalled.
  no_remove_uninstaller:
 
done:


Function .onInit
ReadRegStr $0 HKCU "Software\Software\Microsoft\Windows\CurrentVersion\Uninstall\${UninstId}" "UninstallString"
${If} $0 != ""
${AndIf} ${Cmd} `MessageBox MB_YESNO|MB_ICONQUESTION "Uninstall previous version?" /SD IDYES IDYES`
!insertmacro UninstallExisting $0 $0
${If} $0 <> 0
MessageBox MB_YESNO|MB_ICONSTOP "Failed to uninstall, continue anyway?" /SD IDYES IDYES +2
Abort
${EndIf}
${EndIf}
FunctionEnd
FunctionEnd
</highlight-nsis>


The following variant lets the uninstaller remove itself, so the IfErrors block is not needed any more:
Page Directory
<highlight-nsis>
Page InstFiles
  Exec $INSTDIR\uninst.exe ; instead of the ExecWait line
</highlight-nsis>


NOTE:  Both previous methods assume the program was installed in the default directory, which may not be correct. Here's what I use:
Section
SetOutPath $InstDir
File "/oname=$InstDir\App.exe" "${__FILE__}" ; Dummy file
WriteUninstaller "$InstDir\Uninst.exe"
WriteRegStr HKCU "Software\Software\Microsoft\Windows\CurrentVersion\Uninstall\${UninstId}" "DisplayName" "NSIS Test"
WriteRegStr HKCU "Software\Software\Microsoft\Windows\CurrentVersion\Uninstall\${UninstId}" "UninstallString" '"$InstDir\Uninst.exe"'
WriteRegStr HKCU "Software\Software\Microsoft\Windows\CurrentVersion\Uninstall\${UninstId}" "QuietUninstallString" '"$InstDir\Uninst.exe" /S'
SectionEnd


<highlight-nsis>
Section Uninstall
  uninst:
Delete "$InstDir\App.exe"
    ClearErrors
DeleteRegKey HKCU "Software\Software\Microsoft\Windows\CurrentVersion\Uninstall\${UninstId}"
    Exec $R0
Delete "$InstDir\Uninst.exe"
  done:
RMDir "$InstDir"
</highlight-nsis>  
SectionEnd
 
</highlight-nsis>
[[Category:System Related Functions]]

Latest revision as of 13:45, 3 September 2019

Author: Anders (talk, contrib)


This code can be used to uninstall a existing installation. The previous installation must also be a NSIS based install.

RequestExecutionLevel User
InstallDir "$LocalAppData\Programs\NSISTest"
!define UninstId "NSISTest" ; You might want to use a GUID here
 
!include LogicLib.nsh
 
 
!macro UninstallExisting exitcode uninstcommand
Push `${uninstcommand}`
Call UninstallExisting
Pop ${exitcode}
!macroend
Function UninstallExisting
Exch $1 ; uninstcommand
Push $2 ; Uninstaller
Push $3 ; Len
StrCpy $3 ""
StrCpy $2 $1 1
StrCmp $2 '"' qloop sloop
sloop:
	StrCpy $2 $1 1 $3
	IntOp $3 $3 + 1
	StrCmp $2 "" +2
	StrCmp $2 ' ' 0 sloop
	IntOp $3 $3 - 1
	Goto run
qloop:
	StrCmp $3 "" 0 +2
	StrCpy $1 $1 "" 1 ; Remove initial quote
	IntOp $3 $3 + 1
	StrCpy $2 $1 1 $3
	StrCmp $2 "" +2
	StrCmp $2 '"' 0 qloop
run:
	StrCpy $2 $1 $3 ; Path to uninstaller
	StrCpy $1 161 ; ERROR_BAD_PATHNAME
	GetFullPathName $3 "$2\.." ; $InstDir
	IfFileExists "$2" 0 +4
	ExecWait '"$2" /S _?=$3' $1 ; This assumes the existing uninstaller is a NSIS uninstaller, other uninstallers don't support /S nor _?=
	IntCmp $1 0 "" +2 +2 ; Don't delete the installer if it was aborted
	Delete "$2" ; Delete the uninstaller
	RMDir "$3" ; Try to delete $InstDir
	RMDir "$3\.." ; (Optional) Try to delete the parent of $InstDir
Pop $3
Pop $2
Exch $1 ; exitcode
FunctionEnd
 
 
Function .onInit
ReadRegStr $0 HKCU "Software\Software\Microsoft\Windows\CurrentVersion\Uninstall\${UninstId}" "UninstallString"
${If} $0 != ""
${AndIf} ${Cmd} `MessageBox MB_YESNO|MB_ICONQUESTION "Uninstall previous version?" /SD IDYES IDYES`
	!insertmacro UninstallExisting $0 $0
	${If} $0 <> 0
		MessageBox MB_YESNO|MB_ICONSTOP "Failed to uninstall, continue anyway?" /SD IDYES IDYES +2
			Abort
	${EndIf}
${EndIf}
FunctionEnd
 
Page Directory
Page InstFiles
 
Section
SetOutPath $InstDir
File "/oname=$InstDir\App.exe" "${__FILE__}" ; Dummy file
WriteUninstaller "$InstDir\Uninst.exe"
WriteRegStr HKCU "Software\Software\Microsoft\Windows\CurrentVersion\Uninstall\${UninstId}" "DisplayName" "NSIS Test"
WriteRegStr HKCU "Software\Software\Microsoft\Windows\CurrentVersion\Uninstall\${UninstId}" "UninstallString" '"$InstDir\Uninst.exe"'
WriteRegStr HKCU "Software\Software\Microsoft\Windows\CurrentVersion\Uninstall\${UninstId}" "QuietUninstallString" '"$InstDir\Uninst.exe" /S'
SectionEnd
 
Section Uninstall
Delete "$InstDir\App.exe"
DeleteRegKey HKCU "Software\Software\Microsoft\Windows\CurrentVersion\Uninstall\${UninstId}"
Delete "$InstDir\Uninst.exe"
RMDir "$InstDir"
SectionEnd