1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【.NET】Visual Studio Installer Projectsで WindowsサービスのMSIインストーラーを作成する

1
Last updated at Posted at 2026-03-04

はじめに

.NET Windowsサービスを開発したものの、配布方法に悩んだことはありませんか?

sc.exe で手動登録するのは面倒...」「エンドユーザーに簡単にインストールしてもらいたい...」

そんな時に便利なのが Visual Studio Installer Projects を使ったMSIインストーラーの作成です。

本記事では、.NET Windowsサービス用のMSIインストーラーを ゼロから作成する手順 を、実際のコードと画面設定を交えて解説します。

この記事で分かること

  • Visual Studio Installer Projectsの導入方法
  • Self-contained配布(.NETランタイム同梱)のMSI作成手順
  • カスタムアクションによるWindowsサービスの自動登録/解除
  • トラブルシューティングとベストプラクティス

前提条件

開発環境

項目 要件
OS Windows 10/11(開発用マシン)
Visual Studio 2022以降(Community / Professional / Enterprise いずれも可)
VSワークロード 「ASP.NET と Web 開発」または「.NET デスクトップ開発」がインストール済み
.NET SDK 6.0以降がインストール済み

インストール先環境

項目 要件
OS Windows 10/11、Windows Server 2019以降
権限 管理者権限(インストール/アンインストール時に必要)
.NETランタイム 不要(Self-contained配布のため)

その他

  • 配布対象の .NET Windowsサービスプロジェクト(Worker Service)が作成済み であること
  • 本記事ではサービスの開発方法自体は扱いません

全体の流れ

手順1: Visual Studio Installer Projectsのインストール

拡張機能の導入

  1. Visual Studioを起動
  2. メニュー → 拡張機能拡張機能の管理
  3. 検索ボックスに 「Microsoft Visual Studio Installer Projects」 を入力
  4. インストール後、Visual Studioを再起動

インストールが完了すると、新規プロジェクト追加時に 「Setup Project」 テンプレートが選択可能になります。

手順2: サービスプロジェクトのPublish設定

csprojの設定

サービスプロジェクトの .csproj に以下を設定します。

<Project Sdk="Microsoft.NET.Sdk.Worker">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
    <SelfContained>true</SelfContained>
    <PublishSingleFile>true</PublishSingleFile>
    <PublishReadyToRun>true</PublishReadyToRun>
    <IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
  </PropertyGroup>
</Project>

各プロパティの役割は以下の通りです。

プロパティ 説明
SelfContained .NETランタイムを同梱(インストール先に.NET不要)
PublishSingleFile 単一EXEに統合
PublishReadyToRun ネイティブコード事前コンパイルで起動高速化
IncludeNativeLibrariesForSelfExtract ネイティブDLLも単一ファイルに含める

Publish実行

  1. ソリューションエクスプローラーでサービスプロジェクトを右クリック
  2. 発行(Publish) を選択
  3. 発行プロファイルが未作成の場合:
    • フォルダー を選択 → 次へ
    • フォルダーの場所はデフォルトのままでOK → 完了
  4. 発行プロファイル画面で すべての設定を表示 をクリックし、以下を確認:
    • 構成: Release
    • ターゲットランタイム: win-x64
    • 配置モード: 自己完結
  5. 発行 ボタンをクリック

出力フォルダに .exe と必要な依存ファイルが生成されていることを確認してください。

