Chapter 1. Windows Runtime primer

The Microsoft Windows operating system (OS) offers many features, which application developers use to simplify building applications. This book explains many of these Windows features and offers guidance and best practices when using them. Windows exposes its features via an application programming interface (API), called the Windows Runtime (WinRT). WinRT APIs are callable using many programming languages, including JavaScript, native C++, and .NET’s C# and Visual Basic. However, in this book, I chose to demonstrate consuming the WinRT APIs via the C# programming language due to C#’s widespread adoption and the productivity gains it provides over the other languages.

The Windows OS supports many application models. For example, Windows supports several client-side application models, including console user interface (CUI) applications and graphical user interface (GUI) applications. It also supports server-side application models for building services, such as Internet Information Server (IIS), SQL Server, and Exchange. Collectively, all these application models are referred to as desktop apps. Admittedly, the term desktop app is not a great choice because some of these application models have no visible presence on the user’s desktop. As of Windows 8, Windows now supports a new client-side GUI application model referred to as Windows Store apps. This term is also not ideal because Windows Store apps do not have to be installed by way of the Windows Store; they can be manually installed (side-loaded) via other means for development, testing, or employee usage.

Because the WinRT API is part of the OS, any app built using any of the application models can technically consume the API. However, due to time constraints, Microsoft was not able to test many of the WinRT APIs from desktop apps, so some are not yet sanctioned for use by non–Windows Store apps. Furthermore, Windows Store apps run in a different security context than desktop apps. This security context is called an app container (discussed in the appendix, Appendix A), and it restricts which resources a Windows Store app can access. For these reasons, this book focuses heavily on using the WinRT APIs from Windows Store apps. However, note that some of the WinRT APIs described in this book are consumable from desktop apps as well. You should also note that there are some WinRT APIs that are callable only from desktop apps, not Windows Store apps. The MSDN documentation for each WinRT type contains a Requirements section indicating whether the API is callable from desktop apps, Windows Store apps, or both.

While reading the various chapters in this book, keep in mind the principles Microsoft had in mind when designing the Windows Store app model. If you’re wondering why a certain feature works the way it does or why a certain feature is missing, most likely it is because it didn’t fit in with the principles. As you implement Windows Store apps yourself, you’ll need to consider these principles. Here are the principles:

  • SecureWindows Store apps cannot access the user’s data without the user’s permission. This gives the user confidence that an app cannot delete, modify, or upload the user’s data to an unknown Internet location. It also means that an app cannot acquire the user’s location or record audio or video without the user’s consent.

  • Power efficient. For the most part, Windows Store apps can execute code only when the user is interacting with the app. The OS suspends all threads in the app when the app is in the background. This forbids an app from using system resources (like the CPU, network, and storage) that consume battery power while the app is in the background. Blocking the use of system resources also helps the foreground app remain fast and fluid. WinRT does provide some facilities that allow an app to look like is it running while it’s in the background. Various chapters of this book explore these facilities.

  • The user is always in control. Windows Store apps cannot overwrite a user’s desires. For example, the user must grant permission for an app to add its tiles to the Start screen or to show a toast notification. The user decides if an app’s data can sync between the user’s PCs. The user decides which files or folders an app can access. The user decides whether an app can use the network when roaming or when doing so exceeds the user’s monthly data limit. Apps cannot decide these things on behalf of the user. The OS itself actively enforces many of these limitations. However, some of them (such as using the network when roaming or going over the user’s data limit) are enforced by Windows Store policy. That is, your app will not be certified and placed on the Windows Store if it violates this principle.

  • Isolation. Windows Store apps are forbidden from affecting the OS or other apps the user has installed. For example, Windows Store apps cannot access data created and maintained by other apps (unless the user grants access via a file picker). Furthermore, apps cannot communicate with other installed apps; all interprocess communication is prohibited. However, Windows Store apps can communicate with other apps via well-defined mechanisms.

  • Confident install, upgrade, and uninstall. Users can easily discover and install Windows Store apps via the Store app included with Windows. With Windows Store apps, users are confident the app adheres to all these principles. Installed apps remain up to date with the latest bug fixes and features. In addition, users can easily uninstall apps via the Start screen. When uninstalling an app, users are assured the app is fully uninstalled (no leftover directories, files, or registry settings). Moreover, due to app isolation, uninstalling an app cannot negatively affect the OS or other installed apps. Furthermore, unlike desktop apps, which can be installed only by an administrator, Windows Store apps are installable by a standard user.

  • Simplified app management. Historically, desktop apps have many usability problems. These are largely because a user can run multiple instances of an app, each with its own window. For example, when a user is tapping on an app’s icon, should the system launch a new instance of the app or bring the already running instance to the foreground? And, if the app has multiple instances running, which instance should come to the foreground? Exacerbating the problem, users sometimes forget that an app is running if its window is obscured by another app’s window. On the other hand, Windows Store apps are always single instance, so selecting the app launches it if it’s not running or brings it to the foreground if it is.

    Another problem with desktop apps is that the user must decide which app to terminate if the system is running low on memory. But the user doesn’t know which app is consuming the most amount of memory. To fix this problem, users never have to close a Windows Store app. The system can terminate the app automatically if memory is running low, and the system can automatically relaunch the app if the user switches back to it. Also, the system automatically terminates an app if the user upgrades the app to a new version or uninstalls the app. To help make this experience seamless for the user, you (the app developer) must do some additional work in your code. (See Chapter 3.)

  • Fast and fluid. Windows Store apps always respond immediately to user input so that the user always feels in control of the app and has a pleasant experience with it.

  • Content over chrome. Windows Store apps tend to have a touch-optimized user interface that emphasizes its content over chrome (menus, toolbars, frames, and so on). This affords the user an immersive experience with the app and its data.

  • Device flexibility. Windows and apps built for it get to enjoy a wide variety of hardware devices. This allows users to purchase PCs that work right for them. Windows supports three CPU architectures (ARM, x86, and x64), as well as PCs with varying amounts of RAM, storage, and display resolution. Windows PCs also support a large set of other peripherals, including local area network (LAN), wide area network (WAN), and mobile network adapters, cameras, scanners, printers, game controllers, and so on.

Note

