教學如何手動把 WPF 封裝成 appx

隨著微軟提供 Desktop Bridge 的技術,讓原本開發 WPF 或 Win32 的開發人員方便包裝既有的程式來上架到 Store ,減少重新開發 UWP 的成本。本篇將説明如何手動的方式把既有的 WPF 封裝到 appx 之中。

把既有的 WPF 或 Win32 程式封裝成 *.appx 放到 Store 有什麽好處?

  1. 讓既有程式可以操作 Windows Universal Platform (UWP) APIs;可參考 WPF 使用 Windows 10 APIs - 1, 23 的介紹;
  2. 方便的部署,搭配 Store 收集用戶的 review, 下載數, crashes 等;
  3. ... 還有很多這裏就不多做說明;

既有的 WPF 封裝到 *.appx 之前,微軟建議先閲讀 Prepare to package your desktop app 瞭解封裝之後有那些功能不支援或是 .NET Framework 最低需求與各個版本對應的差別,... 等,但微軟不强迫調整,只是開發人員需要自己做相容測試,例如 COM 的使用有很多不支援或是更換整合的方式來繼續支援。

Package desktop applications (Desktop Bridge) 提供多種方式:

利用 Package a desktop application manually 介紹怎麽手動封裝成 *.appx;

  1. 建立 appxmanifest.xml
    <?xml version="1.0" encoding="utf-8" ?>
    <Package
      xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
      xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
      xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
      IgnorableNamespaces="uap rescap">
    
      <!-- 
            Identify 的參數可到 Partner Center 中產品的 Product identify 取得;
            ProcessorArchitecture 根據您預設支援的平臺設定;
       -->
      <Identity Name="{Package/Identity/Name}" 
                Publisher="{Package/Identity/Publisher}"
                Version="9.9.9.9" 
                ProcessorArchitecture="x86" />
    
      <Properties>
        <DisplayName>WPFWindows</DisplayName>
        <PublisherDisplayName>{Package/Properties/PublisherDisplayName}</PublisherDisplayName>
        <Logo>AppIcon\StoreLogo.png</Logo>
      </Properties>
    
      <Dependencies>
        <!-- 要設定 Windows.Desktop 因爲其他設備不支援 -->
        <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.16299.0" MaxVersionTested="10.0.18362.0" />
      </Dependencies>
    
      <Resources>
        <Resource Language="en"/>
        <Resource Language="ja"/>
        <Resource Language="zh-Hans"/>
        <Resource Language="zh-Hant"/>
      </Resources>
    
      <Applications>
        <Application Id="App" Executable="WPFWindows.exe" EntryPoint="Windows.FullTrustApplication">
          <uap:VisualElements DisplayName="WPFWindows"
                              Description="WPFWindows"
                              BackgroundColor="#00AED8"
                              Square150x150Logo="AppIcon\Square150x150Logo.png"
                              Square44x44Logo="AppIcon\Square44x44Logo.png">
            <uap:DefaultTile Wide310x150Logo="AppIcon\Wide310x150Logo.png" />
            <uap:SplashScreen Image="AppIcon\SplashScreen.png" />
          </uap:VisualElements>
        </Application>
      </Applications>
    
      <Capabilities>
        <Capability Name="internetClient" />
        <rescap:Capability Name="runFullTrust" />
      </Capabilities>
    </Package>
    需要注意的地方:
    • 根據您在 Partner Center 注冊的 product 填入它所屬 product identify 中必要的參數:
      • Package/Identity/Name
      • Package/Identity/Publisher
      • Package/Properties/PublisherDisplayName
      如果不知道怎麽填寫,可以參考 How Visual Studio generates an app package manifest 裏面的説明;
    • <Resources /> 記得填入 app 支援的多個語言包或是多個 scale(100,125,150,200,400) 的 app icon;
    • <Application /> 的 EntryPoint="Windows.FullTrustApplication" 是固定的;Executable="WPFWindows.exe" 則設定 WPF 的執行檔;如果您使用 Windows Application Package Project 方式,它在 obj/release/packagelayout 會要產生一樣的内容;
    • <Capabilities/> 記得宣告 <rescap:Capability Name="runFullTrust" />
  2. 準備 appx 需要的 Store logo
    因爲 appxmanifest.xml 從的 tag 寫了這些圖示的 icon,每一張圖要分別準備 scale-100, scale-125, scale-150, scale-200 與 scale-400 五種尺寸。例如:把產生的 app icons 放在一個目錄 AppIcon。 詳細可參考 App icons and logos 的介紹或是搭配 UWP Tile Generator Extension for Visual Studio 產生圖示;
  3. 利用 MakePri.exe 建立必要的 priconfig.xml 與多個 resources.pri
    參考 Generate a Package Resource Index (PRI) file 介紹,App 要支援多語系跟多種 scale 圖示時需要將它們分別建立出來,所以先建立 priconfig.xml 定義需要的内容,才能建立相對應的 resources.dll。 步驟:
    1. 假設 WPF 專案的位置在 c:\sources\WPFWindows\bin,那產生的 priconfig.xml 要放在 *.exe 的相同目錄;
    2. 把上一步產生的 AppIcon 目錄放到跟 *.exe 相同的目錄;
    3. 執行指令 makepri.exe createconfig /pr $srcPath /cf $srcPath\priconfig.xml /dq en-US /o makepri.exe 位置在 C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64 其中 10.0.18362.0 根據您安裝的 Windows 10 SDK 來更換,後面介紹的 makeappx.exe 與 signtool.exe 也在相同的目錄裏面。 $srcPath 換成專案 *.exe 的路徑。
    4. 執行指令 makepri.exe new /pr $srcPath /cf $srcPath\priconfig.xml /o
      如果發現 resources.dll 等多個檔案(根據支援的語系跟 scales) 不在 *.exe 同一個目錄,要記得複製進去。或者,在執行指令前先把 terminal 指定到 *.exe 相同的路徑再執行指令; 原因 makepri.exe new 指令中只能有一個輸出檔案 /of C:\MyApp\src\resources.pri,但是因爲我們需要多個 resources.dll 才會需要先進入該路徑;
  4. 利用 MakeAppx.exe 封裝既有 desktop project 編譯出來的 *.exe 或 *.dll 變成 *.appx
    執行指令 MakeAppx.exe pack /v /h sha256 /d $srcPath /p $appxFile。 $appxFile 換成輸出的目錄,例如:c:\appx\WPFWindows.appx; 這樣就完成了封裝的任務,但是它還無法被人點擊 *.appx 直接安裝,因爲少了憑證的認證。
  5. 利用 SignTool.exe 搭配 *.pfx 或是 *.cer 為 *.appx 加入憑證
    如果您沒有 Store 專用的 pfx, 可以參考 Create a certificate for package signing 的介紹,建立一個憑證。該憑證需要 identify 等資料(同寫在 appxmanifest.xml 裏的内容),我建議您可以產生一個時間比較長效的,避免每一年需要產生一次。 產生好 *.pfx 之後,把檔案放到 *.exe 來方便執行 SignTool.exe sign /fd SHA256 /a /f $pfxFile $appxFile。 $pfxFile 換成您建立的 *.pfx 檔案。這樣才能點擊 *.appx 來安裝; [注意] 由於憑證是自己建立的,如果您直接把 *.appx 交給其他電腦安裝會遇到憑證不受信任的錯誤而無法安裝,可以參考下面的指令建立成 *.bat,並把它與 *.appx 與 *.pfx 同一個目錄,點擊它來完整第一次的憑證安裝:
    @echo off
    pushd "%CD%"
    
    if not "%1"=="am_admin" (powershell start -verb runas '%0' am_admin & exit /b)
    
    CD /D "%~dp0"
    echo %cd%
    echo "install certification ..."
    // myApp_StoreKey.pfx 要換成您建立的憑證名稱
    Powershell.exe Import-PfxCertificate -FilePath myApp_StoreKey.pfx -CertStoreLocation Cert:\LocalMachine\Root
    echo "install msix ..."
    Powershell.exe Add-AppxPackage -path .\*.appx
    pause
  6. 如果要包裝成 *.appxbundle:
    完成 *.appx 的 sign 之後,再利用 MakeAppx.exe 換成封裝成 *.appxbundle 最後再對 *.appxbundle 做 sign。 步驟:
    1. 建立一個目錄 package,放在 *.exe 目錄的上一層或是任何地方;
    2. 修改建立 *.appx 時的輸出目錄到 package 目錄
    3. 讓 terminal 到 package 目錄再執行 MakeAppx.exe bundle /bv $appxVersion /d $bundlePath /p $appxBundleFile。$appxVersion 換成需要的版本號,$bundlePath 則是把 *.appxbundle 放到 package 目錄,更多 bundle 説明可參考 Create an app bundle
    4. 再 sign 一次:SignTool.exe sign /fd SHA256 /a /f $pfxFile $appxBundleFile

