NSIS打包学习

需要安装 NSIS,然后在安装向导工具 HM NIS Edit
使用 HM NIS Edit 来操作即可。
文件-新建脚本向导

nsis
nsis editor

vscode 插件

nsis 作者:idleberg
主要需要配置 NSIS 编译器的路径 NSIS 安装目录下的 makensis.exe 即 NSIS 编译器。
脚本文件的默认扩展名是 .nsi,头文件为 .nsh。
文件名右侧有功能按钮,可以执行编译,显示版本等操作

常用定义

; 安装程序初始定义常量
!define PRODUCT_NAME "Duang" ; 程序名
!define PRODUCT_VERSION "1.0" ; 版本号
!define PRODUCT_PUBLISHER "mxy" ; 作者
!define PRODUCT_PATH "D:\mxy\Desktop\Duang" ; 新增定义源程序路径
!define PRODUCT_WEB_SITE "https://mxy.cool" ; 网址
!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\Duang.exe" ; 注册表相关
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
!define PRODUCT_UNINST_ROOT_KEY "HKLM"


Section -AdditionalIcons
WriteIniStr "$INSTDIR\${PRODUCT_NAME}.url" "InternetShortcut" "URL" "${PRODUCT_WEB_SITE}"

CreateShortCut "$DESKTOP\Duang\Website.lnk" "$INSTDIR\${PRODUCT_NAME}.url"
CreateShortCut "$SMPROGRAMS\Duang\Website.lnk" "$INSTDIR\${PRODUCT_NAME}.url"
CreateShortCut "$SMPROGRAMS\Duang\Uninstall.lnk" "$INSTDIR\uninst.exe"
SectionEnd


; 卸载程序启动时执行
Function un.onInit
MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "您确实要完全移除 $(^Name) ,及其所有的组件?" IDYES +2
Abort
FunctionEnd

; 卸载成功时执行
Function un.onUninstSuccess
HideWindow
MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) 已成功地从您的计算机移除。"
FunctionEnd

Section Uninstall
; 删除快捷方式
Delete "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall.lnk"
Delete "$SMPROGRAMS\${PRODUCT_NAME}\Website.lnk"
Delete "$DESKTOP\${PRODUCT_NAME}.lnk"
Delete "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk"

; 删除开始菜单文件夹
RMDir "$SMPROGRAMS\${PRODUCT_NAME}"

RMDir /r "$INSTDIR" ; 直接删除整个文件夹

; 删除注册表信息
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY}"
SetAutoClose true ; 卸载完毕自动关闭窗口
SectionEnd

注册表相关

Section -Post
WriteUninstaller "$INSTDIR\uninst.exe" ; 写入卸载程序
WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\${PRODUCT_NAME}.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_NAME}.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
SectionEnd

压缩算法

SetCompressor lzma

File

File
[/nonfatal] [/a] ([/r] [/x file|wildcard [...]] (file|wildcard) [...] | /oname=file.dat infile.dat)
Section "MainSection" SEC01
SetOutPath "$INSTDIR"
SetOverwrite ifnewer ; 更新覆盖
File /r /x IPKVM-cn.exe /x IPKVM-en.exe bin\*.*
CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}" ; 创建开始菜单目录并命名为程序名
; 在上一行创建的开始菜单目录下创建程序快捷方式
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk" "$INSTDIR\${PRODUCT_NAME}.exe"
CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" "$INSTDIR\${PRODUCT_NAME}.exe" ; 创建桌面快捷方式
File "${PRODUCT_PATH}\${PRODUCT_NAME}.exe" ; 源可执行程序的路径
Exec "$INSTDIR\registerInfo.exe -h"
SectionEnd

执行程序

Exec "$INSTDIR\registerInfo.exe -h"

调试

MessageBox MB_OK "messagebox_text" 

调试文件输出

GetTempFileName $R0
FileOpen $R1 $R0 w
# Your Code
FileWrite $R1 "文件标志 = $TempFileFlag$\r$\n"
FileWrite $R1 "文件名 = $TempFileName$\r$\n"
FileWrite $R1 "临时路径 = $TempFilePath$\r$\n"
FileWrite $R1 "目标路径 = $TargFilePath$\r$\n$\r$\n"
# Your Code
FileClose $R1
Exec '"notepad.exe" "$R0"'

安装包带版本,时间

