Click here to Skip to main content
Click here to Skip to main content

How to call WinRT APIs from .NET desktop apps

By , 5 Feb 2013
 

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:

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
    • Edit it and add in a target framework directive
    • <TargetPlatformVersion>8.0</TargetPlatformVersion>

    • Reload your project.
  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.

    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:

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!

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.

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).

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.

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.

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

About the Author

ljw1004
Technical Lead
United States United States
Member
Lucian studied theoretical computer science in Cambridge and Bologna, and then moved into the computer industry. Since 2004 he's been paid to do what he loves -- designing and implementing programming languages! The articles he writes on CodeProject are entirely his own personal hobby work, and do not represent the position or guidance of the company he works for. (He's on the VB/C# language team at Microsoft).

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionReason's for Adding WinRT API in Desktop Apps.memberRanjan.D8 Jan '13 - 14:33 
Hi,
 
I would say there are several reasons why one has to add WinRT Api in Desktop Apps. It just doesn't limited to what you have explained.
 
If we are interested in developing Apps which showcases Sensors then we need to access Windows.Devices which comes with WinRT Windows.WinMD. In such cases one has to make a reference in desktop app.
 
I still don't understand why MS did not want to make it easier to reference WinRT Api's in desktop App. Still wonder one has to manually edit the project file to include version 8.0 to reference WinRT Api
 
Thanks,
Ranjan.D

QuestionCan I add references to the Win rt project (dll)?memberHitesh Chaudhary7 Nov '12 - 20:46 
Thanks for sharing this. Really useful.
 

I have shared project of type "windows store class library" which is referenced by winRT app.
 
I created console app as described by you but not able to add reference to "windows store class library"
 
Is there a way to add reference to it?
Questionpublish causes ResolveManifestFiles task failed erromemberEdCoughlin16 Oct '12 - 18:46 
After adding TargetPlatformVersion 8.0 and a reference to the Windows.winmd, I am able to build the project, but I receive the following error after attempting to publish...
 
The "ResolveManifestFiles" task failed unexpectedly.
System.ArgumentException: Value does not fall within the expected range.
 
Afterwards, it will produce the same error when attempting a simple solution build.
 
Can you point me to what might be causing the problem? This occurs whether the project is a console or WPF project.
 
Thanks!
Ed
QuestionThank youmemberAlbarhami13 Sep '12 - 4:11 
well done
QuestionThanksmemberAlbarhami13 Sep '12 - 3:40 
helpful
QuestionUnload Project?memberdtanderson12 Sep '12 - 10:13 
Why does this require unloading the project and editing the files by hand? Is it that console applications can't normally target 8?
 
I don't have 2012 or even Windows 8 yet, so I can't muck around in the interface and find out for myself.
AnswerRe: Unload Project?memberljw100412 Sep '12 - 10:26 
When you create a new desktop app, it by default targets .NET45. There's a drop-down in the IDE that lets you change that to target lower versions of .NET.
 
But there isn't an IDE dropdown that lets you chose which version of Windows your app targets.
 
Editing .proj files by hand is always an easy escape valve, and means that the VS team isn't forced to expose every last thing through a UI under Project>Properties. Other examples: if you have a desktop VB project and want to use the VBCore feature, you have to add Embed manually. And the IDE for VB only shows a few "treat warnings as errors" options, so for fine-grained control you also do that in the .vbproj file.
GeneralMy vote of 5mvpKanasz Robert12 Sep '12 - 3:18 
Good job
QuestionUsing controlsmemberVeler12 Sep '12 - 0:44 
Hi,
 
Do you know if we can add a WinRT control (like progressring) in our WPF desktop application?
 
Thank you Smile | :)
Questionregarding WinRT APIsmemberTridip Bhattacharjee11 Sep '12 - 20:08 
can you please tell me what is WinRT APIs? what it offer special feature and the advantage of using WinRT APIs
tbhattacharjee

AnswerRe: regarding WinRT APIsmemberAndré Ziegler11 Sep '12 - 23:45 
WinRT = Framework for the Windows 8 fullscreen apps also known as Metro style app
'A programmer is just a tool which converts caffeine into code'

GeneralRe: regarding WinRT APIsmemberljw100412 Sep '12 - 6:41 
I added an introductory section to the article, explaining what WinRT APIs are.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 5 Feb 2013
Article Copyright 2012 by ljw1004
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid