初めに
WindowsでGUIアプリケーションを作成する際に.NET1のWPF2は強力なツールです。
しかし、通常のdotnet buildコマンドでビルドした際に、
依存関係のライブラリが同梱されてしまい、単一の exeファイルで出力ができません。
本記事では、.NETで通常のdotnet buildコマンドを使用せずに、
WPFアプリケーションを単一の実行ファイルで出力する方法を紹介します。
経緯
以前、WPFアプリケーションを単一の実行ファイルでビルドする記事を投稿しました。
そこでは、C#3の言語バージョンがC# Ver.5に制限されるという明確な弱点がありました。
本記事では、その弱点を克服し、C# 最新言語バージョンで
WPFアプリケーションをexe 単体でビルドする方法を紹介します。
本記事における「exe 単体」とは、
Windows に標準搭載されている .NET Framework Runtime を前提とし、
追加のランタイム配布が不要であることを意味します
対象読者
- Windows 11 以降
- Visual Studio4 をインストールしたくない
- exe単体で配布できるGUIツールを作りたい
- C# に興味がある
- ビルド環境
- .NET SDK(Roslyn / csc.dll を含む)をインストール済み
- 以下の条件を満たしている
- メモ帳、VSCode5などテキストファイルを編集できるエディタがある
-
以下のファイルとフォルダが存在する
C:\Windows\Microsoft.NET\assembly\GAC_MSIL C:\Windows\Microsoft.NET\Framework64 C:\Program Files\dotnet\sdk\X.X.X\Roslyn\bincore\csc.exe ← X.X.Xはインストール済みのバージョン
開発手順
1. デスクトップにソースコードを管理するフォルダ(ルートフォルダ)を新規作成
フォルダ名は何でもよいですが、本記事では「WPFApp」にしています。

