NSIS打包保姆级教程(含后台服务注册)

NSIS 打包保姆级教程(含守护进程注册与系统痕迹清理)

前言

很久没有用 NSIS 来打包安装程序了,导致我忘了如何使用(原先笔者以为之前已经写过 NSIS 相关教程,但那时候只写过某编译器的教程),所以记录下保姆级的教程,并补充近期踩坑的「排雷指南」

内容

必要条件

安装 NSIS v3.08 版本,这里我下的是中文版,学习之初方便查看帮助文档。

感谢作者翻译!!!

NSIS 中文版界面

具体操作

基础向导流程

  • 点击 VNISEdit,进入脚本编写界面
  • 文件 ==> 新建脚本:向导 ==> 进入向导界面,根据向导提示填写内容

向导界面1

向导界面2

  • 图标与命名:可以更改为自己的图标(否则是 NSIS 的默认图标),安装程序文件名也能自定义(默认为 Setup)。建议在此处完成「品牌标识设定」,避免后续手动修改脚本。

图标设置

  • 授权文件一般用不着,除非要定制化安装界面,我现在是选择直接跳过。如无需授权,直接删除路径即可。

授权文件

删除路径

文件组织策略

  • 到这一步,默认会有两个文件,直接删除它们,开始填充自己打包需要的文件。
  • 左边 MainSection 表示主节点。如果安装程序不包含「守护进程注册」(后台服务)之类操作,使用默认即可。

MainSection

  • 找到需要打包的文件,将其添加进来(如某编译器直接找 Release 目录)。
  • 如需打包「用户配置记忆库」(数据库/配置文件),按下图设置 SetOverwrite off,防止后续更新覆盖用户数据。

配置文件处理

守护进程注册(后台服务)

