Introduction to Advanced Windows Store App Development using HTML5 and JavaScript
- 10/15/2013
- Objective 1.1: Create background tasks
- Objective 1.2: Consume background tasks
- Objective 1.3: Integrate WinMD components into a solution
- Chapter summary
- Answers
Objective 1.3: Integrate WinMD components into a solution
The Windows Runtime exposes a simple way to create components that can be used by all the supported languages without any complex data marshalling. A WinMD library, called Windows Runtime Component, is a component written in one of the WinRT languages (C#, VB, or C++, but not JavaScript) that can be used by any supported languages.
Understanding the Windows Runtime and WinMD
Windows, since its earliest version, has provided developers with libraries and APIs to interact with the operating system. However, before the release of Windows 8, those APIs and libraries were often complex and challenging to use. Moreover, while working in .NET Framework using C# or VB.NET, you often had to rely on Component Object Model (COM) Interop, and Win32 interoperability via P/Invoke (Platform Invoke) to directly leverage the operating system. For example, the following code sample imports a native Win32 DLL and declares the function capCreateCaptureWindows to be able to call it from .NET code:
Sample of C# code
[DllImport("avicap32.dll", EntryPoint="capCreateCaptureWindow")] static extern int capCreateCaptureWindow( string lpszWindowName, int dwStyle, int X, int Y, int nWidth, int nHeight, int hwndParent, int nID); [DllImport("avicap32.dll")] static extern bool capGetDriverDescription( int wDriverIndex, [MarshalAs(UnmanagedType.LPTStr)] ref string lpszName, int cbName, [MarshalAs(UnmanagedType.LPTStr)] ref string lpszVer, int cbVer);
Microsoft acknowledged the complexity of the previously existing scenario and invested in Windows 8 and the Windows Runtime to simplify the interaction with the native operating system. In fact, the Windows Runtime is a set of completely new APIs that were reimagined from the developer perspective to make it easier to call to the underlying APIs without the complexity of P/Invoke and Interop. Moreover, the Windows Runtime is built so that it supports the Windows 8 application development with many of the available programming languages/environments, such as HTML5/Windows Library for JavaScript (WinJS), common runtime language (CLR), and C++.
The following code illustrates how the syntax is clearer and easier to write, which makes it easier to read and maintain in the future, when leveraging the Windows Runtime. In this example, Photo is an Extensible Application Markup Language (XAML) image control.
Sample of C# code
using Windows.Media.Capture; var camera = new CameraCaptureUI(); camera.PhotoSettings.CroppedAspectRatio = new Size(4, 3); var file = await camera.CaptureFileAsync(CameraCaptureUIMode.Photo); if (file != null) { var bitmap = new BitmapImage() ; bitmap.SetSource(await file.OpenAsync(FileAccessMode.Read)); Photo.Source = bitmap; }
The code for WinJS and HTML5 is similar to the C# version, as follows:
Sample of JavaScript code
var camera = new capture.CameraCaptureUI(); camera.captureFileAsync(capture.CameraCaptureUIMode.photo) .then(function (file) { if (file != null) { media.shareFile = file; } });
Basically, the Windows Runtime is a set of APIs built upon the Windows 8 operating system (see Figure 1-7) that provides direct access to all the main primitives, devices, and capabilities for any language available for developing Windows 8 apps. The Windows Runtime is available only for building Windows 8 apps. Its main goal is to unify the development experience of building a Windows 8 app, regardless of which programming language you choose.
Figure 1.7. The Windows Runtime architecture
The Windows Runtime sits on top of the WinRT core engine, which is a set of C++ libraries that bridge the Windows Runtime with the underlying operating system. On top of the WinRT core is a set of specific libraries and types that interact with the various tools and devices available in any Windows 8 app. For example, there is a library that works with the network, and another that reads and writes from storage (local or remote). There is a set of pickers to pick up items (such as files and pictures), and there are several classes to leverage media services, and so on. All these types and libraries are defined in a structured set of namespaces and are described by a set of metadata called Windows Metadata (WinMD). All metadata information is based on a new file format, which is built upon the common language interface (CLI) metadata definition language (ECMA-335).
Consuming a native WinMD library
The WinRT core engine is written in C++ and internally leverages a proprietary set of data types. For example, the HSTRING data type represents a text value in the Windows Runtime. In addition, there are numeric types like INT32 and UINT64, enumerable collections represented by IVector<T> interface, enums, structures, runtime classes, and many more.
To be able to consume all these sets of data types from any supported programming language, the Windows Runtime provides a projection layer that shuttles types and data between the Windows Runtime and the target language. For example, the WinRT HSTRING type will be translated into a System.String of .NET for a CLR app, or to a Platform::String for a C++ app.
Next to this layered architecture is a Runtime Broker, which acts as a bridge between the operating system and the hosts executing Windows 8 apps, whether those are CLR, HTML5/WinJS, or C++ apps.
Using the Windows Runtime from a CLR Windows 8 app
To better understand the architecture and philosophy behind the Windows Runtime, the example in this section consumes the Windows Runtime from a CLR Windows 8 app.
You can test the use of the native WinMD library by creating a new project in Visual Studio 2012 and using the XAML code in Listing 1-15 for the main page.
Listing 1-15. Main page with a button control
<Page x:Class="WinRTFromCS.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:WinRTFromCS" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <StackPanel> <Button Click="UseCamera_Click" Content="Use Camera" /> </StackPanel> </Grid> </Page>
In the event handler for the UserCamera_Click event, use the following code:
private async void UseCamera_Click(object sender, RoutedEventArgs e) { var camera = new Windows.Media.Capture.CameraCaptureUI(); var photo = await camera.CaptureFileAsync( Windows.Media.Capture.CameraCaptureUIMode.Photo); }
Notice the async keyword and the two lines of code inside the event handler that instantiate an object of type CameraCaptureUI and invoke its CaptureFileAsync method.
You can debug this simple code by inserting a breakpoint at the first line of code (the one starting with var camera =). Figure 1-8 shows that when the breakpoint is reached, the call stack window reveals that the app is called by external code, which is native code.
Figure 1.8. Call stack showing external code
If you try to step into the code of the CameraCaptureUI type constructor, you will see that it is not possible in managed code, because the type is defined in the Windows Runtime, which is unmanaged.
Using the Windows Runtime from a C++ Windows 8 app
The example in this section uses the WinRT Camera APIs to capture an image from a C++ Windows 8 app. First, you need to create a fresh app, using C++ this time.
Assuming you are using the same XAML code as in Listing 1-15, the event handler for the UseCamera_Click event instantiates the same classes and calls the same methods you saw in C# using a C++ syntax (and the C++ compiler). See Listing 1-16.
Listing 1-16. Using the CameraCaptureUI class from C++
void WinRTFromCPP::MainPage::UseCamera_Click( Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { auto camera = ref new Windows::Media::Capture::CameraCaptureUI(); camera->CaptureFileAsync(Windows::Media::Capture::CameraCaptureUIMode::Photo); }
If you debug this code as in the previous section, the outcome will be very different because you will be able to step into the native code of the CameraCaptureUI constructor, as well as into the code of the CaptureFileAsync method.
The names of the types, as well as the names of the methods and enums, are almost the same in C# and in C++. Nevertheless, each individual language has its own syntax, code casing, and style. However, through this procedure, you can gain hands-on experience with the real nature of the Windows Runtime: a multilanguage API that adapts its syntax and style to the host language and maintains a common set of behavior capabilities under the covers. What you have just seen is the result of the language projection layer defined in the architecture of the Windows Runtime.
To take this sample one step further, you can create the same example you did in C# and C++ using HTML5/WinJS. If you do that, you will see that the code casing will adapt to the JavaScript syntax.
The following HTML5 represents the user interface for the Windows Store app using JavaScript version:
<!DOCTYPE html> <html> <head> <title>DevLeap WebCAm</title> <!-- WinJS references --> <link rel="stylesheet" href="/winjs/css/ui-dark.css" /> <script src="/winjs/js/base.js"></script> <script src="/winjs/js/wwaapp.js"></script> <!-- DevLeapWebcam references --> <link rel="stylesheet" href="/css/default.css" /> <script type="text/javascript"> function takePicture() { var captureUI = new Windows.Media.Capture.CameraCaptureUI(); captureUI.captureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.photo) .then(function (photo) { if (photo) { document.getElementById("msg ").innerHTML = "Photo taken." } else { document.getElementById("msg ").innerHTML = "No photo captured." } }); } </script> </head> <body> <input type="button" onclick="takePicture()" value="Click to shoot" /><br /> <span id="msg"></span> </body> </html>
The language projection of the Windows Runtime is based on a set of new metadata files, called WinMD. By default, those files are stored under the path <OS Root Path>\System32\WinMetadata, where <OS Root Path> should be replaced with the Windows 8 root installation folder (normally C:\Windows). Here’s a list of the default contents of the WinMD folder:
Windows.ApplicationModel.winmd
Windows.Data.winmd
Windows.Devices.winmd
Windows.Foundation.winmd
Windows.Globalization.winmd
Windows.Graphics.winmd
Windows.Management.winmd
Windows.Media.winmd
Windows.Networking.winmd
Windows.Security.winmd
Windows.Storage.winmd
Windows.System.winmd
Windows.UI.winmd
Windows.UI.Xaml.winmd
Windows.Web.winmd
Note that the folder includes a Windows.Media.winmd file, which contains the definition of the CameraCaptureUI type used in Listing 1-16.
You can inspect any WinMD file using the Intermediate Language Disassembler (ILDASM) tool available in the Microsoft .NET Software Development Kit (SDK), which ships with Microsoft Visual Studio 2012 and that you can also download as part of the Microsoft .NET Framework SDK. For example, Figure 1-9 shows the ILDASM tool displaying the content outline of the Windows.Media.winmd file, which contains the definition of the CameraCaptureUI type from Listing 1-16.
Figure 1.9. ILDASM displaying the outline of the Windows.Media.winmd file
The MANIFEST file listed at the top of the window defines the name, version, signature, and dependencies of the current WinMD file. Moreover, there is a hierarchy of namespaces grouping various types. Each single type defines a class from the WinRT perspective. In Figure 1-9, you can clearly identify the CaptureFileAsync method you used in the previous example. By double-clicking on the method in the outline, you can see its definition, which is not the source code of the method but rather the metadata mapping it to the native library that will be leveraged under the cover. In the following code excerpt, you can see the metadata definition of the CaptureFileAsync method defined for the CameraCaptureUI type:
method public hidebysig newslot virtual final instance class [Windows.Foundation]Windows.Foundation.IAsyncOperation`1 <class[Windows.Storage]Windows.Storage.StorageFile> CaptureFileAsync([in] valuetype Windows.Media.Capture.CameraCaptureUIMode mode) runtime managed { .override Windows.Media.Capture.ICameraCaptureUI::CaptureFileAsync } // end of method CameraCaptureUI::CaptureFileAsync
The language projection infrastructure will translate this neutral definition into the proper format for the target language.
Whenever a language needs to access a WinRT type, it will inspect its definition through the corresponding WinMD file and will use the IInspectable interface, which is implemented by any single WinRT type. The IInspectable interface is an evolution of the already well-known IUnknown interface declared many years ago in the COM world.
First, there is a type declaration inside the registry of the operating system. All the WinRT types are registered under the path HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsRuntime\ActivatableClassId.
For example, the CameraCaptureUI type is defined under the following path:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsRuntime\ActivatableClassId Windows.Media.Capture.CameraCaptureUI
The registry key contains some pertinent information, including the activation type (in process or out of process), as well as the full path of the native DLL file containing the implementation of the target type.
The type implements the IInspectable interface, which provides the following three methods:
GetIids. Gets the interfaces that are implemented by the current WinRT class
GetRuntimeClassName. Gets the fully qualified name of the current WinRT object
GetTrustLevel. Gets the trust level of the current WinRT object
By querying the IInspectable interface, the language projection infrastructure of the Windows Runtime will translate the type from its original declaration into the target language that is going to consume the type.
As illustrated in Figure 1-10, the projection occurs at compile time for a C++ app consuming the Windows Runtime, and it will produce native code that will not need any more access to the metadata. In the case of a CLR app (C#/VB), it happens during compilation into IL code, as well as at runtime through a runtime-callable wrapper. However, the cost of communication between CLR and the WinRT metadata is not so different from the cost of talking with the CLR metadata in general. Lastly, in the case of an HTML5/WinJS app, it will occur at runtime through the Chakra engine.
Figure 1.10. Projection schema
The overall architecture of the Windows Runtime is also versioning compliant. In fact, every WinRT type will be capable of supporting a future version of the operating system and/or of the Windows Runtime engine by simply extending the available interfaces implemented and providing the information about the new extensions through the IInspectable interface.
To support the architecture of the WinRT and the language projection infrastructure, every Windows 8 app—regardless of the programming language used to write it—runs in a standard code execution profile that is based on a limited set of capabilities. To accomplish this goal, the Windows Runtime product team defined the minimum set of APIs needed to implement a Windows 8 app. For example, the Windows 8 app profile has been deprived of the entire set of console APIs, which are not needed in a Windows 8 app. The same happened to ASP.NET, for example—the list of .NET types removed is quite long. Moreover, the Windows Runtime product team decided to remove all the old-style, complex, and/or dangerous APIs and instead provide developers with a safer and easier working environment. As an example, to access XML nodes from a classic .NET application, you have a rich set of APIs to choose from, such as XML Document Object Model (DOM), Simple API for XML, LINQ to XML in .NET, and so on. The set also depends on which programming language you are using. In contrast, in a Windows 8 app written in CLR (C#/VB) you have only the LINQ to XML support, while the XML DOM has been removed.
Furthermore, considering a Windows 8 app is an application that can execute on multiple devices (desktop PCs, tablets, ARM-based devices, and Windows Phone 8 mobile phones), all the APIs specific to a particular operating system or hardware platform have been removed.
The final result is a set of APIs that are clear, simple, well-designed, and portable across multiple devices. From a .NET developer perspective, the Windows 8 app profile is a .NET 4.5 profile with a limited set of types and capabilities, which are the minimum set useful for implementing a real Windows 8 app.
Consider this: The standard .NET 4.5 profile includes more than 120 assemblies, containing more than 400 namespaces that group more than 14,000 types. In contrast, the Windows 8 app profile includes about 15 assemblies and 70 namespaces that group only about 1,000 types.
The main goals in this profile design were to do the following:
Avoid duplication of types and/or functionalities.
Remove APIs not applicable to Windows 8 apps.
Remove badly designed or legacy APIs.
Make it easy to port existing .NET applications to Windows 8 apps.
Keep .NET developers comfortable with the Windows 8 app profile.
For example, the Windows Communication Foundation (WCF) APIs exist, but you can use WCF only to consume services, therefore leveraging a reduced set of communication bindings. You cannot use WCF in a Windows 8 app to host a service—for security reasons and for portability reasons.
Creating a WinMD library
The previous sections contained some information about the WinRT architecture and the WinMD infrastructure, which enables the language projection of the Windows Runtime to make a set of APIs available to multiple programming languages. In this section, you will learn how to create a library of APIs of your own, making that library available to all other Windows 8 apps through the same projection environment used by the Windows Runtime.
Internally, the WinRT types in your component can use any .NET Framework functionality that’s allowed in a Windows 8 app. Externally, however, your types must adhere to a simple and strict set of requirements:
The fields, parameters, and return values of all the public types and members in your component must be WinRT types.
Public structures may not have any members other than public fields, and those fields must be value types or strings.
Public classes must be sealed (NotInheritable in Visual Basic). If your programming model requires polymorphism, you can create a public interface and implement that interface on the classes that must be polymorphic. The only exceptions are XAML controls.
All public types must have a root namespace that matches the assembly name, and the assembly name must not begin with “Windows.”
To verify this behavior, you need to create a new WinMD file.
To create a WinMD library, create a new project choosing the Windows Runtime Component-provided template. The project will output not only a DLL, but also a WinMD file for sharing the library with any Windows 8 app written with any language.
You must also rename the Class1.cs file in SampleUtility.cs and rename the contained class. Then, add this method to the class and the corresponding using statement for the System.Text.RegularExpressions namespace.
Sample of C# code
public Boolean IsMailAddress(String email) { Regex regexMail = new Regex(@"\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b"); return(regexMail.IsMatch(email)); }
Build the project and check the output directory. You will find the classic bin/debug (or Release) subfolder containing a .winmd file for the project you create. You can open it with ILDASM to verify its content.
Add a new project to the same solution using the Blank App (XAML) template from the Visual C++ group to create a new C++ Windows Store Application.
Add a reference to the WinMD library in the Project section of the Add Reference dialog box, and then add the following XAML code in the Grid control:
Sample of XAML code
<StackPanel> <Button Click="ConsumeWinMD_Click" Content="Consume WinMD Library" /> </StackPanel>
Create the event handler for the click event in the code-behind file using the following code:
Sample of C++ code
void WinMDCPPConsumer::MainPage::ConsumeWinMD_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { auto utility = ref new WinMDCSLibrary::SampleUtility(); bool result = utility->IsMailAddress("paolo@devleap.com"); }
Build the solution and place a breakpoint in the IsMailAddress method of the WinMD library, and then start the C++ project in debug mode. You might need to select Mixed (Managed and Native) in the debugging properties of the consumer project, as shown in Figure 1-11.
Figure 1.11. Debugger settings to debug mixed code
As you can verify, the debugger can step into the WinMD library from a C++ Windows Store application.
You can also verify compatibility with HTML/WinJS project creating a new project based on the Windows Store templates for JavaScript (Blank App).
Reference the WinMD library as you did in the C++ section and add an HTML button that will call the code using JavaScript:
Sample of HTML code
<body> <p><button id="consumeWinMDLibrary">Consume WinMD Library</button></p> </body>
Open the default.js file, which is in the js folder of the project, and place the following event handler inside the file, just before the app.start() method invocation.
function consumeWinMD(eventInfo) { var utility = new WinMDCSLibrary.SampleUtility(); var result = utility.isMailAddress("paolo@devleap.com"); }
Notice that the case of the IsMailAddress method, defined in C#, has been translated into isMailAddress in JavaScript thanks to the language projection infrastructure provided by the Windows Runtime.
You can insert the following lines of code into the function associated with the app.onactivated event, just before the end of the if statement.
// Retrieve the button and register the event handler. var consumeWinMDLibrary = document.getElementById("consumeWinMDLibrary"); consumeWinMDLibrary.addEventListener("click", consumeWinMD, false);
Listing 1-17 shows the complete code of the default.js file after you have made the edits.
Listing 1-17. Complete code for the default.js file
// For an introduction to the Blank template, see the following documentation: // http://go.microsoft.com/fwlink/?LinkId=232509 (function () { "use strict"; WinJS.Binding.optimizeBindingReferences = true; var app = WinJS.Application; var activation = Windows.ApplicationModel.Activation; app.onactivated = function (args) { if (args.detail.kind === activation.ActivationKind.launch) { if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) { // TODO: This application has been newly launched. Initialize // your application here. } else { // TODO: This application has been reactivated from suspension. // Restore application state here. } args.setPromise(WinJS.UI.processAll()); // Retrieve the button and register our event handler. var consumeWinMDLibrary = document.getElementById("consumeWinMDLibrary"); consumeWinMDLibrary.addEventListener("click", consumeWinMD, false); } }; app.oncheckpoint = function (args) { // TODO: This application is about to be suspended. Save any state // that needs to persist across suspensions here. You might use the // WinJS.Application.sessionState object, which is automatically // saved and restored across suspension. If you need to complete an // asynchronous operation before your application is suspended, call // args.setPromise(). }; function consumeWinMD(eventInfo) { var utility = new WinMDCSLibrary.SampleUtility(); var result = utility.isMailAddress("paolo@devleap.com"); } app.start(); })();
Place a breakpoint in the IsMailAddress method or method call and start debugging, configuring Mixed (Managed and Native) for the consumer project and verifying you can step into the WinMD library.
Objective summary
Visual Studio provides a template for building a WinMD library for all supported languages.
Language projection enables you to use the syntax of the application language to use a WinMD library.
The field, parameters, and return type of all the public types of a WinMD library must be WinRT types.
Objective review
Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.
What do public classes of a WinMD library have to be?
Sealed
Marked as abstract
Implementing the correct interface
None of the above
Portable classes in a WinMD library can use which of the following?
All the .NET 4.5 Framework classes
Only a subset of the C++ classes
Only WinRT classes
Only classes written in C++
What are possible call paths? (Choose all that apply.)
A WinJS application can instantiate a C++ WinMD class.
A C++ application can instantiate a C# WinMD class.
A C# application can instantiate a WinJS WinMD.
All of the above