Projecting Async .NET Code from a WinRT Component

This one arose today and it’s something that I’d thought about a little before and I’d had a good read of this blog post in the past about working with async at the WinRT level rather than at the .NET level.

Taking a simple example – let’s say that I want to write a function that downloads from microsoft.com, counts up the length of the stream returned and returns that to the caller.

In the async/await model of .NET, that seems fairly easy as I can lean on Task and await/async;

namespace App4
{
  using System;
  using System.IO;
  using System.Net.Http;
  using System.Threading.Tasks;

  static class MicrosoftDownloader
  {
    async public static Task<long> GetMicrosoftHomePageSize()
    {
      // local error handling omitted
      var length = 0L;
      HttpClient client = new HttpClient();

      HttpResponseMessage message = await client.GetAsync(
        new Uri("http://www.microsoft.com"));

      using (Stream stream = await message.Content.ReadAsStreamAsync())
      {
        length = stream.Length;
      }
      return (length);
    }
  }
}

and using that code might look something like;

 var x = await MicrosoftDownloader.GetMicrosoftHomePageSize();

which would need to be called from an async method to use that await keyword.

However, what happens if I take that .NET code as above and try and make it into a WinRT component to be used across C++, .NET and JavaScript Windows Store projects?

namespace WindowsRuntimeComponent1
{
  using System;
  using System.IO;
  using System.Net.Http;
  using System.Threading.Tasks;

  public static class MicrosoftDownloader
  {
    async public static Task<long> GetMicrosoftHomePageSize()
    {
      // local error handling omitted
      var length = 0L;
      HttpClient client = new HttpClient();

      HttpResponseMessage message = await client.GetAsync(
        new Uri("http://www.microsoft.com"));

      using (Stream stream = await message.Content.ReadAsStreamAsync())
      {
        length = stream.Length;
      }
      return (length);
    }
  }
}


The compiler doesn’t like it. The compiler immediately says;

Error    1    Method 'WindowsRuntimeComponent1.MicrosoftDownloader.GetMicrosoftHomePageSize()' has a parameter of type 'System.Threading.Tasks.Task<System.Int64>' in its signature. Although this generic type is not a valid Windows Runtime type, the type or its generic parameters implement interfaces that are valid Windows Runtime types.  Consider changing the type 'Task' in the method signature to one of the following types instead: Windows.Foundation.IAsyncAction, Windows.Foundation.IAsyncOperation, or one of the other Windows Runtime async interfaces. The standard .NET awaiter pattern also applies when consuming Windows Runtime async interfaces. Please see System.Runtime.InteropServices.WindowsRuntime.AsyncInfo for more information about converting managed task objects to Windows Runtime async interfaces.  

Firstly, that is a great, great error message for a compiler to emit. Someone thought long and hard about that one and congratulations to whoever wrote it.

What it’s saying is that I am using async which means that my method is going to return Task<long> and Task<long> is something that is understood by .NET but is meaningless in C++ or in JavaScript and, hence, can’t be dealt with by a WinRT component that spans those worlds.

I need to rethink in terms of IAsyncOperation<long> rather than Task<long>. How to do that? Well, I can’t use async on my function any more because that implies Task or Task<T> so I end up using tasks inside my function if I want to work at the ‘top level’ of things like;

namespace WindowsRuntimeComponent1
{
  using System;
  using System.IO;
  using System.Net.Http;
  using System.Threading.Tasks;
  using Windows.Foundation;

  public static class MicrosoftDownloader
  {
    public static IAsyncOperation<long> GetMicrosoftHomePageSize()
    {
      // local error handling omitted
      HttpClient client = new HttpClient();

      Task<HttpResponseMessage> httpTask = client.GetAsync(
        new Uri("http://www.microsoft.com"));

      Task<Task<Stream>> streamTaskTask = httpTask.ContinueWith(
          httpT =>
          {
            // TODO: check for success etc.
            return (httpT.Result.Content.ReadAsStreamAsync());
          }
      );
      // Unfortunately, we end up with a task in a task...
      Task<Stream> streamTask = TaskExtensions.Unwrap<Stream>(streamTaskTask);

      Task<long> longTask = streamTask.ContinueWith(
        streamT =>
        {
          return (streamT.Result.Length);
        }
      );
      return (longTask.AsAsyncOperation<long>());
    }
  }
}


but that’s a bit tedious as I’ve lost all the joy of being able to lean on async/await and being able to put Task to one side so an easier way of doing it might be just to delegate the work down to a sub-function which isn’t promoted across the WinRT boundary so can continue to rely on async/await and so on as in;

namespace WindowsRuntimeComponent1
{
  using System;
  using System.IO;
  using System.Net.Http;
  using System.Threading.Tasks;
  using Windows.Foundation;

  // .NET on the inside, WinRT on the outside - much nicer 🙂
  public static class MicrosoftDownloader
  {
    static async Task<long> InternalGetMicrosoftHomePageSize()
    {
      long length = 0;

      HttpClient client = new HttpClient();

      HttpResponseMessage message = await client.GetAsync(
        new Uri("http://www.microsoft.com"));

      using (Stream stream = await message.Content.ReadAsStreamAsync())
      {
        length = stream.Length;
      }
      return (length);
    }
    public static IAsyncOperation<long> GetMicrosoftHomePageSize()
    {
      return (InternalGetMicrosoftHomePageSize().AsAsyncOperation<long>());
    }
  }


and that feels a whole lot nicer Smile