如需打包「后台守护模块」(Windows Service):

  1. 新建 Section 命名为 ServiceSection(或「系统服务单元」
  2. 放入服务可执行文件,目的目录建议命名为 Service(或「系统服务目录」

ServiceSection1

ServiceSection2

  1. 程序一般默认为前台程序,按下图设置快捷方式:

前台程序1

前台程序2

前台程序3

  1. 点击完成保存脚本。如需「守护进程注册」,在 ServiceSection 后追加以下脚本(使用「系统控制工具」 sc.exe 进行服务契约缔结):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
; 检查服务可执行文件是否存在(前置校验)
IfFileExists "$INSTDIR\系统服务目录\${PRODUCT_SERVICEEXE}" +3 0
MessageBox MB_ICONSTOP "守护模块未找到:$\r$\n$INSTDIR\系统服务目录\${PRODUCT_SERVICEEXE}"
Abort

; 开始注册服务(使用 sc.exe 方案)
DetailPrint "正在缔结系统守护契约..."
nsExec::ExecToStack '"$SYSDIR\sc.exe" create "${PRODUCT_SERVICENAME}" binPath= "\"$INSTDIR\系统服务目录\${PRODUCT_SERVICEEXE}\"" start= auto DisplayName= "${PRODUCT_SERVICENAME}"'
Pop $R0 ; 返回码
Pop $R1 ; 输出信息

; 备用方案:若 sc.exe 直接调用失败,尝试通过 cmd 中介
${If} $R0 != 0
DetailPrint "主方案受阻,尝试备用契约缔结... (状态码: $R0)"
nsExec::ExecToStack '"$SYSDIR\cmd.exe" /C "sc create ${PRODUCT_SERVICENAME} binPath= \"$INSTDIR\系统服务目录\${PRODUCT_SERVICEEXE}\" start= auto DisplayName= \"${PRODUCT_SERVICENAME}\" "'
Pop $R0
Pop $R1
${EndIf}

; 异常处理与日志留存
${If} $R0 != 0
MessageBox MB_ICONSTOP "守护契约缔结失败! $\r$\n状态码: $R0 $\r$\n详细信息: $\r$\n$R1"
StrCpy $0 "$TEMP\ServiceInstall.log"
FileOpen $1 $0 w
FileWrite $1 "缔结命令: $\r$\n$SYSDIR\sc.exe create ${PRODUCT_SERVICENAME} binPath= \"$INSTDIR\系统服务目录\${PRODUCT_SERVICEEXE}\" $\r$\n"
FileWrite $1 "返回状态: $R0 $\r$\n"
FileWrite $1 "详细信息: $\r$\n$R1"
FileClose $1
ExecShell "open" "$TEMP"
Abort
${EndIf}

DetailPrint "守护契约缔结成功"

; 启动守护进程
DetailPrint "正在唤醒守护模块..."
nsExec::ExecToLog "net start ${PRODUCT_SERVICENAME}"
Pop $R0
${If} $R0 != 0
MessageBox MB_ICONSTOP "守护模块唤醒失败,请手动启动 (状态码: $R0)"
${EndIf}

卸载阶段(含系统痕迹清理)

找到 Uninstall 节点(「解除安装」章节),追加以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
; 1. 先执行文件释放术(解决"文件被占用"导致的卸载失败)
; 检测主程序是否仍在运行,使用 /nsis 原生方式或系统命令
DetailPrint "正在执行文件释放术..."
nsExec::ExecToStack '"taskkill" /F /IM "${PRODUCT_EXE_NAME}" /T'
Pop $R0
; 注意:若包含衍生实例(如嵌入式浏览器内核),需额外处理,详见下方进阶章节

; 2. 停止并注销守护进程
DetailPrint "正在安抚守护模块..."
nsExec::ExecToStack '"$SYSDIR\cmd.exe" /C "net stop ${PRODUCT_SERVICENAME}"'
DetailPrint "正在解除守护契约..."
nsExec::ExecToStack '"$SYSDIR\cmd.exe" /C "sc delete ${PRODUCT_SERVICENAME}"'
Sleep 3000 ; 等待系统回收资源

; 3. 删除安装目录
RMDir /r "$INSTDIR\系统服务目录"
RMDir /r "$INSTDIR"

进阶排障手册(踩坑记录)

基于近期实战,补充以下「边缘场景处置方案」

1. 静默构建指令(自动化编译)

若需集成到 CI/CD 或脚本自动构建,避免 GUI 弹窗干扰:

1
2
3
4
5
# 使用 /V3 表示静默编译,仅输出错误
makensis /V3 your_script.nsi

# 或在 PowerShell 中实现完全静默(无窗口)
Start-Process -FilePath "makensis" -ArgumentList "/V3","your_script.nsi" -WindowStyle Hidden -Wait

2. 文件释放术进阶(解决”程序自锁”与衍生实例)

当主程序包含「嵌入式浏览核心」(Chromium 类)时,卸载常因子进程残留失败。需在卸载前执行「全族进程回收」

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
; 在卸载头部定义进程清理宏
!macro TerminateTree processName
DetailPrint "正在回收运行实例: ${processName}"
nsExec::ExecToStack '"taskkill" /F /IM "${processName}" /T'
Pop $R0
Sleep 1000 ; 给予系统缓冲期
!macroend

; 在 Uninstall 节中使用
Section "Uninstall"
; 回收主实例
!insertmacro TerminateTree "YourApp.exe"

; 回收衍生浏览实例(如使用 webview2/chromium 嵌入)
!insertmacro TerminateTree "YourAppHelper.exe"
!insertmacro TerminateTree "chrome.exe" ; 若需,谨慎使用

; 强制刷新文件句柄(解决顽固占用)
System::Call 'kernel32::FlushFileBuffers(i -1)'
SectionEnd

3. 系统痕迹深度清理(注册表残留)

若程序设置了「自启动印记」(注册表 Run 键),卸载时需擦除痕迹:

1
2
3
4
5
6
; 清理当前用户启动项(系统配置表:HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run)
DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "${PRODUCT_NAME}"
DeleteRegValue HKLM "Software\Microsoft\Windows\CurrentVersion\Run" "${PRODUCT_NAME}"

; 清理软件自身配置痕迹(如有)
DeleteRegKey HKCU "Software\${PRODUCT_PUBLISHER}\${PRODUCT_NAME}"

注意HKCU 表示「当前用户配置域」HKLM 表示「本地机器配置域」,需根据软件安装范围选择。

4. 品牌标识自定义(图标与命名完全控制)

若对向导生成的图标/名称不满意,可在脚本头部手动指定「视觉标识」「产物命名」

1
2
3
4
5
6
7
8
9
; 安装包视觉标识
!define MUI_ICON "你的图标.ico"
!define MUI_UNICON "你的卸载图标.ico" ; 卸载程序独立图标

; 产物命名(避免默认 Setup.exe)
OutFile "你的软件名-版本号-Installer.exe"

; 卸载程序命名(默认 uninst.exe 可自定义)
UninstallExeName "你的软件名-Uninstall.exe"

效果展示

实际安装图片

  • 在安装时自动注册守护服务

安装时注册服务

实际卸载图片

  • 卸载时自动执行文件释放、停止并删除服务、清理系统痕迹

卸载时删除服务