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