HoloLens Emulator and ‘The emulator is unable to connect to the device operating system’

NB: The usual blog disclaimer for this site applies to posts around HoloLens. I am not on the HoloLens team. I have no details on HoloLens other than what is on the public web and so what I post here is just from my own experience experimenting with pieces that are publicly available and you should always check out the official developer site for the official source of information.

Just in case this helps anyone else who finds it on a web search. On my long-suffering Surface Pro 3 I’ve had tonnes of different versions of Visual Studio installed and various emulators over time and today I was struggling to get the HoloLens emulator (14393) to run.

I’d followed the setup guide over here;

https://developer.microsoft.com/en-us/windows/holographic/install_the_tools

and that all went well but whenever I ran the emulator I would hit;

Capture

and I was specifically finding that the emulator would run up ok but then I couldn’t really interact with it via mouse/keyboard etc. but I managed to find a few threads over on the HoloLens forums that related to this error like these;

but I couldn’t quite narrow down the specifics of the error that was causing me trouble and some of the solutions mentioned seemed quite drastic like involving the complete uninstall/install of Visual Studio.

A lot of those threads though related to the configuration of virtual switches within Hyper-V and so what I ended up doing to resolve my error was;

  1. Uninstalled the Windows Phone emulators.
  2. Uninstalled the HoloLens emulator.
  3. Uninstalled any remaining virtual machines from Hyper-V that related to emulators.
  4. Uninstalled any virtual switches that seemed related to ‘Emulators’.
  5. Reinstalled the HoloLens emulator.

and then had a look at my networking configuration for the HoloLens emulator and that seemed to have been recreated successfully;

image

and the emulator started successfully without the ‘Object reference not set to an instance of an object’ error and the emulator seems to run up fine.

If you’re seeing that error on startup, I can’t promise that this will fix your problem but hopefully it might be an additional pointer towards trying to narrow down what your specific problem might be.

Windows 10, 1607 and UWP –Returning to Rome for an Experiment with Remote App Services

I wanted to return to the experiment that I did with ‘Project Rome’ in this post;

Windows 10 Anniversary Update (1607) and UWP Apps – Connected Apps and Devices via Rome

where I managed to experiment with the new APIs in Windows 10 1607 which allow you to interact with your graph of devices.

If you’ve not seen ‘Rome’ and the Windows.Systems.RemoteSystems classes then there’s a good overview here;

Connected Apps and Devices

In that previous post, I’d managed to use the RemoteSystemWatcher class to determine which remote devices I had and then to use its friends the RemoteSystemConnectionRequest and the RemoteLauncher class to have code on one of my devices launch an application (Maps) on another one of my devices. That post was really my own experimentation around the document here;

Launch an app on a remote device

I wanted to take that further though and see if I could use another capability of ‘Rome’ which is the ability for an app on one device to invoke an app service that is available on another device. That’s what this post is about and it’s really my own experimentation around the document here;

Communicate with a remote app service

In order to do that, I needed to come up with a scenario and I made up an idea that runs as follows;

  • There’s some workflow which involves redacting faces from images
  • The images to be redacted are stored in some blob container within Azure acting as a ‘queue’ of images to be worked on
  • The redacted images are to be stored in some other blob container within Azure
  • The process of downloading images, redacting them and then uploading the new images might be something that you’d want to run either locally on the device you’re working on or, sometimes, you might choose to do it remotely on another device which perhaps was less busy or had a faster/cheaper network connection.

Getting Started

Clearly, this is a fairly ‘contrived’ scenario but I wandered off into one of my Azure Storage accounts with the ‘Azure Storage Explorer’ and I made two containers named processed and unprocessed respectively;

Capture

and here’s the empty processed container;

Capture1

I then wrote some a fairly clunky class based on top of the Nuget package WindowsAzure.Storage which would do a few things for me;

  • Get me lists of the URIs of the blobs in the two containers.
  • Download a blob and present it back as a decoded bitmap in the form of a SoftwareBitmap
  • Upload a StorageFile to the processed container given the file and a name for the new blob
  • Delete a blob given its URI

i.e. it’s pretty much just the subset of the CRUD operations that I need for what my app needs to do.

That class ended up looking like this and, if you take a look at it, then note that it’s hard-wired to expect JPEG images;

namespace App26
{
  using Microsoft.WindowsAzure.Storage;
  using Microsoft.WindowsAzure.Storage.Auth;
  using Microsoft.WindowsAzure.Storage.Blob;
  using System;
  using System.Collections.Generic;
  using System.IO;
  using System.Linq;
  using System.Threading.Tasks;
  using Windows.Graphics.Imaging;
  using Windows.Storage;

  public class AzurePhotoStorageManager
  {
    public AzurePhotoStorageManager(
      string azureStorageAccountName,
      string azureStorageAccountKey,
      string unprocessedContainerName = "unprocessed",
      string processedContainerName = "processed")
    {
      this.azureStorageAccountName = azureStorageAccountName;
      this.azureStorageAccountKey = azureStorageAccountKey;
      this.unprocessedContainerName = unprocessedContainerName;
      this.processedContainerName = processedContainerName;
      this.InitialiseBlobClient();
    }
    void InitialiseBlobClient()
    {
      if (this.blobClient == null)
      {
        this.storageAccount = new CloudStorageAccount(
          new StorageCredentials(this.azureStorageAccountName, this.azureStorageAccountKey),
          true);

        this.blobClient = this.storageAccount.CreateCloudBlobClient();
      }
    }
    public async Task<IEnumerable<Uri>> GetProcessedPhotoUrisAsync()
    {
      var entries = await this.GetPhotoUrisAsync(this.processedContainerName);
      return (entries);
    }
    public async Task<IEnumerable<Uri>> GetUnprocessedPhotoUrisAsync()
    {
      var entries = await this.GetPhotoUrisAsync(this.unprocessedContainerName);
      return (entries);
    }
    public async Task<SoftwareBitmap> GetSoftwareBitmapForPhotoBlobAsync(Uri storageUri)
    {
      // This may not quite be the most efficient function ever known to man🙂
      var reference = await this.blobClient.GetBlobReferenceFromServerAsync(storageUri);
      await reference.FetchAttributesAsync();

      SoftwareBitmap bitmap = null;

      using (var memoryStream = new MemoryStream())
      {
        await reference.DownloadToStreamAsync(memoryStream);

        var decoder = await BitmapDecoder.CreateAsync(
          BitmapDecoder.JpegDecoderId,
          memoryStream.AsRandomAccessStream());

        // Going for BGRA8 and premultiplied here saves me a lot of pain later on
        // when using SoftwareBitmapSource or using CanvasBitmap from Win2D.
        bitmap = await decoder.GetSoftwareBitmapAsync(
          BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
      }
      return (bitmap);
    }
    public async Task PutFileForProcessedPhotoBlobAsync(
      string photoName,
      StorageFile file)
    {
      var container = this.blobClient.GetContainerReference(this.processedContainerName);

      var reference = container.GetBlockBlobReference(photoName);
      
      await reference.UploadFromFileAsync(file);
    }
    public async Task<bool> DeletePhotoBlobAsync(Uri storageUri)
    {
      var container = await this.blobClient.GetBlobReferenceFromServerAsync(storageUri);
      var result = await container.DeleteIfExistsAsync();
      return (result);
    }
    async Task<IEnumerable<Uri>> GetPhotoUrisAsync(string containerName)
    {
      var uris = new List<Uri>();
      var container = this.blobClient.GetContainerReference(containerName);

      BlobContinuationToken continuationToken = null;

      do
      {
        var results = await container.ListBlobsSegmentedAsync(continuationToken);

        if (results.Results?.Count() > 0)
        {
          uris.AddRange(results.Results.Select(r => r.Uri));
        }
        continuationToken = results.ContinuationToken;

      } while (continuationToken != null);

      return (uris);
    }
    CloudStorageAccount storageAccount;
    CloudBlobClient blobClient;
    string azureStorageAccountName;
    string azureStorageAccountKey;
    string unprocessedContainerName;
    string processedContainerName;
  }
}

and is probably nothing much to write home about Smile I also wrote another little class which attempts to take a SoftwareBitmap, to use the FaceDetector (UWP) API to find faces within that SoftwareBitmap and then to use Win2D.uwp to replace any faces that the FaceDetector finds with black rectangles.

For my own ease, I had the class then store the resultant bitmap into a temporary StorageFile. That class ended up looking like this;

namespace App26
{
  using Microsoft.Graphics.Canvas;
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Runtime.InteropServices.WindowsRuntime;
  using System.Threading.Tasks;
  using Windows.Foundation;
  using Windows.Graphics.Imaging;
  using Windows.Media.FaceAnalysis;
  using Windows.Storage;
  using Windows.UI;

