目次

インストーラーの作成

Visual Studio Installer (インストーラー プロジェクト) は Visual Studio 2012 からサポートされなくなっていて、無償で手軽にインストーラーを作成するには Flexera 社の InstallShield LE をダウンロードして使う必要がありました。
しかし、Visual Studio Installer が復活しました。

※InstallShield LEについては、Visual Studio 2015 communityはサポートしていません。

Visual Studio Installer の入手方法

ツール - 拡張機能と更新プログラム で、installer を検索し、Microsoft Visual Studio Installer Projects Extension をダウンロード

インストーラープロジェクトの作成

インストールが完了したら、対称のソリューションをVisual Studio で開き、ファイル - 新規作成 - プロジェクト を実行し、その他のプロジェクトの種類を展開。WindowsSetup プロジェクトをソリューションに追加する。

インストールのシナリオ

DefaultLocationプロパティを固定フォルダにしてしまうとセットアップ時にフォルダ選択画面が表示されない。その場合、DefaultLocationプロパティを[WindowsVolume]\[ProductName]にする。
※ProductNameとフォルダ名が一致している場合
インストーラーでProgram Files 以外のフォルダをデフォルトにする

プロパティの設定

【画像挿入】

InstallAllUsersTrue:すべてのユーザー / False:このユーザーのみ

ファイルシステムの設定

Setupプロジェクトにて右クリックメニューにある「View」から「ファイルシステム」を選択します。

下記のファイルシステムが表示されます。

プライマリ出力

「Application Folder」を選択して右クリックメニューにある「Add」から「プロジェクト出力」を選択します。

プライマリ出力を選択し、構成を「アクティブ」のまま「OK」ボタンをクリックします。

プライマリ出力のファイルと関連したdllが出力されます。

フォルダやファイルの追加

「Application Folder」を選択して右クリックメニューにある「Add」から「Folder」や「ファイル」を選択します。
その後に必要なフォルダを作成しつつ必要なファイルを選択します。
【画像挿入】
フォルダを作成した場合、プロパティの「AlwaysCreate」を「True」にしておかないとインストール時に作成されません。
【画像挿入】

ショートカットの作成

「プライマリ出力 from XXXX」の右クリックメニューにある「Create Shortcut to プライマリ出力 from XXXX」をクリックします。
【画像挿入】

ショートカットのコピー

デスクトップ画面にショートカットを作成します。
先程作成した「Create Shortcut to プライマリ出力 from XXXX」をドラッグ&ドロップして、「User's Desktop」に持ってきます。
【画像挿入】
※「User's Desktop」の右クリックによるショートカット作成は、フォルダが開くだけのショートカットになってしまいます。
プログラムメニューも同様にします。先にフォルダを作成します。
「Create Shortcut to プライマリ出力 from XXXX」をドラッグ&ドロップして、「User's Program Menu」の配下に作成したフォルダに持ってきます。
【画像挿入】

※どちらのショートカットともプロパティの「Icon」を設定して変更します。

カスタム動作の作成

今回、アンインストール時にログファイル等が1つでもあるとフォルダが残るため、フォルダを完全削除するような仕組みとする。
CustomActionプロジェクトを作成します。
そして、カスタムアクション用にInstallクラスを継承する「MyInstall.vb」を作成し「CustomAction.dll」を出力します。
【画像挿入】

削除するか確認し、「はい」を選択したらテンポラリフォルダにコピーしたフォルダ削除バッチ(removeDir.bat)を動作させる。
※単純にフォルダ削除APIを実行するだけだと「CustomAction.dll」が削除できずにアンインストールエラーとなってしまう

MyInstall.vb
Imports System.ComponentModel
Imports System.Configuration.Install
Imports System.Windows.Forms
 
