Rust: your first package

Next: An IDE for your project
Previous: Getting started

So, you’ve installed the tools and you’re ready to get started. What now? Hello world is satisfying but not very practical. It’s time to create your first package. Open the Visual Studio x64 command prompt. It’s important that the architecture (x64) matches what Rust assumes as its default. It should look something like this:

Now change to a directory where you can create a few projects. I tend to use a root directory called git for all my projects:

C:\git>

There are a few reasons for this. The obvious one being that I tend to use Git for version-control. The other is that a default installation of Windows does not include any other folder starting with a G in the root of the C drive, making it easy to switch to the git folder with tab completion, otherwise I might have gone with something like “projects” if not for the two or three folders that start with a P.

Last time I introduced the Cargo tool just enough to print out the Rust version. In a moment, we’re going to use it do something useful. As a Windows developer, you might fire up Visual Studio and use that to create a new project. There are numerous project templates for all manner of languages and applications. As a Rust developer, you’re going to use cargo to do that and much more.

But first, I want to dispel any mystery around what a Rust package is. If you’ve spent any time with Visual Studio, particularly as a C++ developer, you know that managing project configurations can be a nightmare. Fortunately, Rust packages are quite simple to the point where you can easily create them by hand. It helps to do this at least once so that you understand just what makes up a Rust package.

There are two main kinds of project or package types in Rust. I’ll talk about a third category a little later in this series. You can create a binary package or a library package. A binary package produces an executable that you can run, just like any Windows executable. A library package is similar to a C++ library.

Start by creating a folder for your package and switching to that folder:

C:\git>md hello

C:\git>cd hello

Now create a text file called cargo.toml in this folder:

C:\git\hello>notepad cargo.toml

A .toml file is similar to a .ini file with an actual specification, making for a very simple way to define key/value pairs for describing and configuring your Rust package and its dependencies. Unlike the horrendously complicated XML format used by Visual Studio, you can actually write your package’s cargo.toml file by hand without any difficulty. Here’s all you’ll need to get started:

[package]
name = "hello"
version = "0.1.0" 

That’ll do. Be sure to save that in the cargo.toml file in the root of the package folder. You’ll notice that it doesn’t say anything about what kind of package this is or how it ought to be built. Much of that will be inferred. Cargo will figure this out based on what Rust source files are present. Having created the cargo.toml file, you can now create a src subfolder for the package’s source code.

C:\git\hello>md src

Within the src folder, you can create one of two Rust source files. If you create a file named main.rs then cargo will assume you are creating a binary package. If on the other hand you create a file named lib.rs then cargo will assume you are creating a library package. Let’s start with a binary package.

C:\git\hello>notepad src\main.rs

A .rs file is where you write your Rust source code, much like .cs files for C# and .cpp/.h files for C++. Here again is the simple Hello World example we used last time:

fn main() {
    println!("Hello world!");
}

Be sure to save that in the src\main.rs source file. And that’s it: you’ve created your first Rust package! You can now use Cargo to build the package as follows:

C:\git\hello>cargo build

You’ll notice that running the Cargo build command caused a few other artifacts to be created, but you should only consider the cargo.toml file along with any source files in the src subfolder to be the actual source code for your project that you might include in version control. You will notice that Cargo created a file called Cargo.lock in your package folder. This file is entirely managed by Cargo and you should not edit it yourself. There are reasons why you may or may not want to include this file in version control, but we’ll talk about that later. More importantly, the Cargo build command created an executable from your package:

C:\git\hello>dir /s /b hello.exe
C:\git\hello\target\debug\hello.exe

You can run this directly:

C:\git\hello>target\debug\hello.exe
Hello world!

And if it’s more convenient, you can have Cargo run it for you:

C:\git\hello>cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `target\debug\hello.exe`
Hello world!

The cargo tool also provides commands for creating packages so you can save yourself a few moments if so desired. You can ask Cargo to create a binary package as follows:

C:\git>cargo new hello
     Created binary (application) `hello` package

C:\git>cd hello

C:\git\hello>cargo build
   Compiling hello v0.1.0 (C:\git\hello)
    Finished dev [unoptimized + debuginfo] target(s) in 0.71s