  public class PhotoFaceRedactor
  {
    public async Task<StorageFile> RedactFacesToTempFileAsync(SoftwareBitmap incomingBitmap)
    {
      StorageFile tempFile = null;

      await this.CreateFaceDetectorAsync();

      // We assume our incoming bitmap format won't be supported by the face detector. 
      // We can check at runtime but I think it's unlikely.
      IList<DetectedFace> faces = null;
      var pixelFormat = FaceDetector.GetSupportedBitmapPixelFormats().First();

      using (var faceBitmap = SoftwareBitmap.Convert(incomingBitmap, pixelFormat))
      {
        faces = await this.faceDetector.DetectFacesAsync(faceBitmap);
      }
      if (faces?.Count > 0)
      {
        // We assume that our bitmap is in decent shape to be used by CanvasBitmap
        // as it should already be BGRA8 and Premultiplied alpha.
        var device = CanvasDevice.GetSharedDevice();

        using (var target = new CanvasRenderTarget(
          CanvasDevice.GetSharedDevice(),
          incomingBitmap.PixelWidth,
          incomingBitmap.PixelHeight,
          96.0f))
        {
          using (var canvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(device, incomingBitmap))
          {
            using (var session = target.CreateDrawingSession())
            {
              session.DrawImage(canvasBitmap,
                new Rect(0, 0, incomingBitmap.PixelWidth, incomingBitmap.PixelHeight));

              foreach (var face in faces)
              {
                session.FillRectangle(
                  new Rect(
                    face.FaceBox.X,
                    face.FaceBox.Y,
                    face.FaceBox.Width,
                    face.FaceBox.Height),
                  Colors.Black);
              }
            }
          }
          var fileName = $"{Guid.NewGuid()}.jpg";

          tempFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(
            fileName, CreationCollisionOption.GenerateUniqueName);

          using (var fileStream = await tempFile.OpenAsync(FileAccessMode.ReadWrite))
          {
            await target.SaveAsync(fileStream, CanvasBitmapFileFormat.Jpeg);
          }
        }
      }
      return (tempFile);
    }
    async Task CreateFaceDetectorAsync()
    {
      if (this.faceDetector == null)
      {
        this.faceDetector = await FaceDetector.CreateAsync();
      }
    }
    FaceDetector faceDetector;
  }
}

I also wrote a static method that co-ordinated these two classes to perform the whole process of getting hold of a photo, taking out the faces in it and uploading it back to blob storage and that ended up looking like this;

namespace App26
{
  using System;
  using System.Threading.Tasks;

  static class RedactionController
  {
    public static async Task RedactPhotoAsync(Uri photoBlobUri, string newName)
    {
      var storageManager = new AzurePhotoStorageManager(
        Constants.AZURE_STORAGE_ACCOUNT_NAME,
        Constants.AZURE_STORAGE_KEY);

      var photoRedactor = new PhotoFaceRedactor();

      using (var bitmap = await storageManager.GetSoftwareBitmapForPhotoBlobAsync(photoBlobUri))
      {
        var tempFile = await photoRedactor.RedactFacesToTempFileAsync(bitmap);

        await storageManager.PutFileForProcessedPhotoBlobAsync(newName, tempFile);

        await storageManager.DeletePhotoBlobAsync(photoBlobUri);
      }
    }
  }
}

Adding in Some UI

I added in a few basic ‘ViewModels’ which surfaced this information into a UI and made something that seemed to essentially work. The UI is as below;

Capture2

and you can see the 2 lists of processed/unprocessed photos and if I click on one of the View buttons then the UI displays that photo;

Capture3

and then tapping on that photo takes it away again. If I click on one of the ‘Process’ buttons then there’s a little bit of a progress ring followed by an update to the UI which I’m quite lazy about in the sense that I simply requery all the data from Azure again. Here’s the UI after I’ve processed that particular image;

Capture4

and if I click on that bottom View button then I see;

Capture5

As an aside, the Image that is displaying things here has its Stretch set which is perhaps why the images look a bit odd Smile

Without listing all the XAML and all the view model code, that got me to the point where I had my basic bit of functionality working.

What I wanted to add to this then was a little bit from ‘Project Rome’ to see if I could set this up such that this functionality could be offered as an ‘app service’ and what especially interested me about this idea was whether the app could become a client of itself in the sense that this app could choose to let the user either do this photo ‘redaction’ locally on the device they were on or remotely on another one of their devices.

Making an App Service

Making a (basic) app service is pretty easy. I simply edited my manifest to say that I was making an App Service but I thought that I’d highlight that it’s necessary (as per the official docs) to make sure that my service called PhotoRedactionService has marked itself as being available to remote systems as below;

Capture6

and then I wrote the basics of a background task and an app service using the new mechanism that’s present in 1607 which is to override the OnBackgroundActivated method on the App class and do the background work inside of there rather than having to go off and write a completely separate WinRT component. Here’s that snippet of code;

    protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
    {
      this.taskDeferral = args.TaskInstance.GetDeferral();
      args.TaskInstance.Canceled += OnBackgroundTaskCancelled;

      var details = args.TaskInstance.TriggerDetails as AppServiceTriggerDetails;

      if ((details != null) && (details.Name == Constants.APP_SERVICE_NAME))
      {
        this.appServiceConnection = details.AppServiceConnection;
        this.appServiceConnection.RequestReceived += OnRequestReceived;        
      }
    }
    void OnBackgroundTaskCancelled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
    {
      this.appServiceConnection.Dispose();
      this.appServiceConnection = null;
      this.taskDeferral?.Complete();
    }
    async void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
    {
      var deferral = args.GetDeferral();

      var incomingUri = args.Request.Message[Constants.APP_SERVICE_URI_PARAM_NAME] as string;

      var uri = new Uri(incomingUri);

      // TODO: Move this function off the viewmodel into some utiliy class.
      await RedactionController.RedactPhotoAsync(uri, MainPageViewModel.UriToFileName(uri));

      deferral.Complete();
    }
    AppServiceConnection appServiceConnection;
    BackgroundTaskDeferral taskDeferral;

In that code fragment – you’ll see that all that’s happening is;

  1. We receive a background activation.
  2. We check to see if its an ‘app service’ type of activation and, if so, whether the name of the activation matches my service (Constants.APP_SERVICE_NAME = “PhotoRedactionService”)
  3. We handle the RequestReceived event
    1. We look for a URI parameter to be passed to us (the URI of the photo to be redacted)
    2. We call into our code to do the redaction

and that’s pretty much it. I now how have an app service that does ‘photo redaction’ for me and I’ve got no security or checks around it whatsoever (which isn’t perhaps the best idea!).

Adding in some ‘Rome’

In that earlier screenshot of my ‘UI’ you’d have noticed that I have a Checkbox which says whether to perform ‘Remote Processing’ or not;

Capture7

this Checkbox is simply bound to a property on a ViewModel and the ComboBox next to it is bound to an ObservableCollection<RemoteSystem> in this way;

        <ComboBox
          Margin="4"
          MinWidth="192"
          HorizontalAlignment="Center"
          ItemsSource="{x:Bind ViewModel.RemoteSystems, Mode=OneWay}"
          SelectedValue="{x:Bind ViewModel.SelectedRemoteSystem, Mode=TwoWay}">
          <ComboBox.ItemTemplate>
            <DataTemplate x:DataType="rem:RemoteSystem">
              <TextBlock
                Text="{x:Bind DisplayName}" />
            </DataTemplate>
          </ComboBox.ItemTemplate>
        </ComboBox>

The population of that list of ViewModel.RemoteSystems is pretty easy and it was something that I learned in my previous post. I simply have some code which bootstraps the process;

      var result = await RemoteSystem.RequestAccessAsync();

      if (result == RemoteSystemAccessStatus.Allowed)
      {
        this.RemoteSystems = new ObservableCollection<RemoteSystem>();
        this.remoteWatcher = RemoteSystem.CreateWatcher();
        this.remoteWatcher.RemoteSystemAdded += OnRemoteSystemAdded;
        this.remoteWatcher.Start();
      }

and then when a new RemoteSystem is added I make sure it goes into my collection;

    void OnRemoteSystemAdded(RemoteSystemWatcher sender, RemoteSystemAddedEventArgs args)
    {
      this.Dispatch(
        () =>
        {
          this.remoteSystems.Add(args.RemoteSystem);

          if (this.SelectedRemoteSystem == null)
          {
            this.SelectedRemoteSystem = args.RemoteSystem;
          }
        }
      );
    }

and so now I’ve got a list of remote systems that might be able to process an image for me.

Invoking the Remote App Service

The last step is to invoke the app service remotely and I have a method which does that for me that is invoked with the URI of the blob of the photo to be processed;

    async Task RemoteRedactPhotoAsync(Uri uri)
    {
      var request = new RemoteSystemConnectionRequest(this.selectedRemoteSystem);
      using (var connection = new AppServiceConnection())
      {
        connection.AppServiceName = Constants.APP_SERVICE_NAME;

        // Strangely enough, we're trying to talk to ourselves but on another
        // machine.
        connection.PackageFamilyName = Package.Current.Id.FamilyName;
        var remoteConnection = await connection.OpenRemoteAsync(request);

        if (remoteConnection == AppServiceConnectionStatus.Success)
        {
          var valueSet = new ValueSet();
          valueSet[Constants.APP_SERVICE_URI_PARAM_NAME] = uri.ToString();
          var response = await connection.SendMessageAsync(valueSet);

          if (response.Status != AppServiceResponseStatus.Success)
          {
            // Bit naughty throwing a UI dialog from this view model
            await this.DisplayErrorAsync($"Received a response of {response.Status}");
          }
        }
        else
        {
          await this.DisplayErrorAsync($"Received a status of {remoteConnection}");
        }
      }
    }

For me, the main things of interest here would be that this code looks pretty much like any invocation to an app service except that we have the extra step here of constructing the RemoteSystemConnectionRequest based on the RemoteSystem that the ComboBox has selected and then on the AppServiceConnection class I use the OpenRemoteAsync() method rather than the usual OpenAsync() method.

The other thing which I think is unusual in my scenario here is that the PackageFamilyName that I set for the remote app is actually the same as the calling app because I’ve conjured up this weird scenario where my app talks to its own app service on another device.

It’s worth noting that I don’t need to have the app running on another device to invoke it, it just has to be installed.

Wrapping Up

As is often the case, my code here is sketchy and quite rough-and-ready but I quite enjoyed putting this little experiment together because I wasn’t sure whether the ‘Rome’ APIs would;

  1. Allow an app to invoke another instance of ‘itself’ on one of the user’s other devices
  2. Make (1) difficult if it even allowed it

and I was pleasantly surprised to find that the APIs actually made it pretty easy and it’s just like invoking a regular App Service.

I need to have a longer think about what sort of scenarios this enables but I found it interesting here to toy with the idea that I can run this app on my phone, get a list of work items to be processed and then I can elect to process those work items (using the exact same app) on one of my other devices which might have better/cheaper bandwidth and/or more CPU power.

I need to think on that. In the meantime, the code is here on github if you want to play with it. Be aware that to make it run you’d need;

  1. To edit the Constants file to provide storage account name and key.
  2. To make sure that you’d created blob containers called processed and unprocessed within your storage account.

Enjoy.

Windows 10 1607, Composition, XAML and a Blurred Background

Apologies for a bit of a ‘radio silence’ on this blog in recent weeks, I’ve been doing some travelling and other bits and pieces.

Whilst I was away, a mail flooded in from my reader saying;

“Hi,I read your blog and I have a question with blur effect and really need your help.

The main page has a background image and the bottom of the main page is a grid, I want it with blur effect. I have been looking at some answers on stackoverflow,but it seems useless.

Is there a way easily to make it?”

I should say that the title of the mail includes the acronym UWP so this is about UWP rather than, say, WPF.

I think that the UWP Composition APIs can achieve this sort of effect for a UI so I could post an example that did that using those ‘raw’ APIs but I wanted to take this question in a different direction and look into a new thing that has arrived since I last posted on this blog site.

That’s the UWP Community Toolkit. and there’s a video about it over on Channel9 on Robert Green’s excellent Toolbox show;

image

and if you dig into those resources you’ll find that one of the things that this Toolkit offers to developers is an easier way to do some animations that are powered by the composition APIs.

The Blur animation is powered by the CompositionBackdropBrush from Windows 10 build 1607 so you’d need to be on that target platform to have it do something for you but, otherwise, it should be fine to have a small piece of XAML like this one;

 

