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 using IRestrictedErrorInfo
or 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 implement the equivalent code without the functions provided by this interface or do without factory caching altogether. - 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. - 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. - 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
IMediaExtension
and 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.
History
- Initial version
- Addition of VS 2012 Update 4, VS 2013 Update 2 support with Windows 8.1 projects, support of Universal apps with Windows Phone 8.1, ARM, x64, x86 support simultaneous build, support winmd through generated IDL
- Now supporting VS 2012/2013 Update 5, VS 2015 Update 1 and Windows 10 with backward compatibility