Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#4.0

How to call WinRT APIs from .NET desktop apps

4.83/5 (12 votes)
5 Feb 2013Public Domain6 min read 92.6K   1.4K  
Explains how to create a .NET app which invokes WinRT APIs on Windows 8.

Introduction

This article shows the steps you need to make a .NET desktop app that invokes the new WinRT APIs that come with Windows 8.

File > NewProject > Online > Templates > VisualBasic > WinRT Console Application Template

That's all you need (requires VS2012 and Windows 8).

What are WinRT APIs?

  1. WinRT APIs are a bunch of new, modern APIs that were introduced in Windows 8. Some of them are UI APIs for specifically for the XAML UI of "app-store-style" apps, formerly known as Metro. Others of them provide core OS functionality like files, streams, bitmaps. In many cases, .NET and Win32 already had similar functionality. In some cases, the WinRT APIs are cleaner and better.
  2. WinRT APIs are the APIs you'll primarily be using when you build app-store-style apps (Metro apps). These app-store-style apps are also allowed to use a partial set of .NET APIs, and a partial set of Win32 APIs, and a partial set of COM APIs. Typically you'll use .NET APIs if they're available, and otherwise you'll fall back to WinRT APIs.
  3. WinRT APIs are a cross-language API. Microsoft did lots of work to make it easy to access these APIs from JavaScript, C++, VB, C#, and F#.

  4. WinRT APIs are a new form of interop. They are found in a new kind of assembly, .WinMD. You can find these assemblies on a Win8 machine under c:\windows\system32\WinMetadata, and also (if you install the Windows SDK) under C:\Program Files (x86)\Windows Kits\8.0\References\CommonConfiguration\Neutral. Also, third-parties will be producing their own WinMDs.
  5. WinRT APIs are an enhanced form of COM. In particular, WinMD files are the equivalent of old COM type-libraries (.TLB), but stored using the same binary format as .NET assemblies. That means you can load .WinMD files into your favourite inspection tool such as Redgate Reflector. Fortunately, the extra work done my Microsoft makes it much easier to use WinRT APIs than it was to use COM.

The accompanying code is a VB console app which demonstrates a few WinRT APIs - StorageFolder (to look at files and folders), BitmapDecoder (to load images), StreamSocketListener (to handle TCP requests).

There are two reasons why you might want to use WinRT APIs from desktop apps:

  • First, if you're prototyping code for a Windows App Store application, it's sometimes easier to prototype + debug it first in a console app: easier to set breakpoints, use Console.WriteLine, faster to start-up, less boilerplate, etc.
  • Second, the new WinRT APIs are clean and modern, and better in many cases than the alternatives. If you're willing to restrict the audience of your code to just Windows 8, then they're worth looking into.

Quick checklist

Or, if you want to do the same manually... If you have already read this article before, and are just visiting to remember the steps, here they are:

Unload .vbproj or .csproj:

<TargetPlatformVersion>8.0</TargetPlatformVersion>

Add References:

Windows:

  • System.Net.Http.dll (pick 4.0.0.0)
  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\System.Runtime.WindowsRuntime.dll
  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Runtime.dll
  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Threading.Tasks.dll
  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.IO.dll
  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Runtime.InteropServices.WindowsRuntime.dll

Project-level imports:

  • System.IO
  • System.Runtime.InteropServices.WindowsRuntime

Async Main:

VB.NET
Sub Main
    MainAsync().GetAwaiter().GetResult()
End Sub 

Async Function MainAsync() As Task 
    ... async code goes here
End Function

Getting started

You'll need to have VS2012 and Windows 8 to use this code, and follow this article. I've written everything out in VB, but the same techniques work equally well in C#.

  1. Start a new project:
  2. File > New > Project > VisualBasic > Windows > ConsoleApplication

  3. Mark your project as targeting (requiring) the Win8 platform.
  4. This is documented on MSDN, How to: Add or Remove References By Using the Reference Manager subheading "Core Subgroup" -- well-hidden under the stairs in a filing cabinet with a "beware of the tiger" sign on it!

    • Unload your project
    • Image 1

    • Edit it and add in a target framework directive
    • XML
      <TargetPlatformVersion>8.0</TargetPlatformVersion>

      Image 2

      Image 3

    • Reload your project.
    • Image 4

  5. Add references:
  6. AddReference > WindowsCore > Windows.winmd

    The “Windows” tab only appears in the AddReference dialog if your project is set to target the Windows 8.0 platform.

    Image 5

    AddReference > Assemblies > Framework > System.Net.Http.dll (v4, not v2!)

    AddReference to the following files:

    • AddReference > Browse > C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\System.Runtime.WindowsRuntime.dll
    • AddReference > Browse > C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Runtime.dll
    • AddReference > Browse > C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Threading.Tasks.dll
    • AddReference > Browse > C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.IO.dll
    • AddReference > Browse > C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades\System.Runtime.InteropServices.WindowsRuntime.dll

    Project > Properties > References > ImportedNamespaces > for the following namespaces:

    • System.IO
    • System.Runtime.InteropServices.WindowsRuntime
    • These imported namespaces are there so you can use the handy extension methods like IBuffer.ToArray() and IInputStream.AsStreamForRead().