Any app that must violate one or more of these principles should not use the Windows Store application model. For example, apps that need to affect the OS and other apps—such as debuggers and other system tools and utilities—must be implemented as desktop apps.

Note

When Windows is running on a PC with an ARM CPU, we call that PC a Windows RT PC. On a Windows RT PC, users can install only Windows Store apps or desktop apps signed by Microsoft’s Windows division (like Task Manager, Microsoft Office apps, or Visual Studio’s Remote Debugging tools[1]). Developers frequently ask me why they cannot install their own desktop apps on a Windows RT PC. The reason for this goes back to all the principles listed. Because desktop apps do not adhere to the principles, a desktop app could compromise the PC, thereby breaking user confidence, weakening security, hurting system responsiveness, and wasting battery power. By preventing the installation of arbitrary desktop apps on a Windows RT PC, Microsoft is assuring users that their Windows RT PC will be an excellent user experience over the lifetime of the PC.

To comply with many of the principles, Windows Store apps are created and deployed as self-contained package files (ZIP files), as discussed in Chapter 2. Everything an app needs to run has to be in this package. Because the package is a completely self-contained deployment unit, it includes all necessary dependencies. When a user installs an app, Windows automatically deploys its dependencies in the same directory. Hence, another app that needs the same component or dependency gets its own copy in its own directory.

Package files contain an XML manifest file describing how the app integrates into the system. This file specifies what capabilities (system resources)—such as storage locations, network access, location, webcam, and so on—the app desires. Before installing an app, the user is shown what capabilities the app desires and the user decides whether to install the app or not. The app also declares in the manifest file the various ways the system can activate the app. For example, an app declares that it can be activated to run code in the background, process certain file types or protocols, or accept shared data. Chapter 3 explains how an app supports these various activations.

When installing an app’s package, the system parses the XML file and integrates the app into the system. When uninstalling the app, the system parses the XML file again and undoes the integration. By taking full control of app install and uninstall, Windows ensures that uninstall leaves no files, subdirectories, or registry keys behind and also ensures that no part of the system or another app depends on a component or setting that has been removed.

Microsoft’s vision is that the Windows Runtime offers one unified programming model, allowing developers to write apps for any Windows device, including phones, tablets, notebook PCs, desktop PCs, server PCs, Xbox, and even large devices like Microsoft PixelSense. Unifying the programming model for all these devices takes time, and Microsoft fully admits that it has a long way to go. While this book does not focus on Windows Phone or Xbox directly, it is my hope that the content of this book will help you if you decide to target any of these other devices.

Windows Store app technology stacks

Figure 1-1 shows the three technology stacks you can use to create a Windows Store app. The bottom of the figure shows a large rectangle titled “Windows.” This box indicates the various features that Windows exposes to developers. On the right, the “Win32 and COM” box represents APIs that have shipped with Windows for many versions now; these technologies continue to ship in order to support desktop apps. The Win32 and COM APIs are old and complicated. Furthermore, they were designed to be used by native C/C++ developers. The WinRT box on the left represents the new WinRT APIs. WinRT is a modern, simple, and object-oriented API designed for use by many programming languages. By the way, the boxes are not shown to scale: there are far more Win32 and COM APIs than there are WinRT APIs.

