Desktop Bridge 爲了讓 win32 程式在轉成 UWP 運作架構後保有 win32 的一些特性,例如:自動隨系統啓動被開啓,可以常駐在 System tray 等。Fall creators update 開放給一般的 UWP 程式具有部分功能。讓我們來看看要怎麽使用吧。
參考 What's New in Windows 10 for developers, version 1709 提到幾個用來啓動 app 的方式:
- 利用 StartupTask Class 讓 app 可以在用戶登入或是系統啓動後自動執行
- 利用 launched from the command line 識別 app 被啓動的原因,做出對應的處理
- 利用 RequestRestartAsync() 或 RequestRestartForUserAsync() 來重新啓動 App。需注意:
- 只能是 app 顯示中且在 foreground 才能使用
- 如果啓動失敗,用戶手動再啓動 app 原本使用的 restart arguments 會被忽略
- 可利用 LaunchActivatedEventArgs.PreviousExecutionState 識別 app 被啓動是來自 restart 或 resume
- 如果 app 有任何 in-process background tasks 呼叫該 api 會無效; out-of-process background tasks 不受影響
- 如果 app 不是以正常方式啓動,而是用 share contract, file picker, app service 等,就不應該呼叫該 api ,因爲不會是用戶預期的
- 由於可以送入 User 需要確認該 User 是否有權限執行
- 如果呼叫該 api 就不應該在呼叫 Extended Execution session
- 設定 Startup Task 之後,App 隨著用戶登入或系統重新啓動會被開啓,預設開啓後自動視窗最小化
- 系統的 URI scheme 加入了新的内容: Launch the Windows Settings app
本篇介紹 StartupTask Class 讓 App 可以隨著用戶登入或系統自動執行。需注意:StartupTask Class 只有在 Desktop 才能使用。
實現的步驟如下:
step 1. 將專案 target version 選擇到 16299 或以上
step 2. 設定 Package.appxmanifest 加入新的 UAP namespace (contract version 5) 與 extensions
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
<!-- 加入 uap5 的宣告 -->
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
IgnorableNamespaces="uap mp uap5">
<Applications>
<Application>
<Extensions>
<!-- 加入 uap5 定義的 extension -->
<uap5:Extension
Category="windows.startupTask"
Executable="MyStartup.exe"
EntryPoint="MyStartup.App">
<uap5:StartupTask
TaskId="MyStartupId_UWP"
Enabled="false"
DisplayName="My Startup UWP" />
</uap5:Extension>
</Extensions>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClient" />
</Capabilities>
</Package>
[注意]
- 如果 Package.appxmanifest 有自行設定 TargetDeviceFamily 要記得也更新 MaxVersionTested 到 10.0.6299.0 以上,不然會出現參數錯誤的 System.ArgumentException: 'The parameter is incorrect.',參考如下設定:
<Dependencies> <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14393.0" MaxVersionTested="10.0.16299.0" /> </Dependencies>
- UAP namespace (contract version 5) 目前只有支援 Desktop 而已,記得要在 code 裏面做判斷
- Category 屬性固定是 windows.startupTask
- TaskId 用來識別 task 的唯一值,在 app 裏面利用 StartupTask Class 呼叫 Task 時需要
- Desktop Bridge 的 EntryPoint 屬性使用 Windows.FullTrustApplication,在 UWP 程式使用 app 執行檔 (*.exe)
- Desktop Bridge 的 Enabled 屬性可以設定 true/false,UWP 的屬性系統會自動忽略,因爲需要用戶手動同意才可以使用
- Desktop Bridge 可以使用多組 startupTask extensions (每一個都有自己的 Executable),UWP 只能有一組
stet 3. 利用 StartupTask Class 取得狀態,如果未啓用跳出讓用戶選擇是否啓動
private async void OnRequestStartupClick(object sender, RoutedEventArgs e)
{
// 取得目前 TaskId (MyStartupId_UWP, 根據設定在 package.appmanifest 的值) 的狀態
var startupTask = await StartupTask.GetAsync("MyStartupId_UWP");
if (startupTask.State == StartupTaskState.Disabled)
{
// 如果是 disabled 代表還沒有被加入到 Startup 裏面
var newState = await startupTask.RequestEnableAsync();
tblState.Text = newState.ToString();
}
else
{
tblState.Text = startupTask.State.ToString();
}
}
StartupTask Class 只能在 foreground 時呼叫,可利用 RequestEnableAsync() 開啓 或 Disable() 取消 task 的使用。
StartupTaskState 的列舉有下列幾種:
Name | Description |
Disabled | The task is disabled. 未啓用,可搭配 RequestEnableAsync() 請求授權 |
DisabledByPolicy | The task is disabled by the administrator or group policy. Platforms that don't support startup tasks also report DisabledByPolicy. 權限不足,需要先去設定帳號或是帳號群組的權限才能使用 |
DisabledByUser | The task was disabled by the user. It can only be re-enabled by the user. 這個很特別,如果在 RequestEnableAsync() 時按下取消,之後再詢問該 Task 就都會是 DisabledByUser; 如果從 Task Manager -> Startup 關閉該 Task 一樣會得到這個狀態 |
Enabled | The task is enabled. 代表該 Task 已經被啓動 |
step 4. 處理自動啓動時傳入的 arguments 先在 App.xaml.cs 注冊處理 OnActivated 事件:
private Frame rootFrame;
protected override void OnActivated(IActivatedEventArgs args)
{
CreateRootFrame(args.PreviousExecutionState);
base.OnActivated(args);
string parameters = string.Empty;
if (args.Kind == ActivationKind.StartupTask)
{
// 處理來自 StartupTask 的參數
var startupTaskArgs = args as StartupTaskActivatedEventArgs;
// 準備需要的處理邏輯,帶入 MainPage
parameters = "from onactivated";
}
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
rootFrame.Navigate(typeof(MainPage), parameters);
}
// Ensure the current window is active
Window.Current.Activate();
}
private void CreateRootFrame(ApplicationExecutionState previousState)
{
rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (previousState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
}
CreateRootFrame 是我自定義的來維持 rootFrame 正常被建立,因爲 Startup 之後啓動 App 是不會進去 OnLaunched 的所以需要處理 rootFrame 跟 App 初始化的邏輯。 這樣就完成了,是不是很容易。
======
UWP 支援度離 win32 又接近了一大步,希望有更多新功能的推出幫助開發。希望對大家有所幫助。
References:
- How to Configure your app to start at log-in
- ActivationKind Enum
- CoreApplication Class
- StartupTask Class
- Microsoft/AppModelSamples
- Launch the Windows Settings app
- Desktop Bridge – Enhancing a desktop application with the UWP APIs
- Desktop Bridge
- Microsoft/DesktopBridgeToUWP-Samples
- Configure your app to start at log-in