Sub Main and Async

Most interesting WinRT APIs are async, so they have to go in an async method. But Sub Main isn’t itself allowed to be async. So this is how we do it:

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

    Async Function MainAsync() As Task
        ' code goes here...
    End Function
End Module  

You can also write MainAsync().Wait(), but that's not as good. The reason has to do with what happens to any exception that might come out of MainAsync(). As we've written it above, GetAwaiter().GetResult() will show you the details of that exception, and allow you to "[X] break if this exception is thrown". But all that Wait() will show is an AggregateException.

Incidentally, each time our code resumes after an “await”, it might resume on a different thread. You can avoid this by coding up an AsyncPump.Run() method, as described here: http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx

Using WinRT APIs

StorageFolders. Note: you can't use FilePickers in a console app since they need a UI!

VB.NET
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(file.Path)
Next  

Streams. Note: when you dispose of an IO.StreamReader, it automatically disposes the underlying stream that it wraps.

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

Graphics. Incidentally, Windows.Graphics.Imaging is for the raw graphics (which can be done in a console app), but Windows.UI.Xaml.Media.Imaging is for graphics as they appear in App-Store style applications (can't be done in a console app).

VB.NET
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. Actually, this is just part of the .NET Framework (not WinRT), and is the recommended replacement for WebRequest and WebClient. I’m mentioning it here because it’s in System.Net.Http.dll, which is referenced automatically in Windows App Store projects, but not in desktop projects.

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

WinRT Sockets. To end, here’s a bigger example using my favorite language features – async and XML literals! Note the "using r=, w=" clauses. AsStreamForRead() produces an IO.Stream which, when disposed, will also dispose the underlying WinRT Socket.InputStream. And New IO.StreamReader produces a stream which, when disposed, disposes of its underlying stream. Likewise for the writers. So all six(!) streams in this code will dispose correctly.

VB.NET
Using server As New Windows.Networking.Sockets.StreamSocketListener
    Dim serverDoneSignal As New TaskCompletionSource(Of Object)
    AddHandler server.ConnectionReceived, 
        Async Sub(sender, args)
            Using r = New IO.StreamReader(args.Socket.InputStream.AsStreamForRead()),
                  w = New IO.StreamWriter(args.Socket.OutputStream.AsStreamForWrite())
                Dim request = ""
                While True
                    Dim s = Await r.ReadLineAsync() 
                    If String.IsNullOrWhiteSpace(s) Then Exit While
                    request &= s & vbCrLf 
                End While 
                Console.WriteLine("<--------" & vbCrLf & request)
                Dim body = <html>
                              <head>
                                   <title>Hello World</title>
                               </head>
                               <body>
                                   <%= Iterator Function()
                                          For i = 0 To 9
                                               Yield <p>Hello word #<%= i %></p>
                                           Next
                                       End Function() %>
                               </body>
                          </html> 
                Dim headers = "HTTP/1.0 200 OK" & vbCrLf &
                              "Content-Type: text/html; charset=utf-8" & vbCrLf & vbCrLf
                Await w.WriteAsync(headers)
                Await w.WriteAsync(body.ToString())
            End Using 
            serverDoneSignal.SetResult(Nothing)
        End Sub 
    Await server.BindServiceNameAsync("")
    Diagnostics.Process.Start("http://127.0.0.1:" & server.Information.LocalPort & "/")
    Await serverDoneSignal.Task
End Using  

Disclaimer

Disclaimer: Although I work at Microsoft on the VB/C# language team, this article is strictly a personal amateur effort based on public information and experimentation - it's not in my professional area of expertise, is written in my own free time, and neither Microsoft nor I make any claims about its correctness.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication