プログラムコードで ClickOnce を制御する

ApplicationDeployment クラス（System.Deployment.Application 名前空間）を利用した、ClickOnce によるバージョン更新を行うための汎用クラスを作成しました。ソースコードは [Github](https://github.com/mxProject/ClickOnceHelper) で公開しています。

## 機能 ##

* 更新チェックやダウンロードの非同期処理を Task 形式の非同期メソッドとして定義しています。ApplicationDeployment クラスでもイベントコールバック形式の非同期処理が提供されていますが、今となっては Task 形式のほうがなじみやすいと思います。
* 更新チェックやダウンロードの進捗情報を受け取ることができます。但し、非同期処理の場合のみ。
* アセンブリ名とダウンロードグループの組み合わせをあらかじめ設定しておくことによって、そのアセンブリが必要になった時点で自動的にダウンロードさせることができます。この処理には AppDomain.AssemblyResolve イベントを利用しています。

## サンプル ##

### 更新チェック ###

ClickOnceController クラスの CheckForUpdate メソッドを使用します。非同期の場合は CheckForUpdateAsync メソッドを使用します。
ログ出力に関連する WriteLog、WriteExceptionLog メソッドの説明は割愛します。

```C#

private ClickOnceController m_ClickOnce;

/// <summary>
/// 
/// </summary>
private void CheckForUpdate()
{
    try
    {
        IClickOnceUpdateInfo info = m_ClickOnce.CheckForUpdate();

        WriteLog(string.Format("AvailableVersion = {0}", info.AvailableVersion));
        WriteLog(string.Format("IsUpdateRequired = {0}", info.IsUpdateRequired));
        WriteLog(string.Format("MinimumRequiredVersion = {0}", info.MinimumRequiredVersion));
        WriteLog(string.Format("UpdateAvailable = {0}", info.UpdateAvailable));
        WriteLog(string.Format("UpdateSizeBytes = {0}", info.UpdateSizeBytes));
    }
    catch (Exception ex)
    {
        WriteExceptionLog(ex);
    }
}

/// <summary>
/// 
/// </summary>
private async Task CheckForUpdateAsync()
{
    try
    {
        IClickOnceUpdateInfo info = await m_ClickOnce.CheckForUpdateAsync().ConfigureAwait(false);

        WriteLog(string.Format("AvailableVersion = {0}", info.AvailableVersion));
        WriteLog(string.Format("IsUpdateRequired = {0}", info.IsUpdateRequired));
        WriteLog(string.Format("MinimumRequiredVersion = {0}", info.MinimumRequiredVersion));
        WriteLog(string.Format("UpdateAvailable = {0}", info.UpdateAvailable));
        WriteLog(string.Format("UpdateSizeBytes = {0}", info.UpdateSizeBytes));
    }
    catch (Exception ex)
    {
        WriteExceptionLog(ex);
    }
}

```

### バージョン更新 ###

ClickOnceController クラスの Update メソッドを使用します。非同期の場合は UpdateAsync メソッドを使用します。
ログ出力に関連する WriteLog、WriteExceptionLog メソッドの説明は割愛します。

```C#

private ClickOnceController m_ClickOnce;

/// <summary>
/// 
/// </summary>
/// <returns></returns>
private bool UpdateApplication()
{
    try
    {
        bool result = m_ClickOnce.Update();
        WriteLog(result ? "The version of this application has been updated." : "The version of this application was not updated.");
        return result;
    }
    catch (Exception ex)
    {
        WriteExceptionLog(ex);
        return false;
    }
}

/// <summary>
/// 
/// </summary>
/// <returns></returns>
private async Task<bool> UpdateApplicationAsync()
{
    try
    {
        bool result = await m_ClickOnce.UpdateAsync().ConfigureAwait(false);
        WriteLog(result ? "The version of this application has been updated." : "The version of this application was not updated.");
        return result;
    }
    catch (Exception ex)
    {
        WriteExceptionLog(ex);
        return false;
    }
}
```

### ダウンロードグループのダウンロード ###

ClickOnceController クラスの DownloadFileGroup メソッドを使用します。非同期の場合は DownloadFileGroupAsync メソッドを使用します。
ログ出力に関連する WriteLog、WriteExceptionLog メソッドの説明は割愛します。

```C#

private ClickOnceController m_ClickOnce;

/// <summary>
/// 
/// </summary>
/// <returns></returns>
private bool DownloadFileGroup(string groupName)
{
    try
    {
        bool result = m_ClickOnce.DownloadFileGroup(groupName);
        WriteLog(string.Format(result ? "The version of '{0}' has been updated." : "The version of '{0}' was not updated.", groupName));
        return result;
    }
    catch (Exception ex)
    {
        WriteExceptionLog(ex);
        return false;
    }
}

/// <summary>
/// 
/// </summary>
/// <returns></returns>
private async Task<bool> DownloadFileGroupAsync(string groupName)
{
    try
    {
        bool result = await m_ClickOnce.DownloadFileGroupAsync(groupName).ConfigureAwait(false);
        WriteLog(string.Format(result ? "The version of '{0}' has been updated." : "The version of '{0}' was not updated.", groupName));
        return result;
    }
    catch (Exception ex)
    {
        WriteExceptionLog(ex);
        return false;
    }
}
```
### ダウンロードグループの自動ダウンロード ###

* アセンブリ名とダウンロードグループ名の組み合わせを指定しておきます。
* アセンブリが参照されるときにそのアセンブリが存在しない場合は AppDomain.AssemblyResolve が発生しますが、このイベントに渡されたアセンブリ情報からダウンロードグループを特定してダウンロードし、ダウンロードされたアセンブリを返してアプリケーションの実行が継続されるようにしています。
 
```c#

// 自動ダウンロードを有効にするには、第二引数に true を指定します
ClickOnceController clickOnce = new ClickOnceController(null, true);

// アセンブリ名とダウンロードグループ名の組み合わせを登録します
clickOnce.RegistAssemblyDownloadGroup("ClassLibrary1", "Group1");
clickOnce.RegistAssemblyDownloadGroup("ClassLibrary2", "Group2");
```
### 進捗表示 ###

* 進捗表示を行いたい場合は IClickOnceProgressNotifier インターフェースを実装したクラスのインスタンスを ClickOnceController クラスのコンストラクタで渡します。ポップアップウィンドウのような UI にする場合、Start メソッドで表示し、Complete メソッドで閉じるように実装します。

```c#:IClickOnceProgressNotifier.cs

    /// <summary>
    /// 
    /// </summary>
    public interface IClickOnceProgressNotifier
    {

        /// <summary>
        /// 
        /// </summary>
        void Start();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="progress"></param>
        void Progress(IClickOnceProgressInfo progress);

        /// <summary>
        /// 
        /// </summary>
        void Complete();

    }
```