UWP with Desktop Extension – Part 3

desktop_yellow

If you haven’t read part 1 or part 2 of this series yet, please start there first. I have covered the “Hello World” version of a UWP with Desktop extension and explained how to launch different process types and pass in arguments. Now the next set of questions I want to answer in this post are:

  • How to establish a communication channel between the components?
  • How to send requests and receive responses from either side?
  • How to handle process exit scenarios?

TL; DR;

Short answers: An AppServiceConnection can be used to connect between the components and to allow bi-directional communication via ValueSets. Typically you want to host the AppService in-proc with your UWP app in this type of scenario.

Just show me the code on github …
https://github.com/StefanWickDev/UWP-FullTrust/tree/master/UWP_FullTrust_3

Let me run your sample from the Microsoft Store …
https://www.microsoft.com/store/apps/9NR0G35Q2F65

Declaring the AppService

The first step is to declare the AppService extension in our manifest, which is what will provide us with a two-way communication pipe between our components:

<Extensions>
 <uap:Extension Category="windows.appService">
  <uap:AppService Name="SampleInteropService" />
 </uap:Extension>
 <desktop:Extension Category="windows.fullTrustProcess" Executable="FullTrust\FullTrust.exe" />
</Extensions>

For full details on AppServices in the Universal Windows Platform our developer documentation has this good read: https://docs.microsoft.com/en-us/windows/uwp/launch-resume/how-to-create-and-consume-an-app-service

Establishing the Connection

Now in order to establish the connection, let’s take a look at our WPF app. One of the first things it does during launch is to establish the connection to the AppService, and then retain the instance of the AppServiceConnection for the lifetime of the connection. We are also hooking up the event handlers here to receive requests and to get notified when the connection gets closed:

// MainWindow.xaml.cs in WPF project
private async void InitializeAppServiceConnection()
{
 connection = new AppServiceConnection();
 connection.AppServiceName = "SampleInteropService";
 connection.PackageFamilyName = Package.Current.Id.FamilyName;
 connection.RequestReceived += Connection_RequestReceived;
 connection.ServiceClosed += Connection_ServiceClosed;

 AppServiceConnectionStatus status = await connection.OpenAsync();
 if (status != AppServiceConnectionStatus.Success)
 {
  // something went wrong ...
  MessageBox.Show(status.ToString());
  this.IsEnabled = false;
 } 
}

On the other side in the UWP, this will now trigger the OnBackgroundActivated event, and we’ll grab a reference to the AppServiceConnection instance there as well and hold on to it.

In our case the UWP is already running in the foreground, but note that it doesn’t have to: Client processes can also connect to the AppService if the foreground app is not running. It would activate the UWP in the background and establish the AppServiceConnection. The UWP process would then continue running in the background for as long as the connection is active.

// App.xaml.cs in UWP project
protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
{
 base.OnBackgroundActivated(args);

 if (args.TaskInstance.TriggerDetails is AppServiceTriggerDetails details)
 {
  // only accept connections from callers in the same package
  if (details.CallerPackageFamilyName == Package.Current.Id.FamilyName)
  {
   // connection established from the fulltrust process
   AppServiceDeferral = args.TaskInstance.GetDeferral();
   args.TaskInstance.Canceled += OnTaskCanceled;

   Connection = details.AppServiceConnection;
   AppServiceConnected?.Invoke(this, args.TaskInstance.TriggerDetails as AppServiceTriggerDetails);
  }
 }
}

Sending Requests and Responses

Now that the connection has been established and either side keeps a reference to the AppServiceConnection instances, we can send requests from either side, and respond to them accordingly.

Scenario 1 – UWP sends request to desktop extension

UWP apps don’t have access to the registry. Let’s assume for an LOB enterprise scenario I need to read a certain reg key. The UWP can use a desktop extension to get the job done, by sending an AppServiceRequest that contains the requested key. The desktop extension can then query the registry and send the name/value pairs back to the UWP as an AppServiceResponse.

screenshot8

// MainPage.Xaml.cs in UWP project
private async void btnClick_ReadKey(object sender, RoutedEventArgs e)
{
 ValueSet request = new ValueSet();
 request.Add("KEY", tbKey.Text);
 AppServiceResponse response = await App.Connection.SendMessageAsync(request);

// display the response key/value pairs
 tbResult.Text = "";
 foreach (string key in response.Message.Keys)
 {
 tbResult.Text += key + " = " + response.Message[key] + "\r\n";
 }
}

This triggers the registry reading code in the WPF app. The implementation can be found here on github in the ‘Connection_RequestReceived’ event handler. In a real production scenario you wouldn’t implement this extension as a windowed WPF app of course, but instead use a headless background process. But since we need UI for scenario #2, I went with WPF …

Scenario 2 – Desktop extension sends request to UWP

To show the reverse communication flow I am just picking a dummy scenario: I will send two double values from WPF to UWP and let the UWP app respond with the sum of both:

screenshot9

// MainWindow.xaml.cs in WPF project
private async void Button_Click(object sender, RoutedEventArgs e)
{
 // ask the UWP to calculate d1 + d2
 ValueSet request = new ValueSet();
 request.Add("D1", d1);
 request.Add("D2", d2);
 AppServiceResponse response = await connection.SendMessageAsync(request);
 double result = (double)response.Message["RESULT"];
 tbResult.Text = result.ToString();
}
// in MainPage.Xaml.cs in UWP project
private async void AppServiceConnection_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
 double d1 = (double)args.Request.Message["D1"];
 double d2 = (double)args.Request.Message["D2"];
 double result = d1 + d2;
 
 ValueSet response = new ValueSet();
 response.Add("RESULT", result);
 await args.Request.SendResponseAsync(response);

