Share via


Implementing a complete WRL native C++ XAML blank project template

 

Download

Download source code - 667.9 KB

 

Introduction 

Most developers using C++/CX have not explored further the possibility of implementing a native C++ WRL solution or share code base which can compile both to C++ WRL or C++/CX.  It is however possible with some more internal and detailed knowledge of the WRL.  MSDN partially documents this though there are cases where sample code for implementing Platform objects or accessing certain statics or interfaces is simply not documented and must be discovered.  As objects are inspectable, the interfaces may not even be exposed in the libraries but instead only available either through documentation, or in the code itself which can of course be inspected at runtime.  

 

Background

One should be familiar with C++, IDL, basic WRL, the Windows Runtime and C++/CX to be able to manage an entire project in native C++ and be aware of the significant overhead required with the main savings being in size from not having to link with the C++/CX platform wrapper library. 

 

Using the code  

  • Applications are launched through WinMain and must call RoInitialize to make an MTA COM thread before using the IApplicationStatics Start method which provides an IApplicationInitializationCallback.  The callback object implements only IUnknown and which only contains an  Invoke method which constructs the application object on an STA callback thread.  
  • Ro* and WindowsString* functions must be used for various specific functionality although if the functionality can be found in some of the wrapper libraries it is preferable to use the wrappers such as using Microsoft::WRL::ActivateInstance as opposed to RoActivateInstance or using HString instead of HSTRING
  • There is no reference for which native C++ interfaces are implemented on various RuntimeClass_* objects.  You may find it in the documentation or by disassembling the actual DLL modules.  It is important to write an IInspectable interface listing debug tool to check the interfaces on various WinRT objects so you can be sure how to safely and properly cast and use them. 
  • All reference operators must generally be translated to pointers: "^" -> "*".  Generally, the references should be wrapped in Microsoft::WRL::ComPtr<> so they are cleaned up appropriately. 
  • All  "ref new" must be replaced with "Microsoft::WRL::Make" for internal objects or "Microsoft::WRL::ActivateInstance" for external ones unless a *Factory is available in which case it created through a static method call as described below. 
  • All static method calls must be replaced with an IActivationFactory which is created using a RuntimeClass_* name  through "Microsoft::WRL::GetActivationFactory".  This factory will return an object from which the desired *Statics interface must be queried. 
  • All objects must be referenced by the interface typically prefixed with a I* and must include appropriate header files.  Object references must be tracked using the Microsoft::WRL::ComPtr wrappers or HString wrappers for string otherwise special care must be taken to do perform IUnknown ``AddRef and Release.  All namespaces must be prefixed with "ABI::".  
  • All derived XAML objects should implement an *Impl base class that is an InspectableClass and implements any I*Overrides interfaces.  On creation, the CreateInstance call will "mix" the base class with the internal inner implementation which must be stored for aggregation.  A derived class must override QueryInterface and delegate any interfaces not implemented on the *Impl base classes to the aggregated internal inner  implementation. 
  • All Platform namespace objects must be re-implemented with an equivalent object either already provided in the header files or custom implemented as is done in the C++/CX library.  Platform::Object is equivalent to IInspectable*.  Platform::Exception must be transformed to be thrown with the Ro* functions and caught with a structured exception for EXCEPTION_RO_TRANSFORMERROR
  • Event handlers must use a pointer to an interface generally with an "Invoke" method.  Custom event handlers can be implemented EventSource object to keep track of cookies or custom written EventSources that use the global interface table (GIT), custom marshalling or contexts.
  • Any objects in the Platform namespace must be translated to use equivalents such as IInspectable for objects, any type created using the IPropertyValueStatics interface unless you want to roll your own custom implementation which implements all of the same objects that those do.  This also allows the WinRT primitive types to "grow" a vtable so they can be all treated as IInspectable*
  • IDL must be used to expose interfaces of WinRT components through a .winmd file. 
  • Microsoft can use desktop-app only functionality in its own vccorlib.dll for example: Only Microsoft's C++/CX library can use the desktop-app only CoRevokeInitializeSpy function for their factory cache mechanism in Windows Store apps yet if coding in native C++, you will have to do implement the equivalent code without the functions provided by this interface or do without factory caching altogether. 
  • Care must be taken for Windows 8.1 verses 8 which generally added some minor enhancements and restructured things a bit in some of the libraries. The base template project contains very few changes. The MSC_VER compiler definition can be checked against VS2013/Windows 8.1 which is greater than 1800 while VS2012/Windows 8 targeting would be in the 1700 range. Similarly, MSC_FULL_VER can be checked for 180000000.
  • Much of the source code for the C++/CX library called vccorlib can be found in the Visual Studio C++ source code and the interfaces needed to port it in the Windows SDK. Some C++/CX compiler built in features such as __is_valid_winrt_type and such must be implemented through macros in native C++ to try to preserve the integrity of the original code when porting.
  • Microsoft provides the following important information on Casting (C++/CX): http://msdn.microsoft.com/en-us/library/windows/apps/hh755802.aspx.  
  • winmdidl.exe utility can be used from a Visual Studio command prompt on a compiled C++/CX project winmd file to generate the IDL for making a native C++ winmd file.

The reinterpret_cast table is repeated here as it is essential when going between the two styles:

HSTRING

String^

HSTRING*

String^* 

IInspectable*

Object^

IInspectable**

Object^*

IInspectable-derived-type*

same-interface-from-winmd^

IInspectable-derived-type**

same-interface-from-winmd^*

IDefault-interface-of-RuntimeClass*

same-RefClass-from-winmd^

IDefault-interface-of-RuntimeClass**

same-RefClass-from-winmd^* 

 

Points of Interest  

  • Microsoft tasks library already has task interfaces to wrap IAsync* which can be translated to native C++.  Returning a custom IAsync* object also can be wrapped by building a wrapper on top of IAsyncBase.  Special care must be taken to handle contexts when using UI objects that require STA thread calls and can be derived from the tasks library code.  Please see my other article on the task library for WRL. 
  • Microsoft Media Foundation objects can be implemented by deriving from IMediaExtensionand making the object "activatable" through the ActivatableClass macro which exports the interface through DllGetClassObject which can be implemented in DLLs as well as executables.  Activatable objects must be declared in the application manifest to be given permission. 
  • Conditional compilation can be used with the __cplusplus_winrt compiler definition to create source code that can compile under both situations.  The projects generally should be separate though as though only the Windows Runtime /ZW compiler option.
  • Generated XAML files must be properly separated by having a separate project folder for each project that will use them. IDL files must also be properly separated as x86, x64 and ARM versions or even potentially configurations such as debug and release are all using different code paths specific to those architectures so folder hierarchies are used to separate them.

Support

  • Supporting VS 2012/2013 Update 5, VS 2015 Update 1 and Windows 10 with backward compatibility as well as Windows Phone 8.1, ARM, x64, x86 support simultaneous build, support winmd through generated IDL