NSIS使用安装日志卸载程序文件
侧边栏壁纸
  • 累计撰写 29 篇文章
  • 累计收到 178 条评论

NSIS使用安装日志卸载程序文件

nsisfans
2022-01-14 / 0 评论 / 111 阅读 / 正在检测是否收录...

贾可同学写的,安装时记录释放文件日志,卸载时根据日志记录进行卸载,有效防止误删文件。完整代码如下:

!define nsAppSetup          "App_20210916_x86.exe"
!define nsAppName           "App"
!define nsAppDesc           "${nsAppName} 1.0"
!define nsAppId             "{03D9BE5D-2411-425F-96C7-24246D4D58FC}"
!define nsAppVersion        "1.0.0.0"
!define nsAppUrl            "http://www.example.cn/"
!define nsAppPublisher      "Example Inc."
!define nsAppCompany        "Example"

!define NS_UNINST_ROOT_KEY  "HKLM"
!define NS_UNINST_INFO_KEY  "Software\Microsoft\Windows\CurrentVersion\Uninstall\${nsAppId}"

!define NS_UNINST_MASK_DIR  0x8000
!define NS_UNINST_MASK_LEN  0x7fff



!include LogicLib.nsh
!include WinCore.nsh

SetCompressor lzma
SetCompressorDictSize 32

Name "${nsAppName}"
OutFile "${nsAppSetup}"

#Icon ".\install.ico"
#UninstallIcon ".\uninstall.ico"

InstallDirRegKey ${NS_UNINST_ROOT_KEY} "${NS_UNINST_INFO_KEY}" "InstallLocation"
InstallDir "$PROGRAMFILES\${nsAppCompany}\${nsAppName}"

RequestExecutionLevel admin
AutoCloseWindow false

ShowInstDetails show
ShowUninstDetails show

InstallColors /windows
XPStyle on

BrandingText "${nsAppUrl}"

#ChangeUI all ".\example.exe"

Page directory
Page instfiles

UninstPage uninstConfirm
UninstPage instfiles

LoadLanguageFile "${NSISDIR}\Contrib\Language files\SimpChinese.nlf"
LoadLanguageFile "${NSISDIR}\Contrib\Language files\TradChinese.nlf"
LoadLanguageFile "${NSISDIR}\Contrib\Language files\English.nlf"

LangString MSG_FILE_NAME_INFO ${LANG_SIMPCHINESE} "文件"
LangString MSG_FILE_NOT_FOUND ${LANG_SIMPCHINESE} "不存在!"
LangString MSG_FILE_CORRUPTED ${LANG_SIMPCHINESE} "已损坏!"

LangString MSG_FILE_NAME_INFO ${LANG_TRADCHINESE} "檔案"
LangString MSG_FILE_NOT_FOUND ${LANG_TRADCHINESE} "不存在!"
LangString MSG_FILE_CORRUPTED ${LANG_TRADCHINESE} "已損毀!"

LangString MSG_FILE_NAME_INFO ${LANG_ENGLISH} "The file"
LangString MSG_FILE_NOT_FOUND ${LANG_ENGLISH} "doesn't exist!"
LangString MSG_FILE_CORRUPTED ${LANG_ENGLISH} "is corrupted!"

VIProductVersion "${nsAppVersion}"

VIAddVersionKey "FileDescription" "${nsAppDesc}"
VIAddVersionKey "FileVersion" "${nsAppVersion}"
VIAddVersionKey "ProductName" "${nsAppName}"
VIAddVersionKey "ProductVersion" "${nsAppVersion}"
VIAddVersionKey "LegalCopyright" "${nsAppPublisher}"
VIAddVersionKey "CompanyName" "${nsAppCompany}"

