2019-07-20 (Sat) [長年日記]

_ NSIS (アン)インストーラ

NSIS でインストーラを作っていて、インストール前に既存バージョンを消したいとき、.onInit に何をどう書けばいいか悩んでいましたが、結局こうしました。

; GetTime のために FileFunc を使う
!Include "FileFunc.nsh"
Function .onInit
  ; これは普通どおりレジストリを見て既インストールか確認してるだけ
  ReadRegStr $R0 ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString"
  StrCmp $R0 "" done

  ; これも別に要らないけど、まあ動作を知らせている
  MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "既存の ${PRODUCT_NAME} を先にアンインストールしてください。$\r$\n$\r$\nOK を押すと続行します。その後で、改めてインストール画面を出します。" IDOK uninst
  Abort

uninst:
  ClearErrors
  ${GetTime} "" "L" $0 $1 $2 $3 $4 $5 $6
  ; セキュリティ的にはもっと推測や衝突を考えた方がいいだろうが
  StrCpy $R1 "$TEMP\uninstall-$2-$1-$0-$4-$5-$6.exe"
  CopyFiles "$R0" "$R1"
  ExecWait '"$R1" _?=$INSTDIR' $R2
  Delete "$R1"
  ; アンインストーラが正常終了したときだけ続行するなら以下のようにしておく
  StrCmp $R2 0 done
  Abort

done:

FunctionEnd

アンインストーラは自分を消す必要があるので、_? が指定されていなければ一時フォルダに自分をコピーしてからそっちを呼びます。だから ExecWait (子を待つが孫は待たない)が効かないぞ、というのは FAQ みたいです。

で、どうするのかというと、その動作を止め(つつ消去先を指定す)るオプション _? をただ呼べばいいのではありません。それだけでは「自分が起動中なので消せない」というエラーになったり、エラーを消そうとして REBOOTOK にしたら再起動後に(新しい)アンインストーラが消えちゃってアンインストールできなくなったり、という落とし穴があるみたいです。

なのでアンインストーラを ExecWait したいなら自分で TEMP にコピーするというのが一番簡単みたいです。

ちなみに、上記のようにするまでは、アンインストーラが新規インストーラの上に来るように MessageBox を遅らせたりしていました。

Function un.onInit
  Sleep 800
  MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "$(^Name) を削除していいですか?" IDYES +2
  Abort
FunctionEnd

«前の日記(2019-07-15 (Mon)) 最新