  <Grid
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
      <RowDefinition />
      <RowDefinition />
    </Grid.RowDefinitions>
    <Image
      Source="ms-appx:///Assets/eva.jpg"
      Stretch="UniformToFill"
      Grid.RowSpan="2">
    </Image>
    <Grid
      Grid.Row="1"
      xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
      xmlns:behaviors="using:Microsoft.Toolkit.Uwp.UI.Animations.Behaviors"
      xmlns:core="using:Microsoft.Xaml.Interactions.Core">
      <interactivity:Interaction.Behaviors>
        <behaviors:Blur
          x:Name="blurBehavior"
          Value="10"
          Duration="0"
          Delay="0"
          AutomaticallyStart="True" />
      </interactivity:Interaction.Behaviors>
    </Grid>
  </Grid>

and bring in the NuGet packages Microsoft.Toolkit.Uwp, Microsoft.Toolkit.Uwp.UI, Microsoft.Toolkit.Uwp.UI.Animations along with Win2D.uwp and that should be all that’s needed to blur the bottom half of the image that’s being displayed here.

Hopefully, that might answer the question that got asked.

Only one note here – I’m still trying to figure out whether this will work with the V1.0.0 NuGet packages that are currently published. I seemed to struggle to get my blur to show using those packages whereas it worked fine when I built the source code for the toolkit from github. I need to investigate but apply a bit of a caveat if you see something similar.

Windows 10 Anniversary Update (1607) and UWP Apps – Connected Apps and Devices via Rome

I tried something out for the first time today that really caught my interest and so I thought I’d share.

I first heard reference to ‘Project Rome’ back at //build/ 2016 but I must admit that I hadn’t found time to watch the session about it properly until recently and that’s partly just due to ‘never having time’ and also partly because I had started to watch it and it didn’t seem to immediately leap into the topic and so I’d put it to one side.

I should have had more faith Smile because it’s a really good session and it’s absolutely worth watching – click the image below to jump into it (it’s short at around 30mins).

image

There’s two discrete parts to that session.

The first part about ‘Web to App Linking’. The core idea there is that an app can register itself (under certain circumstances and user control) to be a handler for what I’d call a ‘portion of the HTTP/HTTPS namespace’ such that if the device comes across a URI like “http://www.myAmazingApp.com” then it’s possible that when the system launches that URI the result ends up in the app from ‘My Amazing App’ rather than the standard system action which would be to launch the browser.

That’s written up really well here;

Support web-to-app linking with app URI handlers

But that //build/ 2016 session then switches at the 15m mark to talk about ‘Project Rome’ which feels like it is doing some of the legwork on the idea of an experience which spans devices rather than being confined to a single device.

There’s been lots of talk at Microsoft around ‘the mobility of the experience’ and it feels to me that ‘Project Rome’ has a part to play in that idea by providing some pieces of a framework which allows for asking questions about a user’s set of devices and then taking higher level actions across that set of devices.

I wanted to try it out for the first time so having watched the video above I went and got my developer laptop running 1607 and I started to see if the docs for classes like RemoteSystem that I spotted in the video were online (they are).

I then paused for a while as I hadn’t really considered whether I had multiple devices running Windows 10 1607 to test on until I remembered that I had a home all-in-one that I’d upgraded to 1607 which is sitting in another room on standby with some active logon sessions.

It’s worth saying that the set of devices that is being worked with here is the set of devices that you see when you go to;

http://account.microsoft.com

and then have a look at your list of Devices there;

Untitled

and I have this home office PC which shows up there.

Capture

As is often the case, I’d started to write a little bit of code when I found that there’s an article which brings some of these classes together;

Launch an app on a remote device

and that led me to the higher level documentation;

Connected apps and devices (Project “Rome”)

and my ‘Hello World’ is pretty much a duplicate of what happens in that former article in that I made simple XAML page which presents a data-bound ListView and a Button;

  <Grid
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
      <RowDefinition />
      <RowDefinition
        Height="Auto" />
    </Grid.RowDefinitions>
    <ListView
      Margin="8"
      Header="Remote Systems"
      ItemsSource="{x:Bind RemoteSystems}"
      SelectionMode="Single"
      SelectedItem="{x:Bind SelectedSystem,Mode=TwoWay}">
      <ListView.ItemContainerStyle>
        <Style
          TargetType="ListViewItem">
          <Setter
            Property="HorizontalContentAlignment"
            Value="Stretch" />
        </Style>
      </ListView.ItemContainerStyle>
      <ListView.ItemTemplate>
        <DataTemplate
          x:DataType="remote:RemoteSystem">
          <Grid>
            <Grid.ColumnDefinitions>
              <ColumnDefinition />
              <ColumnDefinition />
              <ColumnDefinition />
              <ColumnDefinition />
              <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <TextBlock
              Text="{x:Bind DisplayName}" />
            <TextBlock
              Text="{x:Bind Id}"
              Grid.Column="1" />
            <TextBlock
              Text="{x:Bind Kind}"
              Grid.Column="2" />
            <TextBlock
              Text="{x:Bind Status}"
              Grid.Column="3" />
            <TextBlock
              Text="{x:Bind IsAvailableByProximity}"
              Grid.Column="4" />
          </Grid>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
    <Button
      Margin="8"
      Grid.Row="1"
      HorizontalContentAlignment="Center"
      HorizontalAlignment="Stretch"
      VerticalAlignment="Stretch"
      Click="{x:Bind OnLaunchUri}">
      <Button.Content>
        <StackPanel Orientation="Horizontal"
                    HorizontalAlignment="Center">
          <SymbolIcon
            Symbol="Map"/>
          <TextBlock
            Margin="4,0,0,0"
            Text="launch map of New York" />
        </StackPanel>
      </Button.Content>
    </Button>
  </Grid>

and then added some code-behind to see if I could get the basics of this to work;

namespace App24
{
  using System;
  using System.Collections.ObjectModel;
  using System.ComponentModel;
  using System.Linq;
  using System.Runtime.CompilerServices;
  using System.Threading.Tasks;
  using Windows.System;
  using Windows.System.RemoteSystems;
  using Windows.UI.Core;
  using Windows.UI.Xaml;
  using Windows.UI.Xaml.Controls;

  public sealed partial class MainPage : Page, INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;