!define /date PRODUCT_TIME %Y%m%d%H%M%S
!define /date PRODUCT_DATE %Y%m%d
OutFile "Test_${PRODUCT_VERSION}_${PRODUCT_TIME}.exe"

;生成的文件名为:Test_1.0.0.0_20140428.exe
; 注意大小写。

检测.net 版本

!include "WordFunc.nsh"

Section - "比较版本号"
DetailPrint "正在检测安装环境..."
Call GetNetFrameworkVersion
Pop $R1
${VersionCompare} "4.7" "$R1" $R2
${If} $R2 == 0
DetailPrint "当前版本($R1),无需安装组件"
${ElseIf} $R2 == 1
SetDetailsPrint textonly
DetailPrint "准备安装.NetFramework 4.7.2组件"
SetDetailsPrint listonly
SetOutPath "$TEMP"
SetOverwrite on
File "SPCAssist\bin\x64\Release\NDP472-KB4054530-x86-x64-AllOS-ENU.exe"
ExecWait '$TEMP\NDP472-KB4054530-x86-x64-AllOS-ENU.exe ' $R1
Delete "$TEMP\NDP472-KB4054530-x86-x64-AllOS-ENU.exe"
${ElseIf} $R2 == 2
DetailPrint "当前版本($R1),无需安装组件"
${EndIf}
SectionEnd

Function GetNetFrameworkVersion
;获取.Net Framework版本,支持
Push $1
Push $0
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" "Version"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5" "Version"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.0\Setup" "InstallSuccess"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.0\Setup" "Version"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727" "Version"
StrCmp $1 "" +1 +2
StrCpy $1 "2.0.50727.832"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v1.1.4322" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v1.1.4322" "Version"
StrCmp $1 "" +1 +2
StrCpy $1 "1.1.4322.573"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\.NETFramework\policy\v1.0" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\.NETFramework\policy\v1.0" "Version"
StrCmp $1 "" +1 +2
StrCpy $1 "1.0.3705.0"
StrCmp $0 1 KnowNetFrameworkVersion +1
StrCpy $1 "NO_NetFramework"
KnowNetFrameworkVersion:
Pop $0
Exch $1
FunctionEnd

注册 DLL

Make sure you use RegDLL and UnRegDLL because the regsvr32 executable is not on all Windows platforms.

SetOutPath "$INSTDIR"
RegDLL "$INSTDIR\kvmclient.dll"

SetOutPath "$INSTDIR"
UnRegDLL "$INSTDIR\combysocketfullduplex.dll"

安装证书

Section - "离线安装需要证书"
DetailPrint "正在安装证书..."
Push $INSTDIR\MicrosoftRootCertificateAuthority2011.cer
Call AddCertificateToStore
Pop $0
${If} $0 != success
MessageBox MB_OK "import failed: $0"
${EndIf}
SectionEnd



!define CERT_QUERY_OBJECT_FILE 1
!define CERT_QUERY_CONTENT_FLAG_ALL 16382
!define CERT_QUERY_FORMAT_FLAG_ALL 14
!define CERT_STORE_PROV_SYSTEM 10
!define CERT_STORE_OPEN_EXISTING_FLAG 0x4000
!define CERT_SYSTEM_STORE_LOCAL_MACHINE 0x20000
!define CERT_STORE_ADD_ALWAYS 4

Function AddCertificateToStore
Exch $0
Push $1
Push $R0
System::Call "crypt32::CryptQueryObject(i ${CERT_QUERY_OBJECT_FILE}, w r0, \
i ${CERT_QUERY_CONTENT_FLAG_ALL}, i ${CERT_QUERY_FORMAT_FLAG_ALL}, \
i 0, i 0, i 0, i 0, i 0, i 0, *i .r0) i .R0"
${If} $R0 <> 0
System::Call "crypt32::CertOpenStore(i ${CERT_STORE_PROV_SYSTEM}, i 0, i 0, \
i ${CERT_STORE_OPEN_EXISTING_FLAG}|${CERT_SYSTEM_STORE_LOCAL_MACHINE}, \
w 'ROOT') i .r1"
${If} $1 <> 0
System::Call "crypt32::CertAddCertificateContextToStore(i r1, i r0, \
i ${CERT_STORE_ADD_ALWAYS}, i 0) i .R0"
System::Call "crypt32::CertFreeCertificateContext(i r0)"
${If} $R0 = 0
StrCpy $0 "Unable to add certificate to certificate store"
${Else}
StrCpy $0 "success"
${EndIf}
System::Call "crypt32::CertCloseStore(i r1, i 0)"
${Else}
System::Call "crypt32::CertFreeCertificateContext(i r0)"
StrCpy $0 "Unable to open certificate store"
${EndIf}
${Else}
StrCpy $0 "Unable to open certificate file"
${EndIf}

Pop $R0
Pop $1
Exch $0
FunctionEnd

判断路径是否存在

file_to_check_for jump_if_present [jump_otherwise]
IfFileExists $WINDIR\notepad.exe 0 +2
MessageBox MB_OK "notepad is installed"

windows 补丁包 静默安装

Windows6.1-KB2533623-x64.msu /quiet /norestart

后台运行

;安装程序完成后后台运行
Function .onInstSuccess
ExecShell "" "$INSTDIR\${PRODUCT_NAME}.exe"
FunctionEnd

判断是否已安装

Var UNINSTALL_PROG
Var OLD_VER
Var OLD_PATH

Function .onInit
ClearErrors
ReadRegStr $UNINSTALL_PROG ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallString"
IfErrors done

ReadRegStr $OLD_VER ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "DisplayVersion"
MessageBox MB_YESNOCANCEL|MB_ICONQUESTION \
"检测到本机已经安装了 ${PRODUCT_NAME} $OLD_VER。\
$\n$\n是否先卸载已安装的版本?" \
/SD IDYES \
IDYES uninstall \
IDNO done
Abort

uninstall:
StrCpy $OLD_PATH $UNINSTALL_PROG -10


ExecWait '"$UNINSTALL_PROG" /S _?=$OLD_PATH' $0
DetailPrint "uninst.exe returned $0"
Delete "$UNINSTALL_PROG"
RMDir $OLD_PATH


done:
FunctionEnd

管理员权限和开机自动启动

安装后自动运行时,管理员权限找到下面的地方添加 RequestExecutionLevel Admin

Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
OutFile "xxxxx"
InstallDir "xxxxx"
InstallDirRegKey HKLM "${PRODUCT_UNINST_KEY}" "UninstallString"
ShowInstDetails show
ShowUnInstDetails show
RequestExecutionLevel Admin

解决问题:程序安装后,通过桌面快捷方式运行程序时不需要再右键选择管理员身份运行。

注意:程序安装时需要使用管理员权限进行安装,才能往注册写入信息

解释:${MAIN_PROGRAM_NAME}M.exe 是要执行的主程序的名称

;针对当前用户有效
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" "$INSTDIR\${MAIN_PROGRAM_NAME}M.exe" "RUNASADMIN"

;针对所有用户有效
WriteRegStr HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" "$INSTDIR\${MAIN_PROGRAM_NAME}M.exe" "RUNASADMIN"

InstallDirRegKey PRODUCT_DIR_REGKEY

记录安装位置,目的是重装时安装到同一个安装目录

在这里设置的。卸载的时候会删掉
Section -Post

安装完毕后是否运行程序

; Finish page
!define MUI_FINISHPAGE_RUN “$INSTDIR\shengdiDTV.exe”
!insertmacro MUI_PAGE_FINISH

变量

变量定义用 Var 关键字,后跟变量名,变量是全局的并且是大小写敏感的。变量引用时需要加上前缀 “$”。
除了用户自定义的变量外,nsi 脚本中与定义寄存器变量 $0~$9、$R0~$R9 用于参数传递,以及系统变量用于特定用途。

$INSTDIR,$OUTDIR,$CMDLINE,$LANGUAGE 这些变量都是可写的

// 程序数据保存根路径 C:\Users\Administrator\AppData\Roaming
$APPDATA

$PROGRAMFILES,$COMMONFILES,$DESKTOP,$EXEDIR,$WINDIR,$SYSDIR,$ TEMP,$STARTMENU
$SMPROGRAMS,$SMSTARTUP,$QUICKLAUNCH,$DOCUMENTS,$SENDTO,$ RECENT,$FAVORITES
$MUSIC,$PICTURES,$VIDEOS,$NETHOOD,$FONTS,$TEMPLATES,$ APPDATA,$PRINTHOOD
$INTERNET_CACHE,$COOKIES,$HISTORY,$PROFILE,$ ADMINTOOLS,$RESOURCES
$RESOURCES_LOCALIZED,$CDBURN_AREA,$HWNDPARENT,$PLUGINSDIR。

