windows桌面软件打包-基于Nsi生成安装包

-- windows桌面软件打包-基于Nsi生成安装包
【官网】:http://nsis.sourceforge.net

应用场景

windows客户端人软件打包,制作安装包,制作安装向导(可自动下载安装依赖项)

基础资源

Nsis 2.x或3.x版本

使用须知

打包安装包时需要注意,避免把不能公开的文档和信息打包进去导致泄密

配置步骤

步骤1)下载安装.

步骤2)准备好软件必要的依赖组件,文件含.exe及必要的说明文档,形成一个可以公开的软件目录:SoftPath。

步骤3)针对上述软件目录SoftPath设计nsis脚本。

3.1.)生成安装包过程中需要的一些logo,生命文件是相对于nsi所在目录来的。
类似:!define MUI_ICON "..Res\install.ico"       //具体参考安装包制作空间.
3.2.)安装过程函数中,读取的比如vc环境组件。
实际上是安装包制作好后,在客户那边运行时首先解压释放后的目录下的相对路径。
因此在安装包制作之前的绿包之中就需要按照预想的相对目录防止,这样会被自动打包压缩进去。释放后的相对目录就是nsi中写的目录。
3.3.)nsi的调试。
如果发现安装包的一些函数执行失败,则可以查看安装包安装后的目录下的安装日志:install.log


[注]本篇快速入门处的示例nsi脚本:

常见问题

快速入门


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 宏定义
!define PRODUCT_SERIESNAME "X助手系列"
!define PRODUCT_NAME "X助手"
!define PRODUCT_NAME_E "ClientStart"
!define PRODUCT_VERSION "2.0.1.0"
!define PRODUCT_VERSION_VI "2.0.1"
!define PRODUCT_PUBLISHER "X助手系列"
!define PRODUCT_PUBLISHER_E "X Tech Co.,Ltd."
!define PRODUCT_WEB_SITE "http://www.x.com"
!define PRODUCT_UNINST_ROOT_KEY "HKLM"
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME_E}"
!define PRODUCT_EXEFILENAME "启动程序.exe"
!define PRODUCT_CHANGELOGFILENAME "Change.Log"
!define PRODUCT_YEAR "2020"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 头文件
!include "MUI2.nsh"
!include "LogicLib.nsh"
!include "WordFunc.nsh"
!include "Sections.nsh"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 安装程序初始化配置
SetCompressor lzma
SetOverwrite on
SetDatablockOptimize on
XPStyle on
CRCCheck on
ShowInstDetails hide
ShowUnInstDetails hide

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; MUI 预定义
!define MUI_ABORTWARNING                                             ; 当用户要关闭安装程序时, 显示一个警告消息框
!define MUI_ABORTWARNING_CANCEL_DEFAULT                              ; 设置"取消"按钮为触发该事件时的默认按钮
!define MUI_ICON "..\Res\install.ico"                          ; 安装程序的图标
!define MUI_UNICON "..\Res\uninstall.ico"                      ; 卸载程序的图标
!define MUI_WELCOMEFINISHPAGE_BITMAP "..\Res\install.bmp"      ; 用于安装时的欢迎页面和完成页面的位图
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "..\Res\uninstall.bmp"  ; 用于卸载时的欢迎页面和完成页面的位图
!define MUI_CUSTOMFUNCTION_GUIINIT myGUIInit

; 页面配置
!define MUI_WELCOMEPAGE_TITLE "欢迎使用 ${PRODUCT_NAME}"    ; 欢迎页面标题
!define MUI_WELCOMEPAGE_TEXT "这个向导将指引你完成“${PRODUCT_NAME} V${PRODUCT_VERSION}”$\r$\n$\r$\n的安装进程。$\r$\n$\r$\n$\r$\n$\r$\n在开始安装之前,建议先关闭其他所有应用程序。以免$\r$\n$\r$\n因不能更新指定文件而造成需要重启计算机或者安装失$\r$\n$\r$\n败的问题。$\r$\n$\r$\n$\r$\n$\r$\n$_CLICK"
!define MUI_COMPONENTSPAGE_NODESC
!define MUI_FINISHPAGE_RUN "${PRODUCT_EXEFILENAME}"
!define MUI_FINISHPAGE_RUN_TEXT "运行${PRODUCT_NAME}"
;!define MUI_FINISHPAGE_SHOWREADME "${PRODUCT_WEB_SITE}"
;!define MUI_FINISHPAGE_SHOWREADME_TEXT "打开官方网站"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 包含的页面

