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?
- 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.
- 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.
-
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#.
- 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.
- 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#.
- Start a new project:
File > New > Project > VisualBasic > Windows > ConsoleApplication
- Mark your project as targeting (requiring) the Win8 platform.
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.
- Add references:
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
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.