多语言

多语言引入后,NSIS安装包是默认根据window系统本地语言环境选择最匹配语言,如果找不到,展示声明的第一种语言。
但是有时候需要提供一个多语言对话框,多语言对话框可以在安装程序加载时展示,代码如下:

$LANGUAGE
当前使用的语言标识符。例如,英语是 1033。你可以在 .onInit 里更改此变量。

!insertmacro MUI_LANGUAGE "English"
!insertmacro MUI_LANGUAGE "SimpChinese"

;初始化函数
Function .onInit
Push ""
Push ${LANG_ENGLISH} ;添加英文代码 语言代码是系统变量,多语言引入后,自动加载,拼接方式是“LANG_语言”,可以查看NSIS手册,LANG_ENGLISH的编号为1033,LANG_SIMPCHINESE为2052;
Push "English"
Push ${LANG_SIMPCHINESE} ;添加简体中文选项
Push "简体中文"
Push A ; A means auto count languages for the auto count to work the first empty push (Push "") must remain
LangDLL::LangDialog "Installer Language" "Please select the language of the installer" ;显示语言选择对话框
Pop $LANGUAGE ;获得用户对于语言的选择结果 ‘$LANGUAGE’是多语言变量,在安装程序结束后,语言代码会存储在这个变量中,手动修改‘$LANGUAGE’的值后,安装包会重新选择最匹配的语言,参考最上面NSIS手册中选择界面语言步骤
StrCmp $LANGUAGE "cancel" 0 +2
Abort
StrCmp $LANGUAGE 2052 ZH_INI EN_INI
EN_INI:
;想干啥干啥
Goto END
ZH_INI:
;想干啥干啥
END:
FunctionEnd

字符串处理

StrCmp

str1 str2 jump_if_equal [jump_if_not_equal]

StrCmp $0 "a string" 0 +3
DetailPrint '$$0 == "a string"'
Goto +2
DetailPrint '$$0 != "a string"'

去空白字符

website

; Usage:
; ${Trim} $trimmedString $originalString

!define Trim "!insertmacro Trim"

!macro Trim ResultVar String
Push "${String}"
Call Trim
Pop "${ResultVar}"
!macroend

; Trim
; Removes leading & trailing whitespace from a string
; Usage:
; Push
; Call Trim
; Pop
Function Trim
Exch $R1 ; Original string
Push $R2

Loop:
StrCpy $R2 "$R1" 1
StrCmp "$R2" " " TrimLeft
StrCmp "$R2" "$\r" TrimLeft
StrCmp "$R2" "$\n" TrimLeft
StrCmp "$R2" "$\t" TrimLeft
GoTo Loop2
TrimLeft:
StrCpy $R1 "$R1" "" 1
Goto Loop

Loop2:
StrCpy $R2 "$R1" 1 -1
StrCmp "$R2" " " TrimRight
StrCmp "$R2" "$\r" TrimRight
StrCmp "$R2" "$\n" TrimRight
StrCmp "$R2" "$\t" TrimRight
GoTo Done
TrimRight:
StrCpy $R1 "$R1" -1
Goto Loop2

Done:
Pop $R2
Exch $R1
FunctionEnd

查找字符串


!define StrStr "!insertmacro StrStr"

!macro StrStr ResultVar String SubString
Push `${String}`
Push `${SubString}`
Call StrStr
Pop `${ResultVar}`
!macroend

Function StrStr
/*After this point:
------------------------------------------
$R0 = SubString (input)
$R1 = String (input)
$R2 = SubStringLen (temp)
$R3 = StrLen (temp)
$R4 = StartCharPos (temp)
$R5 = TempStr (temp)*/

;Get input from user
Exch $R0
Exch
Exch $R1
Push $R2
Push $R3
Push $R4
Push $R5

;Get "String" and "SubString" length
StrLen $R2 $R0
StrLen $R3 $R1
;Start "StartCharPos" counter
StrCpy $R4 0

;Loop until "SubString" is found or "String" reaches its end
${Do}
;Remove everything before and after the searched part ("TempStr")
StrCpy $R5 $R1 $R2 $R4

;Compare "TempStr" with "SubString"
${IfThen} $R5 == $R0 ${|} ${ExitDo} ${|}
;If not "SubString", this could be "String"'s end
${IfThen} $R4 >= $R3 ${|} ${ExitDo} ${|}
;If not, continue the loop
IntOp $R4 $R4 + 1
${Loop}

