.NET 8 の .NET MAUI(Windows)アプリを MSIX ではなく「アンパッケージ」で配布していた場合、.NET 9 へ移行すると publish コマンドをどう変えるべきか迷いがちです。本記事では WindowsPackageType=None を軸に、.NET 9 での具体的な dotnet publish 例と RID の選び方を整理します。
結論:.NET 9 でも「WindowsPackageType=None」のアンパッケージ配布はほぼ同じ書き方
.NET 8 の .NET MAUI(Windows)で WindowsPackageType=None を指定してアンパッケージ配布していた場合、.NET 9 への移行で大きく書き換える必要はありません。基本は Target Framework Moniker(TFM)を net8.0 から net9.0 に置き換えるだけで、dotnet publish の指定は引き続き利用できます。
最小の移行ポイント
| 項目 | .NET 8 | .NET 9 | コメント |
|---|---|---|---|
| Windows の TFM | net8.0-windows10.0.19041.0 | net9.0-windows10.0.19041.0 | まずここを差し替えるのが最優先 |
WindowsPackageType=None | 利用可 | 利用可 | アンパッケージ出力の要 |
RuntimeIdentifierOverride | 利用可 | 利用可 | 配布対象の Windows/CPU に合わせて RID を選ぶ |
そもそも「アンパッケージ配布」とは何か
.NET MAUI の Windows アプリは、配布形態として大きく MSIX(パッケージ) と アンパッケージ(フォルダー配布) のどちらかを選べます。アンパッケージ配布は、ざっくり言うと「publish で出てきたフォルダー一式を配る」方式です。社内配布や、既存の配布基盤(SCCM/Intune/社内ポータル/自社アップデーター)に乗せたい場合に選ばれやすい構成です。
| 観点 | MSIX(パッケージ) | アンパッケージ(WindowsPackageType=None) |
|---|---|---|
| 導入のしやすさ | インストール手順が明確。配布基盤と相性が良い | フォルダー配布で手軽。ただし配布・更新の仕組みは別途必要 |
| 更新(アップデート) | MSIX/アプリインストーラーで管理しやすい | 自前の更新方法(上書き/差分/自動更新)を設計する必要 |
| 依存コンポーネント | 環境により MSIX が依存関係を解決 | Windows App SDK を同梱するか、端末側に入れるかを選ぶ |
| 権限・運用 | ストア/証明書/署名の運用が絡みやすい | 署名は「推奨」だが必須ではないケースも多い(運用設計次第) |
.NET 9 での publish コマンド例(Windows アンパッケージ配布)
もっとも手堅いのは、.NET 8 で使っていた publish コマンドの TFM を net9.0 に置き換えるやり方です。以下は Windows のアンパッケージ配布を想定した例です(Windows のコマンドプロンプトでの複数行表記)。
dotnet publish -f net9.0-windows10.0.19041.0 -c Release ^
-p:WindowsPackageType=None ^
-p:WindowsAppSDKSelfContained=true ^
/p:PackageCertificateThumbprint="c01ba45666762f3615cf6813aeb4dc4b1919541d" ^
-p:RuntimeIdentifierOverride=win10-x64
PowerShell で改行しながら書く場合は、継続文字が ^ ではなくバッククォート(`)になる点に注意してください。
dotnet publish -f net9.0-windows10.0.19041.0 -c Release `
-p:WindowsPackageType=None `
-p:WindowsAppSDKSelfContained=true `
-p:PackageCertificateThumbprint="c01ba45666762f3615cf6813aeb4dc4b1919541d" `
-p:RuntimeIdentifierOverride=win10-x64
また、RuntimeIdentifierOverride を使わずに RID を明示したい場合は、次のように -r を使う書き方もあります。ただし MAUI のマルチターゲット構成では、-r が他ターゲットの restore に影響することがあるため、Windows だけを狙うなら RuntimeIdentifierOverride の方が扱いやすいケースが多いです。
dotnet publish -f net9.0-windows10.0.19041.0 -c Release -r win10-x64 ^
-p:WindowsPackageType=None ^
-p:WindowsAppSDKSelfContained=true
オプションの意味を理解して「再現性の高い publish」にする
コマンドをそのままコピペして動けば一旦は OK ですが、チーム開発や CI/CD で安定させるなら「どの指定が何のためにあるか」を押さえておくとトラブルが減ります。主要プロパティをまとめます。
| 指定 | 役割 | 外すとどうなる? | 実務的な使い分け |
|---|---|---|---|
-f net9.0-windows10.0.19041.0 | Windows 向けの TFM を明示して publish | マルチターゲット構成だと意図しない TFM が選ばれることがある | Windows だけ publish する場合は常に明示が安全 |
-c Release | Release 構成で publish | Debug だとサイズ/速度/依存が変わりやすい | 配布は基本 Release |
-p:WindowsPackageType=None | MSIX ではなくアンパッケージ出力に切り替える | MSIX 生成に寄った出力・設定になりやすい | フォルダー配布をするなら必須級 |
-p:WindowsAppSDKSelfContained=true | Windows App SDK のランタイムを同梱する | 端末側に Windows App SDK ランタイムが必要になることがある | 社内端末がまちまちなら true が楽(ただしサイズ増) |
-p:RuntimeIdentifierOverride=win10-x64 | Windows 向け publish の RID を上書きする | CPU アーキテクチャ不一致、または restore/publish のターゲットがずれる | Windows 向けの成果物を x64/arm64 で分けたい場合に便利 |
PackageCertificateThumbprint | 署名用証明書の指定(主に MSIX 系) | MSIX を作る場合に署名エラーになることがある | アンパッケージのみなら不要な場合が多いが、パイプライン都合で残すことも |
SelfContained と WindowsAppSDKSelfContained の違いを混同しない
WindowsAppSDKSelfContained は名前が似ていますが、.NET の publish オプションである SelfContained(または --self-contained)とは別物です。運用設計では、この 2 つを分けて考えると判断がしやすくなります。
| 項目 | 対象 | 主な効果 | サイズへの影響 | 選び方の目安 |
|---|---|---|---|---|
SelfContained | .NET ランタイム | 端末に .NET ランタイムが無くても動かせるようにする | 増える(大きめ) | 配布先で .NET 9 ランタイムを管理したくないなら検討 |
WindowsAppSDKSelfContained | Windows App SDK ランタイム | Windows App SDK を端末に入れずに動かせるようにする | 増える(中〜大) | 社内端末の状態が揃っていないなら true が現実的 |
「フォルダー配布を楽にしたい」だけなら、まずは WindowsAppSDKSelfContained=true の有無を決めるのが先です。その上で、配布先に .NET 9 ランタイムを入れられない環境(制限端末など)なら SelfContained を追加する、という順番で検討すると整理しやすいです。
RuntimeIdentifierOverride の考え方:どの RID を選ぶべきか
RuntimeIdentifierOverride は .NET 9 でも同じように扱えます。重要なのは「どの Windows / CPU を配布対象にするのか」です。配布物を 1 つに絞るのか、x64 と arm64 を分けるのかで設計が変わります。
代表的な RID の選択肢
| 配布ターゲット | おすすめの例 | 向いているケース | 注意点 |
|---|---|---|---|
| 一般的な Intel/AMD PC | win10-x64 | 社内の大半が x64 の Windows 10/11 | Windows 11 でも動作するが、成果物は x64 固定 |
| ARM 搭載 Windows(Surface など) | win10-arm64 | arm64 ネイティブで配布したい | x64 端末では動かないため配布物を分けるのが基本 |
| x64/arm64 どちらもあり得る | 成果物を 2 つ作る | 端末の混在が前提(BYOD/多拠点) | ダウンロードページや配布基盤で振り分けが必要 |
なお .NET の RID は win-x64 のような汎用 RID もありますが、MAUI(Windows)では Windows の最小バージョン(TFM の末尾)と組み合わせて考えると事故が減ります。まずは既存の .NET 8 で安定していた RID を踏襲し、必要になったタイミングで見直すのが現場では堅実です。
なぜ -r ではなく RuntimeIdentifierOverride を使うのか
MAUI プロジェクトは Android/iOS/MacCatalyst/Windows など複数 TFM を持つことが多く、単純に dotnet publish -r を付けると「プロジェクト全体の RID」として扱われ、他ターゲットの restore/publish に影響することがあります。Windows だけに効かせたいなら、RuntimeIdentifierOverride の方が意図が明確です(CI でターゲットごとに publish する場合は特に)。
csproj に固定して「毎回の publish 引数」を減らす方法
コマンドライン引数が長くなるほど、人による実行差・打ち間違いが増えます。チーム運用なら、安定した値は csproj 側へ寄せるのもおすすめです。ただし MAUI はマルチターゲットなので、Windows の TFM にだけ適用するのがポイントです。
<PropertyGroup Condition="'$(TargetFramework)' == 'net9.0-windows10.0.19041.0'">
<WindowsPackageType>None</WindowsPackageType>
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
<RuntimeIdentifierOverride>win10-x64</RuntimeIdentifierOverride>
</PropertyGroup>
証明書サムプリント(PackageCertificateThumbprint)は、アンパッケージ配布だけなら不要なことが多い一方で、将来的に MSIX も併用する可能性がある・パイプラインを共通化したい、などの理由で残すケースもあります。もし「アンパッケージだけ」に割り切るなら、署名関連の指定は一度整理しておくとトラブルシュートが簡単になります。
publish 出力はどこにできる?配布前に見るべきポイント
一般的には次のようなパスに成果物が出ます(プロジェクト名や RID によって変わります)。
{プロジェクト}/bin/Release/net9.0-windows10.0.19041.0/{RID}/publish/
配布前に最低限チェックしたい観点をまとめます。
| チェック項目 | 確認方法 | 狙い |
|---|---|---|
| 起動 EXE が存在する | publish フォルダー直下の .exe を確認 | 「起動ファイルがどれか分からない」問題を防ぐ |
| 依存 DLL が欠けていない | フォルダー内に DLL が揃っているか | 配布時の取りこぼし(部分コピー)を防ぐ |
| Windows App SDK の同梱有無 | WindowsAppSDKSelfContained の設定を見直す | 端末側の前提条件を明確化する |
| テスト端末で初回起動できる | クリーン寄りの端末/VM で起動テスト | 開発環境だけで動いている状態を排除する |
配布先端末で必要になりやすい前提条件
アンパッケージ配布では「端末に何が入っていれば動くか」を明文化しておくと、問い合わせ対応が激減します。代表的な前提条件を整理しておきます。
| 要素 | 必要になる条件 | 不足すると起きがちなこと | 運用の現実解 |
|---|---|---|---|
| .NET ランタイム | framework-dependent で publish した場合 | 起動できない/ランタイムが見つからない | 端末側で一括配布するか、SelfContained を検討 |
| Windows App SDK ランタイム | WindowsAppSDKSelfContained を false の場合 | 起動直後にクラッシュ、依存が解決できない | 端末に事前配布するか、WindowsAppSDKSelfContained=true |
| WebView2 Runtime | WebView/BlazorWebView などを使う場合 | Web 表示が真っ白、初期化エラー | 社内標準イメージに含める/配布基盤で事前展開 |
アンパッケージ配布でハマりやすいポイントと対処
Windows のアンパッケージ配布は自由度が高い一方で、端末側の前提条件が揃っていないと「開発 PC では動くのに配布先で動かない」が起きがちです。よくある症状と対策を表にまとめます。
| 症状 | 原因の候補 | 対処の方向性 |
|---|---|---|
| publish は通るが配布先で起動しない | Windows App SDK ランタイムが端末に無い/同梱していない | WindowsAppSDKSelfContained=true を検討する、または端末へランタイム配布を行う |
| 起動直後に WebView 系のエラーが出る | WebView2 Runtime が未導入、または古い | 配布対象端末に WebView2 Runtime を用意する(社内配布なら事前展開が現実的) |
| RID 関連の restore/publish エラー | restore されたターゲットと publish の RID が噛み合っていない | RuntimeIdentifierOverride を publish と restore で揃える。CI では publish 前に dotnet restore を同条件で実行 |
| 別 PC にコピーすると DLL が足りないと言われる | publish フォルダーの一部だけをコピーしている | フォルダーを丸ごと zip 化して配布する、もしくは配布ツールでディレクトリ単位配布にする |
| SmartScreen で警告が出る | 実行ファイルが未署名/配布経路が外部扱いになっている | 必要に応じて Authenticode 署名を検討する。社内配布なら信頼済み証明書の運用設計を行う |
CI/CD(自動ビルド)での実用的な流れ
アンパッケージ配布は「フォルダー成果物をそのまま配布できる」反面、ビルド条件がブレると差分が出やすいので、CI/CD に寄せると安定します。最低限の考え方は次のとおりです。
- 使用する .NET SDK(.NET 9)のバージョンを固定する
- Windows 向け publish は TFM と RID を必ず明示する
- 生成物は publish フォルダー丸ごとを成果物(artifact)として保存する
例として、ローカルでも CI でも同じコマンドを再現できるように、コマンドをスクリプト化しておくと便利です。
REM build_publish_windows.cmd(例)
dotnet --info
dotnet restore
dotnet publish -f net9.0-windows10.0.19041.0 -c Release ^
-p:WindowsPackageType=None ^
-p:WindowsAppSDKSelfContained=true ^
-p:RuntimeIdentifierOverride=win10-x64
移行チェックリスト:.NET 8 → .NET 9 のアンパッケージ配布で確認すること
- Windows TFM を
net9.0-windows10.0.19041.0に差し替えた WindowsPackageType=Noneを publish 引数または csproj に設定した- 配布対象の CPU に合う RID(
win10-x64/win10-arm64)を決めた WindowsAppSDKSelfContainedを true にするか、端末側にランタイムを展開するかを決めた- .NET ランタイム要件(SelfContained の有無)を決めた
- クリーンな端末(VM など)で起動確認をした
- WebView2 Runtime など、端末前提条件を運用に落とし込んだ
まとめ
.NET MAUI(Windows)のアンパッケージ配布は、.NET 8 から .NET 9 への移行で「別物になる」わけではありません。まずは TFM を net9.0 に更新し、これまで通り WindowsPackageType=None と RuntimeIdentifierOverride を軸に publish を組み立てるのが最短です。配布先での動作安定には、RID の選定と Windows App SDK/.NET ランタイム/WebView2 などの前提条件を明確にし、再現性のあるビルド(スクリプト化・CI 化)に寄せることが効きます。

コメント