Last time, we learned about COM apartments, with the two main flavors the single-threaded apartment and the multi-threaded apartment. But it turns out you can also create “miniature apartments” inside your apartment. (Is this like Airbnb for COM or something?)
This “miniature apartment” is formally known as a COM context and goes by the name CLSID_
. You create one by calling
IContextCallback* context; CoCreateInstance(CLSID_ContextSwitcher, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&context));
You can then enter the context by calling the IContextCallback::
method with a function you would like to execute inside that context. I’m going to postpone further discussion of the IContextCallback::
method to another article, because the IContextCallback::
method is kind of weird, and untangling it will take a while.
Back to contexts. Why would you want to create a custom context anyway?
The original audience for custom contexts was Windows NT services which expose COM objects to clients. Services also have to respond to shutdown requests. This puts them in a bit of a pickle: They are required to clean up and unload from the process when given a shutdown request,¹ but they also cannot unload (under penalty of access violation) until all clients have released their references to objects in the service.
The solution is for the service to create a private little COM context for its objects. It then enters the context and registers its object factory. When a client requests an object, the factory will be called to produce the desired object. Since the factory is inside a context, the resulting object will also be inside that same context. The client receives a proxy object that talks to the object living inside the context.
Context | |||
object⊸ | ← | proxy⊸ | ←Client |
When the server is told to shut down, it enters the context one last time to revoke its factory and call CoDisconnectContext
. The CoDisconnectContext
function disconnects all outstanding proxies from the underlying objects, erasing the arrow from the proxy to the object:
Context | |||
object⊸ | ❌ | proxy⊸ | ←Client |
The expectation is that disconnecting all the proxies will cause the reference counts of all the objects in the context to drop to zero, and everything will be destroyed. The service can destroy the context, and everything that had references to the service DLL is now gone, thus allowing the service DLL to unload itself from memory.
Meanwhile, the clients are left holding a broken proxy. Any attempt to access the underlying object from the proxy wll return the error RPC_
.
Although Windows NT Services were the original audience for private contexts, they are not the only valid use for them. Next time, we’ll look at another way they can become useful.
¹ This means that the shutdown “request” is more like a shutdown “demand”.
That’s one heck of an old meme in the title, but apt nonetheless!
I have been coding COM objects for years, but I never knew this context feature existed. In my NT service that hosts a COM server, I simply keep track of the number of COM objects that have been requested by clients and have not been released yet, and then I have the service reject SERVICE_CONTROL_STOP requests if there are any active COM objects.
Unrelated to this post (Although awesome read), but can you fix up the links on this page?
https://devblogs.microsoft.com/oldnewthing/20060727-04/?p=30333
It would awesome to read through all of those, I want to learn more, but all the links are broken, and I can’t find them manually.
Hover over the archive links down at the bottom of the page and they’re all in the format `https://devblogs.microsoft.com/oldnewthing/year/month`. That post has links which also has dates in them, so check the July 2006 archives for the posts you’re looking for: https://devblogs.microsoft.com/oldnewthing/2006/07
I think there is something missing from this post. That COM will automatically disconnect the proxies when COM is shutting down for the process. The keyword here being “the process”. If you are doing a service that is doing traditional svchost process sharing then you can’t do that. That’s what contexts solve here.
That explains it, I was confused why they needed to do anything extra since disconnecting at process destruction seems like the obvious default anyhow.
I wonder how many services there are out there that just don’t respond to shutdown requests.
Provided they don't register to receive them, they don't have to. They can continue to run until Windows actually turns the power off, and in fact this is the recommended behaviour per the documentation, I assume because exiting the process would typically consume more resources than just allowing it to keep running.
(This was even more curious back in the days before ACPI; you could write a service and have it continue to run, and...