Of course, you already know that there’s no magic here. It just created the cargo.toml file, the src subfolder, and a main.rs file with a simple main function to get you started. You can also have Cargo create a package in the current folder, rather than a subfolder, using the Cargo init command. Note that Cargo will also default to turning your package into a Git repo. If you’d rather not have it do that you can opt out as follows:

C:\git>cargo new hello --vcs none

OK, you’ve created your first Rust package. Wasn’t that easy! Now what about library packages? And where’s the IDE?! Join me next time for the adventures of a C++ developer learning Rust.

Rust: getting started

Next: Your first package
Previous: My Rust adventure begins

So, you’re a C++ developer and want to learn Rust. You’ve come to the right place. 😊 It’s not hard to get started with Rust, but as a C++ developer and especially one with a preference for Windows you are likely to run into some of the same challenges I have. I hope that sharing my experience will help you to get started with Rust a little more quickly than I did.

On Windows, Rust requires the Microsoft C++ build tools. You guessed right: Rust depends on the thing you love most about C++… the linker! And a few other things… You can download the C++ build tools separately, or just install Visual Studio 2019. I recommend the latter.

https://visualstudio.microsoft.com/downloads/

Visual Studio provides a dizzying array of options, most of which are completely irrelevant to the C++ developer. You can painstakingly pick any of the “individual components” the installer provides or just pick some “workloads”. I suggest you do the latter. Here’s what I do to keep things simple.

That’s right. Pick all three “Windows” workloads. You may not think you need them all but invariably, some dependency will arise where these are required, and this just keeps things simple. The only “individual component” I add to the mix is “Git for Windows”. That’s important because, well, Git rules the world and you’re going to need that real soon.

You can scroll through the endless list or just use the search box to narrow things down. And that’s it. Hit the install button and then go and grab a coffee.

Once Visual Studio is installed, you can head over to the Rust website to get started.

https://www.rust-lang.org/

You should notice the prominent “Get Started” button. Go ahead and click that. You should then see a few options. You can for example “try Rust without installing” but what’s the fun in that. Instead, go for the “RUSTUP-INIT.EXE” button that links to https://win.rustup.rs/ and run the resulting executable. This will pop up a console installer.

Go ahead and pick option 1 and the installer will get on with downloading the various Rust build tools. Soon enough that will be done and you will be ready to create your first project!

To get started you need a console window. You can use any command prompt, but I suggest using the Visual Studio tools command prompt. You can then confirm that Rust is installed with a simple version command: cargo –version

What’s cargo? Well you could also run the same command with rustc, the Rust compiler, but to be honest you’ll almost never run the Rust compiler directly. So, just get used to running Cargo for all your build needs. Cargo is officially Rust’s package manager, whatever that means. Package managers may be a little foreign to the average C++ developer. Unlike C++, Rust has an integrated packaging story and Cargo is the tool that you use to manage packages. It might help if I told you that practically every Rust project is a package. In that light, Cargo is really the Rust project manager. You can use Cargo to create projects, build projects, test projects, publish projects, and much more. It’s just that Rust projects are called packages… Except when they’re called crates, but that’s enough about that confusing topic for one day.

As I mentioned, rustc is the Rust compiler. We can quickly put Hello World behind us as follows. Create a text file, with the .rs extension, and include a simple main function:

C:\hello_world>type app.rs

fn main() {
    println!("Hello world!");
}

Not a fan of that formatting? You’re not alone, but more about formatting later. You can now compile this program as follows:

C:\hello_world>rustc app.rs

And lo and behold you’ve created your first Rust program:

C:\hello_world>dir

    150,528 app.exe
  1,363,968 app.pdb
         49 app.rs

Notice there’s both an executable and a .pdb file for debugging. Naturally, you can simply run the executable from the console:

C:\hello_world>app
Hello world!

OK, you’ve confirmed that Rust is installed and managed to compile your first bit of Rust code. Awesome! What about Cargo and building actual projects? Please tell me there’s an IDE and Notepad isn’t going to be my Rusty lot in life? What about testing and debugging? Oh there’s so much to explore. So join me next time for the adventures of a C++ developer learning Rust.