2. 参照するライブラリをテキストファイルに列挙
ルートフォルダに新規のテキストファイルを作成し、以下のテキストをコピペして、ファイル名を「reference.txt」で保存します。
C:\Windows\Microsoft.NET\assembly\GAC_MSIL\PresentationFramework\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.dll
C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Xaml\v4.0_4.0.0.0__b77a5c561934e089\System.Xaml.dll
C:\Windows\Microsoft.NET\assembly\GAC_MSIL\WindowsBase\v4.0_4.0.0.0__31bf3856ad364e35\WindowsBase.dll
C:\Windows\Microsoft.NET\assembly\GAC_MSIL\UIAutomationClient\v4.0_4.0.0.0__31bf3856ad364e35\UIAutomationClient.dll
C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.IO.Compression\v4.0_4.0.0.0__b77a5c561934e089\System.IO.Compression.dll
C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll
C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll
C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Linq\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Linq.dll
C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Runtime.Serialization\v4.0_4.0.0.0__b77a5c561934e089\System.Runtime.Serialization.dll
C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Runtime.Serialization.Json\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Runtime.Serialization.Json.dll
C:\Windows\Microsoft.NET\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
C:\Windows\Microsoft.NET\assembly\GAC_64\System.Data\v4.0_4.0.0.0__b77a5c561934e089\System.Data.dll
C:\Windows\Microsoft.NET\assembly\GAC_64\PresentationCore\v4.0_4.0.0.0__31bf3856ad364e35\PresentationCore.dll
GAC のパスは .NET Framework の更新状況により異なる場合があります
参照エラーが出る場合は、該当 DLL のバージョンを確認してください
3. C#ソースファイルを作成
ルートフォルダに新規のテキストファイルを作成し、以下のテキストをコピペして、ファイル名を「Application.cs」で保存します。
#nullable enable // null許容参照型
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
namespace MyApp.Core
{
public static class Program
{
[STAThread]
public static void Main(params string[] args)
=> new App(args).Run(); // 式本体メンバー
}
internal sealed class App : Application
{
internal string[] Args { get; private set; }
internal App(params string[] args)
{
this.Args = args;
this.ShutdownMode = ShutdownMode.OnMainWindowClose;
this.DispatcherUnhandledException += OnDispatcherUnhandledException;
}
protected sealed override void OnStartup(StartupEventArgs e)
{
this.MainWindow = new Views.MainWindow();
MainWindow?.Show(); // null演算子
}
protected sealed override void OnExit(ExitEventArgs e)
{
this.DispatcherUnhandledException -= OnDispatcherUnhandledException;
}
private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
MessageBox.Show(e.Exception.Message, "ハンドルされていないエラー", MessageBoxButton.OK, MessageBoxImage.Error);
e.Handled = true;
}
}
}
namespace MyApp.Views
{
internal sealed class MainWindow : Window
{
private Grid grid_root = new ();
internal MainWindow()
{
this.Title = "WPF ウィンドウ";
this.Content = grid_root;
var button = new Button()
{
Content = "Button",
Margin = new Thickness(40, 100, 40, 100)
};
button.Click += (sender, e) => MessageBox.Show("Hello World!");
grid_root?.Children.Add(button); // null演算子
}
}
}
4. コンパイル
PowerShellを起動し、コンソール画面に以下のコードをコピペしてEnterキーを入力する
コードの2行目は適宜ルートフォルダのパスに修正する
コードの8行目は適宜インストールしたバージョンのパスに修正する
複数行コードを張り付けで警告が表示される場合、強制的に張り付けを選択する
# 作成したルートフォルダに移動
cd "C:\Users\username\OneDrive\デスクトップ\WPFApp"
# 入力ファイル
$inputFile = Get-ChildItem -Filter "*.cs" -Recurse | ForEach-Object { $_.FullName };
# 出力ファイル(好みのアプリケーション名.exe)
$outputFile = "app.exe";
# C# コンパイラ
$cscPath = "C:\Program Files\dotnet\sdk\X.X.X\Roslyn\bincore\csc.exe" #X.X.XはインストールしたSDKのバージョン
# 参照DLL
$references = Get-Content "reference.txt" | ForEach-Object { '/reference:' + $_ };
# コンパイル
& $cscPath /target:winexe $references /out:$outputFile $inputFile
以下のメッセージが返れば、コンパイル成功です。
Microsoft (R) Visual C# Compiler バージョン XXX
Copyright (C) Microsoft Corporation. All rights reserved.
5. 実行ファイルを起動
ルートフォルダに実行ファイルがコンパイルされます。
作成された*.exeファイルをダブルクリックで開くとウィンドウを表示できます。
作成された*.exeファイルは別のフォルダへ自由に移動できます。
解説
なぜ exe 単体でビルドできるのか
通常のdotnet buildコマンドでビルドした実行ファイルは
依存関係のdllファイルが同フォルダにないと機能しません。
なぜなら、.NETはクロスプラットフォームで開発されることを前提としており、
GAC の存在を前提としないアセンブリ解決方式を採用しているためです。
そのため、相対パスでdllファイルを参照できるように
ビルド時に実行ファイルと同じフォルダにdllファイルが生成されます。
本記事では、ビルドコマンドにreference.txtでdllファイルの絶対パスをマニュアルで指定しています。
そうすることで依存関係のライブラリを同梱する必要がなくなります。
なお、reference.txtに記載のdllファイルパスは.NET Frameworkの一部であり、
すべての名前空間を網羅していません。
ほかに参照したい名前空間がGACフォルダにある場合はreference.txtに
そのdllファイルパスを追記することで使用が可能です。
まとめ
dotnet buildコマンドを使用せずに、Roslyn C#コンパイラでWPFアプリケーションを作成する最小限の手順をまとめました。
本記事の手順ではUI設計にXAMLを使用できないという欠点はありますが、単一の exeでビルドできる利点があります。
作成した単一の exeファイルを共有する際には、
共有先に標準搭載の.NET Frameworkがあれば追加の依存関係のライブラリ不要で
共有ができるという点でUXの観点で効果的だと思います。
興味があればRoslynについて調べることで本記事の理解が深まるかと思います。
前回の記事
-
.NETについて https://dotnet.microsoft.com/ja-jp/ ↩
-
WPFについて https://learn.microsoft.com/ja-jp/dotnet/desktop/wpf/overview/ ↩
-
Visual Studioについて https://visualstudio.microsoft.com/ja/vs/ ↩
-
VSCodeについて https://code.visualstudio.com/ ↩

