Uninstall only installed files: Difference between revisions

From NSIS Wiki
Jump to navigationJump to search
No edit summary
Line 48: Line 48:
- Aleppin
- Aleppin


== The Script ==
== The Macro Script ==
 
<p>Take the next chunk of code and save it to a file called UnInstallLog.nsh in %ProgramFiles%\NSIS\Include\.</p>
<p>'''This goes before all your own sections in your script...'''</p>


<highlight-nsis>
<highlight-nsis>
!define UninstLog "uninstall.log"
;AddItem macro
Var UninstLog
  !macro AddItem Path
    FileWrite $UninstLog "${Path}$\r$\n"
  !macroend


; Uninstall log file missing.
;File macro
LangString UninstLogMissing ${LANG_ENGLISH} "${UninstLog} not found!$\r$\nUninstallation cannot proceed!"
  !macro File FilePath FileName
    IfFileExists "$OUTDIR\${FileName}" +2
    FileWrite $UninstLog "$OUTDIR\${FileName}$\r$\n"
    File "${FilePath}${FileName}"
  !macroend
 
;CreateShortcut macro
  !macro CreateShortcut FilePath FilePointer Pamameters Icon IconIndex
    FileWrite $UninstLog "${FilePath}$\r$\n"
    CreateShortcut "${FilePath}" "${FilePointer}" "${Pamameters}" "${Icon}" "${IconIndex}"
  !macroend
 
;Copy files macro
  !macro CopyFiles SourcePath DestPath
    IfFileExists "${DestPath}" +2
    FileWrite $UninstLog "${DestPath}$\r$\n"
    CopyFiles "${SourcePath}" "${DestPath}"
  !macroend
 
;Rename macro
  !macro Rename SourcePath DestPath
    IfFileExists "${DestPath}" +2
    FileWrite $UninstLog "${DestPath}$\r$\n"
    Rename "${SourcePath}" "${DestPath}"
  !macroend
 
;CreateDirectory macro
  !macro CreateDirectory Path
    CreateDirectory "${Path}"
    FileWrite $UninstLog "${Path}$\r$\n"
  !macroend
 
;SetOutPath macro
  !macro SetOutPath Path
    SetOutPath "${Path}"
    FileWrite $UninstLog "${Path}$\r$\n"
  !macroend
 
;WriteUninstaller macro
  !macro WriteUninstaller Path
    WriteUninstaller "${Path}"
    FileWrite $UninstLog "${Path}$\r$\n"
  !macroend
 
;WriteRegStr macro
  !macro WriteRegStr RegRoot UnInstallPath Key Value
    FileWrite $UninstLog "${RegRoot} ${UnInstallPath}$\r$\n"
    WriteRegStr "${RegRoot}" "${UnInstallPath}" "${Key}" "${Value}"
  !macroend


; AddItem macro
!macro AddItem Path
FileWrite $UninstLog "${Path}$\r$\n"
!macroend
!define AddItem "!insertmacro AddItem"


; File macro
;WriteRegDWORD macro
!macro File FilePath FileName
  !macro WriteRegDWORD RegRoot UnInstallPath Key Value
IfFileExists "$OUTDIR\${FileName}" +2
    FileWrite $UninstLog "${RegRoot} ${UnInstallPath}$\r$\n"
  FileWrite $UninstLog "$OUTDIR\${FileName}$\r$\n"
    WriteRegStr "${RegRoot}" "${UnInstallPath}" "${Key}" "${Value}"
File "${FilePath}${FileName}"
  !macroend
!macroend
</highlight-nsis>
!define File "!insertmacro File"


; CreateShortcut macro
== The Script ==
!macro CreateShortcut FilePath FilePointer
<p>Now add the include to the top of your script</p>
FileWrite $UninstLog "${FilePath}$\r$\n"
<highlight-nsis>
CreateShortcut "${FilePath}" "${FilePointer}"
!include "UninstallLog.nsh"
!macroend
<highlight-nsis>
!define CreateShortcut "!insertmacro CreateShortcut"


