SquirrelでWindowsデスクトップアプリにサイレントインストール機能を具備させる（次世代ClickOnce？）

# 0. 前書き
Windowsデスクトップアプリケーションの配布・インストールの手段はいくつかあると思います。
・インストーラーをユーザーに渡し（例えば、配布サイトからダウンロードしてもらい）、インストールしてもらう
・ClickOnceを使ってアプリを配布する
　⇒ClickOnceの詳細については、「[ClickOnce 情報のサイト](http://blog.masahiko.info/entry/2006/12/25/195037)」を参照してください。

ClickOnceはWebとデスクトップを融合するという命題のもと.NET Framework 2.0で登場し、すでに10年間使われている枯れた技術と言えます。枯れた技術っていうのも良いですが、より優れた配布手段が考えられないかということで、今回紹介する「Squirrel.Windows」がOSSで開発されています。

# 1. Squirrel.Windows[^*]とは
Squirrel.Windows（[Github:Squirrel.Windows](https://github.com/Squirrel/Squirrel.Windows)）とは、Windowsデスクトップアプリケーションの配布・インストール・アップデートの機能を提供するライブラリーです。これを適用すると、イメージとして、Google Chromeのようなサイレントアップデートの機能をデスクトップアプリに搭載することができます。

上述のサイトでは、Squirrel.Windowsの特徴を次のように説明しています。

> **Integrating** : an app to use Squirrel should be extremely easy, provide a client API, and be developer friendly.
> **Packaging** : is really easy, can be automated, and supports delta update packages.
> **Distributing** : should be straightforward, use simple HTTP updates, and provide multiple "channels" (a-la Chrome Dev/Beta/Release).
> **Installing** : is Wizard-Free™, with no UAC dialogs, does not require reboot, and is .NET Framework friendly.
> **Updating** : is in the background, doesn't interrupt the user, and does not require a reboot.

[^*]: 余談ですが、Squirrelはリスという意味、発音は「skwə:rrəl」です。日本人が発音しづらい単語ランキング高位に来るみたいです。カタカナで書くと、スカァワール？

今回は、Amazon S3をコンテンツ（バージョンアップ用のインストーラー）の配布元として、WPFアプリケーションを配布するまでを手順化してみようと思います。基本的には、Squirrel.Windowsの[Getting-Started](https://github.com/Squirrel/Squirrel.Windows/tree/master/docs/getting-started)をベースに進めますが、前述の通り配布元Amazon S3に変更したりしています。

# 2. 下準備
## 2-1. Squirrel化するアプリケーションを作成する
WPFのアプリケーションを作成します。

動作確認のためにアプリケーションに
　・メッセージを出力する機能
　・Exeファイルの置かれている場所を表示する機能
　・バージョン番号を表示する機能
を載せることとします。

```csharp:MainWindow.xaml.cs
using System.Reflection;
using System.Windows;

namespace MyWpfAppForSquirrel
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            var assembly = Assembly.GetExecutingAssembly();
            //Exeの場所を表示
            exeTextBox.Text = assembly.Location;
            //バージョン番号を表示
            versionTextBox.Text = assembly.GetName().Version.ToString();
        }
    }
}
```

バージョン番号の情報は、AssemblyInfo.csファイルに記載します。

```csharp:AssemblyInfo.cs
[assembly: AssemblyVersion("1.0.0.0")]
```

このままVisual StudioでF5で実行すると、見た目は下記のようになります。
![1.JPG](https://qiita-image-store.s3.amazonaws.com/0/34410/1d10d675-b899-d865-be4c-a2c9640ef312.jpeg)


# 2-2. 配布元のAmazon S3を用意する
Amazon S3のウェブサイトホスティングの機能を使うため、ファイルを置いて、HTTPでアクセスできるように設定します。
設定方法は、AWSのチュートリアル等を参考にしてください⇒「[例: 静的ウェブサイトをセットアップする](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/HostingWebsiteOnS3Setup.html)」

作成したサイトのURLは
http://squirrel-windows-sample.s3-website-ap-northeast-1.amazonaws.com/
とします。

# 2-3. Nuget Package Explorerをインストールする
Squirrelはアプリケーションを配布するためにnugetのパッケージング技術を使います。
移行の手順で利用するので、インストールしてください。
https://npe.codeplex.com/

# 3. さっそく使ってみる

## 3-1. Integrating（統合）
2-1で準備したアプリケーションにSquirrelの機能を追加します。

まずは、nugetでSquirrel.Windowsをインストールします。
![nuget.JPG](https://qiita-image-store.s3.amazonaws.com/0/34410/bef6d71a-8e9f-addc-b2ee-7db21b3a226d.jpeg)

つづいて、アプリケーション起動時に、アプリケーションの更新処理をさせるために、App.xaml.csに処理を記述します。
（この記述がどのような動きになるかは、3-5で確認します）

```csharp:App.xaml.cs
using Squirrel;
using System.Threading.Tasks;
using System.Windows;

namespace MyWpfAppForSquirrel
{
    public partial class App : Application
    {
        public App()
        {
            Task.Run(async () =>
            {
                using (var manager = new UpdateManager("http://squirrel-windows-sample.s3-website-ap-northeast-1.amazonaws.com/"))
                {
                    await manager.UpdateApp();
                }
            });
        }
    }
}

```

## 3-2. Packaging（パッケージング）

最初の配布用にアプリケーションをパッケージ化します。

**リリースビルドをする**
Visual Studioで「Release」モードでアプリケーションをビルドします。

**配布用パッケージのメタデータを作る**
NuGet Package Explorerを用いて、NuGet Package fileを作成します。
NuGet Package Explorerを立ち上げ、「Create a new package」を選択します。
![8.JPG](https://qiita-image-store.s3.amazonaws.com/0/34410/c08479df-20fc-e6c5-4ff6-c1c7447717c3.jpeg)

１．左ペインでメタデータを編集
「Edit Metadata」をクリックし、IDなどを編集

２．右ペインでパッケージングするモジュールを登録
・Add Lib Folder
・Add .NET folder ⇒ v4.5
・ビルド結果のファイル（dll、exe、exe.config、xml等）をドラッグ 

![9.JPG](https://qiita-image-store.s3.amazonaws.com/0/34410/64b25aed-6d0d-e2fd-1df1-50f5a3f5cd39.jpeg)


３．NuGet Package fileを保存
名前を付けて保存（Save as...）を行い、「MyWpfAppForSquirrel.1.0.0.nupkg」という名前でソリューション（.sln）ファイルと同じ階層に保存します。

**配布用パッケージを作る**
Suirrel.exeを利用し、配布用パッケージを作成します。
再びVisual Studioに戻り「パッケージマネージャーコンソール」を開く。
次のコマンドを実行[^2][^3]。
[^2]: nugetでSquirrelをインストールしているため、このコンソールでもSquirrel.exeへのパスが通っています。
[^3]: もし「見つかりません」といったエラーが出たら、Visual Studioを再起動してください。

```ps1
PM> Squirrel --releasify MyWpfAppForSquirrel.1.0.0.nupkg
```

これで、「＜solution-root＞\Releases」フォルダの下に、配布用パッケージができました。
![14.JPG](https://qiita-image-store.s3.amazonaws.com/0/34410/0b4701bf-9c05-e05a-81ce-811c0c9e6018.jpeg)


## 3-3. Distributing（配布）と、Installing（インストール）と、初回の起動
初回の配布は今回は何かしらの方法でダウンロードしたというシナリオにします。
上述のReleasesフォルダの中にある「Setup.exe」を実行します。
これでインストールが行われ、アプリケーションが起動します。

![10.jpg](https://qiita-image-store.s3.amazonaws.com/0/34410/e6efd83a-300a-c23e-781e-e1646e8431f3.jpeg)

スクリーンショット中の「EXEの場所」からもわかるように、さきほどのインストールにより「%LocalAppData%\MyWpfAppForSquirrel」というフォルダが作成され、その中にバージョン番号ごとにフォルダが作られアプリケーションが配置されます。さらにOSにアプリケーションとして登録されるため、スタートメニューの「最近追加されたもの」にも表示されます。

![12.jpg](https://qiita-image-store.s3.amazonaws.com/0/34410/1a389063-655a-5fc4-021a-f07f92131998.jpeg)


## 3-4. アプリケーションのバージョンアップと配布準備 
ここからが本題です。２回目以降のアプリケーションのバージョンアップ時に、「サイレントインストール」を実現させます。

**アプリケーションのバージョンアップ**
まずはアプリケーションをバージョンアップします。

・画面のメッセージ出力を変更します。
「Ver 1.1のアプリケーションです。」と変更しました。

・バージョン番号を、1.1.0.0に変更します。

```csharp:AssemblyInfo.cs
[assembly: AssemblyVersion("1.1.0.0")]
```

**ビルドする**
バージョンアップしたアプリケーションを、再び「Release」モードでビルドします。

**配布用パッケージのメタデータを作る**
3-2と同じ手順で、NuGet Package Explorerを用いて配布用パッケージのメタデータを作成します。
・メタデータ　⇒　Version：1.1.0に変更
・保存する際のファイル名　⇒　MyWpfAppForSquirrel.1.1.0.nupkg

**配布用パッケージを作る**
3-2と同じ手順で、配布用パッケージを作成します。

```ps1
PM> Squirrel --releasify MyWpfAppForSquirrel.1.1.0.nupkg
```

上のスクリプトを流すと「MyWpfAppForSquirrel.1.1.0-delta.nupkg」というファイルが作成されていることが分かります。
このファイルが最初のバージョンとの差分を表現します。

**配布用パッケージを配置する**
更新されたReleasesフォルダの中身をAmazon S3に配置します。
（MyWpfAppForSquirrel-1.1.0-full.nupkgは不要です。）
![11.jpg](https://qiita-image-store.s3.amazonaws.com/0/34410/783ac6d9-776d-ab9f-fddd-830851712443.jpeg)

以上で、配布の準備は完了です。

## 3-5. Updating（更新）
すでに3-3で端末にインストール済みのアプリを起動します。

見た目に変化はありません。3-3と同じアプリケーションが起動します。
ただこの時点で裏側で下記の仕組みが動きこっそりとアプリケーションのバージョンアップの準備が行われます。

1. UpdateManagerがAmazon S3から更新用のモジュールを取得
2. 「%LocalAppData%\MyWpfAppForSquirrel」に「app-1.1.0」が作成され、モジュールが展開

いったんアプリケーションを閉じて、もう１度アプリケーションを起動します。
そうすると、下記のようにアプリケーションがバージョンアップされていることが分かります。
![13.jpg](https://qiita-image-store.s3.amazonaws.com/0/34410/0abb6dd0-0790-0b46-7270-904d1bc6bede.jpeg)

# 4. おわりに
Squirrel.Windowsを利用すると、アプリケーションのサイレントアップデートが可能になることが分かりました。
検証できなかったですが、下記のポイントは柔軟で良さそうです。
・配布の方法
　⇒　Webサイドから配布しなくても、ファイルサーバーとかでももちろんOK。
・「Check for update」みたな機能も作れそう
　⇒　別にサイレントインストールにしなくても、ユーザー駆動で更新モジュールを取得するといった機能も作れそうです。

ただこの手順はいまいちですね。手作業が多すぎるので、もう少し自動化できるようにしたいです。特に、NuGet Package fileを作成する箇所。

# 5. 参考
dotnetConf 2015のChannel9で、作者がデモを行っています
[Squirrel for Windows - installing .NET apps the way it should be](https://channel9.msdn.com/Events/dotnetConf/2015/Squirrel-for-Windows-installing-NET-apps-the-way-it-should-be)