我自己本人不做 Electron 的开发,但有一位合作伙伴在使用 NSIS 打包 Electron 应用的时候遇到了一些问题,主要问题有以下几个,先记录下来,然后追个击破。

  • 打包后应用在 Windows 7 无法直接运行,需要修改兼容性为 Windows 7 才可以使用
  • 打包后安装或者卸载时应用在运行会安装或者卸载失败,无法替换或删除应用(这个与 Electron 无关但也介绍一下)
  • 打包后应用第一次启动无法使用拖拽功能

脚本设置启动程序兼容性

设置程序兼容性有两种方式,一种是手动右键修改,但这种方式明显不可能让用户自己去操作,所以我们需要另外一种方式就是在安装程序后自动写入一个注册表,告诉系统我们启动自己的应用时使用 Windows 7 兼容模式运行,示例注册表如下:

1
2
3
4
Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers]
"C:\\Program Files (x86)\\MyOffice\\avic.exe"="WIN7RTM"

要在 NSIS 的脚本中写入这个注册表,可以像下面这样操作:

1
2
3
4
5
6
7
8
9
10
11
Section -Post
WriteUninstaller "$INSTDIR\uninst.exe"
WriteRegStr HKCU "Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" "$INSTDIR\avic.exe" "WIN7RTM"
WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\avic.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\avic.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

如代码第三行所示,我们将应用安装所在目录下的执行程序通过 WriteRegStr 方法写入到了注册表中,给它设置的值是 WIN7RTM,这样应用在启动的时候,就会以 Windows 7 兼容性模式运行了。

安装或卸载前结束应用

需要两个插件,一个是 KillProcDLL.dll,另一个是 FindProcDLL.dll,这两个插件均可以从 NSIS 官网下载。以下是示例脚本:

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
Function .onInit
FindProcDLL::FindProc "avic.exe"
Pop $R0
IntCmp $R0 1 0 no_run
MessageBox MB_OKCANCELMB_ICONSTOP "安装程序检测到 ${PRODUCT_NAME} 正在运行。$\r$\n$\r$\n点击 “确定” 强制关闭${PRODUCT_NAME},继续安装。$\r$\n点击 “取消” 退出安装程序。" IDCANCEL Exit
KillProcDLL::KillProc "avic.exe"
Sleep 1000
FindProcDLL::FindProc "avic.exe"
Pop $R0
IntCmp $R0 1 0 no_run
Exit:
Quit
no_run:
FunctionEnd

Function un.onInit
MessageBox MB_ICONQUESTIONMB_YESNOMB_DEFBUTTON2 "您确定要卸载[MyOffice]?" IDYES +2
Abort
#检测程序是否运行
FindProcDLL::FindProc "avic.exe"
Pop $R0
IntCmp $R0 1 0 no_run
KillProcDLL::KillProc "avic.exe"
Sleep 1000
FindProcDLL::FindProc "avic.exe"
Pop $R0
IntCmp $R0 1 0 no_run
Quit
no_run:
FunctionEnd

打包后无法使用拖拽功能

经过排查发现,NSIS 打包后安装包程序默认是以管理员身份启动的,第一次安装完成后自动启动应用时会继承安装包的权限(管理员)导致拖拽功能无法使用了,而直接从桌面运行快捷方式是没问题的。解决这个问题的办法就是让安装包不以管理员方式运行就可以了。在初始化代码中加如下代码,让安装包使用普通用户权限:

1
RequestExecutionLevel user

当安装包没有管理员权限后,你安装到 C:\Program Files 目录就会被拒绝,这时你可以参考想 VSCode 一样,将应用安装到 C:\Users\Administrator\AppData\Local\Programs 目录下,来避免权限问题。