; Copy files macro
<p>'''The next chunk of code goes before all your sections; in your script...'''</p>
!macro CopyFiles SourcePath DestPath
IfFileExists "${DestPath}" +2
  FileWrite $UninstLog "${DestPath}$\r$\n"
CopyFiles "${SourcePath}" "${DestPath}"
!macroend
!define CopyFiles "!insertmacro CopyFiles"


; Rename macro
<highlight-nsis>
!macro Rename SourcePath DestPath
;--------------------------------
IfFileExists "${DestPath}" +2
; Configure UnInstall log to only remove what is installed
   FileWrite $UninstLog "${DestPath}$\r$\n"
;--------------------------------
Rename "${SourcePath}" "${DestPath}"
  ;Set the name of the uninstall log
!macroend
    !define UninstLog "uninstall.log"
!define Rename "!insertmacro Rename"
    Var UninstLog
 
 
; CreateDirectory macro
   ;Uninstall log file missing.
!macro CreateDirectory Path
    LangString UninstLogMissing ${LANG_ENGLISH} "${UninstLog} not found!$\r$\nUninstallation cannot proceed!"
CreateDirectory "${Path}"
 
FileWrite $UninstLog "${Path}$\r$\n"
  ;AddItem macro
!macroend
    !define AddItem "!insertmacro AddItem"
!define CreateDirectory "!insertmacro CreateDirectory"
 
 
  ;File macro
; SetOutPath macro
    !define File "!insertmacro File"
!macro SetOutPath Path
 
SetOutPath "${Path}"
  ;CreateShortcut macro
FileWrite $UninstLog "${Path}$\r$\n"
    !define CreateShortcut "!insertmacro CreateShortcut"
!macroend
 
!define SetOutPath "!insertmacro SetOutPath"
  ;Copy files macro
 
    !define CopyFiles "!insertmacro CopyFiles"
; WriteUninstaller macro
 
!macro WriteUninstaller Path
  ;Rename macro
WriteUninstaller "${Path}"
    !define Rename "!insertmacro Rename"
FileWrite $UninstLog "${Path}$\r$\n"
 
!macroend
  ;CreateDirectory macro
!define WriteUninstaller "!insertmacro WriteUninstaller"
    !define CreateDirectory "!insertmacro CreateDirectory"
 
  ;SetOutPath macro
    !define SetOutPath "!insertmacro SetOutPath"
 
  ;WriteUninstaller macro
    !define WriteUninstaller "!insertmacro WriteUninstaller"
 
  ;WriteRegStr macro
    !define WriteRegStr "!insertmacro WriteRegStr"
 
  ;WriteRegDWORD macro
    !define WriteRegDWORD "!insertmacro WriteRegDWORD"
 
  Section -openlogfile
    CreateDirectory "$INSTDIR"
    IfFileExists "$INSTDIR\${UninstLog}" +3
      FileOpen $UninstLog "$INSTDIR\${UninstLog}" w
    Goto +4
      SetFileAttributes "$INSTDIR\${UninstLog}" NORMAL
      FileOpen $UninstLog "$INSTDIR\${UninstLog}" a
      FileSeek $UninstLog 0 END
  SectionEnd</highlight-nsis>


Section -openlogfile
<p>Your sections, (the ones that add files,) need to be placed "here" (Between the above and below chunks of code) in your script.<br />
CreateDirectory "$INSTDIR"
Instead of using AddItem, File, CreateShortcut, CopyFiles, Rename, CreateDirectory, SetOutPath, WriteUninstaller, WriteRegStr and WriteRegDWORD instructions in your sections, use; ${AddItem}, ${File}, ${CreateShortcut}, ${CopyFiles}, ${Rename}, ${CreateDirectory}, ${SetOutPath}, ${WriteUninstaller}, ${WriteRegStr} and ${WriteRegDWORD} instead.</p>
IfFileExists "$INSTDIR\${UninstLog}" +3
  FileOpen $UninstLog "$INSTDIR\${UninstLog}" w
Goto +4
  SetFileAttributes "$INSTDIR\${UninstLog}" NORMAL
  FileOpen $UninstLog "$INSTDIR\${UninstLog}" a
  FileSeek $UninstLog 0 END