;安装
!insertmacro MUI_PAGE_WELCOME                                     ; 欢迎页面
!insertmacro MUI_PAGE_LICENSE "..\Res\用户协议.txt"  ; 用户协议页面
!insertmacro MUI_PAGE_LICENSE "..\Res\隐私声明.txt"  ; 隐私声明页面
!insertmacro MUI_PAGE_COMPONENTS                                  ; 组件选择页面
!insertmacro MUI_PAGE_DIRECTORY                                   ; 安装目录选择页面
!insertmacro MUI_PAGE_INSTFILES                                   ; 安装过程页面
!insertmacro MUI_PAGE_FINISH                                      ; 安装完成页面

;卸载
!insertmacro MUI_UNPAGE_INSTFILES                                 ; 安装卸载过程页面

!insertmacro MUI_LANGUAGE "SimpChinese"                           ; 安装界面包含的语言设置

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 安装程序生成配置

Name ${PRODUCT_NAME}
OutFile "OutFile\${PRODUCT_NAME}\${PRODUCT_NAME}安装程序_V${PRODUCT_VERSION}.exe"
InstallDir "D:\${PRODUCT_SERIESNAME}\${PRODUCT_NAME}"
BrandingText "${PRODUCT_PUBLISHER_E}"
RequestExecutionLevel admin

VIProductVersion "${PRODUCT_VERSION}"
;VIFileVersion "${PRODUCT_VERSION}"
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "CompanyName"      "${PRODUCT_PUBLISHER}"
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "FileDescription"  "${PRODUCT_NAME}"
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "FileVersion"      "${PRODUCT_VERSION}"
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "InternalName"     "${PRODUCT_NAME_E}"
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "LegalCopyright"   "Copyright 2011 - ${PRODUCT_YEAR}, ${PRODUCT_PUBLISHER_E}"
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "LegalTrademarks"  "${PRODUCT_NAME_E}"
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "OriginalFilename" "${PRODUCT_NAME_E}"
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "ProductName"      "${PRODUCT_NAME}"
VIAddVersionKey /LANG=${LANG_SIMPCHINESE} "ProductVersion"   "${PRODUCT_VERSION}"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

/************************
*  以下是安装程序部分  *
************************/
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 区段设置
Section "-LogSetOn" ; 激活安装日志记录,该日志文件将会作为卸载文件的依据(注意,本区段必须放置在所有区段之前)
LogSet on
SectionEnd

Section "!${PRODUCT_NAME}" SEC_Application
SectionIn RO
SetOutPath "$INSTDIR"
File /r "..\Build\${PRODUCT_NAME}\*.*"
;File /r "..\Res\附加文件\${PRODUCT_NAME}\*.*"

WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "${PRODUCT_NAME}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.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}"

WriteUninstaller "$INSTDIR\uninst.exe"
SectionEnd

SectionGroup "必要组件" SECG_Component
Section " Microsoft Visual C++ 2015 运行库 (x86) " SEC_VC
Call Install_VC_2015
SectionEnd
SectionGroupEnd

SectionGroup /e "快捷方式" SECG_Short
Section "桌面快捷方式" SEC_Short_Desktop
CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" "$INSTDIR\${PRODUCT_EXEFILENAME}"
SectionEnd
Section "开始菜单快捷方式" SEC_Short_StartMenu
CreateDirectory "$SMPROGRAMS\${PRODUCT_SERIESNAME}\${PRODUCT_NAME}"
CreateShortCut "$SMPROGRAMS\${PRODUCT_SERIESNAME}\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk" "$INSTDIR\${PRODUCT_EXEFILENAME}"
CreateShortCut "$SMPROGRAMS\${PRODUCT_SERIESNAME}\${PRODUCT_NAME}\升级日志.lnk" "$INSTDIR\${PRODUCT_CHANGELOGFILENAME}"
CreateShortCut "$SMPROGRAMS\${PRODUCT_SERIESNAME}\${PRODUCT_NAME}\卸载软件.lnk" "$INSTDIR\uninst.exe"
SectionEnd
SectionGroupEnd

#-- 根据 NSIS 脚本编辑规则,所有 Function 区段必须放置在 Section 区段之后编写,以避免安装程序出现未可预知的问题。--#

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 初始化GUI函数。用于初始化安装组件勾选状态
Function myGUIInit

Var /GLOBAL CompVar