My Rust adventure begins

Next: Getting started

I have come to the point with C++/WinRT where I am largely satisfied with how it works and leverages C++ to the best of its ability. There is always room for improvement and I will continue to evolve and optimize C++/WinRT as the C++ language itself advances. But as a technology, the Windows Runtime has always been about more than just one language and we have started working on a few different projects to add support for various languages. None of these efforts could however draw me away from C++… that is until Rust showed up on my radar.

Rust is an intriguing language for me. It closely resembles C++ in many ways, hitting all the right notes when it comes to compilation and runtime model, type system and deterministic finalization, that I could not help but get a little excited about this fresh new take on language design. I have spent almost every waking moment over the last few months (that I’m not hanging out with my family) exploring, studying, and experimenting with Rust. I looked for the usual signs of a language that is not really geared for a systems programmer like myself, but found none. To the contrary, I found that while it has its own unique and dramatic learning curve, it also has the potential to solve some of the most vexing issues with C++’s relationship to WinRT. Imagine C++/WinRT without any need for IDL, faster build times, and a simple and integrated build system.

And so it is that I have started building the WinRT language projection for Rust. I’m just getting started and have much to learn, but the plan is to build complete and deep support for WinRT in a way that is natural and familiar for the Rust developer. This is not going to look very much like C++/WinRT because idiomatic Rust does not look and feel like C++, but I plan to apply the same level of rigor in producing WinRT support for Rust that is both very efficient and a joy to use.

I’ll be sharing more about my adventures with Rust right here on kennykerr.ca but if you’d like to follow along more closely, take a look at the Rust winmd parser I wrote to get things started:

https://github.com/microsoft/winmd-rs

This is largely based on the C++ winmd parser library. While certainly not complete, it has just enough in place to allow me to now spend some time exploring and laying the groundwork for the WinRT support. The plan is to turn this Rust crate into a complete winmd parser for both reading and generating winmd files. A separate Rust crate will then provide the actual support for consuming and producing WinRT APIs.

But I’m getting ahead of myself. Do let me know what you think. I’d love to hear from you. And don’t forget to check back soon as I will probably start writing about the adventures of a C++ developer learning Rust. 🙂

C++/WinRT and xlang repos

If you follow along on GitHub, you may have noticed a few changes in the C++/WinRT and xlang world. It became clear that having one repo for a variety of projects and languages just wasn’t practical. Developers interested in working on one language or library inevitably had to deal with all of it, creating an unnecessarily steep learning curve. And while we have a lot of ambitions for the xlang project, its clear that C++/WinRT remains our flagship project. To that end, and to make it easier to work with the two most popular projects under the xlang umbrella, we’ve split the projects up as follows:

C++/WinRT
Repository: https://github.com/microsoft/cppwinrt
Documentation: https://aka.ms/cppwinrt
NuGet package: http://aka.ms/cppwinrt/nuget
Visual Studio extension: http://aka.ms/cppwinrt/vsix

C++ winmd parser library
Repository: https://github.com/microsoft/winmd
NuGet package: http://aka.ms/winmd/nuget

Existing projects related to cross-platform support
Repository: https://github.com/microsoft/xlang

You may also have noticed some new GitHub repos for Java and C# language support. Obviously, we’d love to add support for every popular language, but our resources are limited. We have experimented with adding support for both. The C# project might seem a little curious given that C# currently supports WinRT directly, but we have discovered through our experience with C++/WinRT that we can provide a far better experience by separating the WinRT support from the compiler itself. On a personal note, I’m spending a lot of my time working with the Rust language and can’t wait to share more about that eventually. 😉

The Old New Thing on C++/WinRT

In case you haven’t noticed, Raymond Chen has joined me in writing about C++/WinRT. He has published a great collection of tips and tricks internally and plans to publish them all publicly as time allows. Here are a few to get you started:

Detecting whether the -opt flag was passed to cppwinrt.exe: Using __has_include

How can I determine in a C++ header file whether C++/CX is enabled? How about C++/WinRT?

