NSISでインストーラを作ってみる その1

# はじめに
個人的に一押しのインストーラ NSIS(Nullsoft Scriptable Install System)を使ってインストーラを作る方法をサンプルを用いてレクチャーします。
何故NSISが一押しなのかについてはここでは深く述べませんが、例えば開発者が流動的で継続的に開発を行うようなプロジェクトで導入が容易(インストールが軽量・習得コストが低い)なことが一つのポイントです。
本文執筆時のNSISのバージョンは3.03、OSはWindows 10 1809です。
尚、スクリプトの詳細については公式サイトのドキュメント類で解説されているので、ここでは詳しくは説明しません。
# 公式サイト
[NSIS Wiki](http://nsis.sourceforge.net)
Stackoverflowなんかで調べなくても大抵のことは公式サイトで理解できます。
英語ですが[Google翻訳](https://translate.google.co.jp)なんかにURLを貼り付けるかChromeを使えば容易に日本語訳が読めます。
ドキュメントが豊富で色々なことができるのがわかりますが、シンプルなインストーラを作るだけであれば理解することはそんなに多くありません。
# チュートリアル
[Category:Tutorials](http://nsis.sourceforge.net/Category:Tutorials)
目的別の説明があります。
# 公式のサンプル
[Code Examples](http://nsis.sourceforge.net/Category:Code_Examples)
幾つかサンプルがあります。
#シンプルなインストーラ
始めに単純なファイルをコピーするだけのインストーラを作り、それから様々な機能を付加していきます。
## NSI スクリプトの記述
NSISをインストールしたら、適当な場所に、Install.nsiというテキスト ファイルを作成します。
インストールしたいファイルが、C:\Sample\Sample.exeというファイルとします。
~~NSISはUnicodeに対応していない(対応版もありますが、未検証)ので、~~
NSISでUnicodeを使用するには別途手順が必要ですがここでは割愛します。Unicode文字を使用したディレクトリ／ファイル名は使用せず、Install.nsiはShift-JISで記述してください。

```NSIS:Install.nsi
# 作成されるインストーラ
OutFile "Install.exe"
# インストールされるディレクトリ
InstallDir "$DESKTOP\NSISSample"

# デフォルト セクション
Section
  # 出力先を指定します。
  SetOutPath "$INSTDIR"
  # インストールされるファイル
  File "C:\Sample\Sample.exe"
SectionEnd
```

## コンパイル
![001.png](https://qiita-image-store.s3.amazonaws.com/0/149140/a3c0ea3a-969f-d567-d759-4d83428464ab.png)
NSISを起動して、"Compile NSI scripts"を選択します。
![002.png](https://qiita-image-store.s3.amazonaws.com/0/149140/a8eb26e7-5be0-7eef-95fd-6d3e680ca3cc.png)
MakeNSISWというウィンドウが開くので、左上のツールバーからファイルを開くアイコンをクリックします。
(又は、メニューバーから"File">"Load Script...")
Install.nsiのあるディレクトリに移動し、Install.nsiを選択し、"開く"をクリックします。

もしくは、Install.nsiをエクスプローラから右クリックし、コンテキストメニューから"Complie NSIS Script"を選択します。

すぐにコンパイルが開始され、Install.nsiのあるディレクトリに、"Install.exe"が作成されます。
## インストールしてみる
作成されたInstall.exeを実行してみます。
"Name Setup: Completed"というウィンドウが表示され、何も問題がなければ、"Completed"と表示されているはずです。
![003.png](https://qiita-image-store.s3.amazonaws.com/0/149140/cb23fbda-e1ef-aab4-1485-e43f6dfdc43f.png)
"Close"をクリックして、インストーラのウィンドウを閉じます。
デスクトップに"NSISSample"というディレクトリが作成され、Sample.exeがコピーされています。

## アプリケーション名を定義する
このままでは何のアプリケーションのインストーラなのかわからないので、アプリケーション名を定義します。

```NSIS:Install.nsi
# アプリケーション名
Name "サンプル アプリケーション"
# 作成されるインストーラ
OutFile "Install.exe"
# インストールされるディレクトリ
InstallDir "$DESKTOP\NSISSample"

# デフォルト セクション
Section
  # 出力先を指定します。
  SetOutPath "$INSTDIR"
  # インストールされるファイル
  File "C:\Sample\Sample.exe"
SectionEnd
```

![001.png](https://qiita-image-store.s3.amazonaws.com/0/149140/b90d7ed2-909e-86e1-9fbd-e923fdbc50a3.png)
Install.exeを実行すると、ウィンドウは"サンプル アプリケーション Setup: Completed"となり、オリジナリティが出ました。
## 言語を日本語にする
全体的なUIが英語ですが、多くの現場では国際的なソフトウェアよりも国内で利用されるアプリケーションが多いと思います。
UIを日本語にし、親近感のあるインストーラにしてみます。

```NSIS:Install.nsi
# 日本語UI
LoadLanguageFile "${NSISDIR}\Contrib\Language files\Japanese.nlf"
# アプリケーション名
Name "サンプル アプリケーション"
# 作成されるインストーラ
OutFile "Install.exe"
# インストールされるディレクトリ
InstallDir "$DESKTOP\NSISSample"

# デフォルト セクション
Section
  # 出力先を指定します。
  SetOutPath "$INSTDIR"
  # インストールされるファイル
  File "C:\Sample\Sample.exe"
SectionEnd
```

![001.png](https://qiita-image-store.s3.amazonaws.com/0/149140/90daf7bf-be71-6771-da83-984af8d4f8e0.png)
Install.exeを実行すると、ウィンドウは"サンプル アプリケーション セットアップ：完了"となり、UI類も日本語になっています。
多くの日本人ユーザーはこれで親近感がグッと上がると思います。
## アンインストーラを用意する
このままではインストーラしかできないので、アンインストーラも用意します。

```NSIS:Install.nsi
# 日本語UI
LoadLanguageFile "${NSISDIR}\Contrib\Language files\Japanese.nlf"
# アプリケーション名
Name "サンプル アプリケーション"
# 作成されるインストーラ
OutFile "Install.exe"
# インストールされるディレクトリ
InstallDir "$DESKTOP\NSISSample"

# デフォルト セクション
Section
  # 出力先を指定します。
  SetOutPath "$INSTDIR"
  # インストールされるファイル
  File "C:\Sample\Sample.exe"
  # アンインストーラを出力
  WriteUninstaller "$INSTDIR\Uninstall.exe"
SectionEnd

# アンインストーラ
Section "Uninstall"
  # アンインストーラを削除
  Delete "$INSTDIR\Uninstall.exe"
  # ファイルを削除
  Delete "$INSTDIR\Sample.exe"
  # ディレクトリを削除
  RMDir "$INSTDIR"
SectionEnd
```

インストールが完了すると、デスクトップの"NSISSample"ディレクトリ内に、Uninstall.exeが作成されます。
これを実行すると、NSISSample ディレクトリが丸ごと削除されます。

## コントロール パネルの"プログラムと機能"に登録する
イントール先からアンインストーラを起動するのは不便なので、コントロール パネルの"プログラムと機能"で管理できるようにします。

```NSIS:Install.nsi
# 日本語UI
LoadLanguageFile "${NSISDIR}\Contrib\Language files\Japanese.nlf"
# アプリケーション名
Name "サンプル アプリケーション"
# 作成されるインストーラ
OutFile "Install.exe"
# インストールされるディレクトリ
InstallDir "$DESKTOP\NSISSample"

# デフォルト セクション
Section
  # 出力先を指定します。
  SetOutPath "$INSTDIR"
  # インストールされるファイル
  File "C:\Sample\Sample.exe"
  # アンインストーラを出力
  WriteUninstaller "$INSTDIR\Uninstall.exe"
  # レジストリに登録
  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\NSISSample" "DisplayName" "サンプル アプリケーション"
  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\NSISSample" "UninstallString" '"$INSTDIR\Uninstall.exe"'
SectionEnd

# アンインストーラ
Section "Uninstall"
  # アンインストーラを削除
  Delete "$INSTDIR\Uninstall.exe"
  # ファイルを削除
  Delete "$INSTDIR\Sample.exe"
  # ディレクトリを削除
  RMDir "$INSTDIR"
  # レジストリ キーを削除
  DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\NSISSample"
SectionEnd
```

![001.png](https://qiita-image-store.s3.amazonaws.com/0/149140/2921a918-6526-171e-f3c1-b62b5d81140f.png)
インストールすると、コントロール パネルの"プログラムと機能"の"プログラムのアンインストールまたは変更"の一覧に"サンプル アプリケーション"が表示されます。
それを選択して"アンインストールと変更"をクリックすると、アンインストーラが起動し、アンインストールできます。

## インストール先をユーザーが選択できるようにする
大抵のインストーラはインストール先は既定の場所があらかじめ設定されていますが、ユーザーが任意の場所を選択することもできるようになっているので、ユーザーが選択できるようにします。
又、既定のインストール先を一般的なC:\Program Files(64-bit Windowsの場合はC:\Program Files (x86))にします。
(Windows VistaからProgram Files以下ディレクトリはユーザー権限では書き込みできないので、自身のディレクトリにファイルを書き込むようなアプリケーションは他の場所にした方が良いでしょう)

```NSIS:Install.nsi
# 日本語UI
LoadLanguageFile "${NSISDIR}\Contrib\Language files\Japanese.nlf"
# アプリケーション名
Name "サンプル アプリケーション"
# 作成されるインストーラ
OutFile "Install.exe"
# インストールされるディレクトリ
InstallDir "$PROGRAMFILES\NSISSample"
# ページ
Page directory
Page instfiles

# デフォルト セクション
Section
  # 出力先を指定します。
  SetOutPath "$INSTDIR"
  # インストールされるファイル
  File "C:\Sample\Sample.exe"
  # アンインストーラを出力
  WriteUninstaller "$INSTDIR\Uninstall.exe"
  # レジストリに登録
  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\NSISSample" "DisplayName" "サンプル アプリケーション"
  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\NSISSample" "UninstallString" '"$INSTDIR\Uninstall.exe"'
SectionEnd

# アンインストーラ
Section "Uninstall"
  # アンインストーラを削除
  Delete "$INSTDIR\Uninstall.exe"
  # ファイルを削除
  Delete "$INSTDIR\Sample.exe"
  # ディレクトリを削除
  RMDir "$INSTDIR"
  # レジストリ キーを削除
  DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\NSISSample"
SectionEnd
```

![001.png](https://qiita-image-store.s3.amazonaws.com/0/149140/c455d9e7-2e15-cc65-9e11-859f61b96976.png)
Install.exeを実行すると、まずインストール先を選択するウィンドウが表示されます。
ここでインストール先を変更すると、そのディレクトリにアプリケーションをインストールすることができます。
現状ではサンプル アプリケーションは何度もインストールすることができますが、レジストリは上書きされるので、コントロール パネルからアンインストールできるのは最後にインストールしたディレクトリのものだけになるので注意してください。

## スタート メニューに登録
通常、アプリケーションはエクスプローラからダブルクリックして実行するのではなく、スタート メニュー等から選択して起動するのが一般的なので、インストール時にスタート メニューに登録できるようにします。

```NSIS:Install.nsi
# 日本語UI
LoadLanguageFile "${NSISDIR}\Contrib\Language files\Japanese.nlf"
# アプリケーション名
Name "サンプル アプリケーション"
# 作成されるインストーラ
OutFile "Install.exe"
# インストールされるディレクトリ
InstallDir "$PROGRAMFILES\NSISSample"
# ページ
Page directory
Page instfiles

# デフォルト セクション
Section
  # 出力先を指定します。
  SetOutPath "$INSTDIR"
  # インストールされるファイル
  File "C:\Sample\Sample.exe"
  # アンインストーラを出力
  WriteUninstaller "$INSTDIR\Uninstall.exe"
  # スタート メニューにショートカットを登録
  CreateDirectory "$SMPROGRAMS\NSISSample"
  SetOutPath "$INSTDIR"
  CreateShortcut "$SMPROGRAMS\NSISSample\サンプル アプリケーション.lnk" "$INSTDIR\Sample.exe" ""
  # レジストリに登録
  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\NSISSample" "DisplayName" "サンプル アプリケーション"
  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\NSISSample" "UninstallString" '"$INSTDIR\Uninstall.exe"'
SectionEnd

# アンインストーラ
Section "Uninstall"
  # アンインストーラを削除
  Delete "$INSTDIR\Uninstall.exe"
  # ファイルを削除
  Delete "$INSTDIR\Sample.exe"
  # ディレクトリを削除
  RMDir "$INSTDIR"
  # スタート メニューから削除
  Delete "$SMPROGRAMS\NSISSample\サンプル アプリケーション.lnk"
  RMDir "$SMPROGRAMS\NSISSample"
  # レジストリ キーを削除
  DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\NSISSample"
SectionEnd
```

インストールすると、スタート メニューに"NSISSample"というツリーが作成され、その中から"サンプル アプリケーション"を選択すると、Sample.exeを実行できます。
CreateShortcutの前にSetOutPathを実行していますが、SetOutPath "$INSTDIR"を実行することで、ショートカットの作業ディレクトリがSample.exeのあるディレクトリになります。
これをしないとショートカットの作業ディレクトリが他の場所になってしまうので、注意してください。
## サブディレクトリがある場合
インストールされるアプリケーションは階層構造になっている場合が一般的です。サブディレクトリのファイルも含めてインストールできるようにします。
"C:\Sample\docs\readme.txt"というファイルがある状態とします。

```NSIS:Install.nsi
# 日本語UI
LoadLanguageFile "${NSISDIR}\Contrib\Language files\Japanese.nlf"
# アプリケーション名
Name "サンプル アプリケーション"
# 作成されるインストーラ
OutFile "Install.exe"
# インストールされるディレクトリ
InstallDir "$PROGRAMFILES\NSISSample"
# ページ
Page directory
Page instfiles

# デフォルト セクション
Section
  # 出力先を指定します。
  SetOutPath "$INSTDIR"
  # インストールされるファイル
  File "C:\Sample\Sample.exe"
  # 出力先を指定します。
  SetOutPath "$INSTDIR\docs"
  # インストールされるファイル
  File "C:\Sample\docs\readme.txt"
  # アンインストーラを出力
  WriteUninstaller "$INSTDIR\Uninstall.exe"
  # スタート メニューにショートカットを登録
  CreateDirectory "$SMPROGRAMS\NSISSample"
  SetOutPath "$INSTDIR"
  CreateShortcut "$SMPROGRAMS\NSISSample\サンプル アプリケーション.lnk" "$INSTDIR\Sample.exe" ""
  # レジストリに登録
  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\NSISSample" "DisplayName" "サンプル アプリケーション"
  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\NSISSample" "UninstallString" '"$INSTDIR\Uninstall.exe"'
SectionEnd

# アンインストーラ
Section "Uninstall"
  # アンインストーラを削除
  Delete "$INSTDIR\Uninstall.exe"
  # ファイルを削除
  Delete "$INSTDIR\Sample.exe"
  # ディレクトリを削除
  RMDir /r "$INSTDIR"
  # スタート メニューから削除
  Delete "$SMPROGRAMS\NSISSample\サンプル アプリケーション.lnk"
  RMDir "$SMPROGRAMS\NSISSample"
  # レジストリ キーを削除
  DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\NSISSample"
SectionEnd
```

SetOutPathでインストール先を指定した後、そのディレクトリにインストールされるファイルをFileで指定していくという手順になります。
Fileはワイルドカードも使用できます。
アンインストーラでRMDir /rとしていますが、ディレクトリを再帰的に削除してもよい場合は、このようにします。
アンインストールしてもユーザー設定ファイル等を残しておきたい場合は、削除するディレクトリを個別に指定しなければなりません。
## スクリプトにエラーがあった場合
スクリプトにエラーがあった場合、インストーラ作成者が気づけることと、どこを修正する必要があるかがわかることが重要です。
NSISはエラー報告機能も良く出来ていると思います。

```NSIS:Install.nsi
# 日本語UI
LoadLanguageFile "${NSISDIR}\Contrib\Language files\Japanese.nlf"
# アプリケーション名
Name "サンプル アプリケーション"
# 作成されるインストーラ
OutFile "Install.exe"
# インストールされるディレクトリ
InstallDir "$PROGRAMFILES\NSISSample"
# ページ
Page directory
Page instfiles

# デフォルト セクション
Sectio
  # 出力先を指定します。
  SetOutPath "$INSTDIR"
  # インストールされるファイル
  File "C:\Sample\Sample.exe"
  # 出力先を指定します。
  SetOutPath "$INSTDIR\docs"
  # インストールされるファイル
  File "C:\Sample\docs\readme.txt"
  # アンインストーラを出力
  WriteUninstaller "$INSTDIR\Uninstall.exe"
  # スタート メニューにショートカットを登録
  CreateDirectory "$SMPROGRAMS\NSISSample"
  SetOutPath "$INSTDIR"
  CreateShortcut "$SMPROGRAMS\NSISSample\サンプル アプリケーション.lnk" "$INSTDIR\Sample.exe" ""
  # レジストリに登録
  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\NSISSample" "DisplayName" "サンプル アプリケーション"
  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\NSISSample" "UninstallString" '"$INSTDIR\Uninstall.exe"'
SectionEnd

# アンインストーラ
Section "Uninstall"
  # アンインストーラを削除
  Delete "$INSTDIR\Uninstall.exe"
  # ファイルを削除
  Delete "$INSTDIR\Sample.exe"
  # ディレクトリを削除
  RMDir /r "$INSTDIR"
  # スタート メニューから削除
  Delete "$SMPROGRAMS\NSISSample\サンプル アプリケーション.lnk"
  RMDir "$SMPROGRAMS\NSISSample"
  # レジストリ キーを削除
  DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\NSISSample"
SectionEnd
```

コンパイラを実行すると、以下のように出力されます

```
Invalid command: "Sectio"
Error in script "C:\NSISSample\Install.nsi" on line 14 -- aborting creation process
```

不正なコマンド"Sectio"(正しくは"Section")が記述され、それが14行目にあることがわかります。
## インストール時にエラーが発生した場合
インストールは、必ず成功するとは限りません。
例えばインストール先の実行ファイルが実行中であれば、ファイルをコピーすることができないので、失敗します。
その時にユーザーがインストールに失敗したことに気付かなければなりません。
NSISではそのような場合にダイアログが表示され、中止・再試行・無視を選択することができます。
中止した場合にはプログレス バーが最後まで到達せず、"閉じる"ボタンがグレーアウトされ"キャンセル"しかクリックできず、インストールが失敗したことがユーザーに伝わります。
"無視"を選択すればインストールを完了することはできますが、無視されたファイルはコピーされません。
"詳細を表示"をクリックすればログが表示され、ファイルがコピーされていないことがわかります(無視した、とは記録されないので注意)。
又、これを右クリックして"詳細をクリップボードへコピー"を選択すれば、テキストとしてクリップボードにコピーすることもできます。

インストーラもアプリケーションなので、必ずテストが必要です。
保守作業時に思わぬトラブルに遭遇しないよう、作ったら終わりではなく、インストーラのテストも行ってからリリースするようにしましょう。
# 最後に
[Notepad++](https://notepad-plus-plus.org)はNSIS スクリプトに対応していて、スクリプトが見やすいです。
(CreateShortcutをCreateShortCutと書かないと認識しない…公式は前者なのに)

次は、Modern UIを使用したフレキシブルなインストーラについてレクチャーしようと思います。