手順3: Setup Projectの作成

  1. ソリューションエクスプローラーで右クリック → 追加新しいプロジェクト
  2. テンプレート検索で 「Setup Project」 を選択
  3. プロジェクト名を <サービス名>Installer に設定(例: MyServiceInstaller
  4. 作成 をクリック

サービスプロジェクトがソリューションに含まれていない場合は、先にソリューションに追加しておきましょう。インストーラーから「プロジェクト出力」として参照するために必要です。

手順4: インストーラープロジェクトの構成

4.1 プロパティの設定

プロパティの開き方に注意!
Setup Projectを 左クリックで選択F4キー でプロパティウィンドウを開きます。
右クリック→プロパティ ではありません

以下のプロパティを設定します。

プロパティ 設定値例 説明
Author 会社名 作成者
Description サービスの説明 インストーラーの説明文
Manufacturer 会社名 製造元(インストールパスに使用)
ProductName サービスの製品名 製品名(表示名に使用)
Version 1.0.0 バージョン番号
TargetPlatform x64 64ビット版Windows用

ポイント: ManufacturerProductName がデフォルトのインストールパスに使われます。
C:\Program Files\[Manufacturer]\[ProductName]\

4.2 ファイルシステムエディタの設定

Application Folderにファイルを追加

  1. Setup Projectを右クリック → 表示ファイル システム
  2. Application Folder を右クリック → 追加プロジェクト出力
  3. ダイアログで以下を選択:
    • プロジェクト: サービスプロジェクト
    • 出力内容: 「項目の公開」 を選択

「プライマリ出力」ではなく 「項目の公開」 を選択してください!
「項目の公開」を選ぶことで、Self-containedのpublish成果物(EXE + すべての依存DLL)が自動的にインストーラーに含まれます。

インストールパスの変更(64bit対応)

TargetPlatformx64 に設定した場合、インストール先パスも変更が必要です。

  1. Application Folder を選択 → F4キー
  2. DefaultLocation を変更:
# 変更前(32bit用)
[ProgramFilesFolder][Manufacturer]\[ProductName]

# 変更後(64bit用)
[ProgramFiles64Folder][Manufacturer]\[ProductName]

設定ファイルの追加

Application Folder を右クリック → 追加ファイル で、appsettings.json などの設定ファイルを追加できます。

手順5: カスタムアクションの設定

カスタムアクションを使って、インストール/アンインストール時にWindowsサービスの登録/削除を自動実行します。

カスタムアクションエディタを開く

Setup Projectを右クリック → 表示カスタム アクション

Installアクションの設定

  1. インストール ノードを右クリック → カスタム アクションの追加
  2. Application Folder → サービスの .exe を選択 → OK
  3. 追加されたアクションをF4キーで設定:
プロパティ 設定値
Name InstallService
Arguments /InstallService
InstallerClass False

Uninstallアクションの設定

  1. アンインストール ノードを右クリック → カスタム アクションの追加
  2. 同様にサービスの .exe を選択
  3. プロパティを設定:
プロパティ 設定値
Name UninstallService
Arguments /UninstallService
InstallerClass False

InstallerClass は必ず False に設定してください。Trueのままだと、.NETのInstallerクラスを探そうとしてエラーになります。

手順6: サービスコードの修正

カスタムアクションから渡される引数を処理するコードを、Program.cs に追加します。

コマンドライン引数処理

using System.Diagnostics;

// コマンドライン引数チェック(MSIカスタムアクション用)
if (args.Length > 0)
{
    switch (args[0])
    {
        case "/InstallService":
            InstallWindowsService();
            return;
        case "/UninstallService":
            UninstallWindowsService();
            return;
    }
}

// --- 通常のサービス起動処理 ---
var builder = Host.CreateApplicationBuilder(args);
// ... (既存のコード)

サービスインストール処理

static void InstallWindowsService()
{
    try
    {
        var exePath = Process.GetCurrentProcess().MainModule?.FileName
            ?? throw new InvalidOperationException("実行ファイルパスを取得できませんでした");

        // サービス作成
        RunScCommand($"create \"MyService\" " +
                     $"binPath=\"{exePath}\" " +
                     $"start=delayed-auto " +
                     $"DisplayName=\"My Application Service\" " +
                     $"obj=LocalSystem");

        // サービス説明設定
        RunScCommand($"description \"MyService\" " +
                     $"\"データ処理を行うバックグラウンドサービス\"");

        // 障害時リスタート設定
        RunScCommand($"failure \"MyService\" reset=86400 " +
                     $"actions=restart/60000/restart/60000/restart/60000");

        Console.WriteLine("サービスのインストールが完了しました。");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"エラー: {ex.Message}");
        Environment.Exit(1);
    }
}

サービスアンインストール処理

static void UninstallWindowsService()
{
    try
    {
        // サービス停止
        RunScCommand("stop \"MyService\"");
        Thread.Sleep(2000); // 停止待機

        // サービス削除
        RunScCommand("delete \"MyService\"");

        Console.WriteLine("サービスのアンインストールが完了しました。");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"エラー: {ex.Message}");
        Environment.Exit(1);
    }
}

sc.exe実行ヘルパー

static void RunScCommand(string arguments)
{
    var startInfo = new ProcessStartInfo
    {
        FileName = "sc.exe",
        Arguments = arguments,
        UseShellExecute = false,
        CreateNoWindow = true
    };

    using var process = Process.Start(startInfo);
    process?.WaitForExit();
}

sc.exeの主要オプション

オプション 説明
start=delayed-auto 遅延自動起動(推奨)
start=auto 自動起動
start=demand 手動起動
obj=LocalSystem LocalSystemアカウントで実行
obj=NetworkService NetworkServiceアカウントで実行