// log the request in the UI for demo purposes
 await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
 {
 tbRequests.Text += string.Format("Request: {0} + {1} --> Response = {2}\r\n", d1, d2, result);
 });
}

 

Handling Process Exit Scenarios

Now what happens if one side of the communication pipeline goes away, for whatever reason? It’s up to the app to handle this and decide what should happen. Generally speaking there are three options: try to relaunch the companion component, or keep going without the companion (if it’s non-essential), or failfast to terminate yourself. For our demo I will implement two scenarios:

Scenario 1 – UWP will relaunch closed WPF

// MainPage.xaml.cs in UWP project
private async void MainPage_AppServiceDisconnected(object sender, EventArgs e)
{
 await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, ()=>
 {
  // disable UI to access the connection
  btnRegKey.IsEnabled = false;

  // ask user if they want to reconnect
  Reconnect();
 }); 
}

private async void Reconnect()
{
 if (App.IsForeground)
 {
  MessageDialog dlg = new MessageDialog("Connection to desktop process lost. Reconnect?");
  UICommand yesCommand = new UICommand("Yes", async (r) =>
  {
   await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync();
  });
  dlg.Commands.Add(yesCommand);
  UICommand noCommand = new UICommand("No", (r) => { });
  dlg.Commands.Add(noCommand);
  await dlg.ShowAsync();
 }
}

Scenario 2 – WPF will terminate itself when UWP closes

// MainWindow.xaml.cs in WPF project
private void Connection_ServiceClosed(AppServiceConnection sender, AppServiceClosedEventArgs args)
{
 // connection to the UWP lost, so we shut down the desktop process
 Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
 {
  Application.Current.Shutdown();
 })); 
}

This is it for part 3 of this series. Hopefully I answered most of the question around communication between the app and its extension in this sample. If not, please leave comments here, hit me up on Twitter or post your questions on Stackoverflow. If they are tagged with ‘UWP’ they will show on my radar.

Next up: Part 4 – Submitting to the Microsoft Store

11 comments

  1. Hello Stefan,

    Thank you for this lesson.

    At this moment I develop an extension for Microsoft Edge based on native messaging. Could you please consider this official example? https://github.com/MicrosoftEdge/MicrosoftEdge-Extensions-Demos/blob/master/SecureInput/NativeMessagingHostInProcess/App.xaml.cs

    So my question is how to handle multiple connections to the app correctly? In this sample there is very strange synchronization in the OnBackgroundActivated method and no such a thing in other methods…

    Well I did some experiments and it seems that OnBackgroundActivated is always executed by only one thread, but RequestReceived and ServiceClosed event handlers for different connections can be executed by several threads and the same time… Thus a correct synchronization for dictionaries is required.

    Could you please shed more light on this?

    Like

    1. Yes, that’s correct. The event handlers may fire on different threads and things will get more complicated if you have more than one client for your AppService. This would be a good thing to cover in an additional, dedicated sample.

      Like

  2. Hi Stefan,

    Great lessons for desktop extension!

    I am having UWP app written in C++, and I want to add desktop extension, again written in C++. Your example from part 2 shows it is possible to have UWP with C++. My question is, can I
    establish “channel” in between for information exchange? Concept of AppService is great, but as far as I saw, it can’t be used from pure C++. Or, there is something else you can propose?

    Regards,
    Aleksandar

    Like

  3. Hello Stefan,

    Really great article on how to communicate between UWP and Windows Application.
    I have implemented WPF application as a service and UWP application as a client, following all above steps I’m able to read Registry, able to check the Process using “System.Diagnostics.Process” from a service.

    The only question I had in mind though, will Microsoft Store allow/accept my application when I will be submitting it to Store to verify and publish the same, as we are checking other applications registry and checking for the Process which is not allowed in UWP application.

    Thanks in advance!

    Like

  4. Hello Stefan,

    Really great article on how to communicate between UWP and Windows Application.
    I have implemented WPF application as a service and UWP application as a client, following all above steps I’m able to read Registry, able to check the Process using “System.Diagnostics.Process” from a service.

    Only question I had in mind though, will Microsoft Store allow/accept my application when I will be submitting it to Store to verify and publish the same, as we are checking other applications registry and checking for the Process which is not allowed in UWP application.

    Thanks in advance!

    Like

      1. Sorry for the late reply, I just returned from vacation.

        What you describe is allowed and can be accepted in the Windows Store, unless you do other things in the app that would violate Store policy. Hope this helps.

        Thanks,
        Stefan

        Like

  5. Hi Stefan,

    I am using visual studio 2017 with SDK 16299 installed, but when I build UWP_FullTrust_3, I am having a warning “warning MSB3245: Could not resolve this reference. Could not locate the assembly “Windows” and some errors

    error CS0234: The type or namespace name ‘ApplicationModel’ does not exist in the namespace ‘Windows’ (are you missing an assembly reference?)
    error CS0234: The type or namespace name ‘Collections’ does not exist in the namespace ‘Windows.Foundation’

    Do you know what’s wrong about it? Where can I add reference to assembly “Windows”.

    BR,
    CY

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s