Jeffrey Richter's Blog

Using the Windows Runtime from a Non-Metro Application

I know a lot of people believe that the new Windows Runtime APIs included in Windows 8 can only be accessed from Metro style applications. A big part of the reason why people believe this is because the version of Visual Studio that ships with the Windows 8 Developer Preview only supports creating Metro style applications. However, I set out this morning to use Windows Runtime APIs from a non-Metro application. Here's what I did.

First, in Notepad, I created the following C# source code in EnumDevices.cs:

using System;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Foundation;

class App {
static void Main() {
EnumDevices().Wait();
}

private static async Task EnumDevices() {
// To call DeviceInformation.FindAllAsync:
// Reference Windows.Devices.Enumeration.winmd when building
// Add the "using Windows.Devices.Enumeration;" directive (as shown above)
foreach (DeviceInformation di in await DeviceInformation.FindAllAsync()) {
Console.WriteLine(di.Name);
}
}
}

Second, I created a Build.bat file which I run from the Developer Command Prompt to build this code (This should be 1 line but I wrap it here for read ability):

csc EnumDevices.cs
/r:c:\Windows\System32\WinMetadata\Windows.Devices.Enumeration.winmd
/r:c:\Windows\System32\WinMetadata\Windows.Foundation.winmd
/r:System.Runtime.WindowsRuntime.dll
/r:System.Threading.Tasks.dll

Then, at the command prompt, I just run the EnumDevices.exe to see the output.

 

Let me also explain how the async stuff works:

The XxxAsync methods (like DeviceInformation.FindAllAsync above) is a Windows Runtime method (defined in Windows.Devices.Enumeration.winmd) that returns a DeviceInformationFindAllAsyncOperation object which implements the Windows Runtime's IAsyncOperation<TResult> interface where TResult is the Windows.Devices.Enumeration.DeviceInformationCollection type. This interface is defined in the Windows.Foundation.winmd file.

Using the await keyword causes the compiler to look for a GetAwaiter method on this interface. Since IAsyncOperation<TResult> does not define a GetAwaiter method, the compiler wants to look for an extension method. In the System.Runtime.WindowsRuntime.dll assembly is the static System.WindowsRuntimeSystemExtensions class. This class defines several GetAwaiter extensions methods and the compiler emits code that invokes the GetAwaiter extension method that takes an IAsyncOperation<TResult> and returns a System.Runtime.CompilerServices.TaskAwaiter<TResult> object (defined in the System.Threading.Tasks.dll which is why this assembly is referenced). Since TResult is the Windows.Devices.Enumeration.DeviceInformationCollection type, the await operator ultimately returns this type which is then used by the foreach loop. Inside the loop, I just display the Name property for each DeviceInformation object.

 

Because you cannot mark an entry point method (like Main) as being async, I moved the code that calls await into its own EnumDevices method, marked this method as async, and make it return a Task. Then Main invokes this method and calls Wait on the returned Task so that the application doesn't terminate until EnumDevices has run all the way through to completion.

On Sep 20 2011 9:23 AMBy jeffreyr With 9 Comments

Comments (9)

  1. Thanks for this blog post! I am hoping that WinRT will be usable from Desktop apps (officially); it would be nice to kiss p/Invoke goodbye.

    BTW, I have an AsyncContext class that is nice to use for async Console apps and unit testing (you can keep async void instead of changing to async Task). It's part of this library:
    http://nitoasyncex.codeplex.com/

  2. Am I right if I say... Form Metro style apps we can use only Windows 8 Runtime API and not Win32 API ?

  3. Sriharsha Vardhan

    Hi Jeffrey

    I know you always make things feel fundamental by bridging gap for old time programmers like me. Learnt VC++, and then .Net from your books. Now looking forward to learn this phenomenal new trend from Microsoft.

  4. Thanks very much for this sample. I tried to build it on Consumer Preview 8250 x64 with the matching version of VS 11 Ultimate beta installed, and it would not compile. There appears to be some breaking change.

    First, a message said to add a reference to System.Runtime, so I added a ref to System.Runtime.dll to the command line.

    But then I got the following new error:

    enumdevices.cs(15,34): error CS4028: 'await' requires that the type
    'Windows.Foundation.IAsyncOperation Windows.Devices.Enumeration.DeviceIn
    formationCollection ' have a suitable GetAwaiter method. Are you missing
    a using directive for 'System'?

    The code already has "using System". I have tried many things, but cannot resolve this. For example, I searched and cannot find a version of System.Runtime.WindowsRuntime.dll on my system which lists GetAwaiter() in System.WindowsRuntimeSystemExtensions. (e.g. 4.0.30319)

    Any help in resolving this error will be appreciated! Thx.

Leave a Comment