WPF 使用 Windows 10 APIs - 1 介紹如何包裝 WPF 成爲 Bridge App 與如何跟 UWP App 互動,這篇繼續補充 WPF 成爲 Bridge App 後,原本 Win32 程式的特性怎麽對齊。
Integrate your packaged desktop application with Windows 10 介紹重點:
- 讓 WPF 已經被建立的捷徑能繼續使用,例如:Start menu 或 Task bar 上面的捷徑:
在 Package.appxmanifest 中補上:
<Extension Category="windows.desktopAppMigration"> <DesktopAppMigration> <DesktopApp AumId="[your_app_aumid]" /> <DesktopApp ShortcutPath="[path]" /> </DesktopAppMigration> </Extension>
- windows.desktopAppMigration:來自 http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/3 告訴系統在安裝時要如何串聯定義到 AumId 與 ShortcutPath。
- AumId (Application User Model ID):是加上驚嘆號和應用程式識別碼的套件系列名稱,例如:{Package family name}!{Applcation's Id}。或是可參考 Find the AUMID (Application User Model ID) of an installed UWP app 來取得。它可以被用來做自動化啓動使用,參考 自動化啟動 Windows 10 UWP App。
- ShortcutPath:設定原本 WPF 使用的 *.Ink 位置,範例。
- 讓 packaged application 取代原本 desktop application 預設處理的檔案類型:
需要先拿到每一個程式專屬的 programmatic identify (ProgID),並將它與 file association 綁定在一起。
<Extension Category="windows.fileTypeAssociation"> <FileTypeAssociation Name="[AppID]"> <MigrationProgIds> <MigrationProgId>"[ProgID]"</MigrationProgId> </MigrationProgIds> </FileTypeAssociation> </Extension>
- windows.fileTypeAssociation 固定值
- programmatic identifier (ProgID) 代表 App 的唯一識別碼,例如:MyWPFBridge;
- MigrationProgId 給 desktop application 當時的 ProgID,它會與設定 Name 整合在一起,例如:oldApp.jpb.a; 範例
- 讓用戶對檔案按下右鍵時,可以在 open with 裏面找到 packaged application ,並取代 desktop application;
重點是注冊必要的 file extension,設定方式同 UWP 的 FileTypeAssociation。
<Extension Category="windows.fileTypeAssociation"> <FileTypeAssociation Name="[AppID]"> <SupportedFileTypes> <FileType>"[file extension]"</FileType> </SupportedFileTypes> </FileTypeAssociation> </Extension>
- windows.fileTypeAssociation 固定值
- FileType 例如: .jpb, .avi 等副檔名
- 為特定的檔案類型增加按下右鍵後,出現多個選項,例如:開啓或列印的範例;
一樣在 FileTypeAssocation tag 下加入 SupportedVerb, 由於是用 http://schemas.microsoft.com/appx/manifest/uap/windows10/3 的命名空間,因此這個設定一樣支援 UWP application。如下:
<Extension Category="windows.fileTypeAssociation"> <FileTypeAssociation Name="[AppID]"> <SupportedVerbs> <Verb Id="[ID]" Extended="[Extended]" Parameters="[parameters]">"[verb label]"</Verb> </SupportedVerbs> </FileTypeAssociation> </Extension>
- Ver 代表在 File Explorer 的右鍵内容要顯示的項目名稱,可搭配 ms-resources 支持多語系。
- Id 代表 Ver 的唯一值。
如果是 UWP application,該參數會被帶入 activation event args 裏面;如果是 full-trust packaged app,則會收到下面介紹的 parameters。
- Parameters 代表啓動該 Verb 要帶入的參數;如果是 full-trust packaged application 該參數將會被傳入在程式啓動時。Parameters 可搭配 Verb 客制為自己需的内容,甚至帶入 file path,請利用引號(")做為包裝避免有空白造成錯誤,例如: Parameters="/e "%1""。Parameters 不支援 UWP application。
- Extended 該特性只支援用戶先按住 shift 按鈕,再到檔案上按下滑鼠右鍵時,該 Verb 才會顯示。預設是 false 代表用戶在檔案按右鍵就會顯示。
- 支持檔案利用 URL 開啓 packaged application:
<Package xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" IgnorableNamespaces="uap, uap3"> <Applications> <Application> <Extensions> <uap:Extension Category="windows.fileTypeAssociation"> <uap3:FileTypeAssociation Name="documenttypes" UseUrl="true" Parameters="%1"> <uap:SupportedFileTypes> <uap:FileType>.txt</uap:FileType> <uap:FileType>.doc</uap:FileType> </uap:SupportedFileTypes> </uap3:FileTypeAssociation> </uap:Extension> </Extensions> </Application> </Applications> </Package>
- 在防火墻加入例外你的 App:
如果您的 application 用到特殊的網路 Port 需要向防火墻宣告,才能正常使用。使用 http://schemas.microsoft.com/appx/manifest/desktop/windows10/2 命名空間,只支援 Desktop 環境使用。
<Package xmlns:desktop2="http://schemas.microsoft.com/appx/manifest/desktop/windows10/2" IgnorableNamespaces="desktop2"> <Extensions> <desktop2:Extension Category="windows.firewallRules"> <desktop2:FirewallRules Executable="Contoso.exe"> <desktop2:Rule Direction="in" IPProtocol="TCP" Profile="all"/> <desktop2:Rule Direction="in" IPProtocol="UDP" LocalPortMin="1337" LocalPortMax="1338" Profile="domain"/> <desktop2:Rule Direction="in" IPProtocol="UDP" LocalPortMin="1337" LocalPortMax="1338" Profile="public"/> <desktop2:Rule Direction="out" IPProtocol="UDP" LocalPortMin="1339" LocalPortMax="1340" RemotePortMin="15" RemotePortMax="19" Profile="domainAndPrivate"/> <desktop2:Rule Direction="out" IPProtocol="GRE" Profile="private"/> </desktop2:FirewallRules> </desktop2:Extension> </Extensions> </Package>
- windows.firewallRules 固定值,搭配 FirewallRules 使用
- Executable 代表這條規則的名稱,它會被加入到 firewall 的例外清單裏
- Direction 設定為 inbound 或 outbound
- IPProtocol 設定為 TCP, UDP 或其他 communication protocol
- LocalPortMin 代表 local port numbers 的最小值
- LocalPortMax 代表 local port numbers 的最大值
- RemotePortMin 代表 remote port numbers 的最小值
- RemotePortMax 代表 remote port numbers 的最大值
- Profile 網路類型:private, public, ... 等
還有更多整合 File Explorer 與設定 Environment 的部分可以參考:Integrate with File Explorer 介紹。 接下來介紹 packaged application 如何與其他 applications 整合。
- 讓 packaged application 支援其他 applications 想要列印時,可以找到您的 application:
例如用戶從 notepad 按下列印,列印程式選單裏面會出現您的 application。
<Extension Category="windows.appPrinter"> <AppPrinter DisplayName="[DisplayName]" Parameters="[Parameters]" /> </Extension>
- DisplayName 代表要顯示的名稱
- Parameters 給任何您程式需要的參數,例如:Parameters="/insertdoc %1"
- 分享 packaged application 中的自定義 font 給其他 applications:
<Extension Category="windows.sharedFonts"> <SharedFonts> <Font File="[FontFile]" /> </SharedFonts> </Extension>
- 從 UWP application 啓動 win32 process:
<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" xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"> ... <Capabilities> <rescap:Capability Name="runFullTrust"/> </Capabilities> <Applications> <Application> <Extensions> <desktop:Extension Category="windows.fullTrustProcess" Executable="fulltrustprocess.exe"> <desktop:FullTrustProcess> <desktop:ParameterGroup GroupId="SyncGroup" Parameters="/Sync"/> <desktop:ParameterGroup GroupId="OtherGroup" Parameters="/Other"/> </desktop:FullTrustProcess> </desktop:Extension> </Extensions> </Application> </Applications> </Package>
- GroupId 代表要傳送 parameter 的識別值
- Parameters 代表要使用的内容
上面介紹的内容比較詳細的範例程式,可以參考 WPF picture viewer with transition/migration/uninstallation。 在 WPF 使用 Windows 10 APIs - 1 介紹幾個常用的 Windows 10 APIs 以及 UWP app 與 AppService 的互動 (需要注意 windows.fullTrustProcess 只能在 Desktop 使用),這一篇再補充整合 UWP app 與 Background Task 的使用。
- 建立一個 windows runtime component 的 Background Task:
BackgroundTaskDeferral _deferral; public async void Run(IBackgroundTaskInstance taskInstance) { _deferral = taskInstance.GetDeferral(); // 利用 send toast 的方式表示有處理 string msg = $"收到 TimeZoneChanged 的事件,{TimeZoneInfo.Local.DisplayName}"; ToastTemplateType toastTemplate = ToastTemplateType.ToastText02; XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(toastTemplate); XmlNodeList toastTextElements = toastXml.GetElementsByTagName("text"); toastTextElements[0].AppendChild(toastXml.CreateTextNode(msg)); toastTextElements[1].AppendChild(toastXml.CreateTextNode(DateTime.Now.ToString())); ToastNotification toast = new ToastNotification(toastXml); ToastNotificationManager.CreateToastNotifier().Show(toast); _deferral.Complete(); }
- 在 WPF 加入 C:\Program Files (x86)\Windows Kits\10\UnionMetadata\Windows.winmd 與 C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\*WindowsRuntime*.dll 的參考,並注冊 Background Task 與 entry point。
private void RegistBackgroundTask() { var taskRegistered = false; var exampleTaskName = "ExampleBackgroundTask"; foreach (var task in BackgroundTaskRegistration.AllTasks) { if (task.Value.Name == exampleTaskName) { taskRegistered = true; break; } } if (taskRegistered) { return; } var builder = new BackgroundTaskBuilder(); builder.Name = exampleTaskName; builder.TaskEntryPoint = "MyBackgroundTask.ExampleBackgroundTask"; builder.SetTrigger(new SystemTrigger(SystemTriggerType.TimeZoneChange, false)); builder.AddCondition(new SystemCondition(SystemConditionType.UserPresent)); BackgroundTaskRegistration registResult = builder.Register(); }
- 在 Windows Application Packaging Project 分別把 WPF 專案與 Windows runtime component 專案加入,並宣告必要的内容:
<Proejct> <PropertyGroup> <ProjectGuid>fd2d401e-0cd1-48df-9812-cd56909d97cf</ProjectGuid> <TargetPlatformVersion>10.0.17763.0</TargetPlatformVersion> <TargetPlatformMinVersion>10.0.15063.0</TargetPlatformMinVersion> <DefaultLanguage>en-US</DefaultLanguage> <PackageCertificateKeyFile>WapProjTemplate1_TemporaryKey.pfx</PackageCertificateKeyFile> <EntryPointProjectUniqueName>..\WPFWithBackgroundTask\WPFWithBackgroundTask.csproj</EntryPointProjectUniqueName> </PropertyGroup> <ItemGroup> <ProjectReference Include="..\WPFWithBackgroundTask\WPFWithBackgroundTask.csproj" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\MyBackgroundTask\MyBackgroundTask.csproj" /> </ItemGroup> </Project>
<Package> <Applications> <Application> <Extensions> <Extension Category="windows.backgroundTasks" EntryPoint="MyBackgroundTask.ExampleBackgroundTask"> <BackgroundTasks> <Task Type="systemEvent"/> </BackgroundTasks> </Extension> </Extensions> </Application> </Applications> <Capabilities> <Capability Name="internetClient" /> <rescap:Capability Name="runFullTrust" /> </Capabilities> </Package>
上面步驟完成就可以讓 WPF 程式呼叫 Background Task 與觸發 Windows runtime component 了。 另外,可以參考 UWP APIs available to a packaged desktop app 瞭解那些 APIs 是 packaged application 可以使用的。
[範例程式]
======
如果您的 WPF 程式不想上 Store 只是想要用到 Windows 10 APIs,可以參考 Calling Windows 10 APIs From a Desktop Application 介紹的 UWPDesktop package 方便您使用 APIs。
另外需要注意的是 2019 開始 Windows 10 支援 ARM64,目前不確定 Desktop Bridge 上去的 App 是否還有其他需要調整的地方,我也會研究再做補充。
References:
- Calling Windows 10 APIs From a Desktop Application
- Desktop Bridge – The Migrate phase: invoking a Win32 process from a UWP app
- Enhance your desktop application for Windows 10
- App Consult Team - Desktop Bridge
- Add Push Notifications the easy way with Partner Center + Microsoft Store Services SDK
- Extend your desktop application with modern UWP components
- Send a local toast notification from desktop C# apps
- Package a desktop application by using Visual Studio
- Microsoft/DesktopBridgeToUWP-Samples
- o your existing PC software
- UWP APIs callable from a classic desktop app
- Calling Windows 10 APIs From a Desktop Application
- Sending toast notifications from desktop apps sample