/*After this point:
------------------------------------------
$R0 = ResultVar (output)*/

;Remove part before "SubString" on "String" (if there has one)
StrCpy $R0 $R1 `` $R4

;Return output to user
Pop $R5
Pop $R4
Pop $R3
Pop $R2
Pop $R1
Exch $R0
FunctionEnd

.net core 安装文件

填上 Section “MainSection” SEC01 和 Section Uninstall ,然后检查一下相关文件名就可以了
自动安装.net core 3.6.1 64位,win7 补丁包

; Script generated by the HM NIS Edit Script Wizard.

; HM NIS Edit Wizard helper defines
!define PRODUCT_NAME "app name"
!define PRODUCT_VERSION "1.0"
!define PRODUCT_PUBLISHER "xx技术有限公司"
!define PRODUCT_WEB_SITE "http://www.xx.cn/"
!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\${PRODUCT_NAME}.exe"
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
!define PRODUCT_UNINST_ROOT_KEY "HKLM"
!define PRODUCT_PATH "D:\Project\H264Converter\bin\" ; 新增定义源程序路径
!define USE_LICENSE0 ;USE_LICENSE打开许可 USE_LICENSE0关闭许可

; MUI 1.67 compatible ------
!include "MUI.nsh"
!include LogicLib.nsh
!include x64.nsh

; MUI Settings
!define MUI_ABORTWARNING
!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"

; Language Selection Dialog Settings
!define MUI_LANGDLL_REGISTRY_ROOT "${PRODUCT_UNINST_ROOT_KEY}"
!define MUI_LANGDLL_REGISTRY_KEY "${PRODUCT_UNINST_KEY}"
!define MUI_LANGDLL_REGISTRY_VALUENAME "NSIS:Language"
; Welcome page
!insertmacro MUI_PAGE_WELCOME
; License page
!ifdef USE_LICENSE
!insertmacro MUI_PAGE_LICENSE $(myLicenseData)
!endif
; Directory page
!insertmacro MUI_PAGE_DIRECTORY
; Instfiles page
!insertmacro MUI_PAGE_INSTFILES
; Finish page
!define MUI_FINISHPAGE_RUN "$INSTDIR\${PRODUCT_NAME}.exe"
!insertmacro MUI_PAGE_FINISH

; Uninstaller pages
!insertmacro MUI_UNPAGE_INSTFILES

; Language files
!insertmacro MUI_LANGUAGE "English"
!insertmacro MUI_LANGUAGE "SimpChinese"

!ifdef USE_LICENSE
LicenseLangString myLicenseData ${LANG_ENGLISH} "bin\Licence-en.txt"
LicenseLangString myLicenseData ${LANG_SIMPCHINESE} "bin\Licence.txt"
LicenseData $(myLicenseData)
!endif
LangString uninstallAsk ${LANG_ENGLISH} "${PRODUCT_NAME} $OLD_VER. Already installed $\n$\n uninstall current version?"
LangString uninstallAsk ${LANG_SIMPCHINESE} "检测到本机已经安装了 ${PRODUCT_NAME} $OLD_VER。$\n$\n是否先卸载已安装的版本?"
LangString unsintallAsk2 ${LANG_ENGLISH} "Confirm to uninstall $(^Name)?"
LangString unsintallAsk2 ${LANG_SIMPCHINESE} "你确实要完全移除 $(^Name) ,其及所有的组件?"
LangString uninstallDoneMsg ${LANG_ENGLISH} "$(^Name) has been removed。"
LangString uninstallDoneMsg ${LANG_SIMPCHINESE} "$(^Name) 已成功地从你的计算机移除。"
LangString unsintallAskConfig ${LANG_ENGLISH} "Confirm to delete config files?"
LangString unsintallAskConfig ${LANG_SIMPCHINESE} "是否删除配置文件?"


; MUI end ------

Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
!define /date PRODUCT_TIME %Y%m%d%H%M%S
!define /date PRODUCT_DATE %Y%m%d
OutFile "${PRODUCT_NAME}_${PRODUCT_VERSION}_${PRODUCT_TIME}.exe"
InstallDir "$PROGRAMFILES\${PRODUCT_NAME}"
InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" ""
ShowInstDetails show
ShowUnInstDetails show
RequestExecutionLevel Admin