完成上面的步驟就大功告成了。

您可以直接上傳 *.appxbundle 到 partner 或是交給 Partner Center 來上架。

完整版的指令如下(以 powershell 來做範例):

$projectFolder = 'WPFWindows'
$srcFolder = '$projectFolder\src'
$setupBundleFolder = '$projectFolder \package'
$appxVersion = '10.0.0.0'

# 建立 appx 與 appxbundle
Write-Host '1. copy appxManifest.xml and AppIcon into srcFolder'
Copy-Item -Path $projectFolder\AppIcon -Destination $srcFolder -force -recurse
Copy-Item -Path $projectFolder\appxManifest.xml -Destination $srcFolder -force -recurse

Write-Host '2. create mutiple resources, msix file and sign it'
$sdkPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64";
$makePri = "${sdkPath}\makepri.exe"
$makeAppx = "${sdkPath}\MakeAppx.exe"
$signTool = "${sdkPath}\SignTool.exe"
$srcPath = "${currentPath}\${srcFolder}"
$bundlePath = "${currentPath}\${setupBundleFolder}"
$appxPath = "${bundlePath}\WPFWindows_${appxVersion}_x86"
$appxFile = "${appxPath}.appx"
$appxBundleFile = "${appxPath}.appxbundle"
$pfxFile = "${currentPath}\${projectFolder}\myApp_StoreKey.pfx"

& $makePri createconfig /pr $srcPath /cf $srcPath\priconfig.xml /dq en-US /o
& cd $srcPath
& $makePri new /pr $srcPath /cf $srcPath\priconfig.xml /o
& cd ../../../
& $makeAppx pack /v /h sha256 /d $srcPath /p $appxFile
& $signTool sign /fd SHA256 /a /f $pfxFile $appxFile

& $makeAppx bundle /bv $appxVersion /d $bundlePath /p $appxBundleFile
& $signTool sign /fd SHA256 /a /f $pfxFile $appxBundleFile

最後建立好 *.appx 或是 *.appxbundle 時,要記得做 Windows Desktop Bridge app tests

======

這篇把根據微軟的教學補充上我自己踩過的雷,因爲建立好的 *.msix 或 *.appx 竟然在送到 Store 審核時檢查的條件不同,所以去看了 Supported OS versions for installed packages 才知道兩者在 Windows 10 版本的要求有不同。

希望對大家有幫助,謝謝。

References