    public MainPage()
    {
      this.InitializeComponent();
      this.Loaded += OnLoaded;
      this.RemoteSystems = new ObservableCollection<RemoteSystem>();
    }
    public ObservableCollection<RemoteSystem> RemoteSystems
    {
      get; private set;
    }
    public RemoteSystem SelectedSystem
    {
      get
      {
        return (this.selectedSystem);
      }
      set
      {
        if (this.selectedSystem != value)
        {
          this.selectedSystem = value;
          this.FirePropertyChanged();
        }
      }
    }
    // Note, this is bound to from an x:Bind, just trying that out for the
    // first time.
    public async void OnLaunchUri()
    {
      if (this.SelectedSystem != null)
      {
        var request = new RemoteSystemConnectionRequest(this.SelectedSystem);

        await RemoteLauncher.LaunchUriAsync(
          request, 
          new Uri("bingmaps:?cp=40.726966~-74.006076&lvl=10&where=New%20York "));
      }
    }
    void FirePropertyChanged([CallerMemberName] string prop = "")
    {
      this.PropertyChanged?.Invoke(
        this, new PropertyChangedEventArgs(prop));
    }
    async void OnLoaded(object sender, RoutedEventArgs e)
    {
      var status = await RemoteSystem.RequestAccessAsync();

      if (status == RemoteSystemAccessStatus.Allowed)
      {
        this.watcher = RemoteSystem.CreateWatcher();
        this.watcher.RemoteSystemAdded += OnAdded;
        this.watcher.RemoteSystemRemoved += OnRemoved;
        this.watcher.RemoteSystemUpdated += OnUpdated;
        this.watcher.Start();
      }
    }
    async void OnUpdated(RemoteSystemWatcher sender, RemoteSystemUpdatedEventArgs args)
    {
      await this.DispatchAsync(
        () =>
        {
          // Laziness on my part not wanting to create a view model for a remote system
          // with property change notification etc.
          this.Remove(args.RemoteSystem.Id);
          this.Add(args.RemoteSystem);
        }
      );
    }
    async void OnRemoved(RemoteSystemWatcher sender, RemoteSystemRemovedEventArgs args)
    {
      await this.DispatchAsync(
        () =>
        {
          this.Remove(args.RemoteSystemId);
        }
      );
    }
    async void OnAdded(RemoteSystemWatcher sender, RemoteSystemAddedEventArgs args)
    {
      await this.DispatchAsync(
        () =>
        {
          this.Add(args.RemoteSystem);

        }
      );
    }
    void Add(RemoteSystem remoteSystem)
    {
      this.RemoteSystems.Add(remoteSystem);
    }
    async Task DispatchAsync(DispatchedHandler action)
    {
      await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, action);
    }
    void Remove(string remoteSystemId)
    {
      var entry = this.RemoteSystems.SingleOrDefault(system => system.Id == remoteSystemId);

      if (entry != null)
      {
        this.RemoteSystems.Remove(entry);
      }
    }
    RemoteSystem selectedSystem;
    RemoteSystemWatcher watcher;
  }
}

and so most of this code is just using the RemoteSystemWatcher class and, having asked for permission, this is going to deliver RemoteSystem information to me via events for Added/Removed/Updated. I use that to build up an ObservableCollection which is bound into the ListView on the screen and I also data-bind the SelectedSystem to the selected item in that ListView.

I liked that the RemoteSystemWatcher follows what feels like a pattern that’s consistent with (e.g.) looking for AllJoyn devices or looking for Bluetooth LE devices – there are quite a few ‘watcher’ objects within the UWP APIs and this one feels natural to use.

I hooked the button press to then construct a RemoteSystemConnectionRequest instance and to do a LaunchUriAsync on that remote system and I’m letting the default options stand here but it’s possible to specify the list of apps that I’d prefer handle the request remotely and also any launch parameters to be passed to the remote app.

I must admit that the first time that I ran this code, I was not expecting success. It seemed complex enough to be unlikely to work and I figured that there’d likely be some configuring and messing around to do.

I got success Smile 

The little UI listed the all-in-one office machine that had been sitting in another room on standby the whole time I’d been writing this code;

Capture

and I tapped on the button, went off to visit that machine and (sure enough) there was the Maps app displaying a map of New York.

I like this a lot Winking smile

Being an awkward type, I logged myself out of that machine to see if the status would go ‘Unavailable’ and it didn’t seem to and so I tried to launch the Maps app remotely again without an active logon session on that machine and this caused me to add a bit of code to display the return value from the LaunchUriAsync method;

        var response = await RemoteLauncher.LaunchUriAsync(
          request, 
          new Uri("bingmaps:?cp=40.726966~-74.006076&lvl=10&where=New%20York "));

        var messageDialog = new MessageDialog(response.ToString(), "return status");

        await messageDialog.ShowAsync();

and, sure enough, this now returned a RemoteSystemUnavailable status even though the RemoteSystem itself was still claiming availability so I’m not sure quite how that ‘availability’ status works and when it updates.

I also noticed a very interesting thing in that when I logged back in to that system the Maps app popped up with a map of New York – it seems like my request has been queued somewhere and delivered at a later point in time but I’d need to dig into that more to understand whether that’s by design or not.

Being an awkward type, I took the machine that is acting as a client here off the network that it shares with the ‘mthome’ PC and moved it to a 4G network to see what might happen there and it worked just the same way as you might expect.

I wondered how I might get these 2 PCs to be ‘proximally’ connected without a cloud connection and so I played with a few options there but have yet to figure out quite how that works for 2 PCs, maybe I’d have more luck with a phone for that scenario but it needs returning to.

Being able to launch an app on a remote machine with parameters opens up a tonne of scenarios which involve transferring a task from one device to another and especially when you think that those parameters might be some identifier to a piece of shared state in the cloud but there’s another part of this ‘Rome’ framework that interests me here too and that’s the idea of;

Communicate with a remote app service

and that gives us the possibility of an app on one device making use of app services from an app on another device.

That’s a topic for another post but it seems really quite powerful in that it has the potential to open up an opportunity to run the constituent pieces of an application on the machines that I think are best suited to the task at the time. I could see examples where I might choose to run some processing on a remote device because it has more processing power or a better (or cheaper, or more reliable) connection to the internet.

I want to dig into that but, again, that’s for another post…

Windows 10 Anniversary Update, Desktop App Converter Preview– Experimenting with Open Live Writer

Following on from my previous post;

Windows 10 Anniversary Update, Desktop App Converter Preview – Starting from Source

What I’d done in that previous post was to make a desktop application that really didn’t “do anything” and so it was fairly easy for me to create a .APPX package from the .EXE that it consisted of and deploy and run it.

That desktop application doesn’t depend on any specific file locations or any particular registry key and so it’s going to run ok both in a ‘regular desktop’ context and in a ‘UWP’ context.

As another, more realistic experiment, I wanted to take more of a real desktop application and see what it’s like to try and package that up into the .APPX format.

I used to build desktop applications for a living but I don’t any more and so I don’t have the source for a reasonably sized desktop application kicking around and so I had to be a little imaginative and have a think about where I might borrow a desktop application from.

Given that I use Live Writer to write my blog posts, my imagination didn’t have to wander too far to come up with the idea of using;

Open Live Writer

