Larry Osterman's WebLog

Confessions of an Old Fogey
Blog - Title

COM registration for cross process access

COM registration for cross process access

  • Comments 13

Yesterday I posted a minimal COM registration.  But it had some serious issues.  Among them the COM objects couldn't be used cross process, and they couldn't be used from a STA application unless the object aggregated the free threaded marshaller.

So what if you want to go cross-process?  Well, in order to go cross process, you need to be able to know how to marshal the parameters for your interfaces.  COM knows how to marshal the standard interfaces (like IClassFactory, IUnknown, etc) but most objects need more than just those interfaces.

There are basically two ways of letting COM know about your interfaces.  The first is by using a typelib, the second is by using a proxy DLL.  If you don't need to worry about COM interop or interaction with older scripting architectures (like VB6 or script), then using the proxy DLL is unquestionably the way to go - when structures are mapped to a typelib, there is a slight loss of fidelity which can cause "issues".  As an example of the loss of fidelity, typelibs can't contain unnamed unions, while C allows them.  Thus if an interface attempts to marshal a structure containing an unnamed union, the typelib will replace the unnamed union with a named union.  Normally this isn't a problem, the actual structure data doesn't change, but it means that the definitions don't round-trip.

I'm not going to discuss typelibs currently (they're the next post in this mini-series), this time I want to talk about using proxy DLLs for your interface marshaling.

A proxy DLL is simply a DLL that contains the logic needed to marshal your interfaces.  To build one, follow the examples in MSDN here.  There are lots of options when building proxy DLLs, personally I prefer to merge the proxy DLL with an existing DLL (it just seems cleaner), that's a bit trickier, but not too hard (if you define the REGISTER_PROXY_DLL definition, then the _p file generated uses a hard coded name of DllRegisterServer, you need to define the ENTRY_PREFIX macro to rename the built-in name, etc).  The macros to make that stuff work are described here.

Once you've gotten the proxy definitions for your interfaces built, you need to let COM know about them.  When COM realizes it has to marshaling a COM object, it starts looking for information to let it know how to marshal its interfaces.  First it checks to see if the object supports IMarshal, to let the object do custom marshaling.  If that doesn't work, it starts looking elsewhere.  One of first places looks is to see if it's been explicitly told about how to marshal the interface by looking in HKCR\Interface for the IID.

First, you need to come up with a GUID for the proxy DLL, uuidgen can come up with one quickly.  And you need to let COM know about it (using the minimal set of registrations mentioned in the other article):

Key: HKEY_CLASSES_ROOT\CLSID\<PS Factory GUID>\
    Default Value: <MyInterfaceName>_PSFactory    // Again, not needed, but convenient
Key: HKEY_CLASSES_ROOT\Interface\<IID>\InProcServer32\
    Default Value: <Proxy Server DLL>
 

Next, you want to register the interfaces and let COM know how to find your proxy DLL.  Add the following to the registry for each of your interfaces:

Key: HKEY_CLASSES_ROOT\Interface\<IID>\
    Default Value: <friendly name for the interface> Again, not really required, but nice for oleview
Key: HKEY_CLASSES_ROOT\Interface\<IID>\ProxyStubClsid32\
    Default Value: <Proxy Stub CLSID>

And with that, you're done.  COM can now marshal your custom interfaces across process boundaries (or apartment boundaries).  Once again, 2 keys for each interface, plus 2 keys for the proxy DLL, which is a fair amount less than some of the stuff I've seen in the registry.

Next, what if you want to interoperate with VB or .Net?

  • "older scripting architectures (like VB6 or script)"

    I'm thinking I missed something. Does this "older" version include JScript as implemented in IE6 and IE7/Vista? What is the newer scripting architecture that has replaced it? Can I write a "newer" script component that is called from a web page in IE7 the same way as the "older" ones?
  • Isn't there a way to do this without using a custom proxy dll? (doesn't result in minimal data in the registry, but I think it results in a fair tradeoff in required effort)

  • If you are running inproc, why use COM at all? I have been using COM for years, until I finally figured out that what I only needed to expose from component was a C-entry point. The type-library provides intellisense and the object browser to IDEs, while the client code just loads the dll, gets the entry point, calls to get the root interface, and that's it.

    When it comes to running out-of-proc, I wonder again why you would want to stick to COM, while a simple client/server socket would do just as well.

    COM is sooooooooo 90s.

    (thoughts from an ISV happily moving away from COM)

  • Each time I read a COM article by you, Raymond or whoever it increases two things: my realisation that I don't really know much about COM, and my fear that learning more about it will drive me insane.

    I'm ashamed to say that I've never really played much with COM at its lowest level. I've dealt with COM objects from scripting languages, obviously. However, each time I learn about the internals it seems to get more and more complicated to the point where I start forgetting what I learned to start with.

    I'm glad the .NET framework unites all of these different COM thingies (Code, Interfaces, Type Libraries, Proxy DLLs, ...) into a small set of simple concepts that I've actually had time to figure out. (I think!)

    However, part of me still wants to take the time to figure it out just for the sake of understanding it. I'll probably learn about it when it's obsolete just like I've done with most technologies that seemed clever at the time, since they seem so much more exciting and quaint with the benefit of hindsight! :)
  • Dave: the .NET framework installs an OLE MIME filter for PE executables (application/x-msdownload, and application/octet-stream too, just in case), so that if an executable pulled in through an <object> is found to be a .NET application or assembly, it's instantiated in the Internet security zone (thus sandboxed so that it cannot do any damage, and as safe as a Java applet would be). You really don't need anything else to embed .NET code in a webpage. Here's the detailed documentation:

    http://msdn.microsoft.com/library/en-us/cpguide/html/cpcondeployingcommonlanguageruntimeapplicationusingie55.asp
  • Stephane, COM provides a convenient activation model, and there are times you simply must use COM (for example, if you're a shell plugin, you're pretty much stuck with COM).

    And as far as using sockets when running out-of-proc, COM/RPC has some HUGE advantages - for example, COM and RPC can use the LPC transport which is significantly faster than sockets. And you don't have to deal with manually marshalling all your data structures. Also COM and RPC provide contract validation for free - DCOM/RPC guarantee that the contract specified in the IDL file is enforced - if you specify that a pointer parameter is non optional, you'll never get a NULL pointer. If you say that a buffer is <n> bytes large, you can guarantee that you've got at least <n> bytes. With sockets you have to do all that work yourself.

    mirobin, you don't need the proxy DLL, you can use a typelib, but (as I mentioned), marshalling isn't full fidelity. How to add typelib info is next.

  • Perhaps Monad would be considered a "newer" scripting architecture. Since it's .NET based, it should allow any regular interop you would get with C# and such.

    As for Stephane's ideas, client-server sockets are so 80's. COM provides many advantages for components to allow them to be used by other programs. I do believe that COM may be overused for intraprogram use even where it is not necessary to expose something to the outside world. Odds are a lot of those 4,000(!) entries in my HKCR\CLSID key are unnecessary.

    COM is great for things like OLE, where programs can interact with objects that they've never seen before. COM is overkill for splitting your program up into modules.
  • I want to ask a stupid question:

    What is this all about?

    Larry, you/your team are reducing the needed registry keys. But why? From these posts it looks not like they would be sooooo many!

    How many components do you have and how many needless interfaces per component?? 12 by 10? Makes 120 keys which should not really make Vista faster??

    (Maybe I'm not understand COM).


    But of course it is still great that unnedded stuff is removed! So I don't want to argue that it should be done, I just want to understand what the result is.
  • Christian, why not the posts?

    As I mentioned in the other post, there are LOTS of COM objects out there with way too much unnecessary information in their COM definitions. And reducing information in the registry is a good thing.

    Think of it this way. There are several thousand COM objects registered in Windows by default. If half of them have two or three unnecessary keys, each key takes up say a hundred bytes, that means that there are maybe twenty or thirty k of information in the registry. The entire registry is paged into memory during boot, which means that removing that unnecessary data will reduce the size of the registry by maybe 5 pages or so. That means that there are 40 fewer sectors read from the disk during boot. To read each sector takes somewhere around 10 milliseconds (it doesn't take that much if they're contiguous, but bear with me). That means that by removing the unnecessary information from the registry you could speed up Windows boot by 400 milliseconds or so.

    Of course these numbers are all SWAGs, but you get the point - if there's less stuff in the registry, the system boots faster.

    So it's worth documenting this stuff.

  • "COM provides a convenient activation model, and there are times you simply must use COM (for example, if you're a shell plugin, you're pretty much stuck with COM)."

    That's exactly my point. COM makes people stuck. It's bad enough to have to deal with it with client code, it should be a no-brainer on the server side. If you write cross-platform code, you need to abstract this COM thing away, or you're stuck.
    Sockets also provide you a homogeneous way to talk with machines regardless the OS they are running. I think this far outweighs the benefits of using DCOM/RPC, which happens to be disabled (or at least heavily scrutinized by firewalls and AVs) on a bunch of PCs these days anyway.

    When it comes to client shell or IE plugins, that makes me laugh a little bit, because while it's true you need to expose yourself as a true COM component, the current awareness on spywares and that sort of thing makes it such that I wonder who else than Microsoft can successfully deploy and make a business out of such plugins from now on.

    That's really a decade old debate.

    Go away COM, go away OLE, go away.

  • I have to echo Ben Cooke's comment.

    An example is your note about the ENTRY_PREFIX macro. It does not seem to be officially documented anywhere except in the rpcproxy.h header itself.

    It really does need some bit of documentation, since if you want to use it to make a single DLL that can register both the in-proc COM server and the proxy server you must know that the DllRegisterServer and DllUnregisterServer routines that register the in-proc service must call through to the proxy versions as well.

    It looks like nothing has to be done for the Class Factory export - it appears that the system calls NdrDllGetClassObject instead of DllGetClassObject to get a class factory interface pointer (actually an IPSFactoryBuffer pointer for proxies). I have not verified this with a debugger, but that's what it looks like form examining the headers.

    Since obviously there are plenty of people using COM, I guess most COM programmers just let the 'magic' happen and don't really care how this stuff works.

    That said, I like these posts on COM you're making because they shed a lot of light on what goes on fundamentally with COM, and it seems I'm going to be using a lot of this information in the coming months.
  • Mikeb: the ENTRY_PREFIX macro is actually documented on the linked page (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/midl/midl/c_compiler_definitions_for_proxy_stubs.asp).

    I was surprised when I started this to find the documentation - it was better than I'd thought.
  • Yesterday, someone asked me how to inform client about outof process object it is using is crashed
Page 1 of 1 (13 items)