C++/WinRT: Hosting the Windows Runtime

Previous: Understanding Weak References and the Dispose Pattern

Most developers will use Windows Runtime APIs from a hosting environment already set up to support those APIs. Whether you are authoring a Windows Runtime API or simply making use of various APIs from an app, chances are you can simply start writing code and not think too much about how it all comes to life. We are also working on making it even more seamless by essentially having an “always on” mode where the Windows Runtime is as ubiquitous as the CRT is for the C++ developer.

So, what do I mean by a hosting environment? It is not as mysterious as it sounds. WinRT is just an extension of COM and relies on COM, specifically the runtime services offered by combase.dll, to implement and support essential services like activation and marshaling. Ideally, most APIs will be agile and thus not affected by marshaling. On the other hand, activation is essential and must occur within the context of a COM apartment.

Traditionally, a developer would call CoInitializeEx to associate the calling thread with some apartment. While the apartment is important, calling CoInitialize also ensures that the COM runtime is active within the process, making it possible to retrieve a given WinRT activation factory. CoInitializeEx is by no means the only way to ensure that the COM runtime is loaded. You can for example call CoIncrementMTAUsage to achieve the same end. The resulting relationship between thread and apartment will be different, but you will still be able to access WinRT types. WinRT also introduced a variant of CoInitializeEx called RoInitialize. The differences between these three functions (and others) is not insignificant, but not important for today’s topic.

As I mentioned, most developers will not have to think too much about hosting and in the future, it should be largely ubiquitous. Until then, C++/WinRT provides a few helper functions that you may need to use to control your environment. The first is init_apartment and you have probably seen it in a variety of console and desktop samples:

int main()
{
    init_apartment();

    // Use C++/WinRT here!
}

The init_apartment function wraps RoInitialize and will, by default, cause the calling thread to join the multi-threaded apartment. A console apps will typically be satisfied with just this one call. A desktop app may need a single-threaded apartment:

int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
{
    init_apartment(apartment_type::single_threaded);

    // Create window and use C++/WinRT here!

    MSG message;

    while (GetMessage(&message, nullptr, 0, 0))
    {
        DispatchMessage(&message);
    }
}

Keep in mind that you should only call init_apartment on a thread you own. Specifically, do not ever call it from a thread pool callback. If you are writing some helper function, you cannot reliably assume that you can simply call init_apartment. Beyond the two examples already shown, there is rarely a case where you need to call this function yourself. However, if you decide that you do need to call it, then you have the responsibility of taking part in the management of the hosting environment. If you can avoid it, please do so because it can get tricky. That is partly why we are working on ubiquitous runtime support. Until then, there are a few things you should know.

If you call init_apartment you should technically call uninit_apartment, but there is nothing quite like process termination to make sure that everything is cleaned up properly. Therefore, you can generally avoid calling uninit_apartment from an app’s primary thread just before returning from main/WinMain. After all, the CRT is required to terminate the process once main returns.

There are two wrinkles with this plan. The first is that the CRT will call the destructors of any statics before terminating the process. That means that whatever code those destructors execute must be reachable during this short window of time. If you were to call uninit_apartment prior to returning from main/WinMain, this would typically cause the COM runtime to shut down. This in turn causes any DLLs that COM loaded to be abruptly unloaded. However, if any of those statics have outstanding references to objects living inside those DLLs, their destructors will attempt to make virtual calls to Release functions in pages that have already been unloaded. This will cause an access violation and result in undesirable process shutdown crashes.

This of course is only a problem if you have COM statics and Windows developers have known for decades about the perils of COM statics. The other wrinkle is that C++/WinRT caches activation factories to improve performance. The performance gain is substantial, but the challenge is that statics are used for caching. I will say that this problem has already been fixed in RS5 builds of C++/WinRT but remains an issue if you are using the RS4 version of C++/WinRT. Fortunately, C++/WinRT provides another helper function for clearing the cache. So, if you find yourself in the position of having to call uninit_apartment prior to process shutdown then you can call clear_factory_cache first to ensure that those references are released before COM decides to start unloading DLLs.

int main()
{
    init_apartment();

    {
        // Use C++/WinRT here!
    }

    clear_factory_cache();
    uninit_apartment();
}

That is the reason why Raymond Chen calls clear_factory_cache in this example.