.Net CI/CD 之 NSIS

300 阅读2分钟

Wix Tools VS NSIS

  • Wix tools:wixtoolset.org 功能齐全/强大,支持模块化安装,支持Windows 服务,检测/安装.Net framework依赖,可以调用batch脚本,调用C# Dll/exe,还可以使用WPF编写完全自定义的安装/卸载界面,缺点是维护比较困难,每个文件都需要单独维护,如新增文件比较多的话一般会借助脚本生成 wxs 文件。需要专业的人维护,比较适合大型项目。
  • NSIS: nsis.sourceforge.io 开源的 Windows 系统下安装程序制作程序,通过脚本语言来描述安装程序的行为和逻辑的。文件管理简单;支持静默安装,方便自动化脚本集成;很多功能需要第三方的插件支持。简单易用,适合不需要太多额外安装需求的项目。

NSIS 脚本

如果想要从头手撸一个NSIS 脚本的话,不建议这么做,虽然VS code已经支持NSIS插件,但实现起来还是有一定的难度。通常做法是改造已有的nsi脚本;或者使用 nsis edit [new script from wizard...]创建一个简单的模板,在进行改造。

  • 全局变量定义,在脚本头部定义,可以在脚本中引用:${variable name}
# defines
!define PRODUCT_NAME "My application"
!define PRODUCT_VERSION "1.0"

# reference
Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
  • 指定压缩模式:
# https://nsis.sourceforge.io/Reference/SetCompressor
# zlib/bzip2/lzma 大小写不敏感
SetCompressor lzma
  • UI风格/页面/多语言定义
# 风格/主题
!include "MUI.nsh"

# Icon路径
!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"

# Welcome page(不需要可移除)
!insertmacro MUI_PAGE_WELCOME
# License page(不需要可移除)
!insertmacro MUI_PAGE_LICENSE "c:\path\to\licence\YourSoftwareLicence.txt"
...
# MUI end ------
  • 产品名称,安装包名称,安装目录设置
Name "${PRODUCT_NAME} ${PRODUCT_VERSION}" # 显示在安装/卸载页面上
OutFile "Setup.exe" # 脚本输出安装包的名称
InstallDir "$PROGRAMFILES\My application" # 安装路径
InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" "" # 注册表项
ShowInstDetails show # https://nsis.sourceforge.io/Reference/ShowInstDetails
ShowUnInstDetails show # https://nsis.sourceforge.io/Reference/ShowUninstDetails
  • 安装时文件/目录管理:Section "MainSection" SEC01
Section "MainSection" SEC01
# 部分文件需要安装到 "$PROGRAMFILES\My application" 目录下
  SetOutPath "$INSTDIR" # 设置输出目录(安装目录,在此之后的文件都安装到改目录下,直到下一个 "SetOutPath")
  SetOverwrite on # 文件复写模式:on/off/try/ifnewer/ifdiff/lastused
  File /r /x *.pdb /x *.xml ".\build\" # /r 递归 "build" 下所有文件/目录;/x 排除不需要文件;
  File ".\license.txt" # https://nsis.sourceforge.io/Reference/File
  
  # 部分文件需要安装到 C:\ProgramData\My application 目录下
  GetKnownFolderPath $0 {62AB5D82-FDC1-4DC3-A9DD-070D1D495D97} # 获取 C:\ProgramData\
  SetOutPath "$0${PRODUCT_NAME}"
  SetOverwrite ifnewer
  File /nonfatal  ".\xxxxx\config\xxx.json" # /nonfatal 文件找不到不影响打包
SectionEnd
  • 卸载管理: Section Uninstall
  Delete "$INSTDIR${PRODUCT_NAME}.url" # 删除单个文件
  RMDir /r "$INSTDIR" # 递归删除目录

  DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY}" # 删除注册表
  SetAutoClose true
  • 自定义方法
# 自定义一个检查程序是否已经在运行,如果程序在运行则提示错误,需要在C#程序中使用 Mutex 锁
!macro openGlobalMutex
# d05aa690-3cce-4f64-9fa7-31abe810972c,[C# Mutex Name](https://learn.microsoft.com/zh-cn/dotnet/api/system.threading.mutex.-ctor?view=net-8.0#system-threading-mutex-ctor(system-boolean-system-string))
 System::Call 'kernel32::OpenMutex(i 0x100000, i 0, t "d05aa690-3cce-4f64-9fa7-31abe810972c")p.R0'
  IntPtrCmp $R0 0 notRunning # 比较句柄,等于0(互斥锁打开成功),这goto到 notRunning: 这一行
      System::Call 'kernel32::CloseHandle(p $R0)'
      MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION "$(^Name) is running. Please close it first." /SD IDCANCEL IDRETRY -3 IDCANCEL +1 # /SD 静默安装时默认值;-3 Goto到向前数3行的语句,空行也算(kernel32::OpenMutex); +1 Goto到向后数1一行的语句(Abort)
      Abort # 结束安装程序
  notRunning:
!macroend

# 调用 openGlobalMutex

Function .onInit 
  !insertmacro openGlobalMutex # !insertmacro xxx
# ...

FunctionEnd
  • NSI脚本参数传递:程序版本信息通过打包时命令行传递

    1. 注释 nsi 脚本中: !define PRODUCT_VERSION "1.0"
    # !define PRODUCT_VERSION "1.0"
    
    1. 命令行中制定版本号:
     # 注意参数名称前添加 /D
     "C:/Program Files (x86)/NSIS/makensis.exe" /DPRODUCT_VERSION={version} setup.nsi   
    

本文使用 markdown.com.cn 排版