Electron NSIS自定义数据目录(userData)

使用Electron开发了一个软件,为了方便,将运行中生成的一些文件(工作目录)放在了userData目录下,因为默认userData在windows系统是在C盘,而且生成的文件还很大,为了防止把C盘撑满,考虑安装时可由用户选择安装至其他位置。

经过一番搜索和左拼右凑后,写了如下nsis脚本,算是实现了此功能。

!addplugindir Plugins
!include nsDialogs.nsh
!include InstallOptions.nsh
!include LogicLib.nsh

; 在选择安装目录后设置数据保存目录页面
!macro customPageAfterChangeDir
  ; 定义常量和变量
  !define PRODUCT_UNINST_ROOT_KEY "HKLM"
  !define SWP_NOMOVE   0x0002
  !define SWP_NOZORDER 0x0004
  ; typedef struct _RECT {
  ;   LONG left;
  ;   LONG top;
  ;   LONG right;
  ;   LONG bottom;
  ; } RECT, *PRECT;
  !define stRECT "(i, i, i, i) i"

  ; 定义数据目录位置
  Var DataDir
  Var HWND

  ; 自定义数据目录选择页面
  Page Custom MyPageCreate

  Function MyPageCreate

    ;读取之前安装的值
    ReadRegStr $R0 ${PRODUCT_UNINST_ROOT_KEY} "${INSTALL_REGISTRY_KEY}_data" "DataDir"
    ${If} $R0 != ""
      StrCpy $DataDir $R0
    ${Else}
      StrCpy $DataDir "C:\ProgramData\${APP_PACKAGE_NAME}"  ; 默认数据目录
    ${EndIf}

    ; 更新安装时跳过选择数据目录
    ${ifnot} ${isUpdated}
      InitPluginsdir
      ; A real installer would not write the .ini like this, it would extract a file
      WriteIniStr "$PLUGINSDIR\datadir.ini" Settings NumFields 3

      WriteIniStr "$PLUGINSDIR\datadir.ini" "Field 1" Type Label
      WriteIniStr "$PLUGINSDIR\datadir.ini" "Field 1" Text "选择数据保存目录"
      WriteIniStr "$PLUGINSDIR\datadir.ini" "Field 1" Left 0
      WriteIniStr "$PLUGINSDIR\datadir.ini" "Field 1" Right 310
      WriteIniStr "$PLUGINSDIR\datadir.ini" "Field 1" Top 0
      WriteIniStr "$PLUGINSDIR\datadir.ini" "Field 1" Bottom 26

      WriteIniStr "$PLUGINSDIR\datadir.ini" "Field 2" Type GroupBox
      WriteIniStr "$PLUGINSDIR\datadir.ini" "Field 2" Text "数据保存目录"
      WriteIniStr "$PLUGINSDIR\datadir.ini" "Field 2" Left 0
      WriteIniStr "$PLUGINSDIR\datadir.ini" "Field 2" Right 298
      WriteIniStr "$PLUGINSDIR\datadir.ini" "Field 2" Top 70
      WriteIniStr "$PLUGINSDIR\datadir.ini" "Field 2" Bottom 106

      WriteIniStr "$PLUGINSDIR\datadir.ini" "Field 3" Type DirRequest
      WriteIniStr "$PLUGINSDIR\datadir.ini" "Field 3" State $DataDir
      WriteIniStr "$PLUGINSDIR\datadir.ini" "Field 3" Left 10
      WriteIniStr "$PLUGINSDIR\datadir.ini" "Field 3" Right 254
      WriteIniStr "$PLUGINSDIR\datadir.ini" "Field 3" Top 84
      WriteIniStr "$PLUGINSDIR\datadir.ini" "Field 3" Bottom 97

      !insertmacro INSTALLOPTIONS_INITDIALOG "datadir.ini"

      ; 修改数据目录浏览按钮文字
      Pop $HWND
      ; Get HWND of browse button.
      GetDlgItem $R0 $HWND 1203
      ; Set its text.
      SendMessage $R0 ${WM_SETTEXT} "" "STR:浏览(B)..."
      ; Get the dimensions of the button.
      System::Call `*${stRECT} .R1`
      System::Call `user32::GetClientRect(i $R0, i R1)`
      System::Call `*$R1${stRECT} (, , .R2, .R3)`
      ; Change the button width.
      IntOp $R2 $R2 * 3
      ; Set the new width.
      System::Call `user32::SetWindowPos(i $R0, i 0, i 0, i 0, i $R2, i $R3, i ${SWP_NOMOVE} | ${SWP_NOZORDER})`
      System::Free $R1
      ; Display the page.
      !insertmacro INSTALLOPTIONS_SHOW
      Pop $R0
    ${endif}
  FunctionEnd
  
  Section -Post
    ; 更新安装时跳过选择数据目录
    ${ifnot} ${isUpdated}
      ; 数据目录写入注册表
      ReadINIStr $DataDir "$PLUGINSDIR\datadir.ini" "Field 3" State
      WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${INSTALL_REGISTRY_KEY}_data" "DataDir" $DataDir
    ${endif}
  SectionEnd
!macroend

; 启动应用
!macro RunApp
  ${StdUtils.ExecShellAsUser} $0 "$launchLink" "open" ""
!macroend

!macro customInstall
  ; 数据目录存入配置文件
  WriteIniStr "$INSTDIR\config.ini" "dir" DataDir $DataDir
!macroend

因为使用electron-builder打包,在其nsis代码中找到了customPageAfterChangeDir这个宏,从名字看起来是自定义选择安装目录后的页面,正好符合需求,然后向$PLUGINSDIR\datadir.ini文件写入页面上的控件定义,再由

!insertmacro INSTALLOPTIONS_INITDIALOG "datadir.ini"`

生成页面,包含一段文字描述,一个GroupBox,一个目录选择控件。
之后在Section -Post里将选择的目录写入注册表,这里没有写入默认的INSTALLREGISTRYKEY而是另建了一个{INSTALL_REGISTRY_KEY}而是另建了一个INSTALLREGISTRYKEY而是另建了一个{INSTALL_REGISTRY_KEY}_data是因为首次安装注册表可以找到写入的数据目录,一旦覆盖安装,数据目录直接消失了,没找到原因,可能是更新安装的时序没搞顺,干脆另立门户,存到${INSTALL_REGISTRY_KEY}_data里,这下咋样都不会被清掉了。

同时Electron更改userData需要在app ready事件触发前调用app.setPath(‘userData’, ‘dir’),找了下读取注册表的库,基本都是异步的,无法保证这个条件,所以在nsis脚本里又向安装目录下的config.ini写了一次数据目录的值,这样使用fs.readFileSync可以同步读取文件并调用app.setPath设置userData。打包后测试安装时能正常选择,启动时也能读取并设置到指定目录,在此记录下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值