How to call UWP APIs from a desktop VB/C# app


Latest update:

I've put this functionality into a NuGet package. To use UWP APIs from your >=.NET45 desktop app, simply add a NuGet reference:

 

I've also moved the technical information from this blog over to the package's GitHub project site:

 

Out of date: first steps

Add these references to your project, by right-clicking on your project in Solution Explorer and doing References > AddReference > Browse. This minimum needed to get going.

  1. c:\Program Files (x86)\Windows Kits\10\References\Windows.Foundation.FoundationContract\1.0.0.0\Windows.Foundation.FoundationContract.winmd
  2. c:\Program Files (x86)\Windows Kits\10\References\Windows.Foundation.UniversalApiContract\1.0.0.0\Windows.Foundation.UniversalApiContract.winmd
  3. c:\Program Files (x86)\Windows Kits\10\UnionMetadata\Facade\Windows.WinMD
  4. c:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\System.Runtime.WindowsRuntime.dll

The complete set of Desktop WinRT APIs

Here is a MSDN list of WinRT APIs that can be called from desktop apps (but I believe this list is incomplete).

 

Just the four references is enough to get you 90% of the WinRT APIs you'll need in practice.

But to go the full mile, I think the best way would be to package it up as a NuGet package according to the instructions below. I'll explore that idea later. I'd want to get the other UWP facades, get something that works with newer UWP SDKs than just 10240, also include APIs from the Desktop Extension SDK, include the remaining .NET interop DLLs, and get all the default namespace-imports.

Until I or someone else produces that NuGet package, here below is a detailed explanation:

Explanation

To find the UWP WinRT APIs themselves, look for example at this file:

C:\Program Files (x86)\Windows Kits\10\Platforms\UAP\10.0.10240.0\Platform.xml

This Platform.xml file conveys which WinRT APIs are available in "version 10240" of the UWP SDK. (This was the version that came out at VS2015 RTM. Newer versions will come out periodically). The Platform.xml file works by being an index into a set of other winmd files. It looks like this:

<ApiContract name="Windows.Foundation.FoundationContract" version="1.0.0.0" />
<ApiContract name="Windows.Foundation.UniversalApiContract" version="1.0.0.0" />
<ApiContract name="Windows.Networking.Connectivity.WwanContract" version="1.0.0.0" />

Each line denote a .winmd file in the "C:\Program Files (x86)\Windows Kits\10\References directory". That's how I picked up exact filenames and pathnames to add as references. (I should really have added a reference to the WwanContract.winmd file as well, but couldn't be bothered)

These three winmd files make up the WinRT APIs available across all Win10 devices. You might additionally want to use the additional WinRT APIs that are part of Desktop. Look at this file:

C:\Program Files (x86)\Windows Kits\10\Extension SDKs\WindowsDesktop\10.0.10240.0\SDKManifest.xml

It has a load more <ApiContract> directives, pointing to additional .winmd files you should reference from the "C:\Program Files (x86)\Windows Kits\10\References" directory. I haven't bothered to do that.

 

To interop with DLLs or WINMDs build for Win8.1 or WindowsPhone8.1, the issue is that those old DLLs and WinMDs used to think that all WinRT types were in a file called "windows.winmd". But they're not anymore! Instead they're in files called Windows.Foundation.FoundationContract.winmd and the like. The solution is a façade winmd, which is basically a load of type-forwarders from windows.winmd to the new winmd files.

c:\Program Files (x86)\Windows Kits\10\UnionMetadata\Facade\Windows.WinMD

 

To use "await" on WinRT types, this and other interop between .NET and WinRT are provided by the following DLL. Note that it's an 8.1-era DLL, and therefore only works in conjunction with "facade\windows.winmd" above. (There are several other interop assemblies but they're not so crucial).

c:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\System.Runtime.WindowsRuntime.dll

If you fail to add both these two assemblies, then you'll get error messages like this:

  • BC36930: 'Await' requires that the type 'IAsyncAction' have a suitable GetAwaiter method
  • CS0012: The type 'IAsyncAction' is defined in an assembly that is not referenced. You must add a reference to assembly 'Windows, Version=255.255.255.0, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime'.
  • CS4036: 'IAsyncAction' does not contain a definition for 'GetAwaiter' and no extension method 'GetAwaiter' accept a first argument of type 'IAsyncAction' could be found (are you missing a using directive for 'System'?)

 

Example code (VB)

Here's an example console app in VB which uses UWP WinRT APIs:

Imports System.IO