SectionEnd</highlight-nsis>
 
<p>Your own sections need to be placed here in your script.<br />
Instead of using SetOutPath, CreateDirectory, File, CopyFiles, Rename and WriteUninstaller instructions in your sections, use ${SetOutPath}, ${CreateDirectory}, ${File}, ${CopyFiles}, ${Rename} and ${WriteUninstaller} instead.</p>


<p>When using ${SetOutPath} to create more than one upper level directory, e.g.:<br />
<p>When using ${SetOutPath} to create more than one upper level directory, e.g.:<br />
Line 138: Line 186:
<p>'''This is an example of what your sections may now look like...'''</p>
<p>'''This is an example of what your sections may now look like...'''</p>


<highlight-nsis>Section "Install Main"
<highlight-nsis>
Section "Install Main"
SectionIn RO
SectionIn RO
  ${SetOutPath} $INSTDIR
  ${SetOutPath} $INSTDIR
  ${WriteUninstaller} "uninstall.exe"
  ${WriteUninstaller} "uninstall.exe"
Line 146: Line 194:
  ${File} "dir1\" "file2.ext"
  ${File} "dir1\" "file2.ext"
  ${File} "dir1\" "file3.ext"
  ${File} "dir1\" "file3.ext"
 
;Write the installation path into the registry
  ${WriteRegStr} "${REG_ROOT}" "${REG_APP_PATH}" "Install Directory" "$INSTDIR"
;Write the Uninstall information into the registry
  ${WriteRegStr} ${REG_ROOT} "${UNINSTALL_PATH}" "UninstallString" '"$INSTDIR\uninstall.exe"'
SectionEnd
SectionEnd


Section "Install Other"
Section "Install Other"
  ${AddItem} "$INSTDIR\Other"
  ${AddItem} "$INSTDIR\Other"
  ${SetOutPath} "$INSTDIR\Other\Temp"
  ${SetOutPath} "$INSTDIR\Other\Temp"
Line 156: Line 206:
  ${File} "dir2\" "file5.ext"
  ${File} "dir2\" "file5.ext"
  ${File} "dir2\" "file6.ext"
  ${File} "dir2\" "file6.ext"
SectionEnd
SectionEnd


Section "Copy Files & Rename"
Section "Copy Files & Rename"
  ${CreateDirectory} "$INSTDIR\backup"
  ${CreateDirectory} "$INSTDIR\backup"
  ${CopyFiles} "$INSTDIR\file1.ext" "$INSTDIR\backup\file1.ext"
  ${CopyFiles} "$INSTDIR\file1.ext" "$INSTDIR\backup\file1.ext"
  ${Rename} "$INSTDIR\file2.ext" "$INSTDIR\file1.ext"
  ${Rename} "$INSTDIR\file2.ext" "$INSTDIR\file1.ext"
SectionEnd
</highlight-nsis>


SectionEnd</highlight-nsis>
<p>'''This chunk of code comes after all of your sections, in your script...'''</p>
 
<p>'''This comes after all your own sections in your script...'''</p>
 
<highlight-nsis>Section -closelogfile
FileClose $UninstLog
SetFileAttributes "$INSTDIR\${UninstLog}" READONLY|SYSTEM|HIDDEN
SectionEnd


<highlight-nsis>
;--------------------------------
; Uninstaller
;--------------------------------
Section Uninstall
Section Uninstall
 
  ;Can't uninstall if uninstall log is missing!
; Can't uninstall if uninstall log is missing!
  IfFileExists "$INSTDIR\${UninstLog}" +3
IfFileExists "$INSTDIR\${UninstLog}" +3
    MessageBox MB_OK|MB_ICONSTOP "$(UninstLogMissing)"
  MessageBox MB_OK|MB_ICONSTOP "$(UninstLogMissing)"
      Abort
  Abort
 
  Push $R0
Push $R0
  Push $R1
Push $R1
  Push $R2
Push $R2
  SetFileAttributes "$INSTDIR\${UninstLog}" NORMAL
