惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Visual Studio Blog
月光博客
月光博客
T
Tailwind CSS Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
量子位
人人都是产品经理
人人都是产品经理
IT之家
IT之家
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
罗磊的独立博客
博客园 - 三生石上(FineUI控件)
有赞技术团队
有赞技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园_首页
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - Franky
The Cloudflare Blog
博客园 - 【当耐特】
Hugging Face - Blog
Hugging Face - Blog
大猫的无限游戏
大猫的无限游戏
S
SegmentFault 最新的问题
Jina AI
Jina AI
阮一峰的网络日志
阮一峰的网络日志
小众软件
小众软件
Last Week in AI
Last Week in AI

The Old New Thing

std::call_once vs. std::async - The Old New Thing Magic statics vs. std::call_once - The Old New Thing Why do Microsoft job levels start in the high 50's instead of starting at a sane number like 1? - The Old New Thing Why didn't Read­Directory­ChangesW provide a way to correlate the two sides of a rename operation? - The Old New Thing How can I remove the Close button from my window caption? - The Old New Thing Why is the x86 undefined instruction called ud2? Why 2? - The Old New Thing What algorithm did Windows XP use to choose your initial user picture? - The Old New Thing A sample use of the winstart.bat file in Windows 95 - The Old New Thing Why don't we allow stacks to be sparse, instead of forcing them to be contiguous? - The Old New Thing What happens if you change a window class's GCL_CB­WND­EXTRA? - The Old New Thing The case of the progress callback that never got called when progress happened - The Old New Thing The perils of binding to value types in XAML - The Old New Thing Microspeak: Funded / unfunded - The Old New Thing AWE does not require PAE, though PAE makes it much more useful - The Old New Thing On forcing all derived classes to implement a specific non-virtual method, part 2 - The Old New Thing On forcing all derived classes to implement a specific non-virtual method, part 1 - The Old New Thing In the product end game, every change carries significant risk, episode 2 - The Old New Thing Why didn't the Windows Entertainment Pack just run the MS-DOS version inside an emulator? - The Old New Thing Comparing the two holograms on the Windows 95 box - The Old New Thing Reducing C++ template bloat by factoring out the type-dependent portions of the function, practical exam - The Old New Thing Reducing C++ template bloat by factoring out the type-dependent portions of the function - The Old New Thing On wrapping a callable in a lambda that just calls it with the same parameters - The Old New Thing Why did the Microsoft Entertainment Pack for Windows have a special sticker announcing that it also had Tetris? - The Old New Thing How do functions like alloca allocate memory from the stack? - The Old New Thing How do functions like alloca allocate memory from the stack? - The Old New Thing Forcing an ARM64X executable to run as a specific architecture - The Old New Thing A little helper class for managing LPPROC_THREAD_ATTRIBUTE_LISTs - The Old New Thing The comments that go into code versus those that go into the pull request description - The Old New Thing The little-known winstart.bat batch file - The Old New Thing How can I perform a Copy­File&shy in unbuffered mode? - The Old New Thing
Creating a fake agile wrapper that is technically agile b...
Raymond Chen · 2026-08-05 · via The Old New Thing

Last time, we tried to execute on our plan to use the global interface table to hold hold a reference to an object in another apartment that automatically expires when the apartment runs down. But it broke down because objects that are marked INoMarshal can’t go into the global interface table.

So we will just force the square peg into the round hole: We can put the non-marshalable object inside an object that is marshalable.

template<typename Smart>
struct force_marshal :
    winrt::implements<force_marshal<Smart>, ::IUnknown, winrt::non_agile>
{
    force_marshal(Smart const& p) : m_p(p) {}
    Smart m_p;
};

The force_marshal<Smart> object babysits a non-marshalable smart pointer to a COM object and exposes a marshalable wrapper around it. Since the wrapped object is not agile, the wrapper cannot be either. (If the wrapper were agile, then we’d be back where we started: How do we ensure that the m_p is destructed in the correct apartment?)¹

We can put the unmarshalable object inside the wrapper, and then put the wrapper in the global interface table.

template<typename T>
struct fake_agile_ref
{
    ⟦ ... ⟧

    fake_agile_ref(Smart const& p) : m_raw(winrt::get_abi(p))
    {
        if (m_raw) {
            m_context = winrt::capture<IContextCallback>(CoGetObjectContext);
            m_token = get_context_token();
            m_git = winrt::create_instance<IGlobalInterfaceTable>(CLSID_StdGlobalInterfaceTable);
            winrt::check_hresult(m_git->RegisterInterfaceInGlobal(
                winrt::make<force_marshal<Smart>>(p).get(),
                __uuidof(IUnknown), &m_cookie));
        }
    }

    ⟦ ... ⟧
};

template<typename T> fake_agile_ref(winrt::com_ptr<T> const&)
    -> fake_agile_ref<T>;
template<typename T> fake_agile_ref(T const&)
    -> fake_agile_ref<T>;

Okay, so now we have managed to create an agile wrapper around an unmarshalable object. This agile wrapper is agile on paper: You can use it from any thread. However, it is not agile in practice: If you try to use it from the wrong apartment, it throws an exception. But at least the behavior when used from the wrong apartment is well-defined, as opposed to the case of directly using an unmarshalable object from the wrong apartment, which is undefined.

Next time, we’ll do some fine tuning.

Bonus chatter: Of course, now that we have a marshalable wrapper, we could use that wrapper to call the original non-marshalable object.

Original apartment     Other apartment
Wrapper Caller
   
Non-marshalable    

The non-marshalable object does not allow any arrows to come in from other apartments, but the wrapper lives in the same apartment as the non-marshalable object, so its arrow is coming from within the same apartment.

You can think of the wrapper as a VPN into the original apartment, allowing calls to come in from the outside, but to appear to the non-marshalable object as if they came from within the same apartment.

Now, you could do that, but I’m not going to. The original object presumably went out of its way to declare itself non-marshalable for a reason, so we honor that preference and not play funny games to trick it into doing something it said that it didn’t want to do.

¹ Two commenters fell into this trap by suggesting that we wrap the non-marshalable object inside an object that implements IMarshal. If you implement IMarshal, then you are saying, “I’m way cooler than a standard non-agile object. I’m going to do fancy stuff (like being agile).” But we want to be a boring non-agile object, so that COM will do standard marshaling for us.

Category

Topics

Author

Raymond Chen

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.