WinUI 3 踩坑記:從建立專案到釋出

2022-09-16 21:00:11

本文是 WinUI 3 踩坑記 的一部分,該系列釋出於 GitHub@Scighost/WinUI3Keng,若內容出現衝突以 GitHub 上的為準。

建立專案

現在 WinUI 3 的入門體驗比剛釋出那會兒好太多了,至少不會再出現模板專案無法生成的情況 [1]。開啟 Visual Studio 建立 WinUI 3 專案,有如下的三個模板可以選擇:

第一個模板的 WinUI 3 專案和打包專案是同一個專案,第三個模板的則是兩個不同的專案。如果在釋出時需要讓多個可執行檔案存在於不同資料夾,就選擇第三個模板。

打包與非打包

WinUI 3 預設建立打包專案,打包釋出需要使用被使用者裝置信任的證書給包簽名,如果要釋出非打包的專案,在專案檔案(*.csproj)中加入以下內容即可 [2]

<!--  *.csproj  -->
<PropertyGroup>
    ......
    <!--  不打包  -->
    <WindowsPackageType>None</WindowsPackageType>
    <!--  自包含 Windows App SDK Runtime,否則需要單獨安裝  -->
    <WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
</PropertyGroup>

優缺點

優點 缺點
打包 作業系統保證包內檔案不會被篡改 需要程式碼簽名
非打包 部署靈活 無法使用與應用包有關的 API

釋出

建立專案後,會在資料夾 ./Properties/PublishProfiles/ 內建立三個不同 CPU 架構的釋出組態檔,下面以 x64 平臺的為例。

<!-- win10-x64.pubxml -->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <PublishProtocol>FileSystem</PublishProtocol>
    <Platform>x64</Platform>
    <RuntimeIdentifier>win10-x64</RuntimeIdentifier>
    <PublishDir>bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\</PublishDir>
    <!-- 自包含 .NET 執行時 -->
    <SelfContained>true</SelfContained>
    <!-- 不要釋出為單個檔案 -->
    <PublishSingleFile>False</PublishSingleFile>
    <!-- Release 設定時使用 ReadyToRun 編譯 -->
    <PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
    <PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
    <!-- 暫時不能使用剪裁 -->
    <!-- 
    See https://github.com/microsoft/CsWinRT/issues/373
    <PublishTrimmed>True</PublishTrimmed>
    -->
  </PropertyGroup>
</Project>

自包含和 ReadyToRun 編譯讓 WinUI 3 安裝包的體積不遜於 Electron,儘管現在硬碟空間很寬裕,但是在打包後還是要注意一下安裝包的大小,因為第三方庫可能會引入 WinForm 和 WPF 的框架檔案

這裡以雲之幻網路迴環管理器為例,專案中參照的第三方庫僅有四個,但是安裝包大小卻有 90.3 MB。

<!-- LoopbackManager.App.csproj -->
<PackageReference Include="PInvoke.User32" Version="0.7.124" />
<PackageReference Include="ReactiveUI" Version="18.3.1" />
<PackageReference Include="ReactiveUI.Fody" Version="18.3.1" />
<PackageReference Include="ReactiveUI.WinUI" Version="18.3.1" />

開啟安裝後應用的資料夾,以檔案大小排序,發現幾個眼熟的東西,這不就是 WinForm 和 WPF 的嗎。(圖中僅擷取了前幾個,後面還有更多)

究其原因,在專案中有這樣一條參照鏈:網路迴環管理器 -> ReactiveUI -> DynamicData ->System.Reactive,而在 System.Reactive 的專案檔案中有這樣一段,導致在 WinUI 3 專案中會引入 WinForm 和 WPF 的框架檔案。

<!-- System.Reactive.csproj -->
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1' or $(TargetFramework.StartsWith('net5.0-windows'))">
    <UseWPF>true</UseWPF>
    <UseWindowsForms>true</UseWindowsForms>
    <IncludeBuildOutput Condition="'$(TargetFramework)' == 'netcoreapp3.1'">false</IncludeBuildOutput>