Section "-Install" SEC_01

    SetOutPath $INSTDIR
    File "$%WINDIR%\notepad.exe"
    File "$%WINDIR%\regedit.exe"
    SetOutPath $INSTDIR\dir1
    File "$%WINDIR%\notepad.exe"
    File "$%WINDIR%\regedit.exe"

    WriteRegStr ${NS_UNINST_ROOT_KEY} "${NS_UNINST_INFO_KEY}" "DisplayIcon" "$INSTDIR\uninst.exe"
    WriteRegStr ${NS_UNINST_ROOT_KEY} "${NS_UNINST_INFO_KEY}" "DisplayName" "${nsAppDesc}"
    WriteRegStr ${NS_UNINST_ROOT_KEY} "${NS_UNINST_INFO_KEY}" "DisplayVersion" "${nsAppVersion}"
    WriteRegStr ${NS_UNINST_ROOT_KEY} "${NS_UNINST_INFO_KEY}" "UninstallString" '"$INSTDIR\uninst.exe"'
    WriteRegStr ${NS_UNINST_ROOT_KEY} "${NS_UNINST_INFO_KEY}" "InstallLocation" "$INSTDIR"
    WriteRegStr ${NS_UNINST_ROOT_KEY} "${NS_UNINST_INFO_KEY}" "Publisher" "${nsAppPublisher}"
    WriteRegStr ${NS_UNINST_ROOT_KEY} "${NS_UNINST_INFO_KEY}" "HelpLink" "${nsAppUrl}"
    SectionGetSize ${SEC_01} $R0
    WriteRegDWORD ${NS_UNINST_ROOT_KEY} "${NS_UNINST_INFO_KEY}" "EstimatedSize" "$R0"
    WriteRegDWORD ${NS_UNINST_ROOT_KEY} "${NS_UNINST_INFO_KEY}" "NoModify" "1"
    WriteRegDWORD ${NS_UNINST_ROOT_KEY} "${NS_UNINST_INFO_KEY}" "NoRepair" "1"

;    创建卸载程序,日志要记录卸载程序的修改时间供校验。
    WriteUninstaller "$INSTDIR\uninst.exe"

    GetTempFileName $R0
;    生成一个临时文件作为日志,要不然会把日志文件自己的路径也记录进去。
    FileOpen $R1 $R0 w
    ${IfNot} ${Errors}
    GetFileTime "$INSTDIR\uninst.exe" $1 $2
;
;    我们在这里写一个 UTF-8 文件头,主要是为了防止一般用户随意修改此文件。
;    但实际上是二进制格式,包含一些二进制数据和路径,路径以 UTF-16 写入。
;    这样 Windows 记事本打开后默认以 UTF-8 解析,会无法正常显示文本。
;
    FileWriteByte $R1 0xef
    FileWriteByte $R1 0xbb
    FileWriteByte $R1 0xbf
;
;    然后我们再在这里写一些字节 (FileWriteByte),或字符串 (FileWrite),比如公司名称之类的。
;    请尽量保证这些字节和 UTF-8 头(三字节) 加起来是 4 的倍数,不够可以加空格。
;
;    写一些 byte 数据:
;    FileWriteByte $R1 0x6e ; 'N'
;    FileWriteByte $R1 0x73 ; 'S'
;
;    写字符串:
;    注意这里就用三个空格凑满 17 个字节 (如果还写入了 byte 数据,应该计算在内),包含文件头一共凑到 20 字节。
    FileWrite $R1 "Nullsoft, Inc.   "
;    写入卸载程序的修改时间
    IntOp $0 $1 >> 16
    FileWriteWord $R1 $0
    IntOp $0 $1 & 0xffff
    FileWriteWord $R1 $0
    IntOp $0 $2 >> 16
    FileWriteWord $R1 $0
    IntOp $0 $2 & 0xffff
    FileWriteWord $R1 $0
;    开始写入文件夹和文件信息
    Push $INSTDIR # 路径
    Push $R1 # 日志
    Call LoggerWriter
    FileClose $R1
;    将生成的临时日志文件移动到安装目录
    SetDetailsPrint none
    Delete "$INSTDIR\uninst.dat"
    Rename $R0 "$INSTDIR\uninst.dat"
    SetDetailsPrint lastused
    ${EndIf}

SectionEnd

Section Uninstall

    StrCpy $R0 "$INSTDIR\uninst.dat"
    ClearErrors
    FileOpen $R1 $R0 r
    ${IfNot} ${Errors}
;    由于 un.onInit 中已经判断,这里直接跳过。
;    也可以使用一次性跳过
    FileReadByte $R1 $0
    FileReadByte $R1 $0
    FileReadByte $R1 $0
;    FileReadByte $R1 $0
;    FileReadByte $R1 $0
;    注意这里长度
    FileRead $R1 $0 17
;    跳过卸载程序日期
    FileReadWord $R1 $0
    FileReadWord $R1 $0
    FileReadWord $R1 $0
    FileReadWord $R1 $0
;    开始根据日志卸载
    Push $R1
    Call un.LoggerDeleter
    FileClose $R1
    DeleteRegKey ${NS_UNINST_ROOT_KEY} "${NS_UNINST_INFO_KEY}"
;    删除日志自身...
    Delete "$INSTDIR\uninst.dat"
;    删除安装目录
    RMDir "$INSTDIR"
    ${EndIf}

SectionEnd

;
; 这个是递归函数,会遍历文件夹中的所有文件,并将路径写入日志中。
; 如果遇到文件夹,则先进入遍历文件,最后再退出并记录文件夹自身路径,符合卸载流程。
;
Function LoggerWriter
    Exch $R0
    Exch
    Exch $R1
    Push $R2
    Push $R3
    Push $R4
    FindFirst $R3 $R2 "$R1\*.*"