Var /GLOBAL CurrentVersion
Var /GLOBAL CurrentBuildNumber
ReadRegStr $CurrentVersion HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" "CurrentVersion"
ReadRegStr $CurrentBuildNumber HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" "CurrentBuildNumber"
Var /GLOBAL SystemVersion
StrCpy $SystemVersion "$CurrentVersion.$CurrentBuildNumber"
${VersionCompare} "6.1.7600" $SystemVersion $CompVar
StrCmp $CompVar "1" SystemIsNo SystemIsOK
SystemIsOK:
Goto System_Done
SystemIsNo:
MessageBox MB_ICONEXCLAMATION|MB_OK "${PRODUCT_NAME} 目前仅支持在 Windows 7 以及更高版本的系统中安装运行"
Abort
Goto System_Done
System_Done:

Var /GLOBAL VCInstallVersion
ReadRegStr $VCInstallVersion HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{74d0e5db-b326-4dae-a6b2-445b9de1836e}" "DisplayVersion"
${VersionCompare} "14.0.23026.0" $VCInstallVersion $CompVar
StrCmp $CompVar "1" VCIsNo VCIsOK
VCIsOK:
!insertmacro UnselectSection ${SEC_VC}
Goto VC_Done
VCIsNo:
!insertmacro SetSectionFlag ${SEC_VC} ${SF_RO}
Goto VC_Done
VC_Done:
/*
Var /GLOBAL dotNETInstallVersion
ReadRegStr $dotNETInstallVersion HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" "Version"
${VersionCompare} "4.5.2.0" $dotNETInstallVersion $CompVar
StrCmp $CompVar "1" dotNETIsNo dotNETIsOK
dotNETIsOK:
!insertmacro UnselectSection ${SEC_dotNET}
Goto dotNET_Done
dotNETIsNo:
!insertmacro SetSectionFlag ${SEC_dotNET} ${SF_RO}
Goto dotNET_Done
dotNET_Done:
*/

FunctionEnd

Var /GLOBAL DoCmd
Var /GLOBAL DoExitCode
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 安装VC++ 2015
Function Install_VC_2015

IfSilent is_quiet is_not_quiet
is_quiet:
StrCpy $DoCmd "/quiet /norestart"
Goto do_local_install

is_not_quiet:
StrCpy $DoCmd "/passive /norestart"
Goto do_local_install

do_local_install:
SetDetailsPrint textonly
DetailPrint "正在安装  Microsoft Visual C++ 2015 运行库 (x86) "
SetDetailsPrint listonly
;ExecWait ‘"$INSTDIR\Res\vc_redist_2015.x86.exe" $DoCmd‘ $DoExitCode
ExecWait ‘"Res\vc_redist_2015_x86.exe" $DoCmd‘ $DoExitCode
DetailPrint "安装 Microsoft Visual C++ 2015 运行库 (x86) 完成,安装结果:$0、退出代码:$DoExitCode"
${If} $DoExitCode = 1641
${OrIf} $DoExitCode = 3010
SetRebootFlag true
${EndIf}

Done_Function:
SetDetailsPrint both

FunctionEnd

/*

IfSilent is_quiet is_not_quiet
is_quiet:
StrCpy $DoCmd "/q /norestart"
Goto do_local_install

is_not_quiet:
StrCpy $DoCmd "/passive /norestart"
Goto do_local_install

do_local_install:

;MessageBox MB_ICONINFORMATION|MB_OK "即将为您的计算机安装 .NET Framework 4.5.2 。$\n此过程可能需要5至10分钟。请勿关闭安装程序以及您的计算机。"

SetDetailsPrint textonly
DetailPrint "正在安装 .NET Framework 4.5.2"
SetDetailsPrint listonly
File "/oname=$TEMP\NDP452-KB2901907-x86-x64-AllOS-ENU.exe" "Res\其他资源\NDP452-KB2901907-x86-x64-AllOS-ENU.exe"
ExecWait ‘"$TEMP\NDP452-KB2901907-x86-x64-AllOS-ENU.exe" $DoCmd‘ $DoExitCode
Delete "$TEMP\NDP452-KB2901907-x86-x64-AllOS-ENU.exe"
DetailPrint "安装 .NET Framework 4.5.2 完成,安装结果:$0、退出代码:$DoExitCode"
${If} $DoExitCode = 1641
${OrIf} $DoExitCode = 3010
SetRebootFlag true
${EndIf}

SetDetailsPrint textonly
DetailPrint "正在安装 .NET Framework 4.5.2 中文语言包"
SetDetailsPrint listonly
File "/oname=$TEMP\NDP452-KB2901907-x86-x64-AllOS-CHS.exe" "Res\其他资源\NDP452-KB2901907-x86-x64-AllOS-CHS.exe"
ExecWait ‘"$TEMP\NDP452-KB2901907-x86-x64-AllOS-CHS.exe" $DoCmd‘ $DoExitCode
Delete "$TEMP\NDP452-KB2901907-x86-x64-AllOS-CHS.exe"
DetailPrint "安装 .NET Framework 4.5.2 中文语言包 完成,安装结果:$0、退出代码:$DoExitCode"
${If} $DoExitCode = 1641
${OrIf} $DoExitCode = 3010
SetRebootFlag true
${EndIf}


Done_Function:
SetDetailsPrint both

FunctionEnd
*/