</PropertyGroup>

可惜的是,除了不參照這些第三方庫以外,我暫時還沒有找到在打包專案中解決這個問題的辦法。

不同條件下的應用大小

因為有可能會引入額外的框架檔案,我測試了 WinUI 3 模板專案在不同釋出條件下的大小,幫各位讀者排排雷。

這裡的 2W 是指 WinForm 和 WPF,在我的測試中,如果僅使用 UseWindowsFormsUseWPF ,額外引入的框架檔案是一樣的,所以不對二者進行區分。

WindowsAppSDK 是指專案檔案中 WindowsAppSDKSelfContained = true 的情況,使用者在安裝打包專案的安裝包時(msix 或 msixbundle 檔案),系統會自動下載並安裝 Windows App SDK Runtime。但是非打包專案沒有這個福利,所以這一項僅供非打包專案參考。

安裝包 安裝後
WinUI 40.4 MB 101 MB
WinUI + ReadyToRun 51.1 MB 138 MB
WinUI + WindowsAppSDK 59.6 MB 155 MB
WinUI + WindowsAppSDK + ReadyToRun 70.3 MB 193 MB
WinUI + 2W 75.5 MB 188 MB
WinUI + 2W + ReadyToRun 86.2 MB 225 MB
WinUI + 2W + WindowsAppSDK 94.7 MB 242 MB
WinUI + 2W + WindowsAppSDK + ReadyToRun 105.0 MB 279 MB

額外引入的內容

下表列出了額外引入 WinForm 和 WPF 框架時多出的檔案或資料夾。

點選展開
檔案或資料夾名稱
cs/
de/
es/
fr/
it/
ja/
ko/
pl/
pt-BR/
ru/
tr/
zh-Hans/
zh-Hant/
Accessibility.dll
D3DCompiler_47_cor3.dll
DirectWriteForwarder.dll
Microsoft.VisualBasic.Forms.dll
Microsoft.Win32.Registry.AccessControl.dll
Microsoft.Win32.SystemEvents.dll
PenImc_cor3.dll
PresentationCore.dll
PresentationFramework.Aero.dll
PresentationFramework.Aero2.dll
PresentationFramework.AeroLite.dll
PresentationFramework.Classic.dll
PresentationFramework.dll
PresentationFramework.Luna.dll
PresentationFramework.Royale.dll
PresentationFramework-SystemCore.dll
PresentationFramework-SystemData.dll
PresentationFramework-SystemDrawing.dll
PresentationFramework-SystemXml.dll
PresentationFramework-SystemXmlLinq.dll
PresentationNative_cor3.dll
PresentationUI.dll
ReachFramework.dll
System.CodeDom.dll
System.Configuration.ConfigurationManager.dll
System.Design.dll
System.Diagnostics.EventLog.dll
System.Diagnostics.EventLog.Messages.dll
System.Diagnostics.PerformanceCounter.dll
System.DirectoryServices.dll
System.Drawing.Common.dll
System.Drawing.Design.dll
System.IO.Packaging.dll
System.Printing.dll
System.Resources.Extensions.dll
System.Security.Cryptography.Pkcs.dll
System.Security.Cryptography.ProtectedData.dll
System.Security.Cryptography.Xml.dll
System.Security.Permissions.dll
System.Threading.AccessControl.dll
System.Windows.Controls.Ribbon.dll
System.Windows.Extensions.dll
System.Windows.Forms.Design.dll
System.Windows.Forms.Design.Editors.dll
System.Windows.Forms.dll
System.Windows.Forms.Primitives.dll
System.Windows.Input.Manipulations.dll
System.Windows.Presentation.dll
System.Xaml.dll
UIAutomationClient.dll
UIAutomationClientSideProviders.dll
UIAutomationProvider.dll
UIAutomationTypes.dll
vcruntime140_cor3.dll
WindowsFormsIntegration.dll
wpfgfx_cor3.dll

參照