dotnet publishコマンドでPublishSingleFile(単一ファイル出力)をする(あと-p:IncludeNativeLibrariesForSelfExtractの実験)

もくじ
https://tera1707.com/entry/2022/02/06/144447

やりたいこと

以前の記事で、「dotnet publishコマンドだと、どうやってもPubishSingleFileがうまくいかない(exeが1個だけ出力されてほしいのに、依存dll類が全部出てきてしまう)」ということを書いた。

が、そんなわけない、コマンドで出来るはずと思うので、もう一回tryしたい。

前提

下記のアプリで実験した。

  • 2025/05/02に実験
  • VisualStudio2022
  • .net8
  • C#/WPFアプリ

結論

やっぱりdotnet publishでもうまくいった。

前回うまくいかなかったのは、

  • 実は単一ファイル出力がうまくいっていたのに、
  • binフォルダ以下の出力先フォルダに、一旦出力された沢山のファイルをみて、単一ファイル化できてないと思い込んでいたが、
  • 実はその中に「publish」フォルダが出来上がっていて、その中には単一ファイルがちゃんと出力されていた

という顛末だった。(実はうまくいっていた。下図のように埋もれて見えていただけだった)

やったこと

なので、うまくいったコマンドをまとめておく。

ついでに、PublishSingleFile以外にも、なにが出力ファイルとして出てくるかに大きくかかわるパラメータ(-p:IncludeNativeLibrariesForSelfExtract)があったのでメモっておく。

①基本のdotnet publishコマンド

以下の前提である場合は、

  • windows11で動作する
  • 64bit
  • Releaseビルド
  • self-containedなアプリにして、.net8ランタイムがインストールされてなくても動くようにする
  • バッチと同じフォルダに成果物フォルダを作成する
    (前みたいに多数のファイルに埋もれないように出力先を指定する)

下記のコマンドが基本になると思う。

dotnet publish -c Release -r win-x64 --output .\publish_1 --self-contained -p:RuntimeIdentifier=win-x64 -p:Platform=x64

これに、単一ファイル出力するためのパラメータなどを付け足していく。
このコマンドで出力した一式の一部がこちら。(ランタイム一式を含むので、ファイルがたくさんある)

②-p:PublishSingleFile=true(単一ファイル出力)を追加

-p:PublishSingleFile=trueを付けた。

dotnet publish -c Release -r win-x64 --output .\publish_2 --self-contained -p:RuntimeIdentifier=win-x64 -p:Platform=x64 -p:PublishSingleFile=true 

このコマンドを実行したときの出力ファイルがこちら。
(7ファイルのみになった)

③--self-containedを削除

②から、--self-containedのパラメータを抜いた。
(PublishSingleFile を true にすると、--self-contained は自動でtrueになるので、削除した。)

dotnet publish -c Release -r win-x64 --output .\publish_3 -p:RuntimeIdentifier=win-x64 -p:Platform=x64 -p:PublishSingleFile=true 

出力されたものは、容量を比較しても②と同じに見える。

バイナリレベルでも一緒だった。

④-p:IncludeNativeLibrariesForSelfExtract=true を追加

前提の- self-containedなアプリにして、.net8ランタイムがインストールされてなくても動くようにするから外れてしまうのだが、--self-contained=trueを外して、-p:IncludeNativeLibrariesForSelfExtract=trueをした。

dotnet publish -c Release -r win-x64 --output .\publish_4 -p:RuntimeIdentifier=win-x64 -p:Platform=x64 -p:IncludeNativeLibrariesForSelfExtract=true

結果、Nativeっぽいファイルは出力されなくなった。

色々試したところ、この④出力成果物と、self-containedしない(--self-contained=false)下記のdotnet publishの出力成果物は、バイナリレベルで一緒だった。

dotnet publish -c Release -r win-x64 --output .\publish_10 -p:RuntimeIdentifier=win-x64 -p:Platform=x64

なので、-p:IncludeNativeLibrariesForSelfExtract=trueは、self-contained して初めて効果が出るパラメータと思われる。
(self-containedしたうえで、ネイティブのdllもexeに取り込むか?取り込まないか?の設定だと思われる。) (それで、取り込んだら⑤のように、ネイティブdllは一時ファイルとして吐かれる。↓⑤参照)

⑤-p:PublishSingleFile=true かつ -p:IncludeNativeLibrariesForSelfExtract=true

これで出力されたexeを実行時、%Temp%\.net\アプリ名\ランダムな文字列\フォルダの中に、下記のようなフォルダとファイルができた。
exe動作時には、メインのexeに抱え込んだネイティブなdllがここに吐き出される様子。

※今回は試せてないが、自作のネイティブdllを使用している場合も、ここに吐き出されるのか? (未検証) もしそうであれば、メインのdllに組み込む前にそのネイティブdllも署名等を済ませておかないといけないっぽいと思った。

%Temp%.net\ にネイティブdllが吐かれるのは⑤だけだった

今回試したパターンだと、ネイティブdllが吐かれるのは⑤だけだった。

publishしたときに1ファイルになってくれるのは分かりやすくて良いが、⑤のところに書いた懸念(自作ネイティブを使うときにもtempに吐かれたら...?)とかが心配なので、見えないところであれこれされることの少なさを考えると、④が一番良いのかな、と思った。

参考

dotnet publish コマンド

https://learn.microsoft.com/ja-jp/dotnet/core/tools/dotnet-publish

IncludeNativeLibrariesForSelfExtract の説明

https://learn.microsoft.com/ja-jp/dotnet/core/deploying/single-file/overview?tabs=clihttps://learn.microsoft.com/ja-jp/dotnet/core/deploying/single-file/overview?tabs=cli#native-libraries

.NET6の「単一ファイル」

https://qiita.com/up-hash/items/39fa0671bf390147eca9

.NET CoreのSingle Fileを試す
-p:IncludeNativeLibrariesForSelfExtract=trueのときのネイティブdllの吐き出し先をここで知った

https://noxi515.hateblo.jp/entry/2020/07/19/173405