Module Module1
    Sub Main()
        MainAsync().GetAwaiter().GetResult()
    End Sub

    Async Function MainAsync() As Task
        ' basics
        Dim folder = Windows.Storage.KnownFolders.DocumentsLibrary
        Dim opts As New Windows.Storage.Search.QueryOptions(
                              Windows.Storage.Search.CommonFileQuery.OrderByName, {".txt"})
        Dim files = Await folder.CreateFileQueryWithOptions(opts).GetFilesAsync(0, 20)
        For Each file In files
            Console.WriteLine(IO.Path.GetFileName(file.Path))
        Next

        ' streams
        Using reader = New IO.StreamReader(Await files.First.OpenStreamForReadAsync())
            Dim txt = Await reader.ReadToEndAsync()
            Console.WriteLine(txt.Substring(0, 100))
        End Using

        ' pictures
        Dim pics = Await Windows.Storage.KnownFolders.PicturesLibrary.GetFilesAsync(
                         Windows.Storage.Search.CommonFileQuery.OrderBySearchRank, 0, 1)
        Using stream = Await pics.First.OpenReadAsync()
            Dim decoder = Await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(stream)
            Console.WriteLine(decoder.OrientedPixelWidth & "x" & decoder.OrientedPixelHeight)
        End Using

        ' httpclient
        Dim client As New Net.Http.HttpClient()
        Dim html = Await client.GetStringAsync("http://www.microsoft.com")
        Console.WriteLine(html.Substring(0, 100))

    End Function
End Module

 

 

Example code (C#)

Here's an example console app in C# which uses UWP WinRT APIs:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        MainAsync().GetAwaiter().GetResult();
    }

    static async Task MainAsync()
    {
        // basics
        var folder = Windows.Storage.KnownFolders.DocumentsLibrary;
        var opts = new Windows.Storage.Search.QueryOptions(Windows.Storage.Search.CommonFileQuery.OrderByName, new[] { ".txt" });
        var files = await folder.CreateFileQueryWithOptions(opts).GetFilesAsync(0, 20);
        foreach (var file in files)
        {
            Console.WriteLine(System.IO.Path.GetFileName(file.Path));
        }

        // streams
        using (var reader = new System.IO.StreamReader(await files.First().OpenStreamForReadAsync()))
        {
            var txt = await reader.ReadToEndAsync();
            Console.WriteLine(txt.Substring(0, 100));
        }

        // pictures
        var pics = await Windows.Storage.KnownFolders.PicturesLibrary.GetFilesAsync(
                               Windows.Storage.Search.CommonFileQuery.OrderBySearchRank, 0, 1);
        using (var stream = await pics.First().OpenReadAsync())
        {
            var decoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(stream);
            Console.WriteLine($"{decoder.OrientedPixelWidth} x  {decoder.OrientedPixelHeight}");
        }

        // httpclient
        var client = new System.Net.Http.HttpClient();
        var html = await client.GetStringAsync("http://www.microsoft.com");
        Console.WriteLine(html.Substring(0, 100));
    }
}

 

Comments (6)

  1. Anthony Wieser says:

    Is it possible to call WiFiAdapter.RequestAccessAsync() from an app like this.

    It requires an entry in the Package.appxmanifest, specifying:

     <Capabilities>

       <DeviceCapability Name="wiFiControl" />

     </Capabilities>

  2. Talha Zengin says:

    Hi Lucian,
    installed your UWP nuget package for desktop apps. Yeah it works great. But i need WifiDirect Class for my specific application.
    Normally in a UWP application, it’s reference namespace Windows.Devices.WiFiDirect. But with your nuget cant add this namespace. I tried so many ways but cant do it. How can use WiFiDirect namespace? Any way?
    Thanks for your help.

  3. @Talha The WiFiDirect class is in SDK 10586. Last week I updated the Uwp-Desktop NuGet package to include the 10586 SDK. So: update your NuGet reference, and you’ll be able to use this class.

    (also, your code will no longer work on Win10 machines stuck on 10240.)

  4. Paul Tallett says:

    Really great stuff! I futzed with adding references for a day and finally gave up but this NuGet package did the trick! Awesome.

  5. mjeh1 says:

    Hi Lucian
    Thanks for the package – I have installed it but I have no access to the references from UWP from my library project. UWP Version C:\Program Files (x86)\Windows Kits\10\Platforms\UAP\10.0.16299.0
    I try to use OCR part from Windows.Foundation.UniversalApiContract with namespace Windows.Media.Ocr on Assemblies:
    Windows.Media.Ocr.dll, Windows.dll

    Do you have any sugestions? Thanks

Skip to main content