Var UNINSTALL_PROG
Var OLD_VER
Var OLD_PATH

Function .onInit
!insertmacro MUI_LANGDLL_DISPLAY
ClearErrors
ReadRegStr $UNINSTALL_PROG ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallString"
IfErrors done

ReadRegStr $OLD_VER ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "DisplayVersion"
MessageBox MB_YESNOCANCEL|MB_ICONQUESTION \
$(uninstallAsk) \
/SD IDYES \
IDYES uninstall \
IDNO done
Abort

uninstall:
StrCpy $OLD_PATH $UNINSTALL_PROG -10


ExecWait '"$UNINSTALL_PROG" /S _?=$OLD_PATH' $0
DetailPrint "uninst.exe returned $0"
Delete "$UNINSTALL_PROG"
RMDir $OLD_PATH


done:
FunctionEnd

Section - "安装.net 5"
IfFileExists "C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App\5.0.10" +9 0
SetDetailsPrint textonly
DetailPrint "准备安装.Net 5.0.10 组件"
SetDetailsPrint listonly
SetOutPath "$TEMP"
SetOverwrite on
File "${PRODUCT_PATH}\windowsdesktop-runtime-5.0.10-win-x64.exe"
ExecWait '$TEMP\windowsdesktop-runtime-5.0.10-win-x64.exe ' $R1
Delete "$TEMP\windowsdesktop-runtime-5.0.10-win-x64.exe"
SectionEnd

Section "MainSection" SEC01
SetOutPath "$INSTDIR"
SetOverwrite ifnewer ; 更新覆盖
File /r "${PRODUCT_PATH}\*.*"
;SetDetailsView hide ; 显示安装细节
;Delete "$INSTDIR\key.xml"
;Delete "$INSTDIR\clientSize.config"
;Delete "$INSTDIR\config.json"
;SetDetailsView show ; 显示安装细节
;SetOutPath "$APPDATA\${PRODUCT_NAME}"
;SetOverwrite ifnewer ; 更新覆盖
;File bin\key.xml
;File bin\clientSize.config
;File bin\config.json
CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}" ; 创建开始菜单目录并命名为程序名
; 在上一行创建的开始菜单目录下创建程序快捷方式
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk" "$INSTDIR\${PRODUCT_NAME}.exe"
CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" "$INSTDIR\${PRODUCT_NAME}.exe" ; 创建桌面快捷方式
InitPluginsDir
SetOutPath $PLUGINSDIR
IntCmp $Language 2052 SetLangChinese
IntCmp $Language 1033 SetLangEnglish
SetLangChinese:
File ${PRODUCT_PATH}\${PRODUCT_NAME}.exe
CopyFiles "$PLUGINSDIR\${PRODUCT_NAME}.exe" "$INSTDIR\${PRODUCT_NAME}.exe"
Goto SetLangEnd
SetLangEnglish:
File ${PRODUCT_PATH}\${PRODUCT_NAME}.exe
CopyFiles "$PLUGINSDIR\${PRODUCT_NAME}.exe" "$INSTDIR\${PRODUCT_NAME}.exe"
SetLangEnd:
;Exec "$INSTDIR\registerInfo.exe -h"
SectionEnd
Section -AdditionalIcons
CreateShortCut "$SMPROGRAMS\IPKVM\Uninstall.lnk" "$INSTDIR\uninst.exe"
SectionEnd
Section -Post
WriteUninstaller "$INSTDIR\uninst.exe"
WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\${PRODUCT_NAME}.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_NAME}.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
SectionEnd
Function un.onUninstSuccess
HideWindow
MessageBox MB_ICONINFORMATION|MB_OK $(uninstallDoneMsg)
FunctionEnd
Function un.onInit
!insertmacro MUI_UNGETLANGUAGE
MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 $(unsintallAsk2) IDYES +2
Abort
FunctionEnd
Section Uninstall
; 删除快捷方式
Delete "$DESKTOP\${PRODUCT_NAME}.lnk"
; 删除开始菜单文件夹
RMDir "$SMPROGRAMS\${PRODUCT_NAME}"
RMDir /r "$INSTDIR" ; 直接删除整个文件夹
MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 $(unsintallAskConfig) IDNO +2
RMDir /r "$APPDATA\${PRODUCT_NAME}"
; 删除注册表信息
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY}"
SetAutoClose true ; 卸载完毕自动关闭窗口
SectionEnd

