By Advanced Technology Group
Updated August 23, 2017
Update: Note that a newer technology, C++/WinRT, is also available as an alternative to C++/CX extensions.
Introduction
Windows Runtime for Windows
Windows Runtime for Xbox One
Consuming WinRT components in C++ using C++/CX extensions
Windows Runtime data type system
The \^ operator and ref new keyword for instantiation of Windows Runtime objects
Platform::String
WinRT reference classes
Property data members of WinRT reference classes
Distributing WinRT components with .winmd metadata files
Windows Runtime exceptions
Delegates
WinRT asynchronous APIs
Events
Using PPL tasks to implement WinRT async operations
The execution context of event handlers or async operations
Windows Runtime collections
C++/WinRT as an alternative to using C++/CX
WRL as an alternative to using C++/CX
Using C++/CX extensions
Summary
References
Appendix 1: Behind the scenes: how C++/CX language extensions are implemented
The Windows Runtime (WinRT) is a set of APIs for working with modern Microsoft platforms. These APIs use an Application Binary Interface (ABI) and calling style that’s distinct from classic Win32 C-style functions, and different also from classic Component Object Model (COM) APIs. Also known as “Modern COM,” the WinRT APIs expose new system functionality across Microsoft platforms. This paper describes the compiler language extensions, C++/CX, that make it easy to use the WinRT APIs.
Windows Runtime (WinRT) is a technology first introduced in Windows 8 for developing Windows Store apps. Windows Store apps and Universal Windows Platform (UWP) apps can be developed using different languages, including C++, C#, Microsoft Visual Basic, and JavaScript. The key benefit of Windows Runtime is that it enables authoring components that can be consumed directly from multiple languages using similar API surface areas. This creates a unified framework for creating cross-platform and cross-language components.
At the core level, Windows Runtime is a binary object model defined at the application binary interface (ABI) level, which is a binary specification for how WinRT objects should be implemented. This binary specification is what allows WinRT components to be loaded and used at runtime by other executables. Since this is a binary specification, it doesn’t impose a limitation on what programming language is used for implementing WinRT components.
An ABI is a binary contract between binary components that defines how the binaries can communicate or invoke each other’s methods.
A WinRT component is distributed as a DLL along with a .winmd file. The .winmd file contains a set of metadata that describe the component interface in such a way that the metadata can be used by all supported languages.
Each supported language uses a set of language extensions that enables WinRT components to be consumed using the native syntax of that language. These extensions are called language projections (or language extensions). These language projections remove the need to use wrappers for each language, and they provide a familiar syntax for consuming WinRT components in all supported languages.
The majority of Windows 10 system APIs for Universal Windows Platform (UWP) apps are exposed to the application’s code as WinRT components. There are still some Win32 C-style APIs available to UWP apps as well. Developers can also create their own WinRT components using their preferred programming language and consume these components in other languages (for example, develop a library in C++ as a WinRT component and consume it in a JavaScript application).
For more information about Windows Runtime for Windows and UWP apps, see References [1,2,3,4].
Xbox One uses WinRT technology. Core functionalities of the Xbox One XDK are exposed to titles as WinRT components. Although Windows Runtime is the preferred programming methodology, there is still a set of legacy Win32 C-style based APIs available in the Xbox One XDK as well. As an Xbox One developer, you need to learn how to use Windows Runtime-based APIs and data types in your C++ code. As we will discuss in next section, this is achieved by using the C++/CX language extensions.
Xbox One’s high-performance games are developed exclusively using C++ and DirectX. Because no other language is supported for developing Xbox One high-performance games, for Xbox One developers to create a WinRT component as part of their game engines is of limited value. Therefore, in this article we focus only on consuming the WinRT system components in C++, and not on authoring WinRT components.
At the system level, Windows Runtime is implemented using a light and modern version of Component Object Model (COM) [22]. This means that all WinRT objects are implemented at the system level as COM-based objects. C++/CX is a set of language extensions that allows the consumption of WinRT objects in C++ using a syntax that is much easier and cleaner than the underlying COM-based APIs and syntax [5]. These language extensions hide the COM-style programming paradigms from your code and allow you to write code using a syntax that is much easier to develop and maintain. Therefore, as an Xbox One developer using C++/CX extensions, you do not need to learn COM to successfully develop for Xbox One. Instead, you only need to get familiar with C++/CX language extensions for Windows Runtime, which are much simpler and easier to use.
In particular, C++/CX language extensions enable the WinRT language projection to C++ while making your coding easier by providing the following features. We will discuss these items in detail in the next sections.
Object instantiation using constructors and “ref new” keyword: The underlying COM uses the class factory pattern and creates objects by passing a string class ID to the object factory [24]. The C++/CX extensions map this COM pattern to a familiar C++ object instantiation pattern using constructors and ref new keyword. Therefore C++/CX extensions provide type safety checking at compile time. This removes the need for COM-based run-time error checking.
Automatic lifetime management of objects: The C++/CX extensions provide automatic lifetime management of all WinRT objects. This lifetime management is internally implemented using COM reference counting, which is hidden from the code that is consuming the WinRT objects.
Error handling using C++ exceptions instead of HRESULTs: COM uses HRESULT error codes for passing method results. C++/CX reports these error codes back as C++ exceptions. This removes the need for cascading HRESULT checks that exist in COM class implementations.
WinRT collections: Windows Runtime has a collections library that includes generic containers (for example, vectors and map containers), which are similar to C++11 Standard Library containers. These collections are added to enable C++ language projection and are not meant to replace their Standard Library counterparts.
Support for metadata: The WinRT public surface APIs are published in .winmd metadata files. These files can be consumed from all Windows Runtime-supported languages. C++/CX language extensions add support for loading .winmd files in C++ projects.
Delegates and events: Delegates are the Windows Runtime means of implementing function pointers that can be passed across ABIs. Events are special delegates that user code can subscribe to; they are invoked when a special event has happened. Events are internally implemented in COM using event sources and event sinks, which are all hidden from user code by the C++/CX extensions.
Asynchronous APIs: Any WinRT API that can take a long time to complete is implemented asynchronously. The rationale for this design rule is to provide APIs that enable developers to create more-responsive applications and games. All WinRT asynchronous APIs follow a similar pattern (they use either IAsyncAction or IAsyncOperation interfaces) which provides a consistent way for making non-blocking asynchronous calls in Windows Runtime.
On Windows 8, any API with I/O or CPU bound operations that are likely to take longer than 50 msec to complete is implemented using async pattern. On Xbox One, the same design principle is used, but the maximum completion time in msec allowed for blocking calls is TBD.
We discuss the details of the WinRT concepts summarized above in the rest of this white paper. Our focus is on consuming WinRT components using C++/CX extensions and not on creating new components or on how Windows Runtime is implemented internally using COM. Appendix 1 of this white paper provides a deep dive into how C++/CX extensions are implemented by the compiler.
C++/CX defines new data types that are needed by the WinRT system. All WinRT APIs must use these new WinRT data types exclusively. The compiler will raise an error if a WinRT API uses a non-WinRT data type (for example, std::wstring) as a return type or as a parameter in a public method. This requirement, which is imposed by the WinRT ABI contract on all data types passed among WinRT components across different binaries, is needed to enable language projections to all supported languages. Note that this restriction only applies to data used in WinRT public APIs, that is, the APIs published in the .winmd files. We strongly recommend that you use WinRT data types only on your application boundaries, i.e., when making system calls using WinRT APIs, and that you use regular C++ data types, classes, and structures anywhere else in your code base and all other APIs, just as you would with any Standard C++ program.
Most of the new WinRT value data types, specifically the numeric, Boolean, and character types, are directly mapped to existing C++ types. Table 1 summarizes these data types defined in default namespace and the C++ primitive type they are mapped to.
Table 1. WinRT built-in data types defined in default namespace.
| WinRT value data type | Xbox One C++ primitive type | stdint.h / cstdint header |
|---|---|---|
| Platform::Boolean | bool | N/A |
| char16 | wchar_t | N/A |
| float32 | float | N/A |
| float64 | double | N/A |
| int8 | char | int8_t |
| int16 | short | int16_t |
| int32 | int | int32_t |
| int64 | long long | int64_t |
| uint8 | unsigned char | uint8_t |
| uint16 | unsigned short | uint16_t |
| uint32 | unsigned int | uint32_t |
| uint64 | unsigned long long | uint64_t |
There are a few WinRT data types defined in Platform or other namespaces as summarized in the following table.
Table 2. WinRT data types.
| WinRT data type | Description |
|---|---|
| Platform::Object | All WinRT objects derive from Platform::Object and can be casted to Platform::Object (boxing) |
| Platform::String | > Reference data type representing Unicode text |
| Platform::Guid | > Value data type 128-bit data structure that represents a GUID |
| Platform::SizeT | Value data type for representing the size of an object |
| Windows::Foundation::DateTime | Value data type for representing date and time |
| Windows::Foundation::Uri | Value data type for representing the URI value |
All WinRT data types derive from Platform::Object. This rule enables language projections to non-C++/DirectX languages and application frameworks, and it is therefore beyond the scope of this white paper. However, Platform::Object includes two interesting methods, GetType and ToString, which can be used on any WinRT object to get its underlying object type and a string representation of that object, respectively. Casting any WinRT object to Platform::Object is called boxing [6]. The C++/CX extensions enable boxing for all data types, even standard built-in C++ data types, such as int or float. This allows you to call ToString on any object of any C++ data type, even on literals, and get a string representation of that object. Note that boxing does not require an explicit cast, but un-boxing requires an explicit cast.
Example 1. Casting a WinRT object to Platform::Object.
1. int i = 2;
2. Platform::String^ str = i.ToString(); // i is implicitly casted to Platform::Object^
3. // str will contain "2"
4.
5. bool b = true;
6. str = b.ToString(); // str will contain "true"
7.
8. float f = 0.24367234f;
9. str = f.ToString(); // str will contain "0.243672"
10.
11. // ToString can be used on literals as well
12. str = (10).ToString(); // str will contain "10"
13. str = (10.50f).ToString(); // str will contain "10.5"
14.
15. Platform::Object^ obj = 10; // the literal is implicitly casted to Platform::Object^
16. int j = static_cast< int >(obj); // obj is un-boxed
Perhaps the most important and most commonly used concepts in C++/CX extensions are the new \^ (“hat”) operator and ref new keyword used for instantiating WinRT reference data types. The ref new and \^ semantics serve an important purpose: they hide the underlying code for COM object instantiations and reference counting, and instead provide a simple and familiar syntax for working with WinRT types. Here is an example of how they are used:
Example 2. Use of \^ and ref new for WinRT object instantiation.
1. // Instead of Foo* foo = new Foo(); syntax, the WinRT ref objects are instantiated
2. // using ^ and “ref new”
3. WinRTFoo^ foo = ref new WinRTFoo();
4.
5. // Note the new C++11 auto keyword can also be used in many cases where an explicit
6. // ^ may be used. For example, the previous declaration can also be written as:
7. auto foo2 = ref new WinRTFoo();
8.
9. // Just like a regular pointer, use -> operator to invoke methods
10. foo->DoSomeWork();
11.
12. // foo2 will point to the same object as foo. No new object is created here.
13. // The underlying object reference count is incremented because of this assignment
14. WinRTFoo^ foo3 = foo;
15.
16. // The lifetime of WinRT objects we created are managed by the underlying WinRT system.
17. // Therefore, there is no need to call delete on foo; this object is deleted from memory
18. // as soon as both foo and foo3 go out of scope, are assigned, or are set to null.
The \^ operator is used to declare a pointer to a WinRT reference type object, which is automatically reference counted. This means that the object that the \^ operator points to is automatically destroyed and deleted from memory as soon as all the references to that object have gone out of scope, are set to null, or are assigned (see note below). Therefore, you should not explicitly call delete on a pointer variable declared using the \^ operator. WinRT reference types can be declared using stack semantics as well; however, only the pointer variable will be allocated in the stack, and the underlying object that it is pointing to will always be allocated on the heap. When a pointer variable declared using \^ is assigned or is used as an argument of a function, only the pointer is copied, not the underlying object. For example, in line 14 of the preceding code example, foo3 will be pointing to the same object as foo, and the reference count for foo is incremented by 1 because of this assignment.
Note: Windows Runtime object lifetime management by the means of reference counting should not be confused with .NET style object lifetime management. In .NET, the program execution is suspended periodically to run a garbage collection routine that checks all program allocations on the heap and frees up the objects that are no longer needed. In Windows Runtime there is no suspension of the program or any garbage collection routine to execute. The objects are deleted as soon their reference count goes to zero.
The members of a WinRT reference object declared using \^ and ref new are accessed using the arrow ->operator, just like accessing member methods using standard pointers.
Note that value type WinRT types, such as Platform::Bool, Platform::Guid, or the built-in WinRT types such as int32 or float32 have a similar syntax to standard C++ classes or types. That is, they are instantiated using stack semantics or new keywords, and their lifetime is not managed by reference counting.
Platform::String is the WinRT reference type used for representing Unicode text, and it is used extensively as a return or parameter type in WinRT APIs. Platform::String is only meant to be used in WinRT APIs and is not meant to replace the std::wstring type. You should continue to use std::wstring or wchar_t* in your code and only convert them to Platform::String when you are passing your text data across WinRT public APIs. The Platform::String reference type is designed to easily interoperate with standard std::wstring or wchar_t * types. That is, it is very easy to create or copy a Platform::String from std::wstring or wchar_t * and vice versa. See [20] for syntax details.
The Platform::String reference type is immutable, which means that it cannot be changed after it has been created. For this reason, this class doesn’t have any method that would change the underlying text, such as concat, replace, etc. Also, because it is a reference type, assigning it will not result in copying or allocating new buffers.
C++/CX extensions support ref classes, which are user-defined classes that can be passed between WinRT components via their public APIs. These classes are declared using the ref class keyword and must be instantiated using ref new; therefore, their lifetime is always managed through automatic reference counting. These classes are especially useful when creating user-defined WinRT components that can be consumed by other applications. Refer to [7] for more details.
As Xbox One high-performance game developers, we are interested only in consuming system WinRT components, not in creating new WinRT components for other applications. Therefore, in most scenarios you don’t need to declare a ref class in your game codebase. Two exceptions are the view provider and view provider factory classes required by CoreApplication::Run during process activation. Refer to the “Process activation” section of “Process Lifetime Management (PLM) for Xbox One” white paper [25] for a detailed discussion.
WinRT reference classes cannot have public data members. Instead, public data members of classes are exposed as properties, which means that they are implemented using set or get accessor methods for a private data member [8]. A property can be declared as read-only or write-only if it provides only a get or set accessor, respectively. A property may have both set and get accessor methods, which means that it can be read or written to. The properties are accessed with the same syntax used for accessing public data members of a C++ class. However, assigning a read-only property or attempting to read a write-only property will generate compile-time errors. In order to determine whether a property has read or write access, use the Object Browser window in Visual Studio to see whether that property has a get or set accessor.
All WinRT components are distributed as a .winmd metadata file and a separate binary containing the actual executable code. The metadata in the .winmd file describes the public surface of that WinRT component and can be recognized and loaded from any Windows Runtime supported language. The .winmd files use the same format as .NET assemblies’ metadata (ECMA-335 format). You can browse and see the content of a .winmd file using the Object Browser window in Visual Studio. This window gives you the details of the APIs published in that .winmd file. Visual Studio IntelliSense also recognizes the content of .winmd files imported into your project.
The three most commonly used WinRT components in Xbox One titles are platform.winmd, which contains the WinRT data types used in C++/CX; windows.winmd, which contains most WinRT system components shared between Xbox One and Windows 8; and microsoft.xbox.winmd, which contains most Xbox One-specific WinRT components.
The .winmd files are included in a C++ project either using the “#using filename.winmd” in the source code, or by passing the “/FU filename.winmd” option to the compiler. When you use the /ZW compiler option to enable C++/CX extensions on Xbox One, it implicitly includes the windows.winmd, platform.winmd, and microsoft.xbox.winmd files. By default, the compiler looks for .winmd files in the path specified in %LIBPATH% environment variable.
Errors in Windows Runtime are reported back to caller code using exceptions [9]. All WinRT exceptions are reference types derived from Platform::Exception. The underlying COM implementation of WinRT classes use HRESULT error codes, and the C++/CX extensions create a mapping between the underlying HRESULT error codes and WinRT exceptions. The Platform::Exception class has an HResult property that is set to the underlying COM-based HRESULT code.
Example 3. Error handling through WinRT exceptions.
1. WinRTFoo^ foo = ref new WinRTFoo();
2. try
3. {
4. foo->DoSomething();
5. }
6. catch (Platform::Exception^ e)
7. {
8. // DoSomething has encountered an error. Add error handling code here.
9. printf("exception HRESULT code: 0x%x,\n exception message:'%s'",
10. e->HResult, e->Message);
11. }
For convenience, the Platform namespace defines distinct exception classes for the most common HRESULT values. For example, if the underlying COM object encounters an error with HRESULT code E_ACCESSDENIED, then it will throw a Platform::AccessDeniedException, which inherits from Platform::Exception. For a complete list of these exceptions, load the platform.winmd in the Object Browser window in Visual Studio and look up the Platform namespace. See [26] for details on C++ exception handling, as well as for recommendations for exception-safe coding techniques.
A WinRT delegate is a WinRT object that represents a C++ function pointer [11]. A delegate declaration defines a function signature by specifying the return and parameter types for functions that client code can use as handlers. Delegates can be constructed using implementations in the form of a lambda expression [10], a static function, or a pointer to a member method as long as they match the signature defined by the delegate declaration. Delegates enable the client code to provide a custom implementation to a WinRT class, and they are most commonly used in conjunction with asynchronous APIs and WinRT events.
Any WinRT API that can take a long time to complete is implemented asynchronously. These APIs all follow a similar pattern, which provides a consistent way for making non-blocking asynchronous calls in Windows Runtime. By convention, the names of WinRT asynchronous functions end with Async. All async functions that are supposed to produce a result value return an IAsyncOperation or IAsyncOperationWithProgress object. The async functions that do not produce a result return an IAsyncAction or IAsyncActionWithProgress object. The WithProgress interfaces all have similar methods for reporting the operation progress. These interfaces all have similar methods. They all have a Completed property, which is an AsyncOperationCompletedHandler delegate that is executed when the operation has completed. For example, the following code example shows how this delegate is used by the Xbox One XboxLiveAuthenticator class to asynchronously download a token. Similar syntax and code patterns are used for all other async operations that use the AsyncOperationCompletedHandler, such as async file read or write.
Example 4. Asynchronous token download.
1. using namespace Windows::Security::Authentication::XboxLive;
2. void MyClass::RequestToken( )
3. {
4. // Create the XboxLiveAuthenticator WinRT object
5. auto^ authenticator = ref new XboxLiveAuthenticator;
6.
7. // Start an async operation to download a security token from Xbox LIVE for
8. // a given user. Since this is supposed to return a user token which is a
9. // Platform::String, the asyncOp is of type IAsyncOperation< Platform::String^ >
10. auto asyncOp = authenticator->RetrieveUserTokenAsync("UserNameGoesHere");
11.
12. // Set the delegate for RetrieveUserTokenAsync completed to a member method of
13. // MyClass class. This would require MyClass to be a “ref class.” Note we are
14. // using Platform::String^ as the Type passed to AsyncOperationCompletedHandler
15. // because RetrieveUserTokenAsync is returning a Platform::String
16. asyncOp->Completed = ref new AsyncOperationCompletedHandler< Platform::String^ >
17. (this, &MyClass::OnUserTokenReceived);
18. }
19.
20. // Delegate method that handles token downloads Completed
21. void MyClass::OnUserTokenReceived(IAsyncOperation<Platform::String^>^ operation,
22. Windows::Foundation::AsyncStatus status)
23. {
24. if(status == Windows::Foundation::AsyncStatus::Completed)
25. {
26. // Code that processes the retrieved token goes here.
27. // Note the operation->GetResults() may throw an exception if the
28. // operation has encountered an error, and therefore we use a
29. // try/catch block.
30. try
31. {
32. Platform::String^ token = operation->GetResults();
33. }
34. catch (Platform::Exception^ e)
35. {
36. // Our async operation has completed, but has failed to retrieve
37. // the results. Add error handling code here.
38. }
39. }
40. else if(status == Windows::Foundation::AsyncStatus::Canceled)
41. {
42. // The operation was canceled before completion
43. }
44. else if(status == Windows::Foundation::AsyncStatus::Error)
45. {
46. // Our async operation has failed to complete. Add error handling code here.
47. }
48. }
The syntax for using lambda expressions as the delegate instead of a member method looks like this:
Example 5. Use of lambda expressions as a delegate.
1. asyncOp->Completed = ref new AsyncOperationCompletedHandler< Platform::String^ >(
2. [this](IAsyncOperation<Platform::String^>^ operation,
3. Windows::Foundation::AsyncStatus status)
4. {
5. // delegate impl is added here (omitted for brevity).
6. } );
An async operation can be canceled by calling the Cancel method. If an async operation is canceled, the completed delegate is still executed and the AsyncStatus parameter is set to Canceled.
The async operation may fail to finish as a result of errors. In this case the Completed delegate is still executed and the status parameter is set to Error, indicating that the operation did not execute completely. In other cases, the operation may successfully finish but still fail to retrieve the results for other reasons. In this case, the Completed delegate is executed and status parameter is set to Completed, but calling GetResults throws an exception. For example, if you try to open a non-existing file using a WinRT async operation, then the async operation will complete, but calling GetResults will throw an exception.
Note that even though IAsyncAction and IAsyncActionWithProgress do not produce a result value, they still have a GetResults method, which does not return any value. This method is meant to be called in the Completed delegate for checking the results of the async action and catching the exception thrown by GetResults. Therefore, as in the case of async operations, you should call GetResults for async actions inside a try/catch block in the Completed delegate to check for possible errors.
An event is a special member type of WinRT reference classes; it is a collection of delegates, each of which is called an event handler. The client code can add its own implementation of event handlers to this collection. Each event is associated with a predefined criterion for when it is triggered. When this specific criterion is met, the event is raised, which means that the owning class sequentially calls into all event handlers registered for the event. As is the case with delegates, one can use lambda expressions, static functions, or class-member methods as event handlers. If you are adding a class-member method as an event handler, then that class must be WinRT ref class, otherwise you will get compile-time errors. Event handlers are added using the += operator. For example, the Microsoft::Xbox::Input::Gamepad class has a GamepadAdded event, which is raised when a new Gamepad is connected. The following code example shows how a class-member method is added as the handler for this event.
Example 6. Adding an event handler.
1. using namespace Microsoft::Xbox::Input;
2. ref class MyGamepadManager
3. {
4. public:
5. void RegisterEventHandlers()
6. {
7. Gamepad::GamepadAdded += ref new EventHandler< GamepadAddedEventArgs^ >(
8. this, &MyGamepadManager::GamepadAddedEventHandler );
9. }
10. private:
11. void GamepadAddedEventHandler(Platform::Object^ src, GamepadAddedEventArgs^ args)
12. {
13. // Code that handles gamepad added event is here
14. }
15. }
Note that there is no API to iterate through already-registered event handlers or to clear all registered event handlers all at once. When an event handler is added using the += operator, it returns a Windows::Foundation::EventRegistrationToken object. If you intend to explicitly remove or replace an already-added event handler, you must save this event registration token and use the -= operator later to remove it, as shown in the following code example.
Example 7. Removing or replacing an event handler.
1. // Add the GamepadAddedEventHandler event handler and save the event registration token
2. Windows::Foundation::EventRegistrationToken eventToken =
3. Gamepad::GamepadAdded += ref new EventHandler< GamepadAddedEventArgs^ >(
4. this, &MyGamepadManager::GamepadAddedEventHandler);
5.
6. // Use the event token to remove a previously added event handler
7. Gamepad::GamepadAdded -= eventToken;
The Parallel Patterns Library (PPL) is a C++ library that makes parallel programming easier by providing features such as algorithms, containers, and tasks [12]. The PPL is a completely separate library from Windows Runtime and may be used in non-WinRT projects as well. PPL Task is a general-purpose class defined in ppltasks.h that represents work that can be executed asynchronously and in parallel with other tasks [13]. When you enable the C++/CX extensions, the PPL Task class wraps the WinRT asynchronous types, and enables you to use a PPL Task as the delegate of WinRT async APIs. This makes it easier to implement some of the common features used for async operations, such as waiting for an operation to finish, task cancellation, task chaining, and exception handling in a chain of tasks.
<a id=the-execution-context-of-event-handlers-or-async-operations”></a>
After you start an async operation or register an event handler, which thread calls your delegate when the async operation is completed or the event is triggered? The answer depends on the COM threading model that your WinRT object is using under the hood. (See [23] for a detailed discussion of COM’s single-threaded apartment [STA] and multithreaded apartment [MTA] threading models.) As a developer consuming system WinRT components, you don’t need to learn these internal threading models. All you need to know is that, in the majority of cases, the event handler or completed delegate is executed using a worker thread from the application thread pool. The worker threads in the application thread pool are managed by the system. That is, the system may create or destroy worker threads as needed, or it may recycle an existing free worker thread to execute new code. Therefore, your event handlers or async operation callback implementations must be thread-safe. If you add a Completed delegate to an operation that has already completed, then the same thread sequentially executes your Completed delegate routine.
The only exception to using the thread pool for executing event handlers is the view provider class of your game. The view provider is intended to update the UI of your application and uses a different COM threading model than other objects. The event handlers associated with view provider object (which include the PLM Suspending and Resuming events) are guaranteed to be executed from the same thread that is associated with this object, which is the same thread that executes the view provider Run method. See [26] for more details.
Windows Runtime defines a few new data-collection types intended to be used as the return or parameter data types in WinRT APIs. These collections are defined as a set of interfaces (e.g., IVector or IMap) in the Windows::Foundations::Collections namespace and are declared in the windows.winmd metadata file. The XDK’s collections.h header file includes a C++ specific implementation of these interfaces defined in the Platform::Collections namespace. For example, collections.h includes an implementation of the Platform::Collections::Vector class that implements the Windows::Foundations::Collections::IVector interface. The WinRT Vector and Map types have similar functionalities to std::vector and std::map; items are added, removed, or accessed via an iterator using similar patterns as in Standard C++ collections. IVectorView and IMapView are immutable versions of Vector and Map and are used to provide read-only Vector or Map collections. The read-only collections are accessed in a similar manner as mutable collections, except that items cannot be removed or added, or their items cannot be changed.
WinRT collections are not meant to replace the Standard C++ collection types and are designed to interoperate with them easily. For example, a std::vector can be passed as a parameter to a Vector constructor to create a Vector from it. The std::move can be used to efficiently construct a Vector from a std::vector without copying the containing objects.
Example 8. Defining WinRT collection types with the Platform::Collections namespace.
1. #include <collection.h>
2. using namespace Platform::Collections;
3. // Declare a WinRT Vector with items of type integer using ref new
4. Vector< int >^ myWinRTVector = ref new Vector< int >;
5.
6. // Add new items to myWinRTVector
7. myWinRTVector->Append(1);
8. myWinRTVector->Append(2);
9. myWinRTVector->Append(3);
10.
11. // Replace the Vector item at index 0 with -1
12. myWinRTVector->SetAt(0, -1);
13.
14. // Iterate through items in myWinRTVector using an iterator
15. for(auto it = myWinRTVector->First(); it->HasCurrent; it->MoveNext())
16. {
17. printf("%d", it->Current);
18. }
19.
20. // Use std::find to find an item in our WinRT Vector.
21. auto it2 = std::find(begin(myWinRTVector), end(myWinRTVector), 2);
22. if(it2 != end(myWinRTVector))
23. {
24. printf("Found 2 in myWinRTVector");
25. }
26.
27. // Iterate through items in myWinRTVector using Collections::begin and end
28. for(VectorIterator<int> it3 = begin(myWinRTVector); it3 != end(myWinRTVector); ++it3)
29. {
30. printf("%d", *it3);
31. }
32.
33. // Use std::for_each along lambda expression can be used with WinRT vectors
34. std::for_each(begin(myWinRTVector), end(myWinRTVector), [](int i)
35. {
36. printf("%d", i);
37. });
38.
39. // Construct a read-only view of the WinRT Vector
40. IVectorView< int >^ readonlyVector = myWinRTVector->GetView();
41. readonlyVector->Append(10); // <--- Will not compile because IVectorView is readonly
42.
43. std::vector<int> myStdVector;
44. for(int i = 0; i < 3; i++)
45. {
46. myStdVector.push_back(i);
47. }
48.
49. // Construct a WinRT Vector from std::vector. The items in myStdVector are copied over
50. // to winRTCopyOfStdVector.
51. Vector<int>^ winRTCopyOfStdVector = ref new Vector<int>(myStdVector);
52.
53. // Construct a WinRT Vector from std::vector using move semantic more efficiently. The
54. // items in myStdVector are moved over to winRTCopyOfStdVector2, and no item is copied.
55. // myStdVector will be empty after this call.
56. Vector<int>^ winRTCopyOfStdVector2 = ref new Vector<int>(std::move(myStdVector));
57.
The WinRT collections also provide observable collection types using the IObservableVector and IObservableMap interfaces, which allow a user-defined event handler to be called when a collection changes. The arguments passed to these event handlers indicate how the collection was changed (items added, removed, or changed). Refer to [15, 17] for more details.
A new option for working with Windows Runtime (WinRT) APIs is to use the C++/WinRT projections. These are supported in the March 2017 Xbox One XDK and later. For more information, see Hats Off! Getting Started with C++/WinRT.
Windows Runtime Library (WRL) is a C++ template-based library that allows authoring and consuming COM-based WinRT types without using the C++/CX extensions. WRL is very similar to Active Template Library (ATL) and was developed to facilitate porting ATL-based code bases to the Windows Runtime. WRL requires the user code to adhere to all COM programming rules. For example, error codes must be handled using HRESULTs, or the user code must manage the reference counting of COM objects. WRL includes a WRL::ComPtr pointer, which is a built-in COM smart pointer that is useful for managing the lifetime of COM-based pointers. WRL relies on the interface definition language (IDL) compiler to generate or consume required .winmd metadata files. Refer to [18] for more details.
Using WRL to develop or consume Windows Runtime-based components is tedious and requires a lot of coding and careful considerations. WRL is only recommended for porting legacy ATL-based codebases to Windows Runtime, an unlikely scenario for Xbox One developers. WRL is not fully supported on Xbox One, and Xbox One developers are urged to use C++/CX extensions or C++/WinRT projections
C++/CX extensions are enabled by using the /ZW compiler flag. When /ZW is used, the compiler implicitly includes windows.winmd, platform.winmd and microsoft.xbox.winmd files. Also the compiler automatically uses the DLL version of the C Runtime (CRT). Linking to the C static library version is not allowed, and any use of CRT functions that are not part of the Xbox One-approved API set will cause a compile-time error.
None of the new WinRT data types or concepts discussed in this white paper, including \^ and ref new, Platform::String, WinRT collections, and so on, are meant to replace their existing C++ counterparts. They are meant to be used when passing data to WinRT components via the Windows Runtime-based APIs. We recommend that you not use WinRT types in parts of your code that are not making system WinRT API calls. You can link together a mix of libraries built with or without the /ZW option as long as all libraries are built using the same Visual Studio compiler version. We recommend using the /ZW flag only for platform-specific source files or libraries that make WinRT system calls. There is no need to use /ZW for cross-platform or core game engine source files that do not consume WinRT APIs. This means that you can create static libraries that use WinRT APIs and expose the data to other parts of your game using Standard C++ APIs and data types.
Building static libraries that consume WinRT APIs will generate a 4264 linker warning with the message “archiving object file compiled with /ZW into a static library; note that when authoring Windows Runtime types it is not recommended to link with a static library that contains Windows Runtime metadata.” This is because static libraries are not meant for authoring WinRT components, and the linker cannot distinguish whether you are authoring a new WinRT component or simply just consuming one in your library. You can safely ignore this warning using the /IGNORE: 4264 linker switch. Refer to [19] for more details.
Windows Runtime enables the creation of components that can be consumed from different languages on multiple Microsoft platforms, including Windows 8.x, Windows Phone 8.x, Windows 10, and Xbox One.
Windows Runtime is implemented using COM.
You don’t need to learn COM to use Windows Runtime. Instead, become familiar with C++/CX extensions, which provide a simple C++ syntax for using Windows Runtime.
\^ is a pointer to a WinRT object. Its lifetime is automatically managed by the system.
Use the ref new keyword instead of new to instantiate WinRT objects.
WinRT data types are not meant to replace their Standard C++ counterparts. They are meant to be used when making system API calls to WinRT components.
C++/CX uses C++ exceptions (/EHsc) for error handling.
Windows Runtime uses asynchronous API patterns and events; become familiar with these patterns.
WinRT component surface areas are published in .winmd metadata files, and can be viewed in the Object Browser window in Visual Studio.
Do not enable C++/CX for libraries and code bases that do not use WinRT APIs.
**Windows Runtime and C++/CX **
Developer Center for Universal Windows apps. developer.microsoft.com
Getting started with Windows apps. docs.microsoft.com
Sutter, Herb. Using the Windows Runtime from C++. Talk at BUILD conference 2011. Microsoft Channel 9.
Microsoft. Windows Runtime internals: understanding "Hello World." Talk at BUILD conference 2011. Microsoft Channel 9.
Using Windows Runtime Components in Visual C++. www.msdn.com
Boxing in C++/CX. www.msdn.com
Ref classes in C++/CX. www.msdn.com
Ref class properties in C++/CX. www.msdn.com
Exceptions in C++/CX. www.msdn.com
Lambda expressions in C++. www.msdn.com
Delegates in C++/CX. www.msdn.com
Task Parallelism. www.msdn.com
Parallel Patterns Library task Class. www.msdn.com
Xbox One XDK roadmap. Game Developer Network
WinRT Collections. www.msdn.com
IObservableVector<T> interface. www.msdn.com
IObservableMap<K, V> interface. www.msdn.com
Windows Runtime Template Library (WRL). www.msdn.com
Static libraries in C++/CX. www.msdn.com
Strings (C++/CX). www.msdn.com
Brewis, Deon. Under the covers with C++ for Windows Store apps. Talk at BUILD conference 2011. Microsoft Channel 9.
COM
COM documentation on MSDN. www.msdn.com
Threading in COM (apartments, STA and MTA). www.msdn.com
Merry, Matt. Windows Runtime internals: understanding "Hello World". Talk at BUILD conference 2011. Microsoft Channel 9.
Xbox One white papers
Process Lifetime Management (PLM) for Xbox One. Game Developer Network.
Code Generation for Xbox One: Best Practices. Game Developer Network.
Hats Off! Getting Started with C++/WinRT. Game Developer Network.
This section discusses the details of how the C++/CX language extensions are internally implemented by the compiler. You don’t need to know the details discussed in this section in order to successfully use the WinRT types and APIs. However, the details discussed here provide useful insights to C++/CX extensions.
You must be familiar with basic COM concepts to understand the internals of C++/CX and Windows Runtime. Refer to [22] to learn more about COM.
All WinRT classes are internally implemented as a COM-based class. At the ABI level, each WinRT class is a COM-based class that implements IInspectable. When the C++/CX extensions are enabled by using the /ZW compiler flag, the compiler generates code behind and wrapper classes that handle the necessary mappings between the C++/CX syntax and APIs you use in your code and the system-level native implementations of that class using COM. All these code behind and wrapper method’s names start with __abi_ by convention to indicate that these methods are used at the application boundary interface (that is, at the system level that uses COM).
The IInspectable interface inherits from IUnknown, and adds three new methods (GetIids, GetRuntimeClassName, and GetTrustLevel) on top of IUnknown methods. These methods were added to enable JavaScript dynamic language projections for Windows Runtime and are generally irrelevant in C++ programming.
The \^ is a pointer to a vptr: All WinRT classes implement IInspectable and, therefore, all WinRT classes have virtual functions. The compiler adds a hidden member variable called vptr per any interface that a class is implementing, where each vptr is a pointer to a virtual function table (vftable) that contains the address of virtual function implementations for that interface. A WinRT reference variable declared using \^ is simply a pointer to the IInspectable vptr of the underlying COM object that implements that WinRT object. Therefore, at the lowest level, \^ is a pointer to a pointer to an array of the IInspectable method’s implementations, which are QueryInterface, AddRef, Release, GetIids, GetRuntimeClassName, and GetTrustLevel. When you add a \^ variable to the debugger watch window in Visual Studio, it shows up as a Platform::Object with a __vfptr member variable to the IInspectable vftable.
WinRT objects are automatically reference counted: When you use \^ and ref new keyword to instantiate a WinRT reference object, the compiler automatically inserts appropriate method calls to code behind or wrapper methods that enable automatic reference counting of the underlying COM object. These wrapper methods are defined in vccorlib.h in the __abi_details namespace (see __abi_winrt_ptr_ctor, __abi_winrt_ptr_dtor, and __abi_winrt_ptr_assign methods in vccorlib.h). For example, when a new WinRT reference object is constructed using ref new and is assigned to a \^ pointer, the __abi_winrt_ptr_ctor is automatically added to your code by the compiler. This function invokes the AddRef of the underlying COM object to increment its reference count. Similarly, when a \^ pointer is assigned or goes out of scope, other helper methods are invoked by the compiler to update the necessary COM reference count. When the reference count of a COM object becomes 0, it is destructed and deleted from memory.
Automatic mappings between WinRT method calls and COM: All the public Windows Runtime-based methods are internally implemented using a COM-based equivalent that has __abi_ appended to the method name. The compiler generates and uses wrapper methods that call into these COM-based __abi_ methods while taking care of all necessary mappings. For example, suppose we have a WinRT class named MyWinRTClass that has a public method int DoSomething(int param). Then this class is internally implemented using COM as HRESULT __abi_DoSomething(int param, int* result). The public DoSomething method is automatically implemented using a compiler-generated wrapper method that is similar to the following code:
1. inline int ::__IMyWinRTClassPublicNonVirtuals::DoSomething(int __param0)
2. {
3. int __abi_returnValue;
4.
5. long __hr = __abi_DoSomething(__param0, &__abi_returnValue);
6. if(__hr < 0)
7. {
8. __abi_WinRTraiseException(__hr);
9. }
10.
11. return __abi_returnValue;
12. }
This wrapper function implements all necessary mappings to use the underlying COM-based APIs. Specifically, this method maps the COM-based HRESULT error code __hr returned from __abi_DoSomething to a WinRT exception using the __abi_WinRTraiseException; and passes the result of __abi_DoSomething as the return value of the public WinRT method. The same pattern is used for class-property methods via set or get accessor methods.
Your code can directly call into the underlying __abi_ non-WinRT methods. Note that Visual Studio IntelliSense does not recognize these __abi_ methods. However, your code will still compile and run. This can be useful in cases where you want to avoid the use of exceptions in your code and bypass the automatic HRESULT to exception mapping added by Windows Runtime to COM wrappers.
1. MyWinRTClass^ obj = ref new MyWinRTClass();
2. int result;
3.
4. // DoSomething method will throw an exception if it encounters an error,
5. // therefore we need to use a try/catch clause here.
6. try
7. {
8. result = obj->DoSomething(10);
9. }
10. catch(Platform::Exception^ e)
11. {
12. // Handle error cases here.
13. }
14.
15. // The following code calls into the COM equivalent of DoSomething directly,
16. // and handles the errors without using exceptions. IntelliSense marks the
17. // __abi_DoSomething as “not defined”, but this code compiles and runs correctly.
18. HRESULT hr = obj->__abi_DoSomething(10, &result);
19.
20. if(FAILED(hr))
21. {
22. // Handle error cases here.
23. }
1. inline MyWinRTClass::MyWinRTClass()
2. {
3. class Platform::Guid __gd (53, 0, 0, 192, 0, 0, 0, 0, 0, 0, 70);
4.
5. struct Platform::Details::IActivationFactory^ __pActivationFactory;
6.
7. long __hr = __winRT::__getActivationFactoryByPCWSTR(L"MyWinRTClass", __gd ,
8. reinterpret_cast<void**>(&__pActivationFactory ));
9.
10. if(__hr < 0)
11. {
12. __abi_WinRTraiseException(__hr);
13. }
14.
15. class MyWinRTClass^ __abi_returnValue = dynamic_cast<class MyWinRTClass^>(
16. __pActivationFactory->ActivateInstance());
17. return __abi_returnValue;
18. }
This wrapper creates a COM activation factory instance by passing the class name to __getActivationFactoryByPCWSTR. This method uses the registry to find and instantiate an activation factory for your class. The wrapper method then creates an instance of the object using the ActivateInstance method of the factory object. The result is then cast to a \^ pointer and passed back to your code. This wrapper throws a WinRT exception if it fails to create the factory instance or to activate the object instance.
You can see the __abi_ wrappers methods generated by the compiler’s C++/CX extensions for your code by using the /d1ZWtokens compiler switch. The /d1reportAllClassLayout switch dumps layouts of all classes, which is helpful to see the functions in the vftable of each class. Refer to [21].