lbl_loop:
    StrCmp $R2 ".." lbl_skip
    StrCmp $R2 "." lbl_skip
    StrCmp $R2 "" lbl_done
    StrLen $R4 "$R1\$R2"
    IfFileExists "$R1\$R2\*.*" 0 lbl_write
    IntOp $R4 $R4 | ${NS_UNINST_MASK_DIR}
    Push "$R1\$R2"
    Push "$R0"
    Call LoggerWriter
lbl_write:
;    $R4 是 16 位整数,最高位代表文件夹标志,其余位为路径长度,将其有意地取反再写入到日志中。
    IntOp $R4 $R4 ~
;    写入标志
    FileWriteWord $R0 $R4
;    写入路径
    FileWriteUTF16LE $R0 "$R1\$R2"
lbl_skip:
    FindNext $R3 $R2
    Goto lbl_loop
lbl_done:
    Pop $R4
    Pop $R3
    Pop $R2
    Pop $R1
    Pop $R0
FunctionEnd

Function un.LoggerDeleter
    Exch $R0
    Push $R1
    Push $R2
    Push $R3
    Push $R4
lbl_loop:
;    读取一个 16 位整数
    FileReadWord $R0 $R1
    IntCmp $R1 0 lbl_done
;    由于之前已经取反,现在取反以恢复正确数据。
    IntOp $R1 $R1 ~
;    得到长度信息
    IntOp $R3 $R1 & ${NS_UNINST_MASK_LEN}
;    得到文件夹标记
    IntOp $R4 $R1 & ${NS_UNINST_MASK_DIR}
;    根据长度读取路径字符串
    FileReadUTF16LE $R0 $R2 $R3
;    根据文件夹标记执行对应的删除操作
    IfFileExists $R2 0 lbl_done
    IntOp $R4 $R4 !
    IntCmp $R4 0 lbl_path
    DetailPrint `Delete $R2`
    Delete $R2
    Goto lbl_loop
lbl_path:
    DetailPrint `RMDir $R2`
    RMDir $R2
    Goto lbl_loop
lbl_done:
    Pop $R4
    Pop $R3
    Pop $R2
    Pop $R1
    Pop $R0
FunctionEnd

Function un.onInit

    InitPluginsDir
    StrCpy $R0 "$INSTDIR\uninst.dat"
    ClearErrors
    FileOpen $R1 $R0 r
    IfErrors 0 lbl_read
    MessageBox MB_OK|MB_ICONINFORMATION `$(MSG_FILE_NAME_INFO) "$R0" $(MSG_FILE_NOT_FOUND)`
    Abort
lbl_read:
    GetFileTime "$INSTDIR\uninst.exe" $1 $2
    FileReadByte $R1 $R2
    FileReadByte $R1 $R3
    FileReadByte $R1 $R4
;    读取之前写入的字节以供后续校验
;    FileReadByte $R1 $R5
;    FileReadByte $R1 $R6
;    读取之前写入的字符串以供后续校验
;    注意必须指定正确长度(这里是17)
    FileRead $R1 $R7 17
    FileReadWord $R1 $0
    IntOp $R8 $0 << 16
    FileReadWord $R1 $0
    IntOp $R8 $R8 | $0
    FileReadWord $R1 $0
    IntOp $R9 $0 << 16
    FileReadWord $R1 $0
    IntOp $R9 $R9 | $0
    FileClose $R1
;    验证卸载程序的修改时间是否正确
    IntOp $0 $R9 ^ $2
    StrCmp $0 0 0 lbl_error
    IntOp $0 $R8 ^ $1
    StrCmp $0 0 0 lbl_error
    StrCmpS $R7 "Nullsoft, Inc.   " 0 lbl_error
;    IntOp $0 $R6 ^ 0x73
;    StrCmp $0 0 0 lbl_error
;    IntOp $0 $R5 ^ 0x6e
;    StrCmp $0 0 0 lbl_error
    IntOp $0 $R4 ^ 0xbf
    StrCmp $0 0 0 lbl_error
    IntOp $0 $R3 ^ 0xbb
    StrCmp $0 0 0 lbl_error
    IntOp $0 $R2 ^ 0xef
    StrCmp $0 0 lbl_done
lbl_error:
    MessageBox MB_OK|MB_ICONINFORMATION `$(MSG_FILE_NAME_INFO) "$R0" $(MSG_FILE_CORRUPTED)`
    Abort
lbl_done:

FunctionEnd
0

评论 (0)

取消