Technically speaking, all the Win32, COM, and WinRT APIs are callable from both desktop apps as well as Windows Store apps. However, the WinRT APIs are simpler and easier to use, so you should use them whenever possible. And although a Windows Store app can call any Win32 or COM API, many of them will fail due to the app container’s security context. Other Win32 and COM APIs should not be called because their use violates the principles presented earlier. In fact, Microsoft created an approved list of Win32 and COM APIs that a Windows Store app is allowed to use. (See http://msdn.microsoft.com/en-us/library/windows/apps/br205757.) If your Windows Store app uses any Win32 or COM API not on the approved list, your app will fail Windows Store certification.

Window Store app technology stacks.

Figure 1-1. Window Store app technology stacks.

Note

I find it very useful to call some unapproved Win32 and COM APIs during app development. For example, I sometimes call the Win32 MessageBeep API during development so that I can hear when a particular location in my code executes. Then, before I submit my app to the Windows Store for certification, I remove the calls to these APIs.

The main purpose of an operating system is to abstract hardware devices to the application developer. For example, a PC can store files on a hard disk, an SSD drive, a USB drive, a DVD, a network share, and so on. The OS abstraction for this is a file, and an app developer can simply write code that opens a file and reads its contents. The app developer doesn’t have to know what kind of hardware device contains the file and how to communicate with that device. If you look closely at the WinRT APIs, you’ll see that many of them are about abstracting hardware devices—although there are a few APIs (like the Application Model APIs) related to managing your app within the system.

Microsoft supports three different technology stacks that developers can use to build Windows Store apps: native C/C++, .NET (C# and Visual Basic), and JavaScript. Each technology stack has its own programming language, supporting class libraries, presentation layer, and optionally an execution engine or virtual machine. Now, let me explain each technology stack (as shown in Figure 1-1):

  • Native C/C++. Developers can call WinRT APIs to leverage OS features using native C/C++. These developers also can leverage various C and C++ runtime libraries in their code. However, not all C runtime (CRT) functions are available for Windows Store apps. See http://msdn.microsoft.com/en-us/library/windows/apps/jj606124.aspx for more information. For the app’s presentation layer, C/C++ developers can use DirectX APIs (for high-performance graphics) or WinRT’s XAML APIs (for forms-based) apps.[2] In fact, a single app can use DirectX and XAML together; see http://msdn.microsoft.com/en-us/library/windows/apps/hh825871.aspx. DirectX offers Direct2D and Direct3D libraries while XAML provides support for basic 2D primitives and effects.

    Developers typically use this technology stack when they’re concerned about conserving memory and improving performance. Probably the most common scenario in which native C/C++ is used is when developers build real-time games. Because C/C++ is compiled to native code, developers must recompile their code for each CPU architecture they want to support, create a package for each CPU architecture, and submit all packages to the Windows Store for certification.[3]

  • C# and Visual Basic. Developers can call WinRT APIs to leverage OS features using C# or Visual Basic. These developers can also leverage a small subset of the .NET Framework Class Library in their code. Developers typically take this path to increase their productivity because .NET provides many productivity features, such as garbage collection, runtime-enforced type safety, reflection, Language Integrated Query (LINQ), regular expression parsing, and so on.[4] For the app’s user interface, .NET developers can use WinRT’s XAML APIs to create forms-based apps. .NET developers can also use P/Invoke to call DirectX APIs if they want more control over the UI with high performance.[5]

    If you have your project’s Build Platform Target value set to AnyCPU (the default when you create a new project), the resulting EXE or DLL file is not tied to a specific CPU architecture (assuming you’re not dependent on any CPU-specific libraries or SDKs). This allows you to create and submit to the Windows Store a single package capable of running on all CPU architectures.

  • JavaScript. Developers can call WinRT APIs to leverage OS features using JavaScript and a Microsoft-provided JavaScript library called WinJS. This library encapsulates a lot of base functionality, such as application model, promises (for asynchronous function calls), data binding, and some UI controls. Developers also can leverage many existing JavaScript libraries (like jQuery) in their code. For the app’s presentation layer, JavaScript developers can use HTML and CSS to create forms-based apps. Developers typically take this path if they are already familiar with JavaScript, HTML, and CSS, so they have only the WinRT APIs to learn in order to build a Windows Store app.

    JavaScript developers embed their source code into their package file when submitting it for Windows Store certification. Because the source code is just text, it is CPU agnostic; therefore, only one package file needs to be submitted. At runtime, Microsoft’s Internet Explorer virtual machine parses the source code, allowing it to run on all the CPU architectures. The HTML and CSS are ultimately translated into DirectX, which is how the app’s forms-based UI is shown to the user.[6]

Developers commonly ask which language or framework is the best one to use when building a Windows Store app. For the most part, Microsoft encourages developers to use what they already know. The WinRT API is equally exposed to the three technology stacks, but there are reasons you might prefer one stack over another:

  • Performance. Although .NET and JavaScript have a runtime with conveniences such as garbage collection and runtime compilers, C++ does not. For most situations, the performance penalty of these runtimes is negligible—especially if you understand how the runtime and the interoperability layer work. However, at times this can become a determining factor. For example, you can write simple canvas games in HTML and JavaScript; but, for real-time action games, C++ and DirectX is frequently a better choice.

  • Legacy and third-party code. You might choose a technology stack because you already have some existing code written in a particular language. You can deploy a library or component privately inside your Windows Store app’s package, and that code will run inside your app’s app container. This means it has to abide by the same principles and it won’t be able to make any Win32 or .NET calls that are not on the approved list.

  • Sharing. You might choose a technology stack because you want to write code once and share it across different apps. For example, you might write code in C# because you want to use the same logic in a Windows Store app as well as in an a Windows Phone or ASP.NET app, or you might choose JavaScript to share code in a Windows Store app and on a webpage.

  • Framework support. Each technology stack has strengths in different areas. For example, if you need to process a lot of XML, .NET is a good choice because of its LINQ-to-XML support. Similarly, C++ allows the use of STL or BOOST libraries and JavaScript allows the use of jQuery libraries, simplifying HTML document manipulations.

  • IP protection. Windows Store apps written in JavaScript ship the actual source code (.js files) inside the package. This means that the source code files end up on the user’s system where savvy users can find them and explore their contents, although Windows will not load the application if any of its files are modified. Similarly, .NET apps ship with assemblies whose Intermediate Language (IL) can easily be decompiled. This is nothing new for .NET apps. C++ code is compiled to machine language, which is the most difficult to reverse engineer.

Important

You do not have to limit yourself to just one programming language. You can write code using C++, C#, or Visual Basic and compile it into your own WinRT component consumable from any language. For example, you could take some existing code using C++’s STL and wrap it in a C++ WinRT component that is consumable by C#, and then the C# component could be used by JavaScript to present some UI via HTML. By the way, the WinRT components that ship as part of Windows itself are written in C/C++ and consumable from any language. Note that you cannot create WinRT components using JavaScript because there is no compiler capable of producing a WinMD file (discussed later in this chapter).

The Windows Runtime type system

The WinRT APIs that ship as part of Windows are all written in native C/C++, which makes sense for platform code because it has to be fast and use as little memory as possible. However, these WinRT APIs are callable from C/C++, C#, Visual Basic, and JavaScript. To call WinRT APIs from all these languages, a small and simple type system had to be defined. This type system must use features available to all consuming programming languages. Here are the core concepts you need to know to consume the Windows Runtime type system from C#:

  • Common base type. WinRT components do not share a common base class. When using a WinRT component from C#, the Common Language Runtime (CLR) makes the component look like it is derived from System.Object; therefore, you can pass it around throughout your code. In addition, all WinRT components inherit System.Object’s public methods like ToString, GetHashCode, Equals, and GetType; so all these methods are callable on WinRT objects. Because WinRT components are implemented as extended COM objects, internally, the CLR uses Runtime Callable Wrappers (RCWs) to access them. Invoking an RCW’s members cause a managed-to-native code transition, which incurs some performance overhead.

  • Core data types. The WinRT type system supports the core data types, such as Booleans; unsigned bytes;[7] 16-bit, 32-bit, and 64-bit signed and unsigned integer numbers; single-precision and double-precision floating-point numbers; 16-bit characters; strings;[8] and void. As in the CLR, all other data types are composed from these core data types. For a complete list, see http://msdn.microsoft.com/en-us/library/br205768(v=vs.85).aspx.

  • Classes. WinRT is an object-oriented type system, meaning that WinRT components support data abstraction, inheritance, and polymorphism.[9] However, some languages (like JavaScript) do not support type inheritance. To cater to these languages, almost no WinRT components take advantage of inheritance. This means they also do not take advantage of polymorphism. In fact, only WinRT components consumable from non-JavaScript languages leverage inheritance and polymorphism. For the WinRT components that ship with Windows, only the XAML components (for building user interfaces) take advantage of inheritance and polymorphism. Applications written in JavaScript use HTML and CSS to produce their user interface instead.

  • StructuresWinRT supports structures (value types). Unlike CLR value types, WinRT structures can have only public fields of the core data types or of another WinRT structure.[10]

  • Enumerations. WinRT supports two kinds of enumerations. An enumeration can be a signed 32-bit integer with mutually exclusive values or an unsigned 32-bit integer with bit flags that can be OR’d together.

  • Interfaces. WinRT internally uses an extended version of COM, which requires interfaces to describe APIs. Then classes implement one or more interfaces. For this reason, C# developers interact with interfaces more when working with WinRT types than they usually do when working with .NET types.

In addition, the WinRT type system supports delegates, methods, properties (but not indexer properties), events, exceptions, and arrays (single-dimension, 0-based only). It also allows collections created in one language to be passed to and accessed from another language. For more information about the Windows Runtime type system, see my CLR via C#, Fourth Edition book (Microsoft Press, 2012). In Chapter 25, “Interoperating with WinRT Components,” I explain how to define your own WinRT components in C# and consume them from other languages, such as C++ or JavaScript.

Note

The Windows Runtime type system is all about interoperating across programming languages. It has limitations required to perform this cross-language communication. Once across a language barrier, there are no restrictions. For example, although Windows Runtime classes cannot expose public fields, C# code consumed by other .NET code can certainly create classes with public fields.

Windows Runtime type-system projections

How is it possible for the various programming languages to know about and call WinRT APIs? There must be some description of the APIs consumable by all the languages. In the .NET Framework, code written in one language can interoperate with types written in a different language because of metadata. The metadata is programming-language-agnostic information that describes types and their members. Microsoft’s WinRT team uses the same metadata format (ECMA-335) created by Microsoft’s .NET team. That is, the Windows SDK ships with a DLL containing metadata describing all the WinRT components that ship with Windows itself. This DLL has a WinMD file extension (which stands for Windows MetaData), and Visual Studio automatically adds a reference to this WinMD file when you create a new Windows Store app project. The WinMD file is typically found here:

%WindowsSdkDir%ReferencesCommonConfigurationNeutralWindows.WinMD.

Because this file is just like a normal .NET assembly, you can use standard .NET utilities (such as ILDasm.exe or Reflector.exe) to open this file and explore its contents. Of course, because the WinRT APIs are written in native code, this WinMD file does not contain any IL code; it contains only metadata.

When compiling a native C/C++ app, the compiler itself parses the Windows.WinMD file, making the WinRT APIs callable to native C/C++ apps. Similarly, when developing a C# or Visual Basic app, the compiler parses the Windows.WinMD file, ensuring that our code calls the WinRT APIs correctly. At runtime, the CLR uses different WinMD files installed in the %WinDir%System32WinMetadata directory. Having separate, smaller WinMD files at runtime decreases the memory needed by apps because few apps (if any) will use all of Windows’ WinRT components. When running a JavaScript app, Internet Explorer’s virtual machine (VM) also parses the WinMD files, making the WinRT APIs callable to JavaScript code.

What’s happening here is that the C++ compiler, the C# and Visual Basic compilers, the CLR, and Internet Explorer’s VM are all parsing the same WinMD files and then they project the APIs using the Windows Runtime type system as if they were implemented using the C/C++ type system, the CLR’s type system, or the JavaScript type system, respectively. This way, the developer consuming the WinRT APIs has a familiar and natural experience when working with the APIs. Let’s look at an example. Here is some C# code that calls WinRT APIs to open a file and read its contents:

using System;          // .NET Framework Class Library
using Windows.Storage; // Most Windows.* namespaces are for WinRT APIs
using Windows.UI.Popups;
private async void ReadText() {
   var filename = "MyFile.txt";
   StorageFolder folder = ApplicationData.Current.LocalFolder;
   StorageFile file = await folder.GetFileAsync(filename);
   String text = await FileIO.ReadTextAsync(file);
   MessageDialog dialog = new MessageDialog(text, "File's Text");
   await dialog.ShowAsync();
}

You don’t need to understand what this code actually does. What is important to understand is that this code uses lots of WinRT APIs (StorageFolder, StorageFile, ApplicationData, FileIO, and MessageDialog) but the code looks like you’re just consuming ordinary .NET types.

Here’s the same function written in native C++:

using namespace Windows::Storage;
using namespace Windows::UI::Popups;
void SimpleSampleCx::MainPage::ReadText() {
   auto filename = "MyFile.txt";
   create_task(ApplicationData::Current->LocalFolder->GetFileAsync(filename))
      .then([this](StorageFile^ file)  {
         create_task(FileIO::ReadTextAsync(file)).then([this](String^ txt) {
            MessageDialog^ dialog = ref new MessageDialog(txt, "File's Text");
            dialog->ShowAsync();
         });
      });
}

Readers familiar with C++/CLI might think this is actually managed code because it is using ref new and ^. It is not. The syntax used is called C++ Component eXtensions, or C++/CX. It is a set of extensions to the C++ language, making it syntactically easy to invoke WinRT APIs.[11]

And here’s the same function written in JavaScript:

function readText() {
   var filename = 'MyText.txt';
   var localFolder = Windows.Storage.ApplicationData.current.localFolder;
   localFolder.getFileAsync(filename).then( function(file){
      Windows.Storage.FileIO.readTextAsync(file).then( function(txt){
         var dialog = new Windows.UI.Popups.MessageDialog(txt, "File's Text");
            dialog.showAsync();
      })
   });
}

Notice that in JavaScript the first letter of the WinRT methods start with a lowercase letter (like getFileAsync). This is how the Internet Explorer VM projects the WinRT API methods to the JavaScript developer. This gives the developer a natural experience because initial lowercase letters is a standard convention in JavaScript. For .NET developers, there are two kinds of projections:

  • CLR projections. CLR projections are mappings performed implicitly by the CLR, usually related to reinterpreting metadata. For example, the CLR makes all WinRT components look like they’re derived from System.Object (as mentioned earlier). Other WinRT types already have well-known .NET types that .NET developers are familiar with. These types (some of which are listed in Table 1-1) are converted back and forth between .NET and WinRT and, frequently, the .NET projection of the type has more features (methods and properties) than the WinRT equivalent. For example, WinRT defines a System.Foundation.Uri type; but to .NET developers, this is exposed as the familiar System.Uri type.[12]

    Table 1-1. WinRT types and their corresponding .NET type projection.

    WinRT namespace

    WinRT type

    .NET namespace

    .NET type

    .NET assembly

    Windows.Foundation.Metadata

    AttributeUsageAttribute

    System

    AttributeUsageAttribute

    System.Runtime.dll

    Windows.Foundation.Metadata

    AttributeTargets

    System

    AttributeTargets

    System.Runtime.dll

    Windows.UI

    Color

    Windows.UI

    Color

    System.Runtime.WindowsRuntime.dll

    Windows.Foundation

    DateTime

    System

    DateTimeOffset

    System.Runtime.dll

    Windows.Foundation

    EventHandler<T>

    System

    EventHandler<T>

    System.Runtime.dll

    Windows.Foundation

    EventRegistrationToken

    System.Runtime.InteropServices.WindowsRuntime

    EventRegistrationToken

    System.Runtime.InteropServices.WindowsRuntime.dll

    Windows.Foundation

    Hresult

    System

    Exception

    System.Runtime.dll

    Windows.Foundation

    IReference<T>

    System

    Nullable<T>

    System.Runtime.dll

    Windows.Foundation

    Point

    Windows.Foundation

    Point

    System.Runtime.WindowsRuntime.dll

    Windows.Foundation

    Rect

    Windows.Foundation

    Rect

    System.Runtime.WindowsRuntime.dll

    Windows.Foundation

    Size

    Windows.Foundation

    Size

    System.Runtime.WindowsRuntime.dll

    Windows.Foundation

    TimeSpan

    System

    TimeSpan

    System.Runtime.dll

    Windows.Foundation

    Uri

    System

    Uri

    System.Runtime.dll

    Windows.Foundation

    IClosable

    System

    IDisposable

    System.Runtime.dll

    Windows.Foundation.Collections

    IIterable<T>

    System.Collections.Generic

    IEnumerable<T>

    System.Runtime.dll

    Windows.Foundation.Collections

    IVector<T>

    System.Collections.Generic

    IList<T>

    System.Runtime.dll

    Windows.Foundation.Collections

    IVectorView<T>

    System.Collections.Generic

    IReadOnlyList<T>

    System.Runtime.dll

    Windows.Foundation.Collections

    IMap<K, V>

    System.Collections.Generic

    IDictionary<TKey, TValue>

    System.Runtime.dll

    Windows.Foundation.Collections

    IMapView<K, V>

    System.Collections.Generic

    IReadOnlyDictionary<TKey, TValue>

    System.Runtime.dll

    Windows.Foundation.Collections

    IKeyValuePair<K, V>

    System.Collections.Generic

    KeyValuePair<TKey, TValue>

    System.Runtime.dll

    Windows.UI.Xaml.Input

    ICommand

    Windows.UI.Xaml.Input

    ICommand

    System.ObjectModel.dll

    Windows.UI.Xaml.Interop

    IBindableIterable

    System.Collections

    IEnumerable

    System.Runtime.dll

    Windows.UI.Xaml.Interop

    IBindableVector

    System.Collections

    IList

    System.Runtime.dll

    Windows.UI.Xaml.Interop

    INotifyCollectionChanged

    System.Collections.Specialized

    INotifyCollectionChanged

    System.ObjectModel.dll

    Windows.UI.Xaml.Interop

    NotifyCollectionChangedEventHandler

    System.Collections.Specialized

    NotifyCollectionChangedEventHandler

    System.ObjectModel.dll

    Windows.UI.Xaml.Interop

    NotifyCollectionChangedEventArgs

    System.Collections.Specialized

    NotifyCollectionChangedEventArgs

    System.ObjectModel.dll

    Windows.UI.Xaml.Interop

    NotifyCollectionChangedAction

    System.Collections.Specialized

    NotifyCollectionChangedAction

    System.ObjectModel.dll

    Windows.UI.Xaml.Data

    INotifyPropertyChanged

    System.ComponentModel

    INotifyPropertyChanged

    System.ObjectModel.dll

    Windows.UI.Xaml.Data

    PropertyChangedEventHandler

    System.ComponentModel

    PropertyChangedEventHandler

    System.ObjectModel.dll

    Windows.UI.Xaml.Data

    PropertyChangedEventArgs

    System.ComponentModel

    PropertyChangedEventArgs

    System.ObjectModel.dll

    Windows.UI.Xaml

    CornerRadius

    Windows.UI.Xaml

    CornerRadius

    System.Runtime.WindowsRuntime.UI.Xaml.dll

    Windows.UI.Xaml

    Duration

    Windows.UI.Xaml

    Duration

    System.Runtime.WindowsRuntime.UI.Xaml.dll

    Windows.UI.Xaml

    GridUnitType

    Windows.UI.Xaml

    DurationType

    System.Runtime.WindowsRuntime.UI.Xaml.dll

    Windows.UI.Xaml

    GridLength

    Windows.UI.Xaml

    GridLength

    System.Runtime.WindowsRuntime.UI.Xaml.dll

    Windows.UI.Xaml

    GridUnitType

    Windows.UI.Xaml

    GridUnitType

    System.Runtime.WindowsRuntime.UI.Xaml.dll

    Windows.UI.Xaml

    Thickness

    Windows.UI.Xaml

    Thickness

    System.Runtime.WindowsRuntime.UI.Xaml.dll

    Windows.UI.Xaml.Interop

    TypeName

    System

    Type

    System.Runtime.dll

    Windows.UI.Xaml.Controls.Primitives

    GeneratorPosition

    Windows.UI.Xaml.Conrols.Primitives

    GeneratorPosition

    System.Runtime.WindowsRuntime.UI.Xaml.dll

    Windows.UI.Xaml.Media

    Matrix

    Windows.UI.Xaml.Media

    Matrix

    System.Runtime.WindowsRuntime.UI.Xaml.dll

    Windows.UI.Xaml.Media.Animation

    KeyTime

    Windows.UI.Xaml.Media.Animation

    KeyTime

    System.Runtime.WindowsRuntime.UI.Xaml.dll

    Windows.UI.Xaml.Media.Animation

    RepeatBehavior

    Windows.UI.Xaml.Media.Animation

    RepeatBehavior

    System.Runtime.WindowsRuntime.UI.Xaml.dll

    Windows.UI.Xaml.Media.Animation

    RepeatBehaviorType

    Windows.UI.Xaml.Media.Animation

    RepeatBehaviorType

    System.Runtime.WindowsRuntime.UI.Xaml.dll

    Windows.UI.Xaml.Media.Media3D

    Matrix3D

    Windows.UI.Xaml.Media.3D

    Matrix3D

    System.Runtime.WindowsRuntime.UI.Xaml.dll

  • Framework projectionsFramework projections are mappings performed explicitly in your code by leveraging new APIs introduced in .NET Framework Class Library. Framework projections are required when the impedance mismatch between the WinRT type system and the CLR’s type system is too great for the CLR to do it implicitly. Framework projections are used for asynchronous programming (discussed later in this chapter) and when working with streams and data buffers (discussed in Chapter 6).

Calling asynchronous WinRT APIs from .NET code

When a thread performs an I/O operation synchronously, the thread can block for an indefinite amount of time. When a GUI thread blocks for an I/O operation to complete, the application’s user interface stops responding to user input—such as touch, mouse, and stylus events—causing the user to get frustrated with the application. To keep apps responsive, WinRT components that perform I/O operations expose the functionality via asynchronous APIs exclusively. In fact, WinRT components that perform compute operations also expose this functionality via asynchronous APIs exclusively if the CPU operation could take greater than 50 milliseconds. For more information about building responsive applications, see Part V, “Threading,” of CLR via C#, Fourth Edition by Jeffrey Richter.

Because the WinRT APIs are mostly about abstracting hardware, many APIs perform I/O operations; therefore, many WinRT APIs are asynchronous. So, for you to be productive with them requires that you understand how to work with them from C#. To understand it, examine the following code:

public static void WinRTAsyncIntro() {
   IAsyncOperation<StorageFile> asyncOp = KnownFolders.MusicLibrary.GetFileAsync("Song.mp3");
   asyncOp.Completed = OpCompleted;
   // Optional: call asyncOp.Cancel() sometime later
}
// NOTE: Callback method executes via GUI or thread pool thread:
private static void OpCompleted(IAsyncOperation<StorageFile> asyncOp, AsyncStatus status) {
   if (status == AsyncStatus.Canceled) {
      // Process cancellation...
   } else {
      try {
         StorageFile file = asyncOp.GetResults();  // Throws if operation failed
         // Process result (do something with file)...
      }
      catch (Exception ex) {
         // Process exception...
      }
   }
   asyncOp.Close();
}

The WinRTAsyncIntro method invokes the WinRT GetFileAsync method to find a file in the user’s music library. All WinRT APIs that perform asynchronous operations are named with the Async suffix, and they all return an object whose type implements a WinRT IAsyncXxx interface—in this example, an IAsyncOperation<TResult> interface where TResult is the WinRT StorageFile type. This object, whose reference I put in an asyncOp variable, represents the pending asynchronous operation. Your code must somehow receive notification when the pending operation completes. To do this, you must implement a callback method (OpCompleted in my example), create a delegate to it, and assign the delegate to the asyncOp’s Completed property. Now, when the operation completes, the callback method is invoked via some thread (not necessarily the GUI thread). If the operation completed before assigning the delegate to the OnCompleted property, the system invokes the callback as soon as possible. In other words, there is a race condition here, but the object implementing the IAsyncXxx interface resolves the race for you, ensuring that your code works correctly.

As noted at the end of the WinRTAsyncIntro method, you can optionally call a Cancel method offered by all IAsyncXxx interfaces if you want to cancel the pending operation. All asynchronous operations complete for one of three possible reasons: the operation runs to completion successfully, the operation is explicitly canceled, or the operation results in a failure. When the operation completes due to any of these reasons, the system invokes the callback method, passing it a reference to the same object that the original XxxAsync method returned and an AsyncStatus. In my OnCompleted method, I examine the status parameter and either process the result due to the successful completion, handle the explicit cancellation, or handle the failure.[13] Also, note that after processing the operation’s completion, the IAsyncXxx interface object should be cleaned up by calling its Close method.

Figure 1-2 shows the various WinRT IAsyncXxx interfaces. The four main interfaces all derive from the IAsyncInfo interface. The two IAsyncAction interfaces expose a GetResults method with a void return type. If the operation failed, this method throws an exception that you can catch, allowing your error-recovery code to execute. The two IAsyncOperation interfaces expose a GetResults method with a non-void return type. Calling this method returns the result of the operation or throws an exception if the operation failed.

The two IAsyncXxxWithProgress interfaces allow your code to receive periodic progress updates as the asynchronous operation is progressing through its work. Most asynchronous operations do not offer progress updates, but some do (like background downloading and uploading, which are discussed in Chapter 7). To receive periodic progress updates, you define another callback method in your code, create a delegate that refers to it, and assign the delegate to the IAsyncXxxWithProgress object’s Progress property. When your callback method is invoked, it is passed an argument whose type matches the generic TProgress type. We’ll show an example of this in the Cancellation and progress section.

WinRT’s interfaces related to performing asynchronous I/O and compute operations.

Figure 1-2. WinRT’s interfaces related to performing asynchronous I/O and compute operations.

Simplifying the calling of asynchronous methods

In the .NET Framework, we use types in the System.Threading.Tasks namespace to perform asynchronous operations. In addition, C# offers the async and await keywords, allowing you to perform asynchronous operations by using a sequential programming model, thereby simplifying your code substantially. We’ll now look at how C# developers work with asynchronous WinRT APIs.

The following code is a rewrite of the WinRTAsyncIntro method shown earlier. However, this version leverages some framework projections (extension methods) supplied with the .NET Framework Class Library. This code does not show progress reporting (because GetFileAsync doesn’t offer it) and also ignores cancellation:

using System;   // Required for framework projection extension methods defined
                // by the WindowsRuntimeSystemExtensions class
...
// NOTE: If invoked by a GUI thread, all code executes via that GUI thread:
public async static void WinRTAsyncIntro() {
   try {
      StorageFile file = await KnownFolders.MusicLibrary.GetFileAsync("Song.mp3");
      // TODO: Completed code
   }
   catch (SomeOtherException ex) {
      // Error code
   }
}

What’s happening here is that the use of C#’s await operator causes the compiler to look for a GetAwaiter method on the IAsyncOperation<StorageFile> interface returned from the GetFileAsync method. This interface doesn’t provide a GetAwaiter method, so the compiler looks for an extension method. Fortunately, the .NET Framework team has provided a bunch of extension methods that are callable when you have one of WinRT’s IAsyncXxx interfaces:

namespace System {
   public static class WindowsRuntimeSystemExtensions {
      public static TaskAwaiter GetAwaiter(
         this IAsyncAction source);
      public static TaskAwaiter GetAwaiter<TProgress>(
         this IAsyncActionWithProgress<TProgress> source);
      public static TaskAwaiter<TResult> GetAwaiter<TResult>(
         this IAsyncOperation<TResult> source);
      public static TaskAwaiter<TResult> GetAwaiter<TResult, TProgress>(
         this IAsyncOperationWithProgress<TResult, TProgress> source);
   }
}

Internally, all these methods construct a TaskCompletionSource and tell the IAsyncXxx object to invoke a callback that sets the TaskCompletionSource’s final state when the asynchronous operation completes. The TaskAwaiter object returned from these extension methods is ultimately what C# awaits. When the asynchronous operation completes, the TaskAwaiter object ensures that the code continues executing via the SynchronizationContext that is associated with the original thread. If the calling thread is a GUI thread, this ensures that the code after the await executes via the same GUI thread, allowing the UI to be updated correctly; there’s no need to deal with CoreDispatcher objects and writing code that marshals callback methods back to the GUI thread.

Then the thread executes the C# compiler–generated code, which queries the TaskCompletionSource’s Task’s Result property, which returns the result (a StorageFile in my example) or throws some other exception if a failure occurred.

Cancellation and progress

What I’ve just shown is the common scenario of calling an asynchronous WinRT API and discovering its outcome. However, the preceding code ignored cancellation and progress updates. To properly handle cancellation and progress updates, instead of having the compiler implicitly call one of the GetAwaiter extension methods shown earlier, you instead explicitly call one of the AsTask extension methods that the WindowsRuntimeSystemExtensions class also defines:

namespace System {
   public static class WindowsRuntimeSystemExtensions {
      public static Task AsTask<TProgress>(this IAsyncActionWithProgress<TProgress> source,
         CancellationToken cancellationToken, IProgress<TProgress> progress);
      public static Task<TResult> AsTask<TResult, TProgress>(
         this IAsyncOperationWithProgress<TResult, TProgress> source,
         CancellationToken cancellationToken, IProgress<TProgress> progress);
      // Simpler overloads not shown here
   }
}

So now, we can add cancellation and progress. Here’s how to call an asynchronous WinRT API and fully leverage cancellation and progress for those times when you need these enhancements:

using System;        // For WindowsRuntimeSystemExtensions's AsTask
using System.Threading;    // For CancellationTokenSource
internal sealed class MyClass {
   private CancellationTokenSource m_cts = new CancellationTokenSource();
   // NOTE: If invoked by a GUI thread, all code executes via that GUI thread:
   private async void MappingWinRTAsyncToDotNet(WinRTType someWinRTObj) {
      try {
         // Assume XxxAsync returns IAsyncOperationWithProgress<IBuffer, UInt32>
         IBuffer result = await someWinRTObj.XxxAsync(...)
            .AsTask(m_cts.Token, new Progress<UInt32>(ProgressReport));
         // TODO: Completed code
      }
      catch (TaskCanceledException) { // Derived from OperationCanceledException
         // TODO: Cancel code
      }
      catch (SomeOtherException) {
         // TODO: Error code
      }
   }
   private void ProgressReport(UInt32 progress) {
      // Update progress code
   }
   public void Cancel() { m_cts.Cancel(); } // Called sometime later to cancel
}

There are two additional points worth mentioning. First, if you don’t care which thread executes the code after an await, you can improve your app’s performance by calling Task’s ConfigureAwait method, passing in false for the continueOnCapturedContext parameter. Second, there are a few scenarios where you might want to call an asynchronous method and then block the calling thread until the operation completes. These scenarios include calling asynchronous methods in a constructor (which you really should avoid to guarantee quick construction and reduce the chances of an exception), a property (which you also should avoid for the same reasons), or any method where the execution of code cannot continue until the operation is complete. For example, you will sometimes need to call an asynchronous method from inside a callback method, a method overriding a virtual method, or a method implementing an interface method. If you mark the method as async, the code that called your method can continue execution before the asynchronous method completes, and this is frequently undesirable. You’ll see examples of this in Chapter 3 and Chapter 9. In addition, you can mark a method as async only if the method’s return type is Task, Task<TResult>, or void. Because many delegate, virtual, and interface method signatures do not have one of these return types, you must block the thread if your method’s implementation calls an asynchronous method.

Here is an example of the proper way to call a WinRT asynchronous method and then block the thread until the operation completes:

StorageFile file = KnownFolders.MusicLibrary.GetFileAsync("Song.mp3")
   .AsTask().GetAwaiter().GetResult();

The AsTask method converts the returned object into a .NET Task (or Task<TResult>) object. Then the GetAwaiter method returns a TaskAwaiter object that knows how to wait for the operation to complete. Its GetResult method blocks the calling thread until the operation completes, and then it either returns the result or throws an exception if the operation failed.

Although you could write the code like this, you should not:

StorageFile file = KnownFolders.MusicLibrary.GetFileAsync("Song.mp3").AsTask().Result;

The reason is that querying a Task’s Result property throws an AggregateException if the operation fails instead of throwing the correct exception.

Be aware that blocking a GUI thread by calling GetAwaiter().GetResult() could potentially deadlock the thread, forcing the user or operating system to terminate your app. So you should really avoid blocking a thread issuing asynchronous operations whenever possible.

WinRT deferrals

Many WinRT components offer virtual or interface methods you can implement. Additionally, many WinRT classes expose events your app can register callback methods with. When your app returns from your methods, Windows believes your code has completed its operation and then Windows might take some next action. For example, when your app is about to be suspended (discussed in Chapter 3), Windows raises an event to notify your app. Upon receiving this notification, your app might want to persist some app state to the user’s hard disk. When you perform this operation asynchronously, the thread returns to Windows before the asynchronous operation completes. However, when the thread returns to Windows, Windows believes your app has successfully suspended itself and then suspends all your app’s threads, preventing them from executing any more code.

To deal with this, WinRT offers a mechanism known as a deferral. A deferral allows a method to return to Windows while indicating that the operation is not yet complete. This prevents Windows from performing the next action. Then later, after your app has completed its operation, it completes the deferral, telling Windows it can now perform the next action. Deferrals should be used only if your method performs some kind of asynchronous operation. Here is some code demonstrating the use of a deferral in an app’s suspending event handler:

private async void OnSuspending(object sender, SuspendingEventArgs e) {
   // A deferral tells Windows the thread may return but the work is not done
   var deferral = e.SuspendingOperation.GetDeferral();
   // TODO: perform async operation(s) here...
   var result = await XxxAsync();  // Thread returns but app is NOT suspended
   deferral.Complete(); // Now, tell Windows we're done (app is suspended)
}

Remember that the deferral variable refers to a Runtime Callable Wrapper that internally refers to the WinRT component. So, if your code does not call the Complete method, the garbage collector will eventually run and clean up the object, which effectively calls Complete for you. Although, when suspending, the garbage collector (GC) cannot run if your app is terminated.

WinRT defines several deferral classes; the ones relevant to C# programmers are shown here:

Windows.ApplicationModel.SuspendingDeferral
Windows.ApplicationModel.Background.BackgroundTaskDeferral
Windows.ApplicationModel.Calls.LockScreenCallEndCallDeferral
Windows.ApplicationModel.DataTransfer.DataProviderDeferral
Windows.ApplicationModel.DataTransfer.DataRequestDeferral
Windows.ApplicationModel.Search.SearchPaneSuggestionsRequestDeferral
Windows.ApplicationModel.Search.SearchSuggestionsRequestDeferral
Windows.Devices.Printers.Extensions.PrintTaskConfigurationSaveRequestedDeferral
Windows.Devices.SmartCards.SmartCardPinResetDeferral
Windows.Graphics.Printing.PrintTaskRequestedDeferral
Windows.Graphics.Printing.PrintTaskSourceRequestedDeferral
Windows.Media.PlayTo.PlayToSourceDeferral
Windows.Storage.SetVersionDeferral
Windows.Storage.Pickers.Provider.PickerClosingDeferral
Windows.Storage.Pickers.Provider.TargetFileRequestDeferral
Windows.Storage.Provider.FileUpdateRequestDeferral
Windows.UI.StartScreen.VisualElementsRequestDeferral

The OnSuspending method I showed demonstrates what Microsoft considers best practices when you need to defer the execution of an OS action until your code can complete an asynchronous operation. However, you could rewrite the OnSuspending method as follows:

private void OnSuspending(object sender, SuspendingEventArgs e) {
   // TODO: perform blocking async operation(s) here...
   var result = XxxAsync().AsTask.GetAwaiter().GetResult();
}  // App is suspended

This code is simpler, and you have to ask yourself, what is the harm? This code does block the GUI thread, which means the UI could become unresponsive to the user. This would be bad in general; but, in this case, the user is not interacting with the app, which is why it is being suspended in the first place. In addition, the first version of this method used an async method, which makes the code bigger and can decrease app performance. This version does not use an async method and, in the case of suspending, your app has just a few seconds to complete its operation or Windows forcibly terminates your app. Therefore, making your code faster here could make a big difference. Background tasks have a similar time limit when they execute too, so you want to make background task code fast as well.

In addition, many deferral classes are used with operations that do not execute on GUI threads; therefore, app UI responsiveness is not even an issue. For example, background tasks never execute on GUI threads, so there is practically no reason to use the BackgroundTaskDeferral class.[14]

When writing code, always keep in mind the reason why the code is executing, what thread could be executing that code, and what will happen next after your code executes. Then, with this knowledge, decide how to best implement the code so that you are guaranteed to get the behavior you desire.



[1] For more information about remote debugging, see http://msdn.microsoft.com/en-us/library/vstudio/y7f5zaaa.aspx.

[2] Ultimately, all apps draw to their window via DirectX. WinRT’s XAML APIs offer buttons, grid views, toggles, text boxes, and so on. All of these are just abstractions built on top of DirectX.

[3] Technically, you need to compile only for ARM and x86, because x86 can run as is on x64 CPUs.

[4] For a complete list of the .NET APIs usable within a Windows Store app, see http://msdn.microsoft.com/en-us/library/windows/apps/br230232.aspx.

[5] There are some .NET libraries available that wrap DirectX APIs, such as http://SharpDX.org/ and http://SlimDX.org/.

[6] Windows Store apps written in JavaScript, HTML, and CSS run in an environment that is a superset of Internet Explorer’s environment. So Windows Store apps have access to more features than a normal website. See http://msdn.microsoft.com/en-us/library/windows/apps/hh465143.aspx for more information.

[7] Signed byte is not supported.

[8] You cannot pass null to a WinRT component expecting a String. Attempting to do so throws an ArgumentNullException. However, you can pass String.Empty.

[9] Data abstraction is actually enforced because WinRT classes are not allowed to have public fields.

[10] Enumerations are OK because they are really just 32-bit integers.

[11] It’s possible to invoke WinRT APIs without using C++/CX using the Windows Runtime C++ Template Library (WRL). For more information, see http://msdn.microsoft.com/en-us/library/windows/apps/hh438466(v=vs.120).aspx.

[12] If you compare Windows.Foundation.winmd between IlDasm and ILDasm /project, you can see that the CLR projection hides IClosable by making it private. Also, Windows.Storage.Streams.IInputStream (Windows.Storage.winmd) inherits from IClosable before projection and from IDisposable after.

[13] The IAsyncInfo interface offers a Status property that contains the same value that is passed into the callback method’s status parameter. Because the parameter is passed by value, your application’s performance is better if you access the parameter rather than querying IAsyncInfo’s Status property. This is because querying the property invokes a WinRT API via an RCW.

[14] In fact, WinRT has a design flaw with background tasks. If you use BackgroundTaskDeferral and your background task code throws an exception after an await, your app will not be able to determine that the background task failed. I discuss this more in the Background task progress and completionBackground task cancellation section of Chapter 9.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.20.236