Larry Osterman's WebLog

Confessions of an Old Fogey
Blog - Title

How does COM activation work anyway?

How does COM activation work anyway?

  • Comments 6

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

 

  • Somewhere along the way, CoCreateInstance also consults the COM+ catalog to see if the requested CLSID has been registered there.

    I recently spent an hour trying to figure out why CoCreateInstance was returning a proxy rather than a direct pointer to some COM object that was registered with ThreadingModel=both. Then I noticed that the DLL specified in InprocServer32 wasn't even loaded into my process. Turned out this CLSID was also added to an out-of-proc COM+ application, so it was activated in a dllhost.
  • Yeah, I left out the clsid cache from my discussion above - if someone's called CoCreateInstance in the process previously, the information is cached and re-used.

    In your case, I bet it was one of those dual in-proc and out-of-proc objects, and the first caller specified the CLSCTX_LOCAL_SERVER version, so when you came along and specified CLSCTX_INPROC_SERVER, you got the first one's information, not the one you specified.

    I don't know if that's a bug or not, to be honest, since the two versions are theoretically interchangable.
  • Hey Larry, have you ever run into this? It's along the lines of COM activation.

    Say you create an out-of-process COM server that runs as a LocalServer (as opposed to LocalService) using the ATL project wizard in VS6 or VS.NET. Then create a console app that calls CoInitialize() and then CoCreateInstance() on an object in the COM server. When you run the console app, you'll see the COM server startup in the Task Manager. So far so good. If you startup more instances of the console app, they all use the existing running instance of the COM server for their classfactory. Again, so far so good.

    Suppose you then create a barebones service that calls CoCreateInstance() on the COM object in the server. Whenever I do this I observe a second instance of the COM server starts up - even if I tell the service to use the same Account that started the console apps and open up the DCOM permissions on the COM server. I've never figured out how to get a service to use a running instance of the COM server instead of firing up a second instance.
  • You might look at the activate as activator (AAA) stuff there. I believe that your security blanket requires that COM servers be activated as activator (that's what the AAA stuff is). Which means that the COM server is going to run in the context of the user that activated it, if more than one user activates the object, you get more than one server.

    I'm not sure how to disable it, you might try CLSCTX_DISABLE_AAA and see if it works (but you need to be aware that there are REALLY serious security ramifications of doing this, and I'm not sure if it'll work on XP SP2).
  • nice one, thanks,

    perhaps you could explain the point of COM monikers, i've never really understood what use they are!

  • PingBack from http://workfromhomecareer.info/story.php?id=11276

Page 1 of 1 (6 items)