手順7: ビルドとテスト

ビルド手順

すべてVisual Studioから実行します。

1. サービスプロジェクトのPublish

  1. ソリューションエクスプローラーでサービスプロジェクトを右クリック
  2. 発行(Publish) を選択
  3. 発行プロファイルが未作成の場合は新規作成:
    • ターゲット: フォルダー
    • 構成: Release
    • ターゲットランタイム: win-x64
    • 配置モード: 自己完結
  4. 発行 ボタンをクリック

2. Setup Projectのビルド

  1. ソリューションエクスプローラーでSetup Projectを右クリック
  2. ビルド を選択
  3. ビルド完了を待つ

必ず サービスプロジェクトのPublish → Setup Projectのビルド の順番で実行してください。
Publish前にSetup Projectをビルドすると「プロジェクト出力が見つからない」エラーになります。

ビルド成功後、Setup Projectの Release フォルダに以下が生成されます。

  • <インストーラー名>.msi ← これが配布ファイル
  • setup.exe(オプション)

テストチェックリスト

インストールテスト

  • .msi をダブルクリックしてインストール
  • インストール先フォルダにEXEと設定ファイルが配置されている
  • services.msc でサービスが登録されている
  • サービスの表示名・説明が正しい
  • サービスを起動して正常動作する

アンインストールテスト

  • コントロールパネル → プログラムと機能からアンインストール
  • services.msc でサービスが削除されている
  • インストールフォルダが削除されている

トラブルシューティング

サービスが登録されない

原因: カスタムアクションの設定ミス

確認ポイント:

  • InstallerClassFalse になっているか
  • Arguments/InstallService が設定されているか
  • Program.cs に引数処理のコードが追加されているか

ビルドエラー「プロジェクト出力が見つからない」

原因: Publish実行前にSetup Projectをビルドした

解決: サービスプロジェクトを先に dotnet publish → その後Setup Projectをビルド

インストール先が「Program Files (x86)」になる

原因: DefaultLocationに [ProgramFilesFolder] を使用している

解決:

  • TargetPlatformx64 に設定
  • DefaultLocation[ProgramFiles64Folder] に変更

appsettings.jsonがアップグレード時に上書きされる

対策: バージョンアップ時は事前にバックアップを取る運用を推奨。READMEに手順を記載しましょう。

サービスが起動しない

確認ポイント:

  • appsettings.json の構文エラーがないか
  • ログフォルダ等のパスが存在するか
  • サービスアカウントに必要な権限があるか

ベストプラクティス

バージョン管理

  • リリース時は必ずバージョン番号を更新する(メジャー.マイナー.ビルド)
  • バージョン変更時に ProductCode の自動更新を確認

設定ファイル

  • appsettings.json はテンプレートとして配布し、インストール後に手動編集する方式を推奨
  • READMEに設定手順を明記

ログ管理

  • ログ出力先は設定ファイルで変更可能にする
  • ログローテーションを実装する
  • サービスアカウントの書き込み権限を確認

テスト

  • クリーンな環境でのインストールテストを実施
  • アンインストールの完全性を確認
  • バージョンアップグレードのテストも忘れずに

Visual Studio Installer Projectsの制限事項

できること

  • ファイルのコピー・配置
  • カスタムアクション(EXE実行)
  • レジストリ登録
  • ショートカット作成
  • 基本的なUIカスタマイズ

できないこと(またはコード追加が必要)

  • 複雑な設定ダイアログ(入力フォーム)
  • 設定ファイルの自動生成
  • フォルダ権限の詳細設定
  • 自動ファイル収集(WiXのHeat.exe的な機能)

より高度なインストーラーが必要な場合は、WiX Toolset の検討をおすすめします。

まとめ

観点 評価
導入の容易さ Visual Studio拡張機能を入れるだけで即利用可能
操作性 GUIベースで直感的に設定できる
配布の利便性 .NETランタイム不要のSelf-contained配布が可能
CI/CD統合 .vdproj がバイナリ形式のためやや困難
カスタマイズ性 基本的な用途には十分、複雑な要件にはWiXを検討

Visual Studio Installer Projectsは、手軽にMSIインストーラーを作りたい という場面で最適な選択肢です。特にWindowsサービスの配布では、カスタムアクションと sc.exe を組み合わせることで、インストールからサービス登録までをワンクリックで完了できます。

この記事が、Windowsサービスの配布に悩んでいる方の参考になれば幸いです。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?