July 28th, 2026
mind blownlike2 reactions

Making an agile version of a Windows Runtime delegate in C++/WinRT, part 7

Last time, we fixed the problem of creating a unique_ptr whose deleter’s constructor was might throw an exception. But we’re not out of the woods yet.

Let’s take another look at what we have:

    if (d.try_as<::INoMarshal>()) {
        void* p;
        if constexpr (std::is_reference_v<Delegate>) {
            p = winrt::detach_abi(d);
        } else {
            winrt::copy_to_abi(d, p);
        }
        return
            [p = std::unique_ptr<void, in_context_deleter>(p, {}),
            token = get_context_token()](auto&&...args) {
                if (token == get_context_token()) {
                    std::remove_reference_t<Delegate> d;
                    winrt::copy_from_abi(d, p.get());
                    d(std::forward<decltype(args)>(args)...);
                } else {
                    throw winrt::hresult_error(CO_E_NOT_SUPPORTED);
                }
            };
    }

We had originally broken the rule that the unique_ptr(p) constructor requires that the deleter’s default constructor not throw an exception. We fixed it by constructing the deleter explicitly as a parameter, so that the unique_ptr constructor can move it into the stored deleter without an exception.

But wait, if an exception occurs in construction of the in_context_deleter, the raw pointer we created in the previous block will be leaked. It owns a reference count but doesn’t clean up in the case of an exception.

We can fix this by creating the deleter first.

    if (d.try_as<::INoMarshal>()) {
        in_context_deleter del;
        void* p;
        if constexpr (std::is_reference_v<Delegate>) {
            p = winrt::detach_abi(d);
        } else {
            winrt::copy_to_abi(d, p);
        }
        return
            [p = std::unique_ptr<void, in_context_deleter>(p, std::move(del)),
            token = get_context_token()](auto&&...args) {
                if (token == get_context_token()) {
                    std::remove_reference_t<Delegate> d;
                    winrt::copy_from_abi(d, p.get());
                    d(std::forward<decltype(args)>(args)...);
                } else {
                    throw winrt::hresult_error(CO_E_NOT_SUPPORTED);
                }
            };
    }

If there is an exception constructing the custom deleter, it happens before we initialze the raw pointer, so there is no leak of the reference owned by that raw pointer.

Okay, so are we done now?

Nope.

More next time.

Topics

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

3 comments

Sort by :
  • Igor Levicki 6 hours ago · Edited

    @Raymond Chen:

    I have a question unrelated to this topic and I hope you will investigate it when you have time.

    You know that Microsoft.UniversalStore.HardwareWorkflow.SubmissionBuilder.dll (along with InfReader, Catalogs, Cabinets) used by Inf2Cat.exe?

    Whoever wrote it must have been out of their god damn mind because the way it works is -- it parses all infs and driver files into a giant XML and uses that to generate the catalog file.

    If you run it on an unpacked NVIDIA Display.Driver folder to regenerate the catalog it will create 260.52 MB XML file and spend north of 15 minutes creating a catalog because it is...

    Read more
    • Raymond Chen 3 hours ago

      Tip: Insulting people is generally not an effective way to get them to do you a favor.

      • Igor Levicki 3 hours ago

        I updated the "insult" to past tense to allow for the possibility that they came to their senses in the meantime.

        As for the favor part, it's not like I was asking for a fix -- I am long past expecting anytthing good code-wise and API wise coming out from Microsoft and I have a feeling that if it weren't for you Windows would have been nothing more than a random collection of BSODs.

        You are of course free to do anything you want with the above info including ignoring it or getting offended by me pointing out the architectural issue (which...

        Read more