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 callRoInitialize
to make an MTA COM thread before using theIApplicationStatics
Start method which provides anIApplicationInitializationCallback
. The callback object implements onlyIUnknown
and which only contains an Invoke method which constructs the application object on an STA callback thread. Ro*
andWindowsString*
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 usingMicrosoft::WRL::ActivateInstance
as opposed toRoActivateInstance
or usingHString
instead ofHSTRING
.- 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 anIInspectable
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 inMicrosoft::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 aRuntimeClass_*
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 theMicrosoft::WRL::ComPtr
wrappers orHString
wrappers for string otherwise special care must be taken to do performIUnknown ``AddRef
andRelease
. All namespaces must be prefixed with "ABI::
". - All derived XAML objects should implement an
*Impl
base class that is anInspectableClass
and implements anyI*Overrides
interfaces. On creation, theCreateInstance
call will "mix" the base class with the internal inner implementation which must be stored for aggregation. A derived class must overrideQueryInterface
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 toIInspectable*
.Platform::Exception
must be transformed to be thrown with the Ro* functions and caught with a structured exception forEXCEPTION_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 theIPropertyValueStatics
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 asIInspectable*
. - 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^* |
- Microsoft provides its limited official WRL reference here: http://msdn.microsoft.com/en-us/library/jj155853.aspx.
- The how-to and walkthrough guides for WRL are found here: http://msdn.microsoft.com/en-us/library/vstudio/hh438466(v=vs.110).aspx.
Points of Interest
- Microsoft tasks library already has task interfaces to wrap
IAsync*
which can be translated to native C++. Returning a customIAsync*
object also can be wrapped by building a wrapper on top ofIAsyncBase
. 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
IMediaExtension
and making the object "activatable" through theActivatableClass
macro which exports the interface throughDllGetClassObject
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