<RunInstaller(True)>
Public Class MyInstaller
    Inherits Installer
 
    ''' <summary>
    ''' コンストラクタ
    ''' </summary>
    Public Sub New()
        MyBase.New()
    End Sub
 
    ''' <summary>
    ''' アンインストール
    ''' </summary>
    ''' <param name="savedState"></param>
    Public Overrides Sub Uninstall(savedState As IDictionary)
        MyBase.Uninstall(savedState)
 
        ' CustomAction.dllがあるパス取得
        Dim path As String = Me.Context.Parameters("assemblypath")
        ' 親ディレクトリを取得
        Dim parentPath As String = System.IO.Path.GetDirectoryName(path)
 
        If parentPath.IndexOf("CrimpHelper", StringComparison.OrdinalIgnoreCase) >= 0 Then
            If System.IO.Directory.Exists(parentPath + "\Data") OrElse
                System.IO.Directory.Exists(parentPath + "\logs") Then
 
                Dim result As DialogResult =
                    MessageBox.Show(parentPath + "フォルダを10秒後に削除します。" + vbCrLf + "先に必要なデータは退避してください。", "削除確認",
                                    MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2, MessageBoxOptions.DefaultDesktopOnly)
 
                If result = DialogResult.Yes Then
                    ' 単純にフォルダ削除するとCustomAction.dllが削除できずにアンインストールエラーとなるため
                    ' バッチファイルをテンポラリフォルダにコピーして実行させる。
                    Dim tmpPath As String = System.IO.Path.GetTempPath()
                    Const REMOVE_BAT As String = "removeDir.bat"
 
                    System.IO.File.Copy(parentPath + "\" + REMOVE_BAT, tmpPath + REMOVE_BAT, True)
 
                    Dim ps As New System.Diagnostics.ProcessStartInfo
                    ps.FileName = "Cmd.exe"
                    ps.Arguments = " /c " + tmpPath + REMOVE_BAT + " " + parentPath
                    ps.WindowStyle = System.Diagnostics.ProcessWindowStyle.Minimized
                    System.Diagnostics.Process.Start(ps)
                End If
            End If
        End If
    End Sub
 
End Class
removeDir.bat
timeout 10
rmdir /S /Q %1
 
eixt

カスタム動作のプライマリ出力

「Application Folder」を選択して右クリックメニューにある「Add」から「プロジェクト出力」を選択します。
【画像挿入】
プロジェクトを「CustomAction」に変更しプライマリ出力を選択し、構成を「アクティブ」のまま「OK」ボタンをクリックします。
【画像挿入】

この際に「removeDir.bat」のファイルも「Application Folder」に追加しておきます。

カスタム動作のセット

Setupプロジェクトにて右クリックメニューにある「View」から「カスタム動作」を選択します。
【画像挿入】
「Custom Action」の右クリックメニューにある「カスタム動作の追加」で「プライマリ出力 from CustomAction」を選択します。
【画像挿入】

カスタム動作にデータを渡す

Installの「プライマリ出力 from CustomAction」のプロパティに「CustomActionData」があります。

CustomActionDataプロパティに「/name=value」という形式で指定します。ここで「name」は名前で、「value」はその値を示します。複数のデータを渡す場合は、スペース文字で区切ります。
カスタム動作にデータを渡す

例 /TargetDir=“[TARGETDIR]\”

Error 1001

インストール

インストール時に「Error 1001」によりインストールできない。
原因としてCustomActionDataプロパティに「/TargetDir=“[TARGETDIR]“」と末尾の”\”を除外したため。※二重引用符を外して末尾の“\”を除外した場合はエラーにならない。

アンインストール

アンインストール時に「Error 1001 savedState 適合しないデータが含まれており…」によりインストールできない。

原因は不明、GeekUninstallerツールで強制的にアンインストールしたことで整合性が一致しないのかも。

下記の方法でログを出力することはできる。msiexec.exe コマンドライン・オプション
エラー箇所 returned actual error code 1603

msiexec.exe /X "Setup.msi" /L*V "C:\Temp\msilog.log"

対応として、例外エラーが出ても先に進めることを利用して例外エラーをスキップさせる。

Uninstall
Public Overrides Sub Uninstall(savedState As IDictionary)
    Try
        MyBase.Uninstall(savedState)
    Catch ex As Exception
        'MessageBox.Show(ex.Message + vbCrLf & ex.StackTrace)
    End Try
 
    Dim path As String = Context.Parameters("TargetDir")
 
    ProcessBatch(path, "DelService.bat")
 
End Sub

Setup.msiとSetup.exeの違い

setup.exeは同梱のsetup.msiを実行するように作られており、msi からはフォローアップできないいくつかの処理(必須コンポーネントの.NET Framework のランタイムやWindowsInstallerの特定バージョンのインストールなど)をexe側で行う仕組みになっている。

Windows Installer がインストールされてないとmsiファイルが認識できないので、その場合はsetup.exeからの実行が必要となります。
現状Windows OSではWindows Installerが標準でインストールされているので、Setup.msiのみで良い。

アップグレードインストール

Product CodeとProduct Versionは変更して、Upgrade Codeへ変更しない。
RemovePreviousVersionsプロパティをTrueにして、以前のバージョンを削除するようにしておく。

visualstudioinstallerでは、PackageCodeは見当たらない。 PackageCode = ProductCodeが既定と考える。

サービスのインストール

何も設定しない場合、「サービス ログインの設定」のダイアログ画面が表示される。

これを表示しないようにするには下記の設定を行います。これによりサービスがインストールされローカルサービスアカウントで実行されます。

  1. ProjectInstallerを開きます
  2. ServiceProcessInstaller1を右クリックして、プロパティを選択します
  3. [アカウント]ドロップダウンの[その他]で、サービスを実行するアカウントを選択します。 例 LocalSystem

Windowsサービスのインストール時の資格情報

補足

違うバージョンをインストールする際に以前のバージョンをアンインストールしなくても上書きしてインストールしてくれる。

参照