.NET MAUI .NET 9でWindowsアンパッケージ配布する方法(WindowsPackageType=None・dotnet publish・RuntimeIdentifierOverride)

.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 の TFMnet8.0-windows10.0.19041.0net9.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.0Windows 向けの TFM を明示して publishマルチターゲット構成だと意図しない TFM が選ばれることがあるWindows だけ publish する場合は常に明示が安全
-c ReleaseRelease 構成で publishDebug だとサイズ/速度/依存が変わりやすい配布は基本 Release
-p:WindowsPackageType=NoneMSIX ではなくアンパッケージ出力に切り替えるMSIX 生成に寄った出力・設定になりやすいフォルダー配布をするなら必須級
-p:WindowsAppSDKSelfContained=trueWindows App SDK のランタイムを同梱する端末側に Windows App SDK ランタイムが必要になることがある社内端末がまちまちなら true が楽(ただしサイズ増)
-p:RuntimeIdentifierOverride=win10-x64Windows 向け 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 ランタイムを管理したくないなら検討
WindowsAppSDKSelfContainedWindows App SDK ランタイムWindows App SDK を端末に入れずに動かせるようにする増える(中〜大)社内端末の状態が揃っていないなら true が現実的

「フォルダー配布を楽にしたい」だけなら、まずは WindowsAppSDKSelfContained=true の有無を決めるのが先です。その上で、配布先に .NET 9 ランタイムを入れられない環境(制限端末など)なら SelfContained を追加する、という順番で検討すると整理しやすいです。

RuntimeIdentifierOverride の考え方:どの RID を選ぶべきか

RuntimeIdentifierOverride は .NET 9 でも同じように扱えます。重要なのは「どの Windows / CPU を配布対象にするのか」です。配布物を 1 つに絞るのか、x64 と arm64 を分けるのかで設計が変わります。

代表的な RID の選択肢

配布ターゲットおすすめの例向いているケース注意点
一般的な Intel/AMD PCwin10-x64社内の大半が x64 の Windows 10/11Windows 11 でも動作するが、成果物は x64 固定
ARM 搭載 Windows(Surface など)win10-arm64arm64 ネイティブで配布したい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 RuntimeWebView/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 化)に寄せることが効きます。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次