and so off I went to Github and cloned that repository and opened it up inside of Visual Studio “15” preview before making sure that I could actually build it and run/debug it as a regular-desktop-app which was fairly easy.

There’s a few projects in here;

Capture

and having built and run it, I then wanted to try and understand how Open Live Writer built up an installer for itself which it seems to drop out into this folder as part of the build process;

Capture

and I could see that there was the PostBuild.CreateInstaller project which has a post-build step to run a createinstaller.cmd file and the contents of that .cmd file are on the web here.

That .cmd file seems to use nuget.exe to pack a bunch of files listed in OpenLiveWriter.nuspec from the build output folder into a NuGet package.

It then uses squirrel to take that NuGet package and build the OpenLiveWriterSetup.exe from it that’s in the screenshot above.

It then wraps that up into a chocolatey package (I think Confused smile).

With those pieces, there are two (or more) ways that I could go to package this into a .APPX install.

  1. Run the Setup.exe into the desktop app converter and collect the output as per my first blog post.
  2. Add something to the build process to make a .APPX in the first place as per my second blog post.

Either way, I figured that I’d be needing a certificate so I started there.

Step 1 – Making Certificates

From my prior experiments, I know that I’m going to need a signing certificate and so I made one with;

MakeCert.exe -r -h 0 -n “CN=Open Live Writer” -eku 1.3.6.1.5.5.7.3.3 -pe -sv my.pvk my.cer

pvk2pfx.exe -pvk my.pvk -spc my.cer -pfx my.pfx

and I then installed the certificate into my local machine’s trusted roots certificate store.

I also dropped the various pieces I’d used in a top-level Certificates folder within the Open Live Writer folder structure so that I knew where to find them again.

Capture

Experiment 1 – Using the Desktop Converter

I took the OpenLiveWriterSetup.exe file that the build process emits and I ran it through the DesktopAppConverter.ps1 script with the command line below;

.\DesktopAppConverter\DesktopAppConverter.ps1 -Installer .\apps\OpenLiveWriterSetup.exe -Version 1.0.0.0 -MakeAppx -Destination .\OpenLiveWriterOut -Publisher “CN=Open Live Writer” -PackageName “OpenLiveWriter” -Log out.txt

but I found that it seemed to run for a very long time and checking of the log file revealed that it was always waiting for the installation to complete and that never seemed to happen.

I wasn’t sure what the installer might be doing but one guess was that it might be related to the installer running the app at the end of the installation and so appearing to never end.

To see if I could mitigate that, I tried to use the “–silent” flag and in an interactive context that seemed to work (i.e. installation without app launch) but when I tagged it on to the desktop conversion process;

.\DesktopAppConverter\DesktopAppConverter.ps1 -Installer .\Apps\OpenLiveWriterSetup.exe -Version 1.0.0.0 -MakeAppx -Destination .\OpenLiveWriterOut\ -Publisher “CN=Open Live Writer” -PackageName “OpenLiveWriter” -InstallerArguments “–silent”

I found that the tool would still sit there for a long time and a check of the log file revealed the last entry was always;

“Waiting for the installer process to complete inside Isolated Environment”

and it was tricky to know what was happening inside of that environment – was the installer trying to do something like install an earlier version of .NET? Did it relate to the installation targeting the user’s local folder rather than a central location? Was the installer showing a dialog or similar?

It’s possible that I’m just missing something around this particular type of installation but, so far, I haven’t managed to get this to work.

I moved on to the other approach.

Experiment 2 – Altering the Build Script

Manifests, Icons

I wondered if I could alter the build script that the Open Live Writer guys have built in order to package the app ‘automatically’ into a .APPX format as part of the build rather than taking the Setup.exe from the build and trying to record its output with the desktop converter tool.

I made a manifest in line with my previous post, changing a few names to ‘Open Live Writer’ where appropriate.

I’ve copied it below for completeness;

<?xml version="1.0" encoding="utf-8" ?>
<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">
  <Identity Name="c2030cf7-74d5-479f-aa19-f907e4145698"
    ProcessorArchitecture="x64"
    Publisher="CN=Open Live Writer"
    Version="1.0.0.0" />
  <Properties>
    <DisplayName>Open Live Writer</DisplayName>
    <PublisherDisplayName>Open Live Writer</PublisherDisplayName>
    <Description>Open source version of the classic Live Writer blogging application</Description>
    <Logo>assets\StoreLogo.png</Logo>
  </Properties>
  <Resources>
    <Resource Language="en-us" />
  </Resources>
  <Dependencies>
    <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14316.0" MaxVersionTested="10.0.14316.0" />
  </Dependencies>
  <Capabilities>
    <rescap:Capability Name="runFullTrust"/>
  </Capabilities>
  <Applications>
    <Application 
      Id="OpenLiveWriter" 
      Executable="OpenLiveWriter.exe" 
      EntryPoint="Windows.FullTrustApplication">
      <uap:VisualElements
       BackgroundColor="#464646"
       DisplayName="Open Live Writer"
       Square150x150Logo="assets\Square150x150Logo.png"
       Square44x44Logo="assets\Square44x44Logo.png"
       Description="Open Live Writer Blogging App" />
    </Application>
  </Applications>
</Package>

and I made logos at size 50×50, 150×50 and 44×44 based on the image that I found in OpenLiveWriter.CoreServices\Images\SplashScreenLogo.jpg although I suspect there are better logos I could have chosen here.

I added a top level folder named AppxArtefacts to hold my assets and appxmanifest.xml;

Capture

and I added the certificate pieces into a top level folder named Certificates although I really only need the .pfx file to sign the .appx as part of the build script.

I then set about seeing if I could alter the script which creates the installer.

Hacking the Build Script

I’m no expert on anything and especially not on NuGet and I’m even more clueless when it comes to Squirrel so I tried to add something into the build script that wouldn’t destroy what was already there and just move a few things around rather than dramatically change anything.

With that in mind, I added these lines to the script in between the piece which makes the NuGet package and the piece which runs Squirrel.exe on it.

:: mtaulty1 - unpack that nuget package into a folder named UnpackedNuget
"%LocalAppData%\Nuget\Nuget.exe" install -ExcludeVersion OpenLiveWriter -Source %cd% -OutputDirectory UnpackedNuget 
ECHO Unpacked that package.

:: mtaulty2 - copy the AppxArtefacts and that unpacked content into a folder named AppxContents
:: hard-coding the lib\net451 pieces here isn't great.
mkdir AppxContents
xcopy /s "AppxArtefacts\*.*" .\AppxContents
xcopy /s "UnpackedNuget\OpenLiveWriter\lib\net451\*.*" .\AppxContents

:: mtaulty3 - make an appx package out of that. Hard-coding the location of the tool here isn't great.
"c:\program files (x86)\Windows Kits\10\bin\x86\makeappx.exe" pack /d AppxContents /p OpenLiveWriter.appx

:: mtaulty4 - sign that appx package
"c:\program files (x86)\Windows Kits\10\bin\x86\signtool.exe" sign -f Certificates\my.pfx -fd SHA256 -v OpenLiveWriter.appx 

