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.
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.
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
- 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.
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
Release. All namespaces must be prefixed with "
- 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.
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
Platform::Exception must be transformed to be thrown with the Ro* functions and caught with a structured exception using
- 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
- IDL must be used to expose interfaces of WinRT components through a
- 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.
reinterpret_cast table is repeated here as it is essential when going between the two styles:
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.
- 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