How does COM activation work anyway?

One of my co-workers came to me the other day and asked why on earth COM had this "dwClsContext" parameter. In particular, he was concerned about the various CLSCTX_XXX_SERVER options.

In order to explain what the point of the CLSCTX_XXX_SERVER options, you understand how COM activation works. In general, there are two cases: CLSCTX_INPROC_SERVER and CLSCTX_LOCAL_SERVER (there’s a 3rd option, CLSCTX_INPROC_HANDLER, which is half-way between the two as well).

So what does happen when you call CoCreateInstance() to instantiate a class? It turns out that this is documented with the documentation for the CLSCTX parameter (go figure that one out), but in a nutshell (ignoring several issues like COM object activation security), the following happens:

For both cases, the first thing that COM does is to open HKEY_CLASSES_ROOT(HKCR)\CLSID\{<STRINGIZED-GUID-OF-RCLSID>}.

If the user specified CLSCTX_INPROC_SERVER (or CLSCTX_INPROC_HANDLER), COM looks for an InprocServer32 key under the classid. If the InprocServer32 key is found, then the default value of the key specifies the DLL to be loaded. The ThreadingModel value in that key specifies the desired threading model for the object. If the threading model for the object is compatible with the threading model for the thread (see "What are these "Threading Models" and why do I care?" for more info on threading models), then the DLL is loaded. Once the DLL’s been loaded, COM calls GetProcAddress() to find the address of the DllGetClassObject routine for the DLL. The DllGetClassObject routine’s job is to return an instance of an object that implements IClassFactory for all the classes supported by that DLL.

If the user specified CLSCTX_LOCAL_SERVER, it means that the client wants to contact an out-of-proc COM server. For an out-of-proc object, there are a gain two possible choices – the COM server could be implemented by an NT service, or it could be implemented by a local executable.

In both cases, COM needs to know activation information about the object, so it looks to see if there’s an APPID value associated with the class. If there IS an APPID value, then COM opens HKCR\APID\{<STRINGIZED-GUID-OF-APPID>}. Under the APPID key, a number of values are located – the first is the AccessPermission value, which contains the security descriptor that’s used to determine if the caller is allowed to access the COM object. The second is the LaunchPermission, which determines if the application is allowed to load the class factory for this class. Also located under the APPID is the RunAs key which specifies the security principal under which the server should be run. At some point in the future I’ll talk about these keys, but they’re beyond the scope of this article. The last piece of information that’s retrieved from the APPID is the LocalService value, which indicates the service that will handle the COM object.

To handle the first case, the first thing that COM does is to look for a LocalService key. If the LocalService Key is found (or if a LocalService key was found under the APPID), then COM attempts to start the service specified (if it’s not already started).

If there’s no LocalService key, then, COM looks for the LocalServer32 key. If it finds the key, again, the default value of the key specifies an executable name, and the ThreadingModel value specifies the threading model for the object. COM then launches the application (this is where COM checks for CLSCTX_DISABLE_AAA and CLSCTX_ENABLE_AAA).

Once the service (or executable) comes up, it calls CoRegisterClassObject(). The call to CoRegisterClassObject informs the DCOM service (via RPC) of the address of a class factory for a particular ClassID.

In both out-of-proc cases, the COM client next attempts to connect to the DCOM service, which looks up the class object in its table of class factories and returns a pointer to that class factory to the client.

Now that the client has a class factory for the COM object (either by retrieving it locally or via the DCOM service), the client can call into the class factory to retrieve an instance of the class in question. It calls IClassFactory::CreateInstance which will then instantiate the object for the application. And finally CoCreateInstance will return that pointer to the client application.

One thing to note: some COM objects have both in-proc and out-of-proc implementations (NetMeeting is an example of this). In those cases, a client could request that the service run either in OR out-of-proc, transparently.

Oh, one thing I didn’t mention above is the CLSCTX_ALL definition, which is declared in objbase.h – CLSCTX_ALL is simply a combination of CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER|CLSCTX_LOCAL_SERVER – it tries all the above and goes with the one that works ☺