:: mtaulty5 - tidy up
rmdir /s /q AppxContents
rmdir /s /q UnpackedNuget

The attempt here is to make a .APPX file out of the pieces that are in the AppxArtefacts folder that I’ve already pre-populated mixed with the contents of the build outputs that have just been added into the NuGet package by the existing script.

Sure enough, out of this drops a .APPX file and that file install the app just fine.

Capture

Capture

Trying it Out

The app that’s installed by this process runs ‘just fine’ but it’s not entirely surprising that it then hits upon a bit of a snag…

Capture

When installed through its regular installer, this app would end up in the equivalent of c:\users\mtaulty\appdata\local\OpenLiveWriter\app-0.6.0 and when it runs up for the first time within that folder it has the permission to create a folder named plugins if it wants to.

When packaged as a .APPX, the application doesn’t have the permission to create that Plugins folder and hence it fails at startup when it tries to do it.

I wondered why the code would think it was ok to create a ‘Plugins’ folder within its own installation folder and so I dug in and saw that the code in PostEditorPluginManager.cs seems to only attempt to create this folder in the DEBUG build of the app and so maybe this is more of a developer convenience than anything else.

Repackaging the Release Code, not the Debug Code

With that in mind, I went and built the released version and tried running that up instead and that seemed to work out pretty well in the sense that it looks like I can get Open Live Writer installed and running from that .APPX package. Whether all of the functionality then works, I’m not so sure but I tried a few dialogs and it all seemed to be hanging together and so I have 2 Open Live Writers on my Start Menu;

Capture

and the ‘Windows App’ launches and does the right thing.

Wrapping Up

It’s important to say that this is (clearly) just an experiment and I didn’t worry about making the app target .NET 4.6.x and I didn’t overly worry about 32-bit/64-bit or anything like that.

I also didn’t spend too much time concerning myself over whether the app ere does/doesn’t use squirrel to do some level of automatic updating because that could clearly perhaps clash with the app having been installed via .APPX and you perhaps don’t need both the Windows Store and Squirrel both trying to keep the app automatically up to date at the same time Smile

So, to do this properly would require more thought but I wanted to experiment with it and hence the write up here and, of course, if you know why the converter tool can’t get beyond the “waiting for the installer to finish” stage then do leave me a comment and let me know.

Windows 10 Anniversary Update, Desktop App Converter Preview – Starting from Source

Following up on this post;

Initial Experiments with the “Desktop App Converter” Preview

and continuing the theme of ‘experimentation’ with the Desktop App Converter I wanted to see what it was like to start from a place where I had the source code for a desktop application and wanted to build it out into a .appx installer that could potentially go into the Windows Store.

It’s worth flagging that there are official samples that you can look at around this area and you’d find those on github here;

Desktop app bridge to UWP Samples

For my experiments, I thought I’d start small.

That’s “Hello World”-style small Smile

Making Hello World

I’m a world-class expert when it comes to building “Hello World” and so I made a new WPF application and had it show “Hello World” on the screen.

I had to dig deep but within only a few days I had this working Winking smile

Capture

and I even “Gold plated” it by adding a ViewBox around the TextBlock here Winking smile

Ok, so I’ve got a WPF “Hello World” on .NET Framework 4.6.2 and Visual Studio knows how to build a .exe from it and so how do I package that into a .appx format given that I don’t have an installer for it right now?

Making a Blank App Manifest

There’s documentation to help on this topic so I followed it;

Manually convert your Windows desktop application to a Universal Windows Platform (UWP) app

and in doing so I had to think about a few things. Firstly, I made myself a new certificate and I did that by following the docs and using the specific command;

MakeCert.exe -r -h 0 -n “CN=mikedesktop” -eku 1.3.6.1.5.5.7.3.3 -pe -sv my.pvk my.cer

pvk2pfx.exe -pvk my.pvk -spc my.cer -pfx my.pfx

and that gave me a common name of mikedesktop for the publisher here and I put that certificate into my machine’s trusted roots.

I also dug into this document that I found useful;

App packages and deployment (Windows Runtime apps)

and I also found it useful to build a Blank UWP app in Visual Studio and look at the AppxManifest.xml file that gets build as part of that process by way of having a comparison that’s in a ‘known good state’.

I added a few files to my blank WPF project as below;

Capture

I should make it clear that I’m trying to make my life easy here rather than trying to do something smart and I wrote that appxmanifest.xml file to look like this;

<?xml version="1.0" encoding="utf-8" ?>
<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">
  <Identity Name="a2050516-ad7b-4170-ac1d-cb23a21de780"
    ProcessorArchitecture="x64"
    Publisher="CN=mikedesktop"
    Version="1.0.0.0" />
  <Properties>
    <DisplayName>Hello World Desktop App</DisplayName>
    <PublisherDisplayName>Mike Taulty</PublisherDisplayName>
    <Description>Amazing hello world desktop application</Description>
    <Logo>assets\StoreLogo.png</Logo>
  </Properties>
  <Resources>
    <Resource Language="en-us" />
  </Resources>
  <Dependencies>
    <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14316.0" MaxVersionTested="10.0.14316.0" />
  </Dependencies>
  <Capabilities>
    <rescap:Capability Name="runFullTrust"/>
  </Capabilities>
  <Applications>
    <Application 
      Id="HelloWorldDesktopApp" 
      Executable="HelloWorldDesktopApp.exe" 
      EntryPoint="Windows.FullTrustApplication">
      <uap:VisualElements
       BackgroundColor="#464646"
       DisplayName="Hello World Desktop App"
       Square150x150Logo="assets\Square150x150Logo.png"
       Square44x44Logo="assets\Square44x44Logo.png"
       Description="This is my Hello World desktop app" />
    </Application>
  </Applications>
</Package>

and I made sure that the appxmanifest.xml, and the files within my assets folder all got copied to the output folder as illustrated on appxmanifest.xml below;

Capture

I should say that I copied the contents of the assets folder out of a regular, blank UWP project.

It’s also perhaps worth flagging that I set up both x86 and x64 platform targets and I worked solely within the x64 configuration as I think (i.e. not sure) that support for desktop apps packaged in .appx is 64-bit right now and I wasn’t sure how a ‘AnyCPU’ packaging would or wouldn’t work. I need to dig into that more to be sure of what does/doesn’t work.

Making a .APPX

With that set up, I went via what I thought might be the simplest route to getting a .APPX file created and I added the makeapp.exe tool to my project’s ‘post-build step’ (graphically rather than using the MSBuild AfterBuild task) in order to make that .appx;

“c:\program files (x86)\windows kits\10\bin\x86\makeappx.exe” pack /d “$(TargetDir).” /p “$(TargetDir)$(ProjectName).appx”

and that seemed to work reasonably well although it’s worth saying that I’m adding things like .pdb files into my .appx package here because they happen to be in the output folder from my build.

Signing the .APPX

