しばやん雑記

Azure Serverless とメイドさんが大好きなフリーランスのプログラマーのブログ

.NET 6.0 Preview で WPF が ARM64 に対応したのでビルドして Surface Pro X で試した

.NET 5.0 で Win Forms は ARM64 の Windows に対応しましたが、WPF は 2021 年に先送りになっていて非常に残念でしたが、最近リリースされた .NET 6.0 Preview の Nightly Build でついに対応しました。

GitHub の Issue ではマイルストーンが 5.0.0 になっているので、バックポートされるのかも知れません。

既に Twitter では .NET 6.0 Preview で WPF アプリケーションを ARM64 向けにビルドして動かした例が上がっていたので、自分も WPF アプリケーションを公開しているのでビルドを試しておきました。

アプリケーションは Windows Store に公開しているので MSIX まで作成できることを確認しておきました。

まずはシンプルな WPF アプリケーションを使って確認することにします。適当に .NET 6.0 Preview SDK の Nightly Build をインストールして、最近の Visual Studio Preview で WPF アプリケーションを作成すると .NET 6.0 が選べるので、ここから始めます。

f:id:shiba-yan:20210131182422p:plain

とりあえず ARM64 向けにビルド出来るかの確認をしておきたいので、以下のようなコマンドで RID として win-arm64 を指定してビルドします。ReadyToRun も確認してみましたが問題ありませんでした。

この時パッケージの復元でエラーになることがありますが、その場合は .NET 6.0 の Nightly 向け NuGet パッケージソースを追加すれば解決します。

# ARM64 Windows 向けに発行
dotnet publish -c Release -r win-arm64 -o ./publish

# ARM64 Windows 向けに ReadyToRun を有効にして発行
dotnet publish -c Release -r win-arm64 -o ./publish -p:PublishReadyToRun=true

ちなみに .NET 5.0 では WPF に非対応なのでビルド時にエラーになります。

ビルドした dll を dumpbin で確認すると、ちゃんとヘッダーのアーキテクチャが AA64 になっているので ARM64 向けにビルドされていることが分かります。

f:id:shiba-yan:20210131182431p:plain

RID を指定してビルドすると Self-Contained アプリになるので、出力されたファイル一式を Surface Pro X などの ARM64 Windows 上にコピーして持っていくと、それだけで動くようになります。

Self-Contained でファイル数が多いからか、若干時間がかかった気がしましたが無事に起動しました。Surface Pro X 上で 64 bit プロセスとして認識されているので ARM64 です。

f:id:shiba-yan:20210131183459p:plain

.NET CLI を使ったビルドは問題ないことが分かったので、MSIX を各プラットフォーム分作成できれば Windows Store で公開しているアプリケーションの移行がスムーズに出来るはずです。

正直なところ MSIX の作成は結構はまりましたが、最終的には WPF 側の csproj を以下のように定義することで、いい感じに各プラットフォーム向けに ReadyToRun 付きで MSIX をビルドできるようになりました。

何故かこの辺りは情報が非常に少なくて正解なのか分からないですが、各プラットフォーム向けのビルドと MSIX の作成は問題なく動いています。.NET Core 系では RID を指定すれば何とかなる感じはあります。

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net6.0-windows</TargetFramework>
    <UseWPF>true</UseWPF>
    <Platforms>x86;x64;arm64</Platforms>
    <RuntimeIdentifier>win-$(Platform)</RuntimeIdentifier>
    <PublishReadyToRun>true</PublishReadyToRun>
  </PropertyGroup>

</Project>

WPF アプリケーションを作成すると Any CPU 向けで基本は作成されますが、Windows アプリケーションパッケージプロジェクトを追加すると x86 や x64 向けのプラットフォームが自動で作成されます。

この設定と WPF アプリケーションがビルドされる時の RID をキッチリ合わせておかないと、大体の場合は x86 以外のビルド時にエラーとなります。具体的には csproj に Platforms を追加して、構成マネージャーからソリューションプラットフォームへのマッピングを行います。

f:id:shiba-yan:20210131191016p:plain

この時に必要ない Any CPU や ARM 向けのソリューションプラットフォームは削除しておきました。後はソリューションプラットフォームの ARM64 が大文字になっていますが、RID は case-sensitive なので全て小文字に変更しておきます。*1

ビルドした msixbundle ファイルを Surface Pro X にコピーしてインストールして試しましたが、問題なく ARM64 で実行されました。特に ARM64 で動くことによって見た目に違いがあるわけではなく、面白みがないのでインストーラー画面だけ拾ってきました。

f:id:shiba-yan:20210131191656p:plain

msixbundle ファイルには x86 / x64 / ARM64 それぞれの実行ファイルが含まれているので、自動的に適切なプラットフォーム向けのものがインストールされます。

サンプルプロジェクトで ARM64 向けビルドと MSIX の作成まで問題なく行えたので、メインターゲットである WinQuickLook でも ARM64 向けビルドを試しましたが、ALINK でエラーが出て成功しませんでした。一応 ARM64 に対応するための PR だけは作成してあります。

エラーの原因を調べたところ、ALINK が ARM64 に対応していないのが根本原因でした。ALINK がそもそも使われる理由は WPF アプリケーションでサテライトアセンブリのためなので、多言語対応のリソースファイルを削除して 1 つにすると通るようになりました。

ローカライズに WPF 側の仕組みを使うのではなく、MRT と PRI に移行することで解消はしそうですが、このあたりの情報もさらに少ないので非常に悩むところです。

WinUI 3.0 や Project Reunion が正式リリースされたタイミングで改善される気はしますが、この辺りはまだまだ時間がかかりそうなのでしばらくはこれまでのリソース管理を使い続ける予定です。

*1:例えば RID として win-ARM64 を指定するとビルドエラーになる