SetFileAttributes "$INSTDIR\${UninstLog}" NORMAL
  FileOpen $UninstLog "$INSTDIR\${UninstLog}" r
FileOpen $UninstLog "$INSTDIR\${UninstLog}" r
  StrCpy $R1 -1
StrCpy $R1 -1
  GetLineCount:
    ClearErrors
    FileRead $UninstLog $R0
    IntOp $R1 $R1 + 1
    StrCpy $R0 $R0 -2
    Push $R0 
    IfErrors 0 GetLineCount
  Pop $R0
  LoopRead:
    StrCmp $R1 0 LoopDone
    Pop $R0
   
   
  GetLineCount:
    IfFileExists "$R0\*.*" 0 +3
  ClearErrors
      RMDir $R0 #is dir
  FileRead $UninstLog $R0
    Goto +9
  IntOp $R1 $R1 + 1
    IfFileExists $R0 0 +3
  StrCpy $R0 $R0 -2
      Delete $R0 #is file
   Push $R0    
    Goto +6
   IfErrors 0 GetLineCount
    StrCmp $R0 "${REG_ROOT} ${REG_APP_PATH}" 0 +3
 
      DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}" #is Reg Element
Pop $R0
    Goto +3
 
    StrCmp $R0 "${REG_ROOT} ${UNINSTALL_PATH}" 0 +2
LoopRead:
      DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}" #is Reg Element
   StrCmp $R1 0 LoopDone
    
    IntOp $R1 $R1 - 1
    Goto LoopRead
   LoopDone:
   FileClose $UninstLog
  Delete "$INSTDIR\${UninstLog}"
  Pop $R2
   Pop $R1
   Pop $R0
   Pop $R0
  IfFileExists "$R0\*.*" 0 +3
  RMDir $R0  #is dir
  Goto +3
  IfFileExists $R0 0 +2
  Delete $R0 #is file
   
   
   IntOp $R1 $R1 - 1
   ;Remove registry keys
  Goto LoopRead
    ;DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}"
LoopDone:
    ;DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}"
FileClose $UninstLog
Delete "$INSTDIR\${UninstLog}"
RMDir "$INSTDIR"
Pop $R2
Pop $R1
Pop $R0
SectionEnd
SectionEnd
</highlight-nsis>
</highlight-nsis>


-Stu
-Rapitharian


[[Category:Code Examples]]
[[Category:Code Examples]]

Revision as of 21:50, 20 October 2010

Author: Afrow UK (talk, contrib)


Description

This code was written for Rookie12 a long while back. So many people have asked the same question since, so I thought that it was a good idea to put it up on here.


Entire script updated 12th May 2006

  • Added functions for AddItem, CopyFiles and Rename.
  • RMDir /r removed.
  • Log file now read in reverse order.

-Stu


Script updated 28 Sep 2006

  • Fix issue where uninstall.log is not created if $INSTDIR doesn't already exist
  • Fix infinite loop on uninstall - "last" line (actually first line) of uninstall.log is read over and over again

- Anonymoose


Script updated 08 Nov 2006

  • Uninstaller now deletes itself and $INSTDIR

- mark


Script updated 27 April 2007

  • Removed Delete instruction to delete the uninstaller and added ${WriteUninstaller} instead.
  • RMDir $INSDIR was repeated from last update.

- Afrow UK


Script updated 18 August 2009

  • Added ${CreateShortcut} macro based on ${File}.

- Adm.Wiggin


Script updated 28 Dec 2009

  • Uninstaller now works faster (it needs to parse uninstall.log only one time)

- Aleppin

The Macro Script

Take the next chunk of code and save it to a file called UnInstallLog.nsh in %ProgramFiles%\NSIS\Include\.

;AddItem macro
  !macro AddItem Path
    FileWrite $UninstLog "${Path}$\r$\n"
  !macroend
 
;File macro
  !macro File FilePath FileName
     IfFileExists "$OUTDIR\${FileName}" +2
     FileWrite $UninstLog "$OUTDIR\${FileName}$\r$\n"
     File "${FilePath}${FileName}"
  !macroend
 
