のにっき

【C#】自作アプリの自動アップデート機能

社内用にアプリを作成して配布するとき、
配布後にアップデートするのがとても面倒だったりしませんか?
作って終わりじゃないのが業務改善のメンドクサイ所ですよね。。。
今回は、自作アプリの自動アップデート機能の組み方をご紹介します。

前提条件

配布された人たちが全員アクセスできる共有フォルダがあることが条件
※このフォルダを配布先にしておくと便利

組み込み方法

アプリ内定数

まずは、作成するアプリ内に以下の定数を定義します

// バージョン情報
public const string Df_BasePath = @"アプリを置くフォルダのパス";
public const string Df_Version= "01_000";

Df_BasePath
最新バージョンのアプリを配布するフォルダのパスを記入します。
Df_Version
今、作成中のバージョンを記入します。
※このバージョンが「 Df_BasePath 」のパス内にあるファイルと一致していなかったら
 バージョンアップ処理を行う仕様になります。

アプリ起動時の処理

アプリ起動時に以下の処理を行います
①.「 Df_BasePath 」フォルダ存在判定
②.自分のバージョンと配布先のバージョンの一致判定
③.バージョンアップ処理

/*************************************************************************
    【例】フォーム起動時イベント
**************************************************************************/
private void Form1_Load(object sender, EventArgs e)
{
    // ①.フォルダ存在判定  
    if (IsFolderSonzai_Out(Df_BasePath))
    {
        // ②.自分のバージョンと配布先のバージョンの一致判定  
        string basePath = GetAllFileInFolder(Df_BasePath, "exe")[0];
        if (GetFileNameInPath(basePath, true).Split('v')[1] != Df_Version)
        {
            // ③.バージョンアップ処理
            Set_AutoUpdate(Df_BasePath);
            this.Close();
        }
    }
}

以上がイベントの内容です。細かく見ていきましょう。

①.フォルダ存在判定
/// <summary>
/// フォルダ存在判定【タイムアウト付き】
/// </summary>
/// <param name="path">検索されるフォルダパス</param>
/// <returns>検索結果( True = 存在する )</returns>
public static bool IsFolderSonzai_Out(string path)
{
    int i_Wait = 1000;   // この値【ms】だけ処理待ち
    bool exists = true;
    Thread t = new Thread
    (
        new ThreadStart(delegate ()
        {
            exists = Directory.Exists(path);
        })
    );
    t.Start();
    bool completed = t.Join(i_Wait);
    if (!completed)
    {
        exists = false;
        t.Abort();
    }
    return exists;
}

こちらが関数の中身です。
Df_BasePath 」フォルダが存在しない場合はバージョンアップを行いません。
配布先は共有フォルダとなるので、タイムアウト対策は必須です。

②.自分のバージョンと配布先のバージョンの一致判定

バージョンの比較方法ですが、配布先のファイル名にはバージョンを追記しておきます。
【例】ファイル名_v01_001.exe
現バージョンは定数で取得し、最新バージョンはファイル名から取得して比較します。

③.バージョンアップ処理
/// <summary>
/// 自動アップデート処理 ※これ実行した後、自分は閉じないとダメだよ
/// </summary>
/// <param name="PathBase">配布してるフォルダのパス</param>
public static void Set_AutoUpdate(string PathBase)
{
    //ベースパス取得して現階層にコピペ
    string basePath = GetAllFileInFolder(PathBase, "exe")[0];
    string pathAft;
    pathAft = GetFileNameInPath(basePath, false);
    pathAft = GetNowFolderPath() + "\\" + pathAft;
    File.Copy(basePath, pathAft, true);
    // Batファイル出力
    string text = "";
    text += @"@echo バージョンアップ中..." + "\r\n";
    text += @"@echo off" + "\r\n";
    text += @"timeout /t 5 /nobreak > nul" + "\r\n";
    text += @"del " + System.Reflection.Assembly.GetExecutingAssembly().Location + " > nul" + "\r\n";
    text += @"start " + pathAft + "\r\n";
    text += @"del Patch.bat > nul" + "\r\n";
    text += @"exit" + "\r\n";
    StreamWriter sw = new StreamWriter("Patch.bat", false, Encoding.GetEncoding("Shift_JIS"));
    sw.Write(text);
    sw.Close();
    // exe更新
    System.Diagnostics.Process p = new System.Diagnostics.Process();
    p.StartInfo.FileName = "Patch.bat";
    p.StartInfo.CreateNoWindow = true;
    p.Start();
    p.Close();
    p = null;
}
/// <summary>
/// ファイルのパスからファイル名を取得
/// </summary>
/// <param name="str_Path">パス</param>
/// <param name="IsKillKaku">拡張子を除くか?</param>
/// <returns></returns>
public static string GetFileNameInPath(string str_Path, bool IsKillKaku)
{
    // 「\」で分解してファイル名を取得
    string str_Ret = "";
    string[] str_Sepalate;
    str_Sepalate = str_Path.Split('\\');
    str_Ret = str_Sepalate[str_Sepalate.Length - 1];
    // 拡張子が不要なら除く
    if (IsKillKaku)
    {
        str_Sepalate = str_Ret.Split('.');
        str_Ret = str_Sepalate[0];
    }
    return str_Ret;
}
/// <summary>
/// 実行ファイルのパスを文字列で取得
/// </summary>
/// <returns>実行ファイルのパス ※ファイル名は含まない</returns>
public static string GetNowFolderPath()
{
    string str_Ret = "";
    str_Ret = System.Reflection.Assembly.GetExecutingAssembly().Location;
    str_Ret = GetFilePathFolder(str_Ret);
    return str_Ret;
}

少し長くなりましたがやっていることは簡単です。
・最新バージョンのファイルを起動ファイルと同じ階層にコピペ
・Batファイルを作成→起動
 ▼Batの内容
 ・5秒後に古いバージョンのファイル(起動ファイル)を削除
 ・自分(Batファイル)を削除
・最後に、「 this.Close(); 」で起動アプリを閉じる

以上です。
アプリのアップデート対策は必須なので、
自動アップデートで無駄な管理ヘイトをためないようにしましょう!