Passing data between Windows 10 IoT Core headed and headless apps

As shown in my last blog, Windows 10 IoT Core supports headed and headless apps. I can run only one headed (UWP) app at a time but running multiple headless apps is possible too.

But how do I deploy these apps? And can I pass information between apps while running?

In this blog, we dive deeper into the unseen world of background application.

This is part two of a series of blogs about using headless apps:

  • Part 1: Building a Windows 10 IoT Core background webserver
  • Part 2: Combining Windows 10 IoT Core headed and headless apps

In my previous blog, I showed how to build a background application. It was a webserver accepting web calls on port 8081. Now we transform this webserver a bit so we can pass the query string of the call towards a UWP app running at the same time.

And how do we do that? Do we have to reference background app within the UWP app? No exactly, that means: we are not hardcoding a reference. No, we use a connection called an AppServiceConnection. We simply register our background app as an AppService within the Windows 10 IoT Core OS.

And when the UWP app is started, it tries to make a connection to the already available background app using the name it’s registered under.

So here we go!

Register the background service

To register the service, we have to alter the Package.appmanifest. But we have to alter a little bit of XML, the inner logic of this file. To do this, select the file inside the Solution Explorer of Visual Studio 2015. Then press the F7 key or right-click on it and select View Code.

Note: If you are asked “The document ‘yadda yadda yadda’ is already open. Do you want to close it?” just answer Yes; the visual designer was still open. Next time it will still be available.

Now replace:

  <Extension Category="windows.backgroundTasks" 
             EntryPoint="BackgroundApplication1.StartupTask">
    <BackgroundTasks>
      <iot:Task Type="startup" />
    </BackgroundTasks>
  </Extension>
</Extensions>

with:

<Extensions>
  <uap:Extension Category="windows.appService"
              EntryPoint="BackgroundApplication1.StartupTask">
    <uap:AppService Name="BackgroundWebService" />
  </uap:Extension>
</Extensions>

This registers the class StartupTast in the namespace BackgroundApplication1 as an appservice with the name BackgroundWebService.

At this point, the background app could be used as an appservice but it is useless, no data is passed between this app and the consuming UWP app.

So in the background service, we are going to make contact with that connection and we are going to listen for commands passed by the UWP and we are going pass data to the UWP app.

Listening for commands

First, we fix the StartupTask so it makes contact with the connection:

public sealed class StartupTask : IBackgroundTask
{
  private static BackgroundTaskDeferral _Deferral = null;
 
  private AppServiceConnection _connection;
 
  public async void Run(IBackgroundTaskInstance taskInstance)
  {
    _Deferral = taskInstance.GetDeferral();
 
    var triggerDetails = taskInstance.TriggerDetails as 
                                      AppServiceTriggerDetails;
    _connection = triggerDetails.AppServiceConnection;
 
    var webserver = new MyWebserver(_connection);
 
    await ThreadPool.RunAsync(workItem =>
    {
      webserver.Start();
    });
  }
}

So we have a connection and we pass it to the webserver:

internal class MyWebserver
{
  private AppServiceConnection _connection;
 
  private const uint BufferSize = 8192;
 
  private string _name;
 
  public MyWebserver(AppServiceConnection connection)
  {
    _connection = connection;
    _connection.RequestReceived += ConnectionRequestReceived;
  }
 
  private void ConnectionRequestReceived(
                       AppServiceConnection sender, 
                       AppServiceRequestReceivedEventArgs args)
  {
    _name = (string)args.Request.Message.First().Value;
  }

  ...

The message passed is actually a ValueSet, a list of key-value pairs. For this example, we only check the value of the first pair. We assume it’s a name, so we pass it in the HTML:

...
var name = string.IsNullOrEmpty(_name) ? "Background" : _name;
 
var html = Encoding.UTF8.GetBytes($"<html><head><title>Background Message</title></head><body>Hello {name} from the background process!<br/>{query}</body></html>");
...

We can also pass information towards the calling UWP app. We just send a message:

...
string query = GetQuery(request);
 
await _connection.SendMessageAsync(
      new ValueSet { new KeyValuePair<string, 
                     object>("Query", query) });
...

These are all the changes on the background app. Now deploy it (do not execute it on) to the RaspberryPi. Just right click the project and select deploy:

back06

Making the connection in the UWP app

So add a new UWP app to the solution (File|Add|New Project|Blank App (Universal Windows)) and add the following XAML to the MainPage.xaml:

<StackPanel>
  <TextBlock Name="tbMessage"
             FontSize="40" />
  <StackPanel Orientation="Horizontal">
    <TextBox Name="tbName"
             FontSize="40"
             Text="Sander" />
    <Button Name="btnSend"
            Content="Send"
            Click="btnSend_Click"
            FontSize="40" />
  </StackPanel>
</StackPanel>

And we replace the code in the MainPage.xaml.cs with:

public sealed partial class MainPage : Page
{
  private AppServiceConnection _WebService = null;
 
  public MainPage()
  {
    this.InitializeComponent();
 
    SetupAppService();
  }
 
  private async void SetupAppService()
  {
    var listing = await AppServiceCatalog.
          FindAppServiceProvidersAsync("BackgroundWebService");
 
    var packageName = (listing.Count == 1) 
                        ? listing[0].PackageFamilyName 
                        : string.Empty;
 
    _WebService = new AppServiceConnection();
    _WebService.AppServiceName = "BackgroundWebService";
    _WebService.PackageFamilyName = packageName;
 
    var status = await _WebService.OpenAsync();
 
    if (status != AppServiceConnectionStatus.Success)
    {
        tbMessage.Text = "Could not connect: " + 
                         status.ToString();
    }
    else
    {
        tbMessage.Text = "Connected: " + status.ToString();
        _WebService.RequestReceived += 
                               _WebService_RequestReceived;
    }
  }
 
  private async void btnSend_Click(
                             object sender, RoutedEventArgs e)
  {
    await _WebService.SendMessageAsync(
      new ValueSet { 
        new KeyValuePair<string, object>("Value", 
                                         tbName.Text) });
  }
 
  private async void _WebService_RequestReceived(
             AppServiceConnection sender, 
             AppServiceRequestReceivedEventArgs args)
  {
      await Dispatcher.RunAsync(
        Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
          tbMessage.Text = args.Request.Message.First().Key + 
                           "=" + 
                           args.Request.Message.First().Value;
        });
  }
}

On startup, in the mainpage constructor, we start the connection. We especially look for a connection name “BackgroundWebService”. Once the connection is made, we listen for message received and we show them in the textblock.

And finally, we can send messages back to the appservice: the name entered in the textbox.

So first we deploy and start the UWP in the RaspberryPi and we pass a name to the service once it is connected. Click the Send button:

WP_20160411_18_06_30_Rich

Next, we use a browser like Edge to make a request to the background app. We see the name, passed by the UWP, appear in the HTML returned :

back07

And if we check the UWP again, the query passed to the webserver is now passed to the UWP as a message also:

WP_20160411_18_08_49_Rich

So now we know how to pass data between a background app and a UWP app.

 

Advertenties