更新履歴(3件・最終更新 2026年08月02日)
この記事に加えた変更の記録です。アーカイブした更新前のバージョンは、DOI付きの固定URLから読めます。
- 記事の冒頭に「この記事の知識マップ」節を追加しました。本文で扱っている概念とその関係を、要約・図・詳細ページへのリンクにまとめたものです。本文の主張は変えていません。
- 外部レビュー(1283件)への対応として本文を更新しました。個々の変更内容は、この下の履歴を参照してください。
- `/MT`と`/MD`の対比表と`dotnet publish`の3パターンを追加し、レベルA〜Dを階層図にしました。用語表を追加し、single-fileの起動時展開の挙動、`Assembly.Location`が空文字列になるなどのAPI非互換、Native AOTの制約(動的読み込み不可、C++/CLI不可、組み込みCOM不可)、EvergreenとFixed Versionの対比を追記しました。
- 初版公開
この記事を引用する(DOI: 10.5281/zenodo.21589706)
この記事はZenodoにアーカイブされています。常に最新版へ解決されるDOIと、いま表示している版に固定されたDOIの両方を下に示します。
小村 豪(2026)「Windowsアプリの1ファイル配布 - シングルバイナリとOS依存の限界」合同会社小村ソフト. https://doi.org/10.5281/zenodo.21589706 https://staging.comcomponent.com/blog/2026/03/19/003-windows-single-binary-and-os-dependencies/
- DOI(最新版)
- 10.5281/zenodo.21589706
- DOI(この版)
- 10.5281/zenodo.21732728
この記事は、「Windows では、どこまでを シングルバイナリ と言っていいのか」という雑談から始まりました。きっかけになった投稿は次のものです。ここを読み飛ばしても、以降は単独で読めるように書いています。
Windows で「できれば 1 ファイルで配りたい」は、かなり普通の要望です。社内ツール、装置連携ツール、監視端末、オフライン環境、インストーラを極力避けたい現場では、シングルバイナリ化 はとても魅力があります。
ただ、この話は最初に切り分けておかないと、だいたい途中で噛み合わなくなります。Windows で言う「シングルバイナリにしたい」には、実は 4 つの別々の話が混ざりやすいからです。
- 配布物を 1 個にしたい
- .NET や Visual C++ のランタイムを事前インストール不要にしたい
- インストーラや管理者権限なしで置くだけで動かしたい
- 対象 Windows の違いに依存したくない
この 4 つは同じではありません。実務的には、こう考えるのが一番ずれません。
配布物を 1 EXE に寄せることはかなりできる。 でも、対象 Windows への依存をゼロにすることはできない。
この記事では、その境界線を Windows アプリの実務向けに整理します。
1. まず結論
最初に結論だけまとめると、こうです。
- 普通のデスクトップ EXE なら、かなり高いところまで single binary 化できます
- ただし、1 EXE にできること と 対象 Windows に依存しないこと は別です
- Shell 拡張、Windows サービス、ドライバ、WebView2、WinUI 3 の一部は、ファイル数より OS へ何を登録し、何を前提にするか のほうが本題になりやすいです
- 実務で一番大事なのは、single binary 化したいのか、インストーラ不要にしたいのか、OS 依存を減らしたいのか を分けて決めることです
言い換えると、Windows での線引きはこうなります。
- 配布物を 1 個に寄せる: かなり可能
- 追加ランタイムを抱え込む: かなり可能
- xcopy 配布に寄せる: アプリ種別による
- 対象 Windows 側の依存を消す: 不可能
1.1 この記事で使う用語
先に、以降で何度も出てくる言葉だけ整理しておきます。
| 用語 | 読み方・展開 | この記事での意味 |
|---|---|---|
| UCRT | Universal C Runtime | Visual Studio 2015 で C ランタイムが分割されてできた、標準 C ライブラリ部分。Windows 10 以降では OS の構成要素として同梱されています |
| VC++ 再頒布可能パッケージ | Visual C++ Redistributable | UCRT 以外の Visual C++ ランタイム DLL を対象機に入れるためのインストーラ |
| framework-dependent | 対象環境の .NET に依存する配布 | 対象機に .NET ランタイムが入っている前提の配布形態 |
| self-contained | .NET ランタイム同梱 | アプリ側が .NET ランタイム一式を抱えて配る形態 |
| single-file | 単一ファイル発行 | 配布物を 1 個の EXE にまとめる .NET の発行オプション |
| Native AOT | Native Ahead-Of-Time | 発行時に IL をネイティブコードへ事前コンパイルする配布形態。実行時に JIT を使いません |
| app-local 配布 | アプリ隣接配布 | DLL を EXE と同じフォルダーに置き、そのアプリからだけ使う配り方 |
| xcopy 配布 | コピーするだけの配布 | インストーラも管理者権限も使わず、フォルダーごと置けば動く配り方 |
| SCM | Service Control Manager、サービス制御マネージャー | Windows サービスの登録・起動・停止を管理する OS の仕組み |
| UAC | User Account Control、ユーザー アカウント制御 | 管理者権限への昇格を制御する Windows のセキュリティ機構 |
| Shell 拡張 | シェル拡張 | エクスプローラーなどのプロセスへ読み込まれて動く COM コンポーネント |
| Evergreen | 常時更新方式 | WebView2 Runtime を自動更新される共有の 1 本に任せる配布モード |
| Fixed Version | 版固定方式 | 特定バージョンの WebView2 Runtime を自分のアプリに同梱する配布モード |
| arch | architecture、CPU アーキテクチャ | x86 / x64 / Arm64 のこと |
この記事の知識マップ
Windowsアプリは配布物を1個のEXEへ寄せること自体はかなり可能で、.NETのself-containedやsingle-file・Native AOT、C/C++の静的リンク(/MT)が使えるが、OSバージョンやアーキテクチャ、システムDLL、セキュリティモデルへの依存は残り、対象Windowsへの依存をゼロにはできない。self-containedとframework-dependentは排他的な選択肢で、Native AOTは実質single-fileとして動く代わりに組み込みCOMなどが使えなくなる。WebView2はEvergreenかFixed Versionかでランタイムの提供方法を選ぶ必要があり、WinUI 3はpackaged(MSIX)かunpackagedかでPublishSingleFileの可否が変わる。Shell拡張やドライバーは登録・署名の設計が本題になるため、無理に1EXEへ圧縮するよりapp-local配布のほうが保守しやすいことが多い。
flowchart LR
accTitle: Windowsアプリのシングルバイナリ化の知識マップ
accDescr: 配布物を1個にすることとOSへの依存を消すことが別の軸であること、.NETとC/C++それぞれの単一ファイル化手段、WebView2やWinUI 3のようなホスト統合が配布方式そのものを制約することを示す図
single_binary_packaging["シングルバイナリ化(1ファイル配布)"]
windows_os_dependency["対象Windowsへの依存"]
self_contained_deployment["self-contained発行"]
framework_dependent_deployment["framework-dependent発行(.NET)"]
native_aot["Native AOT"]
builtin_com_interop["built-in COM interop"]
single_file_publish["single-file発行(.NET)"]
static_linking_crt["CRTの静的リンク(/MT)"]
dynamic_linking_crt["CRTの動的リンク(/MD)"]
vc_redistributable["Visual C++ 再頒布可能パッケージ"]
ucrt["UCRT(Universal C Runtime)"]
msix_packaging["MSIXパッケージング(packaged)"]
winui3["WinUI 3 / Windows App SDK"]
webview2["Microsoft Edge WebView2"]
webview2_runtime["WebView2 Runtime"]
webview2_evergreen["Evergreen配布モード(WebView2)"]
webview2_fixed_version["Fixed Versionランタイム"]
shell_extension["シェル拡張(エクスプローラー拡張)"]
com["COM(コンポーネントオブジェクトモデル)"]
driver_signing["ドライバー署名"]
driver_package["ドライバーパッケージ"]
app_local_deployment["app-local配布(アプリ隣接配布)"]
single_binary_packaging -->|"前提とする"| windows_os_dependency
self_contained_deployment -->|"両立しない"| framework_dependent_deployment
native_aot -->|"両立しない"| builtin_com_interop
single_file_publish -.->|"前提とする"| windows_os_dependency
native_aot -.->|"前提とする"| windows_os_dependency
static_linking_crt -->|"両立しない"| dynamic_linking_crt
dynamic_linking_crt -->|"前提とする"| vc_redistributable
static_linking_crt -->|"利用する"| ucrt
dynamic_linking_crt -->|"利用する"| ucrt
msix_packaging -.->|"両立しない"| single_file_publish
winui3 -.->|"利用する"| msix_packaging
webview2 -->|"前提とする"| webview2_runtime
webview2_runtime -->|"で構成できる"| webview2_evergreen
webview2_runtime -->|"で構成できる"| webview2_fixed_version
webview2_evergreen -->|"両立しない"| webview2_fixed_version
shell_extension -->|"利用する"| com
driver_signing -->|"前提とする"| driver_package
driver_package -->|"用いるのは非推奨"| single_binary_packaging
app_local_deployment -->|"推奨される対応"| single_binary_packaging
self_contained_deployment -.->|"前提とする"| windows_os_dependency
single_binary_packaging -.->|"利用する"| static_linking_crt
webview2 -.->|"前提とする"| windows_os_dependency
図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全22件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle
2. 「シングルバイナリ」は 4 段階に分けて考える
段階を先に図にすると、こうなります。下から順に難しくなり、一番上は Windows では到達できません。
flowchart BT
A["レベル A: 配布物が 1 個<br/>かなり可能"]
B["レベル B: 言語ランタイムの事前インストールが不要<br/>かなり可能"]
C["レベル C: インストールや登録が不要<br/>アプリ種別による"]
D["レベル D: 対象 Windows に依存しない<br/>Windows では到達できない"]
A --> B
B --> C
C --> D
「シングルバイナリにしたい」という話は、この 4 段のうちどこを指しているのかで、必要な作業がまったく変わります。
2.1 レベル A: 配布物が 1 個
一番表面的なのはこれです。
- メールで 1 個送れる
- USB に 1 個置けばよい
- 展開先に
app.exeだけ置く
これは 見た目の配布単位 の話です。実際には起動時に一時展開していても、OS 側の DLL に依存していても、この条件だけなら満たせます。
2.2 レベル B: 言語ランタイムの事前インストールが不要
次は、対象マシンにあらかじめ .NET ランタイムや VC++ 再頒布可能パッケージを入れなくても動く状態です。
- C/C++ の静的リンク
- .NET の self-contained
- .NET の single-file
- .NET Native AOT
このレベルになると、「単体で持っていける」感じはぐっと強くなります。
2.3 レベル C: インストールや登録が不要
ここから急に難しくなります。
単なる EXE なら、置くだけで動くことがあります。でも、こういうものは別です。
- Shell 拡張
- Windows サービス
- カスタム URL スキームやファイル関連付け
- ドライバ
- Explorer や Office など、他プロセスに読み込まれるコンポーネント
この領域は ファイルを置くだけ では済みません。OS 側の登録や、ホスト側との結線が必要です。
2.4 レベル D: 対象 Windows に依存しない
これは Windows では無理です。
Windows アプリは最終的に Windows の API、ローダー、セキュリティモデル、デバイススタックの上で動くからです。single binary 化できるのは、アプリ側の責任範囲まで です。OS 自体まで持っていくわけではありません。
3. かなり 1 EXE にしやすい領域
Windows でも、比較的 1 EXE に寄せやすいアプリはあります。
- 単独起動されるデスクトップツール
- EXE 自身が UI と処理を持つ業務アプリ
- 通信、ファイル処理、ログ収集、監視、装置制御のようなツール
- Explorer や Office のホスト統合を必要としないもの
- Web ランタイムを前提にしない UI
このタイプなら、アプリ本体に含めやすいものは多いです。
- 自前コード
- リソース
- マニフェスト
- 既定設定
- テンプレートデータ
- 一部のサードパーティ ライブラリ
- 言語ランタイム本体
さらに、DLL を完全に EXE 内へ埋め込まなくても、EXE の隣に DLL を置く app-local 配布 は Windows では普通に有力です。実務では、
app.exe1 個- または
app.exe+ 隣接 DLL 数個 - ただしインストーラ不要、管理者権限不要、xcopy で配れる
この形のほうが、無理に 1 EXE へ圧縮するより保守しやすいことが珍しくありません。
4. 1 EXE でも消えない Windows 依存
「EXE 1 個なら、対象 Windows に依存しない」と思ってしまうと、ここで事故ります。実際には、1 EXE にしても残る依存があります。
4.1 OS バージョン依存
Windows API にはそれぞれ最低サポート OS があります。x64 / Arm64 の違いもあります。つまり単一 EXE にしても、
- Windows 10 まで動くのか
- Windows 11 前提なのか
- Windows Server でも動かすのか
- x86 / x64 / Arm64 のどれを対象にするのか
は最初に固定する必要があります。
4.2 システム DLL 依存
こちらが 1 EXE にしたつもりでも、実行時には当然ながら OS 提供のコンポーネントを使っています。
kernel32.dlluser32.dlladvapi32.dll- COM 基盤
- サービス制御基盤
このあたりは Windows 側の責任範囲です。
4.3 セキュリティモデル依存
- UAC
- ファイル ACL
- サービス制御マネージャ
- レジストリ
- ドライバ署名ポリシー
こうしたものは、アプリが単独で抱え込めません。
4.4 ホストやランタイム依存
単独起動 EXE ではなく、何かのホストに載る設計だと依存は一気に増えます。
- WebView2 を使う: WebView2 Runtime が要る
- WinUI 3 / Windows App SDK を使う: 配布モードの整理が要る
- Shell 拡張を作る: Explorer 側への登録が要る
つまり、UI や統合の選択が、そのまま配布の難しさになる ことが多いです。
5. 技術別に見る現実的な落としどころ
5.1 ネイティブ C/C++
ネイティブ C/C++ は、single binary 化の自由度が高い側です。静的リンクを選ぶ余地があり、単独起動 EXE ならかなり寄せやすいです。
ただし、全部を 1 ファイルに押し込むことより、
- UCRT や VC++ ランタイムをどうするか
- サードパーティ DLL を app-local に置くか
- 対象 CPU / OS をどこまで絞るか
のほうが実務上は重要です。
入口: /MT と /MD の違い
MSVC で「ランタイムを抱えるかどうか」を決めているのは、実質このコンパイラ オプションです。
| オプション | リンクされるもの | 対象機に必要なもの |
|---|---|---|
/MD |
ucrt.lib と vcruntime.lib という DLL 用インポート ライブラリ。実行時は ucrtbase.dll と vcruntime<バージョン>.dll を使う |
UCRT と VC++ ランタイム |
/MT |
libucrt.lib、libvcruntime.lib、libcmt.lib を静的リンク |
追加ランタイムなし |
最小の試し方は、Developer Command Prompt でこれだけです。
cl /std:c++17 /EHsc /O2 /MT app.cpp /Fe:app.exe
ここで注意したい点が 3 つあります。
- UCRT は Visual Studio 2015 で C ランタイムが分割されたときに Windows の構成要素 になり、Windows 10 以降には OS の一部として同梱されています。それより前の Windows を対象にするなら、
vcredistでの再頒布が要ります。 - DLL 側を
/MTで作るのは推奨されません。 静的リンクした CRT は状態がその DLL の中だけに閉じるため、EXE と DLL でメモリ確保やロケール、_set_se_translatorの効き方がずれます。「EXE は/MT、同梱 DLL も/MT」と雑に揃えると、境界をまたいだ確保・解放で事故ります。 /clrと/MTは併用できません。C++/CLI が混ざるなら/MD側です。
5.2 .NET
.NET は single-file、self-contained、Native AOT があるので、見た目の配布単位はかなり小さくできます。
ただし区別は必要です。
- framework-dependent: 対象環境の .NET に依存
- self-contained: .NET ランタイムを抱える
- single-file: 配布物を 1 つへ寄せる
- Native AOT: さらに起動時依存を減らすが、機能制約もある
「single-file だから OS 依存が減る」わけではありません。減るのは主に アプリ配布物のまとまり です。
入口: dotnet publish の 3 パターン
まず動かして違いを見るなら、この 3 本で足ります。
rem 1. framework-dependent + single-file: 対象機に .NET ランタイムが要る
dotnet publish -c Release -r win-x64 --self-contained false -p:PublishSingleFile=true
rem 2. self-contained + single-file: .NET ランタイムを抱える
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true
rem 3. Native AOT: csproj に PublishAot を書いたうえで発行する
dotnet publish -c Release -r win-x64
PublishSingleFile と PublishAot は、コマンドラインではなく csproj に書くほうが公式の推奨です。ビルド中の互換性解析が有効になり、発行してから気づく事故が減るためです。
<PropertyGroup>
<PublishSingleFile>true</PublishSingleFile>
<PublishAot>true</PublishAot>
</PropertyGroup>
なお RuntimeIdentifier を指定すると SelfContained は既定で true になります。framework-dependent にしたいときは --self-contained false を明示してください。Native AOT を Windows で発行するには、Visual Studio の C++ によるデスクトップ開発 ワークロードが要ります。
トレードオフ: 「1 個にする」は無料ではない
ここは「できる」だけ書くと事故るので、代償のほうも並べておきます。
single-file の起動時展開
- 既定では、バンドルされるのは managed DLL だけです。起動時にメモリ上へ読み込まれ、フォルダーへは展開されません。ランタイム本体のネイティブ バイナリは別ファイルのまま残ります。
- ネイティブ バイナリまで 1 個に含めたいなら
IncludeNativeLibrariesForSelfExtract、全部を実行前に展開させるならIncludeAllContentForSelfExtractを使います。どちらも「展開してから起動する」動作 になり、Windows では%TEMP%\.netの下へ展開されます。DOTNET_BUNDLE_EXTRACT_BASE_DIRで場所を変えられますが、権限の違うユーザーやサービスから書ける場所にしないでください。 EnableCompressionInSingleFileを有効にすると EXE はかなり小さくなりますが、起動時にメモリ上で伸長するぶん起動が遅くなります。公式も「使う前にサイズと起動コストの両方を測れ」という書き方をしています。影響はアプリによって大きく変わります。
single-file の API 非互換
配布物を 1 個にすると、ファイルパス前提のコードが静かに壊れます。
| API | single-file での挙動 |
|---|---|
Assembly.Location |
空文字列を返す |
Assembly.CodeBase |
PlatformNotSupportedException |
Assembly.GetFile |
IOException |
Module.Name |
<Unknown> という文字列を返す |
EXE の隣のファイルを触るなら AppContext.BaseDirectory、実行ファイルのパスが要るなら Environment.ProcessPath に寄せます。サードパーティ製ライブラリが内部でこれらを使っていることもあるので、single-file 化したら 一度は実機で起動確認 が要ります。
Native AOT の機能制約
「起動が速く、ランタイム不要」の代わりに、次が使えなくなります。
Assembly.LoadFileのような 動的読み込みSystem.Reflection.Emitのような 実行時コード生成- C++/CLI
- Windows では 組み込み COM
- トリミングが必須なので、トリミングの制約もそのまま乗ります
- 実質 single-file なので、上の API 非互換もそのまま乗ります
System.Linq.Expressionsは常にインタープリタ動作になり、実行時生成コードより遅くなります
対象プラットフォームも決め打ちです。.NET 8 では Windows は x64 と Arm64、.NET 9 以降で x86 が加わりました。「AnyCPU で 1 個」という発想は最初から成立しません。
5.3 WebView2
WebView2 を採用すると、single binary の難しさはがらりと変わります。ここでの本題は EXE の数ではなく、WebView2 Runtime をどう扱うか です。
「1 EXE にできるか」より先に、考えるべき問いがあります。
- Runtime を既存環境前提にするか
- Evergreen を使うか
- Fixed Version を同梱するか
- オフライン配布でどこまで責任を持つか
Evergreen と Fixed Version の違いは、ざっくりこうです。
| Evergreen | Fixed Version | |
|---|---|---|
| 更新するのは | Microsoft。自動更新される | 自分。アプリの更新に合わせて入れ替える |
| 端末上の実体 | 全アプリで 1 本を共有 | アプリごとに同梱 |
| 配布サイズ | ほぼ増えない | 250 MB を超える |
| 入れ方 | Bootstrapper は約 2 MB で、必要なぶんをダウンロードする。オフラインなら Standalone Installer を同梱する | 展開したバイナリ一式をアプリと一緒に配る |
| 既存の制約 | 端末に入っているか、起動前に確認する必要がある | ネットワーク経路や UNC パスからは実行できない |
Evergreen Runtime は Windows 11 には OS の一部としてプリインストールされています。ただし Windows 10 側には入っていない端末も残るため、Microsoft 自身も「Evergreen を選んでも Runtime は配布したほうがよい」という書き方をしています。つまり アプリ側で有無を確認して足りなければ入れる 手順は、どちらにしても要ります。Fixed Version は「更新のタイミングを自分で握れる」代わりに、配布物が数百 MB 増えます。1 EXE の話とは別に、ここを先に決める必要があります。
5.4 WinUI 3 / Windows App SDK
WinUI 3 も、採用した時点で配布要件が変わります。UI 技術の選択が、そのまま配布方式の選択になります。
具体的には、決めるものが 2 軸あります。
- packaging: packaged(MSIX)か、外部の場所を指す packaged か、unpackaged か
- runtime: framework-dependent か self-contained か
そして single binary の観点で効いてくるのが、この組み合わせ制約です。
PublishSingleFileが使えるのは unpackaged かつ self-contained の WinUI 3 アプリだけで、Windows App SDK 1.5 以降が要ります。- packaged なアプリ、および外部の場所を指す packaged なアプリでは
PublishSingleFileは使えません。 - unpackaged にすると package identity を失うので、通知、バックグラウンド タスク、ファイル関連付け、コンテキスト メニュー拡張といった、package identity を前提にした Windows 機能は使えなくなります。
つまり WinUI 3 では、「1 EXE にする」と「Windows の拡張機能を使う」が 正面からぶつかります。single binary を最優先にするなら、最初に UI 技術の前提を見直す ほうが早いことがよくあります。
6. 本質的に「登録・依存」が必要な領域
6.1 Shell 拡張
Explorer に読み込まれる Shell 拡張は、単なる「置くだけ EXE」とは別物です。ここはファイル数より、Explorer へどう登録するか が本題です。
6.2 Windows サービス
サービス本体の exe 自体は 1 ファイルにできても、配布は別問題です。
- SCM への登録
- 権限
- 起動アカウント
- 復旧設定
を考える必要があります。つまりサービスは、「1 EXE にする」より「どうインストールするか」を詰める領域です。
6.3 ドライバ
ドライバはさらに明確です。INF、署名、インストール手順まで含めて成立するので、single binary の土俵に最初から乗りにくいです。
7. 実務での判断表
ざっくり判断するなら、この表が使いやすいです。
| 作りたいもの | 1 EXE 現実度 | 先に考えるべきこと |
|---|---|---|
| 単独起動の Win32 / C++ ツール | 高い | 静的リンク、対象 OS / arch |
| 単独起動の WinForms / WPF ツール | 高い | self-contained、single-file、Native AOT の適性 |
| WinUI 3 / Windows App SDK アプリ | 中 | 配布モード、追加依存 |
| WebView2 ベースのデスクトップ UI | 低から中 | Runtime の配布方式 |
| Explorer 右クリック拡張やプレビュー | 低い | COM / レジストリ登録 |
| Windows サービス | 中 | SCM 登録、権限、更新手順 |
| ドライバ同梱アプリ | 低い | INF、署名、インストール |
この表で一番大事なのは、「バイナリの数」と「配布の責任範囲」は別 だと分かることです。
8. 配布設計で先に決めるべきこと
single binary 化を成功させたいなら、実装より前に決めておきたいことがあります。
8.1 何を 1 個にしたいのかを決める
- 配布物を 1 個にしたいのか
- ランタイム事前インストールをなくしたいのか
- インストーラ不要にしたいのか
- オフライン更新を簡単にしたいのか
この答えによって、選ぶ技術は変わります。
8.2 最低サポート Windows と arch を最初に固定する
single-file も Native AOT も、基本的に OS / architecture specific です。ここを曖昧にしたまま「とにかく 1 ファイルで」と進めると、最後に API 不足や runtime 不一致で詰まります。
8.3 「同梱するもの」と「Windows に任せるもの」を明文化する
実務では、この表を書いておくだけでかなり事故が減ります。
- アプリに同梱するもの
- 本体 exe
- 自前 DLL
- 設定テンプレート
- self-contained runtime
- Windows に任せるもの
- システム DLL
- OS API
- SCM / レジストリ / Explorer
- ドライバ基盤
- 別途前提とするもの
- WebView2 Runtime
- VC++ Redistributable
- Office / Excel
- 専用ドライバ
8.4 single binary を優先するなら、ホスト統合を減らす
これはかなり効きます。
- Shell 拡張をやめて普通の EXE にする
- サービス化せず、タスクスケジューラや明示起動で済ませる
- WebView2 ではなくネイティブ UI を使う
- COM は自プロセス内で閉じる
要するに、OS に「読み込ませる」「登録する」設計を減らす ほど、single binary に近づきます。
9. まとめ
Windows における single binary 化は、かなりのところまで可能です。ただし、行き着くところはこの一文です。
アプリを 1 EXE にすることはできる。 でも、そのアプリが依存する Windows まで 1 EXE にすることはできない。
特に覚えておきたい点を 5 つ挙げます。
- 単独起動の普通の EXE なら、相当なところまで 1 ファイル配布へ寄せられる
- C/C++ の静的リンク、.NET single-file、Native AOT は有力
- ただし OS バージョン、arch、システム DLL、セキュリティモデルへの依存は消えない
- Shell 拡張、サービス、ドライバ、WebView2、WinUI 3 の一部は、OS 登録や追加ランタイムの話が本体になる
- single binary の成否は、「何を 1 個にしたいのか」を最初に切り分けることで決まる
もし single binary を強く優先するなら、技術選定の時点で OS との結合度を下げる 方向で設計したほうが、はるかに成功しやすいです。
10. 参考資料
- Microsoft Learn, Create a single file for application deployment
- Microsoft Learn, Native AOT deployment overview
- Microsoft Learn, C runtime (CRT) and C++ standard library (STL) lib files
- Microsoft Learn, Dynamic-link library search order
- Microsoft Learn, Targeting your application for Windows
- Microsoft Learn, Creating Registration-Free COM Objects
- Microsoft Learn, Registering Shell Extension Handlers
- Microsoft Learn, CreateServiceW function
- Microsoft Learn, Overview of INF Files
- Microsoft Learn, Windows driver signing tutorial
- Microsoft Learn, Distribute your app and the WebView2 Runtime
- Microsoft Learn, Windows App SDK deployment guide for self-contained apps
- Microsoft Learn, Package and deploy Windows apps overview
- Microsoft Learn, Packaging overview - Windows apps
- Microsoft Learn, Evergreen vs. fixed version of the WebView2 Runtime
関連する記事
同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。
Windowsアプリ 外注・受託開発を依頼する前に整理したいこと
Windowsアプリの外注・受託開発を依頼する前に、既存ソフト改修、装置連携、COM/ActiveX、配布・更新、保守の整理ポイントを解説します。
IEモードの次はWebView2でいいのか ── ActiveXが動かない制約と現実的な移行設計
WebView2の基本構造、EvergreenとFixed Versionの配布戦略、ユーザーデータフォルダーの罠、ネイティブとWebの連携方法、そして「ActiveXは動かない」という制約を踏まえたIEモード依存システムからの現実的な移行順序を、社内システム目線で整理します。
ClickOnce とは何か - 仕組み、更新、向いている場面・向いていない場面を実務目線で整理
.NET の Windows デスクトップアプリ配布で使われる ClickOnce について、マニフェスト、更新、キャッシュ、署名、向いている案件・向いていない案件を Mermaid 図つきで整理します。
Windowsアプリで子プロセスを安全に扱うチェックリスト
Windows アプリで子プロセスを安全に扱うには、起動 API よりもプロセス木の所有権と終了手順の設計が重要です。Job Object、終了伝播、標準入出力、watchdog を整理します。
マルチスレッドの実務ベストプラクティス C++編 ── RAIIとjthreadで事故を構造から消す
C++のマルチスレッドはデータ競合が未定義動作になる世界。std::threadのデストラクタの罠、jthreadとstop_tokenによる停止設計、scoped_lockのデッドロック回避、atomicの正しい位置づけ、Win32同期APIとの使い分けまで整理します。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。
このテーマがつながるサービス
この記事は次のサービスページにつながります。近い入口からご覧ください。
Windowsアプリ開発
Windows アプリの配布では、single-file 化、ランタイム同梱、WebView2 や WinUI の採用判断、サービス化の是非まで含めて設計したほうが後戻りを減らせます。
技術相談・設計レビュー
『1 EXE にしたい』という要望は、配布単位、OS 依存、登録要否、更新責任を分けて整理すると判断しやすくなります。
よくある質問
この記事のテーマについて、相談時によくある質問をまとめています。
- Windowsアプリを1つのEXEファイルだけで配布できますか?
- 単独起動されるデスクトップツールであれば、かなり高いところまで可能です。C/C++の静的リンク、.NETのself-containedやsingle-file、Native AOTを使えば、配布物を1個のEXEに寄せられます。ただし、1EXEにできることと、対象Windowsに依存しないことは別の話で、OSバージョン・アーキテクチャ・システムDLL・セキュリティモデルへの依存は消えません。
- .NETのsingle-fileにすればOS依存はなくなりますか?
- なりません。single-fileで減るのは主にアプリ配布物のまとまりで、OS依存が減るわけではありません。framework-dependentは対象環境の.NETに依存し、self-containedは.NETランタイムを抱え、Native AOTは起動時依存をさらに減らせますが機能制約もあります。single-fileもNative AOTも基本的にOS・アーキテクチャ固有なので、最低サポートWindowsとarchは最初に固定する必要があります。
- 1EXEにしにくいのはどんなアプリですか?
- Shell拡張、Windowsサービス、ドライバ、WebView2ベースのUI、WinUI 3の一部です。これらはファイル数よりも、OSへの登録や追加ランタイムの扱いが本題になります。たとえばShell拡張はExplorerへの登録が必要で、サービスはSCMへの登録・権限・起動アカウントの設計が要り、ドライバはINFと署名まで含めて成立するため、置くだけの配布は成立しません。WebView2はWebView2 Runtimeの配布方式を先に決める必要があります。
- 無理に1EXEへ埋め込むより良い配布方法はありますか?
- EXEの隣にDLLを置くapp-local配布が有力です。app.exeに隣接DLL数個という形でも、インストーラ不要・管理者権限不要でxcopy配布できるなら、無理に1EXEへ圧縮するより保守しやすいことが珍しくありません。重要なのは、配布物を1個にしたいのか、ランタイムの事前インストールをなくしたいのか、インストーラを不要にしたいのかを最初に切り分けることです。