填上 Section “MainSection” SEC01 和 Section Uninstall ,然后检查一下相关文件名就可以了
自动安装.net core 3.6.1 64位,win7 补丁包

var strInstallPath
Section - "检测windows补丁包"
clearerrors
nsExec::ExecToStack 'cmd /Q /C "%SYSTEMROOT%\System32\wbem\wmic.exe qfe get hotfixid | %SYSTEMROOT%\System32\findstr.exe "^KB2533623""'
Pop $0 ; return value (it always 0 even if an error occured)
Pop $1 ; command output
;detailprint $0
;detailprint $1
${Trim} $2 $1
${If} $2 == 'KB2533623'
detailprint '补丁包已安装'
${Else}
SetDetailsPrint textonly
DetailPrint "准备安装WIN7 补丁包"
SetDetailsPrint listonly
Push "$INSTDIR\RunMSU.Bat"
Strcpy $strInstallPath "$INSTDIR\Windows6.1-KB2533623-x64.msu"
Strcpy $0 "$INSTDIR\RunMSU.Bat"
FileOpen $0 $0 w #open file
FileSeek $0 0 END #go to end
FileWrite $0 "echo off"
FileWriteByte $0 "13"
FileWriteByte $0 "10"
FileWrite $0 "start $\"$\" wusa $\"$strInstallPath$\" /quiet /norestart"
FileClose $0
Execwait "$INSTDIR\RunMSU.bat"
Strcpy $strInstallPath "reboot"
${EndIf}

SectionEnd

Section -Post
WriteUninstaller "$INSTDIR\uninst.exe"
WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\windowsdesktop-runtime-3.1.6-win-x64.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\windowsdesktop-runtime-3.1.6-win-x64.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
${If} $strInstallPath == 'reboot'
MessageBox MB_YESNO|MB_ICONQUESTION "安装已完成,重启系统后可正常使用!" IDNO +2
Reboot
${EndIf}
SectionEnd

Function .onRebootFailed
MessageBox MB_OK|MB_ICONSTOP "重启失败,请手动重启。" /SD IDOK
FunctionEnd

Function un.onUninstSuccess
HideWindow
MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) 已成功地从你的计算机移除。"
FunctionEnd

Function un.onInit
MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "你确实要完全移除 $(^Name) ,其及所有的组件?" IDYES +2
Abort
FunctionEnd


Var UNINSTALL_PROG
Var OLD_VER
Var OLD_PATH

Function .onInit
ClearErrors
ReadRegStr $UNINSTALL_PROG ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallString"
IfErrors done

ReadRegStr $OLD_VER ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "DisplayVersion"
MessageBox MB_YESNOCANCEL|MB_ICONQUESTION \
"检测到本机已经安装了 ${PRODUCT_NAME} $OLD_VER。\
$\n$\n是否先卸载已安装的版本?" \
/SD IDYES \
IDYES uninstall \
IDNO done
Abort

uninstall:
StrCpy $OLD_PATH $UNINSTALL_PROG -10




ExecWait '"$UNINSTALL_PROG" /S _?=$OLD_PATH' $0
DetailPrint "uninst.exe returned $0"
Delete "$UNINSTALL_PROG"
RMDir /r $OLD_PATH
done:
FunctionEnd



; Trim
; Removes leading & trailing whitespace from a string
; Usage:
; Push
; Call Trim
; Pop
Function Trim
Exch $R1 ; Original string
Push $R2

Loop:
StrCpy $R2 "$R1" 1
StrCmp "$R2" " " TrimLeft
StrCmp "$R2" "$\r" TrimLeft
StrCmp "$R2" "$\n" TrimLeft
StrCmp "$R2" "$\t" TrimLeft
GoTo Loop2
TrimLeft:
StrCpy $R1 "$R1" "" 1
Goto Loop

Loop2:
StrCpy $R2 "$R1" 1 -1
StrCmp "$R2" " " TrimRight
StrCmp "$R2" "$\r" TrimRight
StrCmp "$R2" "$\n" TrimRight
StrCmp "$R2" "$\t" TrimRight
GoTo Done
TrimRight:
StrCpy $R1 "$R1" -1
Goto Loop2

Done:
Pop $R2
Exch $R1
FunctionEnd

Section Uninstall
...
SectionEnd