Why does my C++/WinRT project get errors of the form “Unresolved external symbol void* __cdecl winrt_make_YourNamespace_YourClass(void)“?

Why does my C++/WinRT project get errors of the form ‘winrt::impl::produce‘: cannot instantiate abstract class, missing method GetBindingConnector

Why does my C++/WinRT project get errors of the form “consume_Something: function that returns ‘auto’ cannot be used before it is defined”?

Why does my C++/WinRT project get errors of the form “unresolved external symbol … consume_Something”?

Windows Runtime delegates and object lifetime in C++/WinRT

Meet C++/WinRT 2.0: resume_foreground Improvements

It turns out that resume_foreground was being a little too clever and could introduce deadlocks in some scenarios because it only suspended if not already on the dispatcher thread. This seemed like a good idea at the time, but being able to depend on stack unwinding and re-queuing turns out to be quite important for system stability especially in OS code. Consider this simple example:

fire_and_forget UpdateAsync(TextBlock block)
{
    co_await resume_background();
    hstring message = L"Hello developers!";

    co_await resume_foreground(block.Dispatcher());
    block.Text(message);
}

Here we’re performing some complex calculation on a background thread and then naturally switch to the appropriate UI thread before updating some UI control. The resume_foreground function had some cleverness that looked something like this:

auto resume_foreground(...) noexcept
{
    struct awaitable
    {
        bool await_ready() const
        {
            return m_dispatcher.HasThreadAccess(); // <-- Cleverness...
        }
        void await_resume() const {}
        void await_suspend(coroutine_handle<> handle) const { ... }
    };
    return awaitable{ ... };
};

This has been updated as follows:

auto resume_foreground(...) noexcept
{
    struct awaitable
    {
        bool await_ready() const
        {
            return false; // <-- Queue without waiting
        }
        void await_resume() const {}
        void await_suspend(coroutine_handle<> handle) const { ... }
    };
    return awaitable{ ... };
};

This is analogous to the difference between SendMessage and PostMessage in classic desktop app development. The latter will queue the work and then unwind the stack without waiting for it to complete. This unwinding the stack can be essential.

The resume_foreground function also initially only supported the CoreDispatcher tied to a CoreWindow that was originally introduced with Windows 8. A more flexible and efficient dispatcher has since been introduced. The DispatcherQueue is nice in that you can create them for your own purposes. Consider a simple console app:

using namespace Windows::System;

fire_and_forget RunAsync(DispatcherQueue queue);

int main()
{
    auto controller = DispatcherQueueController::CreateOnDedicatedThread();
    RunAsync(controller.DispatcherQueue());
    getchar();
}

Here I’m creating a private queue thread and then passing this queue object to the coroutine. The coroutine can then presumably use it to await – suspend and resume on this private thread. Another common use of the DispatcherQueue is to create a queue on the current UI thread for a traditional desktop or Win32 app.

DispatcherQueueController CreateDispatcherQueueController()
{
    DispatcherQueueOptions options
    {
        sizeof(DispatcherQueueOptions),
        DQTYPE_THREAD_CURRENT,
        DQTAT_COM_STA
    };

    ABI::Windows::System::IDispatcherQueueController* ptr{};
    check_hresult(CreateDispatcherQueueController(options, &ptr));
    return { ptr, take_ownership_from_abi };
}

Not only does this illustrate how Win32 functions may be called and incorporated into C++/WinRT projects, by simply calling the Win32-style CreateDispatcherQueueController function to create the controller and then transferring ownership of the resulting queue controller to the caller as a WinRT object, but this is precisely how you can support efficient and seamless queuing on your existing Petzold-style Win32 desktop app:

fire_and_forget RunAsync(DispatcherQueue queue);