;CreateShortcut macro
  !macro CreateShortcut FilePath FilePointer Pamameters Icon IconIndex
    FileWrite $UninstLog "${FilePath}$\r$\n"
    CreateShortcut "${FilePath}" "${FilePointer}" "${Pamameters}" "${Icon}" "${IconIndex}"
  !macroend
 
;Copy files macro
  !macro CopyFiles SourcePath DestPath
    IfFileExists "${DestPath}" +2
    FileWrite $UninstLog "${DestPath}$\r$\n"
    CopyFiles "${SourcePath}" "${DestPath}"
  !macroend
 
;Rename macro
  !macro Rename SourcePath DestPath
    IfFileExists "${DestPath}" +2
    FileWrite $UninstLog "${DestPath}$\r$\n"
    Rename "${SourcePath}" "${DestPath}"
  !macroend
 
;CreateDirectory macro
  !macro CreateDirectory Path
    CreateDirectory "${Path}"
    FileWrite $UninstLog "${Path}$\r$\n"
  !macroend
 
;SetOutPath macro
  !macro SetOutPath Path
    SetOutPath "${Path}"
    FileWrite $UninstLog "${Path}$\r$\n"
  !macroend
 
;WriteUninstaller macro
  !macro WriteUninstaller Path
    WriteUninstaller "${Path}"
    FileWrite $UninstLog "${Path}$\r$\n"
  !macroend
 
;WriteRegStr macro
  !macro WriteRegStr RegRoot UnInstallPath Key Value
     FileWrite $UninstLog "${RegRoot} ${UnInstallPath}$\r$\n"
     WriteRegStr "${RegRoot}" "${UnInstallPath}" "${Key}" "${Value}"
  !macroend
 
 
;WriteRegDWORD macro
  !macro WriteRegDWORD RegRoot UnInstallPath Key Value
     FileWrite $UninstLog "${RegRoot} ${UnInstallPath}$\r$\n"
     WriteRegStr "${RegRoot}" "${UnInstallPath}" "${Key}" "${Value}"
  !macroend

The Script

Now add the include to the top of your script

!include "UninstallLog.nsh"
<highlight-nsis>
 
<p>'''The next chunk of code goes before all your sections; in your script...'''</p>
 
<highlight-nsis>
;--------------------------------
; Configure UnInstall log to only remove what is installed
;-------------------------------- 
  ;Set the name of the uninstall log
    !define UninstLog "uninstall.log"
    Var UninstLog
 
  ;Uninstall log file missing.
    LangString UninstLogMissing ${LANG_ENGLISH} "${UninstLog} not found!$\r$\nUninstallation cannot proceed!"
 
  ;AddItem macro
    !define AddItem "!insertmacro AddItem"
 
  ;File macro
    !define File "!insertmacro File"
 
  ;CreateShortcut macro
    !define CreateShortcut "!insertmacro CreateShortcut"
 
  ;Copy files macro
    !define CopyFiles "!insertmacro CopyFiles"
 
  ;Rename macro
    !define Rename "!insertmacro Rename"
 
  ;CreateDirectory macro
    !define CreateDirectory "!insertmacro CreateDirectory"
 
  ;SetOutPath macro
    !define SetOutPath "!insertmacro SetOutPath"
 
  ;WriteUninstaller macro
    !define WriteUninstaller "!insertmacro WriteUninstaller"
 
  ;WriteRegStr macro
    !define WriteRegStr "!insertmacro WriteRegStr"
 
  ;WriteRegDWORD macro
    !define WriteRegDWORD "!insertmacro WriteRegDWORD" 
 
  Section -openlogfile
    CreateDirectory "$INSTDIR"
    IfFileExists "$INSTDIR\${UninstLog}" +3
      FileOpen $UninstLog "$INSTDIR\${UninstLog}" w
    Goto +4
      SetFileAttributes "$INSTDIR\${UninstLog}" NORMAL
      FileOpen $UninstLog "$INSTDIR\${UninstLog}" a
      FileSeek $UninstLog 0 END
  SectionEnd

