How to use specific WinRT API from Desktop apps: capturing a photo using your webcam into a WPF app

Windows Runtime (WinRT) APIs are a platform-homogeneous application architecture on the Windows 8 operating system. These APIs are used to develop Windows Store applications.

However, some APIs can also be called from the desktop. The MSDN documentation is really useful to determine which are compatible. For instance, here is the requirements of the MediaCapture class of WinRT:

As you can see, this API can be called by a desktop application.

A friend of mine, Christophe Nasarre (who is Premier Field Engineer at Microsoft France) wrote a great tool to get the list of all supported WinRT APIs for desktop applications:

Creating a WPF app that can access WinRT APIs

First of all, let’s start with a simple WPF 4.5 application. The main page will look like this:

<Window x:Class="WPF_WinRT.MainWindow"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Image x:Name="displayImage"/>
    </Grid>
</Window>

Nothing special. Just an Image control to display the photo.

To add support for WinRT, you will have to unload the project from Visual Studio 2013 and add the following lines into the project file:

  <PropertyGroup>
    <TargetPlatformVersion>8.0</TargetPlatformVersion>
  </PropertyGroup>

These lines allow you to reference Windows core APIs for WinRT (once you have reloaded your project):

Using MediaCapture API

The MediaCapture class is a wonderful tool to access microphone and camera. Here is a standard (and simple) code used to get a photo from a webcam:

var devices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
if (devices.Count == 0)
{
    MessageBox.Show("Unable to connect to a video capture device", "Error", MessageBoxButton.OK);
    return;
}

var mediaCapture = new MediaCapture();
mediaCapture.Failed += (s, errorEventArgs) => MessageBox.Show("Unable to start capture:" + 
errorEventArgs.Message,
"Error", MessageBoxButton.OK); await mediaCapture.InitializeAsync(); var jpgProperties = ImageEncodingProperties.CreateJpeg(); jpgProperties.Width = (uint)displayImage.ActualWidth; jpgProperties.Height = (uint)displayImage.ActualHeight; using (var randomAccessStream = new InMemoryRandomAccessStream()) { await mediaCapture.CapturePhotoToStreamAsync(jpgProperties, randomAccessStream); }

If you try to compile it, you will get some errors.

The first one is related to System.Runtime:

The type ‘System.Collections.Generic.IEnumerable`1’ is defined in an assembly that is not referenced. You must add a reference to assembly ‘System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’.

To solve this issue (mainly related to missing types), you just have to add a reference to this assembly (System.Runtime.dll):

The second error is the related to the await keyword. Actually, in order to await an IAsyncAction (the result type of FindAllAsync method for instance), you have to provide a GetAwaiter method:

‘await’ requires that the type ‘Windows.Foundation.IAsyncOperation<Windows.Devices.Enumeration.DeviceInformationCollection>’ have a suitable GetAwaiter method. Are you missing a using directive for ‘System’?

To fix this issue, you have to reference the following assembly (System.Runtime.WindowsRuntime.dll):

Finally a last error remains:

Property, indexer, or event ‘Windows.Media.Capture.MediaCapture.Failed’ is not supported by the language; try directly calling accessor methods ‘Windows.Media.Capture.MediaCapture.add_Failed(Windows.Media.Capture.MediaCaptureFailedEventHandler)’ or ‘Windows.Media.Capture.MediaCapture.remove_Failed(System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken)’

To add the missing interop features, just add the following assembly (System.Runtime.InteropServices.WindowsRuntime):

Once you have added these three references you are done! Nothing more is required.

However, you must note that event though some WinRT APIs are available for the Desktop, this is NOT the case for XAML controls (such as CaptureElement for instance).

Converting IRandomAccessStream to System.IO.Stream

The last point here is about the IRandonAccessStream (implemented by the InMemoryRandomAccessStream class). If we want to work with WPF, we have to convert it to System.IO.Stream.

Obviously, there is a simple way to do that: You just have to reference System.IO.WindowsRuntimeStreamExtensions class. Using this class, you will be able to use handy extensions methods such as AsStream:

using (var randomAccessStream = new InMemoryRandomAccessStream())
{
    await mediaCapture.CapturePhotoToStreamAsync(jpgProperties, randomAccessStream);

    randomAccessStream.Seek(0);
    using (var ioStream = randomAccessStream.AsStream())
    {
        var bitmapImage = new BitmapImage();
        bitmapImage.BeginInit();
        bitmapImage.StreamSource = ioStream;
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
        bitmapImage.EndInit();

        displayImage.Source = bitmapImage;
    }
}

And that’s it! You can now use the power of some WinRT APIs from your desktop app!

[Video] Another WinRT/Desktop application

Christophe Nasarre also created an excellent video which talk about the same topic. This is a really good and educational resource:

https://blogs.msdn.com/b/mspfe/archive/2013/09/24/winrt-for-desktop-apps.aspx