int main()
{
    Window window;
    auto controller = CreateDispatcherQueueController();
    RunAsync(controller.DispatcherQueue());
    MSG message;

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

This simple main function starts by creating a window. You can imagine this registers a window class and calls CreateWindow to create the top-level desktop window. The CreateDispatcherQueueController function is then called to create the queue controller before calling some coroutine with the dispatcher queue owned by this controller. A traditional message pump is then entered where resumption of the coroutine naturally occurs on this thread. Having done so, you can return to the elegant world of coroutines for your async or message based workflow within your app:

fire_and_forget RunAsync(DispatcherQueue queue)
{
    ... // Start on the calling thread

    co_await resume_foreground(queue);

    ... // Resume on the dispatcher thread
}

The resume_foreground will always “queue” and then unwind the stack. You can also optionally set the resumption priority:

fire_and_forget RunAsync(DispatcherQueue queue)
{
    ...

    co_await resume_foreground(queue, DispatcherQueuePriority::High);

    ...
}

But if you only care about default queuing order then you can even await the queue itself and save yourself a few keystrokes:

fire_and_forget RunAsync(DispatcherQueue queue)
{
    ...

    co_await queue;

    ...
}

For the control freaks out there, you can even detect queue shutdown and handle that gracefully:

fire_and_forget RunAsync(DispatcherQueue queue)
{
    ...

    if (co_await queue)
    {
        ... // Resume on dispatcher thread
    }
    else
    {
        ... // Still on calling thread
    }
}

The co_await expression will return true, indicating that resumption will occur on the dispatcher thread. In other words, queuing was successful. Conversely, it will return false to indicate that execution remains on the calling thread because the queue’s controller is shutting down and is no longer serving queue requests.

As you can see, you have a great deal of power at your fingertips when you combine C++/WinRT with coroutines and especially when you do some old-school Petzold style desktop app development.

And that’s all for today. I hope you enjoy using C++/WinRT!

Meet C++/WinRT 2.0: Fewer Dependencies

I’ve always loved tools like Sysinternals where there is a single executable that you can simply copy onto your dev box and run. No need for an installer or a carefully managed tree of DLLs. It just works. Well cppwinrt.exe is like that as well. From the start, you could simply copy it onto any Windows 10 machine and it would just work. Still, there’s always room for improvement. Have a look at the dependencies reported by dumpbin for version 1 of cppwinrt:

> dumpbin /dependents cppwinrt.exe

ADVAPI32.dll
SHELL32.dll
api-ms-win-core-file-l1-1-0.dll
api-ms-win-core-processthreads-l1-1-0.dll
XmlLite.dll
api-ms-win-core-libraryloader-l1-2-0.dll
api-ms-win-core-processenvironment-l1-1-0.dll
RoMetadata.dll
SHLWAPI.dll
KERNEL32.dll
api-ms-win-core-rtlsupport-l1-1-0.dll
api-ms-win-core-heap-l1-1-0.dll
api-ms-win-core-localization-l1-2-0.dll
api-ms-win-core-timezone-l1-1-0.dll
api-ms-win-core-console-l1-1-0.dll
OLEAUT32.dll
api-ms-win-core-winrt-error-l1-1-0.dll
api-ms-win-core-winrt-error-l1-1-1.dll
api-ms-win-core-winrt-l1-1-0.dll
api-ms-win-core-winrt-string-l1-1-0.dll
api-ms-win-core-synch-l1-1-0.dll
api-ms-win-core-threadpool-l1-2-0.dll
api-ms-win-core-com-l1-1-0.dll
api-ms-win-core-com-l1-1-1.dll
api-ms-win-core-synch-l1-2-0.dll

In my defense, that’s not as bad as it looks. All of those DLLs are shipped with Windows 10 and those api-ms-win-core-xxx entries are really forwarding DLLs that support API sets. Still, there was one DLL in that list that caused a bit of trouble. RoMetadata.dll provides the implementation of the metadata parser shipped with the operating system. This is the implementation that practically everyone uses either directly or indirectly. We first hit a snag with this because the rather locked down server SKU that the build engineers at Microsoft wanted to use didn’t include this DLL. That turned out to be a Windows setup bug, but it got me thinking more about dependencies.

With C++/WinRT 2.0 I finally started writing a completely independent metadata parser in standard C++ to avoid this dependency and solve all kinds of trouble with this clunky old parser. A few guys on the team chipped in and this parser is now the foundation for all of our modern tooling. I then also ditched the forwarding DLLs to the point where dumpbin now reports a slightly smaller set of dependencies for version 2 of cppwinrt:

> dumpbin /dependents cppwinrt.exe

KERNEL32.dll
ADVAPI32.dll
XmlLite.dll
SHLWAPI.dll

The fun thing about this is that all of those DLLs are available, not only on Windows 10, but all the way down to Windows 7 and even Windows Vista. That means if you happen to have some crazy old build server running Windows 7, well then you can still run cppwinrt to generate the C++ headers for your project. And if you actually want to run C++/WinRT on Windows 7 you can even do that with a bit of work as well.

And that’s all for today. I hope you enjoy using C++/WinRT!

Meet C++/WinRT 2.0: Async Timeouts Made Easy

C++/WinRT took a big bet on C++ coroutines and that bet has paid off. Coroutines are in C++20 and the effect on writing concurrency code in C++ has been transformational. C++/WinRT was also the primary driver for the adoption of coroutines within Windows. Still, there are times when the fact that some API call is async is completely irrelevant and all you want is the result here and now. For that reason, C++/WinRT’s implementation of the various WinRT async interfaces has always sported a get function, similar to what std::function provides:

int main()
{
    IAsyncAction async = ...
    async.get();
    puts("done!");
}

This get function will block indefinitely for the async object to complete. Async objects tend to be very short-lived so this is often all you need. There are however times when this really doesn’t cut it and you need to abandon the wait after some time has elapsed. Writing this has always been possible, thanks to the building blocks provided by WinRT, but it has never been easy. Well C++/WinRT now makes it trivial by providing a wait_for function, again similar to what std::function provides:

int main()
{
    IAsyncAction async = ...

    if (async.wait_for(5s) == AsyncStatus::Completed)
    {
        puts("done");
    }
}

The wait_for in this example (using std::literals) will wait around 5 seconds before checking completion. If the comparison is favorable then you know that the async object completed successfully and you’re done. If you are waiting for some result, then you can simply follow that with a call to the get function to retrieve the result:

int main()
{
    IAsyncOperation<int> async = ...

    if (async.wait_for(5s) == AsyncStatus::Completed)
    {
        printf("result %d\n", async.get());
    }
}

Since the async object has already completed, the get function will return the result immediately without any further wait. As you can see, the wait_for function returns the state of the async object. You can thus use this for more fine-grained control:

switch (async.wait_for(5s))
{
case AsyncStatus::Completed:
    printf("result %d\n", async.get());
    break;
case AsyncStatus::Canceled:
    puts("canceled");
    break;
case AsyncStatus::Error:
    puts("failed");
    break;
case AsyncStatus::Started:
    puts("still running");
    break;
}

As I mentioned, AsyncStatus::Completed means the async object completed successfully and you may call the get function for any result.

AsyncStatus::Canceled means the async object was canceled. Note that the cancellation is typically requested by the caller, so it would be rare to handle this state. Typically, a cancelled async object is simply discarded.

AsyncStatus::Error means the async object has failed in some way. You may call the get function to rethrow the exception if so desired.

Finally, AsyncStatus::Started means that the async object is still running. This is where it gets tricky. The WinRT async pattern does not allow multiple waits or waiters. That means that you cannot call wait_for in a loop. If the wait has effectively timed-out, you are left with a few choices. You may abandon the object or you may poll its status before calling get to retrieve any result, but it’s best just to discard the object at this point.

And that’s all for today. I hope you enjoy using C++/WinRT!

Meet C++/WinRT 2.0: Optimizing Components

You may have noticed that all the 2.0 entries thus far have been focused on the component author. That’s no coincidence: C++/WinRT 2.0 is very much focused on improving the correctness, efficiency, reliability, and productivity of the developer building a WinRT component. One of the improvements for component developers could not be made without introducing a breaking change, so what I’m describing today is opt-in although it is enabled by default for new projects. An existing project can opt-in using the C++/WinRT compiler’s new -optimize command line option or in Visual Studio by setting the “Optimized” option to true:

First, I’ll describe why this is a cool optimization you should care about and then I’ll talk about how it’s implemented and you’ll understand why this is a breaking change worth applying to existing projects.

-optimize enables what is often called uniform construction. This is a feature that was long requested but eluded me for some time and I am very pleased that we can finally rely on this. Uniform or unified construction is the notion that you can use the C++/WinRT language projection itself to create and use your intra-component types, types that are implemented by your component, without getting into weird loader issues and you can do so efficiently. This solves a few pitfalls that hampered developers building complex components in the past. Imagine you have the following WinRT class (defined in IDL):

namespace Component
{
    runtimeclass Class
    {
        Class();
        void Method();
        static void StaticMethod();
    }
}

Naturally, as a C++ developer familiar with using the C++/WinRT library you might want to use the class as follows:

using namespace winrt::Component;

Class c;
c.Method();
Class::StaticMethod();

And this would be perfectly reasonable, if this code didn’t reside within the same component that implements this class. You see, the thing about C++/WinRT is that as a language projection it shields the developer from the ABI. C++/WinRT never calls directly into the implementation. It always travels through the ABI. Now this is not the ABI that C++ compiler developers talk about. This is the COM-based ABI that WinRT defines. So that first line where you are constructing the Class object actually calls the RoGetActivationFactory function to retrieve the class or activation factory and then uses that factory to create the object. The last line likewise uses the factory to make what appears to be a static method call. Thankfully, C++/WinRT has a blazingly fast factory cache, so this isn’t a problem for apps. The trouble is that within a component you’ve just done something that is a little problematic.

Firstly, no matter how fast the C++/WinRT factory cache is, calling through RoGetActivationFactory or even subsequent calls through the factory cache will always be slower than calling directly into the implementation. A call to RoGetActivationFactory followed by IActivationFactory::ActivateInstance followed by QueryInterface is obviously not going to be as efficient as using a C++ new expression for a locally-defined type. As a consequence, seasoned C++/WinRT developers know to use the make or make_self helper functions when creating objects within a component:

// Class c;
Component::Class c = make<implementation::Class>();

But as you can see, this is not nearly as convenient or concise. Not only must you use a helper function to create the object, you must also disambiguate between the implementation type and the projected type. It’s also easy to forget to do so.

Secondly, using the projection to create the class means that its activation factory will be cached. Normally this is a wonderful thing but if the factory resides in the same DLL that is making the call then you’ve effectively pinned the DLL and prevented it from ever unloading. For many developers this probably doesn’t matter but some system components must support unloading, and this can become rather problematic.

So this is where the term uniform construction comes in. Regardless of whether the code resides in a project that is merely consuming the class or whether the code resides in the project that is actually implementing the class, the developer can freely use the same syntax to create the object:

// Component::Class c = make<implementation::Class>();
Class c;

When the component is built with -optimize, the call through the language projection will compile down to the same efficient call to the make function that directly creates the implementation type and avoid the syntactic complexity, the performance hit of calling through the factory, and the problem of pinning the component in the process.

Uniform construction applies to any call that is served by the factory under the hood. Practically, that means this optimization serves both constructors and statics. Here’s the original example again:

Class c;
c.Method();
Class::StaticMethod();

Without -optimize, the first and last statements require calls through the factory object. With -optimize, neither do and those calls are compiled directly against the implementation and even have the potential of being inlined. This speaks to the other term often used when talking about -optimize, namely direct implementation access. Language projections are nice, but when you can directly access the implementation you can and should take advantage of it to produce the most efficient code possible. Now C++/WinRT will do this for you, without forcing you to leave the safety and productivity of the projection.

So why is this a breaking change? Well, the component must cooperate in order to allow the language projection to reach in and directly access its implementation types. As C++/WinRT is a header-only library, you can peek inside and see what’s going on. Without -optimize, the Class constructor and StaticMethod member are defined by the projection as follows:

namespace winrt::Component
{
    inline Class::Class() :
        Class(impl::call_factory<Class>([](auto&& f) { return f.template ActivateInstance<Class>(); }))
    {
    }
    inline void Class::StaticMethod()
    {
        impl::call_factory<Class, Component::IClassStatics>([&](auto&& f) { return f.StaticMethod(); });
    }
}

You don’t need to understand any of this (and remember never to rely on anything in the impl namespace), but it should be clear that both calls involve a call to some function named “call_factory”. That’s your clue that these calls involve the factory cache and are not directly accessing the implementation. With -optimize, these same functions are not defined at all! Instead, they are declared by the projection and their definitions are left up to the component. The component can then provide definitions that call directly into the implementation. This is where the breaking change comes in. Those definitions are generated for you when you use both -component and -optimize and appear in a file called Type.g.cpp where Type is the name of the WinRT class being implemented. That’s why you may hit various linker errors when you first enable -optimize in an existing project. You need to include that generated file into your implementation to stitch things up. In our example, the Class.h might look like this (regardless of whether -optimize is being used):

// Class.h
#pragma once
#include "Class.g.h"

namespace winrt::Component::implementation
{
    struct Class : ClassT<Class>
    {
        Class() = default;

        static void StaticMethod();
        void Method();
    };
}
namespace winrt::Component::factory_implementation
{
    struct Class : ClassT<Class, implementation::Class>
    {
    };
}

Your Class.cpp is where it all comes together:

#include "pch.h"
#include "Class.h"
#include "Class.g.cpp" // <-- Add this line!

namespace winrt::Component::implementation
{
    void Class::StaticMethod()
    {
    }

    void Class::Method()
    {
    }
}

As you can, following the inclusion (and definition) of the implementation class, Class.g.cpp is included to provide the definitions of those functions that the projection left undefined. Here’s what those definitions look like inside the Class.g.cpp file:

namespace winrt::Component
{
    Class::Class() :
        Class(make<Component::implementation::Class>())
    {
    }
    void Class::StaticMethod()
    {
        return Component::implementation::Class::StaticMethod();
    }
}

So this nicely completes the projection with efficient calls directly into the implementation, avoids those calls to the factory cache, and the linker is satisfied.

The final thing that -optimize does for you is to change the implementation of your project’s module.g.cpp, that helps you to implement your DLL’s DllGetActivationFactory and DllCanUnloadNow exports, in such a way that incremental builds will tend to be much faster by eliminating the strong type coupling that was required by version 1 of C++/WinRT. This is often referred to as type-erased factories. Without -optimize, the module.g.cpp file that is generated for your component starts off by including the definitions of all your implementation classes, the Class.h in this example. It then directly creates the implementation factory for each class as follows:

if (requal(name, L"Component.Class"))
{
    return winrt::detach_abi(winrt::make<winrt::Component::factory_implementation::Class>());
}

Again, you don’t need to understand any of this but it is useful to see that this requires the complete definition for any and all classes implemented by your component. This can have a dramatic effect on your inner loop as any change to a single implementation will cause module.g.cpp to recompile. With -optimize, this is no longer the case. Instead, two things happen to the generated module.g.cpp file. The first is that it no longer includes any implementation classes. In this example, it will not include Class.h at all. Instead, it creates the implementation factories without any knowledge of their implementation:

void* winrt_make_Component_Class();

if (requal(name, L"Component.Class"))
{
    return winrt_make_Component_Class();
}

Obviously, there is no need to include their definitions and its up to the linker to resolve the winrt_make_Component_Class function’s definition. Of course, you don’t need to think about this because the Class.g.cpp file that gets generated for you, and that you previously included to support uniform construction, also defines this function. Here’s the entirety of the Class.g.cpp file that is generated for this example:

void* winrt_make_Component_Class()
{
    return winrt::detach_abi(winrt::make<winrt::Component::factory_implementation::Class>());
}
namespace winrt::Component
{
    Class::Class() :
        Class(make<Component::implementation::Class>())
    {
    }
    void Class::StaticMethod()
    {
        return Component::implementation::Class::StaticMethod();
    }
}

As you can see, the winrt_make_Component_Class function directly creates your implementation’s factory. This all means that you can happily change any given implementation and the module.g.cpp need not be recompiled at all. It is only when you add or remove WinRT classes that the module.g.cpp will be updated and need to be recompiled.

And that’s all for today. Stay tuned for more about C++/WinRT 2.0!