I added a second post-build step to sign the .APPX using the certificate that I’d bundled into my Visual Studio project;

“c:\program files (x86)\windows kits\10\bin\x86\signtool.exe” sign -f “$(ProjectDir)Certificates\my.pfx” -fd SHA256 -v “$(TargetDir)$(ProjectName).appx”

and that also seemed to work out fine and I had a signed .APPX file.

Installing the .APPX

Once I’ve got a .APPX signed with a certificate that my machine trusts, I can simply ask the Windows shell to launch the .APPX (or use add-appxpackage) and it pops some UI;

Capture

and then it installs the app (which was too quick for me to capture as a screen shot) and then the app’s ready to go;

Capture

and runs up as you’d expect;

Capture

and that’s all fine.

Calling into UWP APIs

A desktop app can make use of some of the UWP APIs whether or not that desktop app has been packaged as a UWP app. There’s a good article here that talks about this;

UWP APIs callable from a classic desktop app

and that partitions APIs into;

  • APIs that need the calling app to have a package identity (which I read as “packaged as UWP”)
  • APIs that will work without a package identity.

beyond that, there’s a more detailed list on this page;

Supported UWP APIs for converted desktop apps

So in my desktop application I should be able to make use of an API like SpeechSynthesizer and so I thought I’d try that out and it wasn’t quite as simple as I might have hoped but it wasn’t too bad.

The steps that I took went something like this.

Edit the .csproj file to add TargetPlatformVersion

The first thing I did was to unload the project in visual studio (using the right mouse menu on the solution explorer) so that I could edit the XML and I used that to add;

Capture

and that then switches on this section of the References dialog where I added references to Windows.media and Windows.foundation;

Capture

That might work for some scenarios but I found that types like IAsyncOperation<> weren’t present and so I went out and referenced Windows.winmd instead as below;

Capture

and that reference is coming from c:\program files (x86)\Windows Kits\10\UnionMetadata\Windows.winmd.

However, once I started writing async code, this left me without definitions of GetAwaiter() on those pesky IAsyncOperation<> types and I then need to go off and reference System.Runtime.WindowsRuntime.dll as below;

Capture

and that reference is coming from c:\program files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\4.5\System.Runtime.WindowsRuntime.dll.

I’d stress that it’s the 4.5 version I took here rather than the 4.5.1 version as referencing that seemed to give me runtime problems.

And I made sure that I set “Copy Local” to false on that assembly;

Capture

And that got me to the point where I could compile some code and so I added a button to my UI and added this code behind it;


    async void OnSpeak(object sender, RoutedEventArgs e)
    {
      // This is a UWP API.
      SpeechSynthesizer synth = new SpeechSynthesizer();

      using (var stream = await synth.SynthesizeTextToStreamAsync("I am a desktop app"))
      {
        // We need a .NET stream
        using (var netStream = stream.AsStreamForRead())
        {
          // SoundPlayer is a .NET Framework API.
          SoundPlayer player = new SoundPlayer(netStream);

          player.Play();
        }
      }
    }

and, believe it or not, that actually worked although I’m somewhat unsure whether I should Dispose() both of those stream types above or just one of them.

I’ll admit that I had to chase down quite a few web references to get to the point where that code would compile and run correctly – the text above makes it look like it would take maybe 5 minutes whereas I’d say it took about 60 minutes.

I then used my post-build steps to repackage this as a UWP once again and verified that it would deploy and work in that context as well and it worked fine.

Using the Visual Studio 15 Preview Template

Having to keep running through that process of making the desktop app, building it into a .APPX and then installing/uninstalling it is a bit painful so there’s a Visual Studio packaging project template that helps in that area. I referenced it from my earlier blog post and it’s here;

Desktop to UWP Packaging Project for Visual Studio “15”

As far as I understand it, this project template supports a particular workflow where you already have a .APPX layout for your application on disk. Maybe you made that manually as I did in this post or maybe you made it via the Desktop App Converter as I did in my previous post.

Once you’ve got it, what you can then do is;

  • Configure a list of the outputs from your project that would need to be re-deployed into that .APPX layout whenever you build your projects in Visual Studio
  • Set the packaging project as your startup
  • Press F5 and have Visual Studio build, copy over the outputs and debug your UWP packaged app in its UWP context

which takes some of the manual steps out of installing/uninstalling packages and attaching debuggers and so on.

To try this out, albeit on my simplest of examples, I first followed the steps in the docs in that I added a packaging project to my solution;

Capture

and then made sure that this project referenced my other project so that it builds after that project;

Capture

and then I revisited my other project and removed the post-build steps that I had been using to make the .APPX file and sign it.

I then went and got the .APPX file and copied it into a folder c:\temp\PackageLayout on my system where I renamed it to .ZIP and extracted the contents before getting rid of both the .ZIP and the .APPX leaving just the files and folders that were within it;

Capture

Clearly, there are some extra pieces in there like the .vshost files but that comes from me building the .APPX from the output folder of the Debug build process in the first place.

With that in place, I changed the properties of the packaging project such that the “Package Layout” option pointed to my artefacts as below;

Capture

And then, as per the instructions, I edited the AppXPackageFileList.xml which the project uses to copy files from the build output into the package layout as those files get re-built.

My file ended up looking like this;

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  
  <PropertyGroup>
    <MyProjectOutputPath>c:\temp\helloworlddesktopapp\helloworlddesktopapp\</MyProjectOutputPath>
  </PropertyGroup>
  <ItemGroup>
    <LayoutFile Include="$(MyProjectOutputPath)bin\x64\Debug\HelloWorldDesktopApp.exe">
      <PackagePath>$(PackageLayout)\HelloWorldDesktopApp.exe</PackagePath>
    </LayoutFile>
  </ItemGroup>

</Project>

and, while there’s a couple of embedded, hard-coded paths in there, it does seem to work and I can build and set this packaging project as my start up project and hitting F5 runs it up.

Capture

Whose Desktop App Is It Anyway?

I’ve now got these 2 projects with 2 views of the same thing. If I set the HelloWorldDesktopApp project to be my startup project then I run a ‘regular’ desktop app;

Capture

and if I set the PackagingProject project to be my startup then I run a ‘UWP’ desktop app;

Capture

and they look pretty similar to me and they’re running the same code and it works in both contexts so how can I be convinced that there’s anything different going on here?

I thought that perhaps I could try one of those UWP APIs that require a package identity in order to function and see what it returns in the two different contexts here and the first API that I could think of was the API that would return the current package identity.

So, I modified my code such it was trying to make use of Package.Current.Id as below;

    void OnLoaded(object sender, RoutedEventArgs e)
    {
      var packageId = "package id is not set";

      try
      {
        packageId = $"package id is {Package.Current.Id.FullName}";
      }
      catch (InvalidOperationException)
      {
      }
      this.txtPackageId.Text = packageId;
    }

and now I get 2 different results based on whether I run the HelloWorldDesktopApp project or the PackagingProject;

Capture

versus;

Capture

and that makes it clearer that these two contexts are quite different.