3流プログラマのメモ書き

元開発職→現社内SEの三流プログラマのIT技術メモ書き。 このメモが忘れっぽい自分とググってきた技術者の役に立ってくれれば幸いです。(jehupc.exblog.jpから移転中)

Visual Studioセットアッププロジェクトのカスタム動作

Visual Studioセットアッププロジェクトでの、カスタム動作の使い方です。
カスタム動作を使うとVisualStudioで作ったインストーラだけでは難しい操作が行えるみたいです。

カスタム動作の発生するイベントには、インストール時、確定時、ロールバック時、アンインストール時に指定した動作を行えることができます。

カスタム動作自体も、すでにビルドされたDLLやEXE以外にもInstallerクラスを使う方法があります。

今回の自分がしたい要件は下記の通りです。
・ソフト配布用アーカイブを解凍した一時フォルダをインストール確定後に削除。
・ソフトのデータ用ファイルを任意の場所にコピー。

上記をInstallerクラスを使った方法でやってみました。


まず、配置プロジェクトと同じソリューションにクラスライブラリプロジェクトを作成します。
そのクラスライブラリにクラスを追加するため、新しい項目から「インストーラ クラス」を選択します。
(別にインストーラクラスじゃなくてもInstallerを継承したクラスを作り、属性RunInstallerをtrueにすればいいだけみたいです。)

このクラスに中に親クラスのInstall , Commit , Rollback , Uninstallメソッドをオーバライドして、各イベント時の処理を書きます。

下記が一時フォルダ削除とデータ用ファイルコピーの処理を確定時に実行するよう記述したソースです。
(プロジェクト名は InstallClass とします。)

/// <summary>
/// Installerから呼び出されるクラス。インストール、アンインストール時のカスタム動作を記述する
/// </summary>
[RunInstaller(true)]
public partial class InstallClass : Installer

{
    public InstallClass()
    {
        InitializeComponent();
    }
    /// <summary>
    /// インストール確定時の処理をオーバーライド。

    /// アーカイブ一時解凍フォルダ削除。
    /// データファイルを既定のフォルダにコピー。
    /// </summary>
    /// <param name="savedState"></param>
    public override void Commit(System.Collections.IDictionary savedState)

    {
        base.Commit(savedState);
        try
        {
            //一時フォルダ取得(システムの一時ディレクトリ)
            string tmpDir = Path.GetTempPath();
            tmpDir += "\\解凍したフォルダ名";
            if (Directory.Exists(tmpDir))

            {
                //インストール時に使用した一時データ削除
                Directory.Delete(tmpDir, true);
            }
            //データファイルをC:\aaa\bbbフォルダにコピー
            string AppDataFld = Environment.GetEnvironmentVariable("SystemDrive");
            AppDataFld += "\\aaa\\bbb\\";
            //C:\aaa\bbbが存在しないときのみDBファイルコピー

            if (!Directory.Exists(AppDataFld))
            {
                Directory.CreateDirectory(@AppDataFld);
                //インストーラからインストールフォルダのパスを取得する
                string instDir = this.Context.Parameters["dir"];
                File.Copy(instDir + "データファイル名", AppDataFld + "データファイル名");
            }

        }
        catch
        {
            //エラー時は何もしない
        }
    }
}


あとは配置プロジェクト側の設定となります。
配置プロジェクトのファイルシステム→アプリケーションフォルダにて、InstallClassプロジェクト(Installerクラスのあるプロジェクト)をプライマリ出力します。

配置プロジェクトを右クリック→カスタム動作で、確定を右クリック→カスタム動作の追加を選択し、上記で指定したアプリケーションフォルダのInstallClassのプライマリ出力を選択します。

InstallClassにインストール先のアプリケーションフォルダを教えるために、カスタム動作→InstallClassのプライマリ出力の下記プロパティに値をしています。また、InstallerClassのプロパティもTrueにしないといけません。(Exe等をカスタム動作にするときはInstallerClassのプロパティをFalseにします)

CustomActionDataプロパティ:/dir="[TARGETDIR]\"
InstallerClassプロパティ:True

ここで指定した値をInstallClassで取得するときは下記のようにおこなえます。

this.Context.Parameters["dir"];


これで試してみたのですが、「InstallStateが見つかりません」というエラーが発生しました。
いろいろ探した結果こちらに解決策が書かれてました。
どうやらインストール確定時だけ動かしたいときでも、配置プロジェクトのカスタム動作の「インストール」にもInstallクラスのプライマリ出力を加えないといけないようです。


ただ、いろいろ試してみたところ、アンインストールしたときに*.InstallStateファイルが残るときがあるみたいです。
詳しく調べてみる必要がありそうです。

参考(カスタム動作以外にも、VisualStudioセットアッププロジェクトの使い方のリンクも入れてます)
VSセットアッププロジェクトのプロパティ
.NET Visual Studio Installerを使ったインストーラの作成 VSでのインストーラ作成がわかりやすく解説されてます。
カスタム動作エディタの使い方
チュートリアル : カスタム動作を使用した、インストール時のアセンブリのプリコンパイル MS公式のチュートリアルです