Your sections, (the ones that add files,) need to be placed "here" (Between the above and below chunks of code) in your script.
Instead of using AddItem, File, CreateShortcut, CopyFiles, Rename, CreateDirectory, SetOutPath, WriteUninstaller, WriteRegStr and WriteRegDWORD instructions in your sections, use; ${AddItem}, ${File}, ${CreateShortcut}, ${CopyFiles}, ${Rename}, ${CreateDirectory}, ${SetOutPath}, ${WriteUninstaller}, ${WriteRegStr} and ${WriteRegDWORD} instead.

When using ${SetOutPath} to create more than one upper level directory, e.g.:
${SetOutPath} "$INSTDIR\dir1\dir2\dir3", you need to add entries for each lower level directory for them all to be deleted:

${AddItem} "$INSTDIR\dir1"
${AddItem} "$INSTDIR\dir1\dir2"
${SetOutPath} "$INSTDIR\dir1\dir2\dir3"

This is an example of what your sections may now look like...

Section "Install Main"
SectionIn RO
 ${SetOutPath} $INSTDIR
 ${WriteUninstaller} "uninstall.exe"
 ${File} "dir1\" "file1.ext"
 ${File} "dir1\" "file2.ext"
 ${File} "dir1\" "file3.ext"
 ;Write the installation path into the registry
   ${WriteRegStr} "${REG_ROOT}" "${REG_APP_PATH}" "Install Directory" "$INSTDIR"
 ;Write the Uninstall information into the registry
   ${WriteRegStr} ${REG_ROOT} "${UNINSTALL_PATH}" "UninstallString" '"$INSTDIR\uninstall.exe"'
SectionEnd
 
Section "Install Other"
 ${AddItem} "$INSTDIR\Other"
 ${SetOutPath} "$INSTDIR\Other\Temp"
 ${File} "dir2\" "file4.ext"
 ${File} "dir2\" "file5.ext"
 ${File} "dir2\" "file6.ext"
SectionEnd
 
Section "Copy Files & Rename"
 ${CreateDirectory} "$INSTDIR\backup"
 ${CopyFiles} "$INSTDIR\file1.ext" "$INSTDIR\backup\file1.ext"
 ${Rename} "$INSTDIR\file2.ext" "$INSTDIR\file1.ext"
SectionEnd

This chunk of code comes after all of your sections, in your script...

;--------------------------------
; Uninstaller
;--------------------------------
Section Uninstall
  ;Can't uninstall if uninstall log is missing!
  IfFileExists "$INSTDIR\${UninstLog}" +3
    MessageBox MB_OK|MB_ICONSTOP "$(UninstLogMissing)"
      Abort
 
  Push $R0
  Push $R1
  Push $R2
  SetFileAttributes "$INSTDIR\${UninstLog}" NORMAL
  FileOpen $UninstLog "$INSTDIR\${UninstLog}" r
  StrCpy $R1 -1
 
  GetLineCount:
    ClearErrors
    FileRead $UninstLog $R0
    IntOp $R1 $R1 + 1
    StrCpy $R0 $R0 -2
    Push $R0   
    IfErrors 0 GetLineCount
 
  Pop $R0
 
  LoopRead:
    StrCmp $R1 0 LoopDone
    Pop $R0
 
    IfFileExists "$R0\*.*" 0 +3
      RMDir $R0  #is dir
    Goto +9
    IfFileExists $R0 0 +3
      Delete $R0 #is file
    Goto +6
    StrCmp $R0 "${REG_ROOT} ${REG_APP_PATH}" 0 +3
      DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}" #is Reg Element
    Goto +3
    StrCmp $R0 "${REG_ROOT} ${UNINSTALL_PATH}" 0 +2
      DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}" #is Reg Element
 
    IntOp $R1 $R1 - 1
    Goto LoopRead
  LoopDone:
  FileClose $UninstLog
  Delete "$INSTDIR\${UninstLog}"
  Pop $R2
  Pop $R1
  Pop $R0
 
  ;Remove registry keys
    ;DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}"
    ;DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}"
SectionEnd

-Rapitharian