UWP透過AppServiceConnection和Win32 Process溝通

曾國維
3 min readAug 18, 2020

--

1.首先建立UWP專案

在參考新增Windows Desktop Extensions for the UWP 套件

修改Package.appmainifest讓UWP可以啟動Win32並透過AppService溝通,首先新增標籤

<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"
xmlns:rescap=”http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
xmlns:desktop=”http://schemas.microsoft.com/appx/manifest/desktop/windows10"
xmlns:uap3=”http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
IgnorableNamespaces=”uap mp uap3">

在Application標籤最後之前加入Extensions

<Extensions>
<desktop:Extension Category=”windows.fullTrustProcess” Executable=”exe檔案的位置(Assets/ConsoleFolder.exe)”>
<desktop:FullTrustProcess></desktop:FullTrustProcess>
</desktop:Extension>
<uap:Extension Category=”windows.appService”>
<uap:AppService Name=”自訂建立連線的Service名稱(GetFolderService)”/>
</uap:Extension>
</Extensions>
</Application>

在後面的Capabilities標籤加上runFullTrust

<Capabilities>
<Capability Name=”internetClient” />
<rescap:Capability Name=”runFullTrust” />
</Capabilities>

2.在App.xaml.cs中引入元件

using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.AppService;
using Windows.ApplicationModel.Background;

在OnLaunched function中,啟動Win32 Process

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
LaunchWin32Process();

}
private async void LaunchWin32Process()
{
await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync();
}

3.新增一個Console Project

右鍵對方案的屬性進行設定,讓FolderApp對ConsoleFolder進行依賴

在ConsoleFolder中的屬性進行編譯後的設定,將編譯後的exe放入FolderApp中

執行一次後會出錯,但在Assets中會出現ConsoleFolder編譯完成的執行檔,
將此執行檔加入專案中就不會再出錯了

4.建立AppServiceppService物件,當背景啟動App時用AppServiceTriggerDetails創建連線並實作連線方法,以及Cancel處理(在App.xaml.cs中實作)

static AppServiceConnection Connection = null;
BackgroundTaskDeferral AppServiceDeferral = null;
protected async override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
{
base.OnBackgroundActivated(args);
if(args.TaskInstance.TriggerDetails is AppServiceTriggerDetails)
{
AppServiceDeferral = args.TaskInstance.GetDeferral();
args.TaskInstance.Canceled += OnTaskCanceled;
AppServiceTriggerDetails details = args.TaskInstance.TriggerDetails as AppServiceTriggerDetails;
Connection = details.AppServiceConnection;
// Send request to win32
await SendToWin32();
}
}

private void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
if(this.AppServiceDeferral!= null)
{
// Complete the service defferral
this.AppServiceDeferral.Complete();
}
}

private async Task SendToWin32()
{
ValueSet valuePairs = new ValueSet();
valuePairs.Add(“GetListFolder”, “”);
AppServiceResponse response = null;
response = await Connection.SendMessageAsync(valuePairs);
ApplicationDataContainer container = Windows.Storage.ApplicationData.Current.LocalSettings;
container.Values[“FolderList”] = response.Message[“FolderList”];

}

5.回到ConsoleFolder專案,先加入引用(到WinSDK路徑上去找)

選擇Windows.winmd和System.Runtime.WindowsRuntime.dll

6.在Program.cs中建立AppServiceConnection物件,並開一個Thread去監聽來自UWP的訊息

static AppServiceConnection connection;
static void Main(string[] args)
{
new Thread(ThreadProc).Start();

while (true) ;
}
static async void ThreadProc()
{
connection = new AppServiceConnection();
connection.AppServiceName = “GetFolderService”;
connection.PackageFamilyName = Package.Current.Id.FamilyName;
connection.RequestReceived += Connection_RequestReceived;
AppServiceConnectionStatus status = await connection.OpenAsync();
switch (status)
{
case AppServiceConnectionStatus.Success:
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(“Connection established — waiting for requests”);
Console.WriteLine();
break;
case AppServiceConnectionStatus.AppNotInstalled:
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(“The app AppServicesProvider is not installed.”);
return;
case AppServiceConnectionStatus.AppUnavailable:
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(“The app AppServicesProvider is not available.”);
return;
case AppServiceConnectionStatus.AppServiceUnavailable:
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(string.Format(“The app AppServicesProvider is installed but it does not provide the app service {0}.”, connection.AppServiceName));
return;
case AppServiceConnectionStatus.Unknown:
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(string.Format(“An unkown error occurred while we were trying to open an AppServiceConnection.”));
return;
}
}

在Connection收到資料時處理的Function,回傳資料時一樣用ValueSet放Key和Value,用SendResponseAsync回傳給UWP

private static async void Connection_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
string key = args.Request.Message.First().Key;
Console.WriteLine(key);
ValueSet valuePairs = new ValueSet();

valuePairs.Add(“FolderList”, folder2.ToArray());
valuePairs.Add(“FileList”, fileList.ToArray());

await args.Request.SendResponseAsync(valuePairs);
}

7.最後,因為要在MainPage.xaml.cs和Program.cs溝通,在App.xaml.cs需要將載入Frame的時間點在OnBackgroundActivated而不是在OnLaunched,到了MainPage就可以Call使用

AppServiceResponse response = null;
response = await App.Connection.SendMessageAsync(valuePairs);

response一樣用ValueSet的方式進行取值

--

--