/******************************
*  以下是安装程序的卸载部分  *
******************************/

; 根据安装日志卸载文件的调用宏
!macro DelFileByLog LogFile
ifFileExists `${LogFile}` 0 +4
Push `${LogFile}`
Call un.DelFileByLog
Delete `${LogFile}`
!macroend
Section Uninstall
; 调用宏只根据安装日志卸载安装程序自己安装过的文件
!insertmacro DelFileByLog "$INSTDIR\install.log"

; 删除开始菜单目录
RMDir "$SMPROGRAMS\${PRODUCT_SERIESNAME}\${PRODUCT_NAME}"
RMDir "$SMPROGRAMS\${PRODUCT_SERIESNAME}"

; 清除安装程序创建的且在卸载时可能为空的子目录,对于递归添加的文件目录,请由最内层的子目录开始清除(注意,不要带 /r 参数,否则会失去 DelFileByLog 的意义)
RMDir /r "$INSTDIR\Library"
RMDir /r "$INSTDIR\Res"

MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON1 "您是否保留用户配置文件?" IDYES +4
RMDir /r "$INSTDIR\Data"
RMDir /r "$INSTDIR"
Goto +3
RMDir "$INSTDIR\Data"
RMDir "$INSTDIR"

DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
SetAutoClose true
SectionEnd
#-- 根据 NSIS 脚本编辑规则,所有 Function 区段必须放置在 Section 区段之后编写,以避免安装程序出现未可预知的问题。--#
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
; ============================================================
; 以下是卸载程序通过安装日志卸载文件的专用函数,请不要随意修改
; ============================================================
Function un.DelFileByLog
Exch $R0
Push $R1
Push $R2
Push $R3
FileOpen $R0 $R0 r
${Do}
FileRead $R0 $R1
${IfThen} $R1 == `` ${|} ${ExitDo} ${|}
StrCpy $R1 $R1 -2
StrCpy $R2 $R1 11
StrCpy $R3 $R1 20
${If} $R2 == "File: wrote"
${OrIf} $R2 == "File: skipp"
${OrIf} $R3 == "CreateShortCut: out:"
${OrIf} $R3 == "created uninstaller:"
Push $R1
Push `"`
Call un.DelFileByLog.StrLoc
Pop $R2
${If} $R2 != ""
IntOp $R2 $R2 + 1
StrCpy $R3 $R1 "" $R2
Push $R3
Push `"`
Call un.DelFileByLog.StrLoc
Pop $R2
${If} $R2 != ""
StrCpy $R3 $R3 $R2
Delete /REBOOTOK $R3
${EndIf}
${EndIf}
${EndIf}
StrCpy $R2 $R1 7
${If} $R2 == "Rename:"
Push $R1
Push "->"
Call un.DelFileByLog.StrLoc
Pop $R2
${If} $R2 != ""
IntOp $R2 $R2 + 2
StrCpy $R3 $R1 "" $R2
Delete /REBOOTOK $R3
${EndIf}
${EndIf}
${Loop}
FileClose $R0
Pop $R3
Pop $R2
Pop $R1
Pop $R0
FunctionEnd
Function un.DelFileByLog.StrLoc
Exch $R0
Exch
Exch $R1
Push $R2
Push $R3
Push $R4
Push $R5
StrLen $R2 $R0
StrLen $R3 $R1
StrCpy $R4 0
${Do}
StrCpy $R5 $R1 $R2 $R4
${If} $R5 == $R0
${OrIf} $R4 = $R3
${ExitDo}
${EndIf}
IntOp $R4 $R4 + 1
${Loop}
${If} $R4 = $R3
StrCpy $R0 ""
${Else}
StrCpy $R0 $R4
${EndIf}
Pop $R5
Pop $R4
Pop $R3
Pop $R2
Pop $R1
Exch $R0
FunctionEnd
; ============================================================




参考资料