6

I've used COM for some years now but I keep learning new (and strange) things.

Recently I've realized that COM interfaces didn't had to be registered in the registry for components implementing them to work.

I've come to this conclusion after analysing the registry of a workstation where COM DLLs (implemented in .Net/C#) were registered with .reg files created by RegAsm because the user was not an administrator. And RegAsm only generates registry keys for COM classes and not interfaces.

If that's true my guess is that interfaces are important for early binding and have only to be present in TLB files. On the contrary registering implementations (classes) is essential because they are backed by physical code on the file-system that need to be referenced.

1) So am I crazy, missing something, or interfaces can be omitted?

2) If they can be omitted what are the consequences if any?

4
  • 1
    I suspect your question is about specific case of using COM objects... In general registering COM interfaces was never a requirement, neither presence of interfaces in TLB files. As long as code can somehow get implementation of interface the code expects it is fine. Commented Sep 8, 2015 at 16:17
  • @AlexeiLevenkov Thanks for clearly confirming and stressing this fact. Commented Sep 8, 2015 at 17:55
  • 1
    You may also find it worthwhile to investigate registration-free com. At a high-level, registration-free com works by stuffing the registration information in your application manifest instead of the windows registry. This is great if you want to support xcopy deployment. See also stackoverflow.com/tags/regfreecom/info . Commented Sep 9, 2015 at 21:25
  • Thanks for the suggestion @Brian but AFAIK this is not possible with my use-case: an Office/Excel addin used by VBA. Commented Sep 10, 2015 at 8:06

3 Answers 3

7

There are a lot things that you can't do without the interface being registered. Many of the features of COM -- marshaling, proxying, asynchronous calling -- have standard implementations that prevent you from having to roll this stuff yourself. For example, CoMarshalInterface is a standard way of taking any COM object interface and marshaling that interface into a stream so that it can be unmarshaled in another thread, process or machine. The interface information is critical in this -- without the interface metadata, the standard COM implementations of things like this won't work, as the infrastructure simply doesn't know enough about your interfaces to do what it needs to do in a generic way that works for all COM objects.

Additionally, while most automation clients (like VBA, C# and C++) can reference a type library file directly for purposes of early-binding, there are still limitations. For example, suppose you're working with a type library that contains some classes that implement interfaces from a different type library, or maybe the interfaces in the first type library accept parameters or return values that are defined by interfaces/enums/etc in another type library. In order for an automation client to work with these interfaces which contain cross-references, the cross-referenced type library must be discoverable somehow. Registration is the way this is accomplished.

Worth noting: In my experience, pretty much everything that works when a COM object is registered machine-wide (registered in HKLM) works exactly the same when registered per-user (in HKCU). This often makes COM registration more palatable in situations where machine-wide registration can't be performed (e.g. the user is not an admin). However, there are some significant gotchas, most notably https://techcommunity.microsoft.com/t5/Windows-Blog-Archive/Per-User-COM-Registrations-and-Elevated-Processes-with-UAC-on/ba-p/228531

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for pointing out the limitations, I would never have though to them because with typical .Net COM addins development you deliver a single self-contained DLL and TLB.
Understood. And in many cases, you may never need some of this plumbing. The designers rightfully consider these things fundamental to COM because these things are done so often. So they've thought the process through and come up with generic solutions that work for most cases, provided there is enough metadata available. If you know that you'll never need a proxy, you might be able to get away without any registration. It's just hard to tell sometimes when you need some of these fundamentals and when you don't.
More often than not we know we need it when it breaks. ;-)
@schwarz I found an updated link hosted my microsoft. See edits to post.
3

Pretty vague, not sure I could read all the words between the bold ones. There is in general more than one way to skin this cat. COM requires using a class factory to get an object created, the generic work-horse one is CoCreateInstance(). CreateObject() is popular in scripting environments. You give it a number and it spits an interface pointer back. With the COM runtime taking care of the job to locate the executable file that contains the coclass, loading it and finding the proper class factory implementation.

Finding the executable is the tricky part, this is commonly done by info in the registry. Entered there when the component was registered. Not exclusively, a manifest can also be the source of this info. It needs to be embedded in the client app, one reason it is not a universal solution. More modern is the package manifest in a Windows Store/Phone/Universal application. Required, only very privileged components can still use the registry to let themselves be found. Microsoft components.

A completely different tack is having custom class factories. The way it is done in DirectX for example, it doesn't depend on the registry at all. You call CreateDevice() instead. Still calling this COM is a bit of a stretch, it is a more general technique called interface-based programming.

This all applies to objects, interfaces are different. You call IUnknown::QueryInterface() to obtain an interface pointer. No registration required, it is the coclass that handles it.

Nevertheless, you'll find lots and lots of registered interfaces with Regedit.exe in the HKLM\Software\Classes\Interface registry key. They take care of another COM detail, if the component does not live in the same machine or same process or the same thread as the client code then extra work must be done to get the call serialized across the machine/process/thread boundary. Same kind of thing that happens in .NET Remoting, it requires a proxy. An object that also implements the same interface but doesn't execute the method directly, passing the arguments to the stub instead so it can make the call.

Simple to do in .NET, Reflection makes it very easy. Not simple in COM, an extra component is required that knows how to serialize the arguments into an interop packet. And get the return value back the same way. Proxy/stubs are normally automatically built from the IDL. Or very common in .NET since it doesn't use IDL, you use the marshaller that digs out method details from the type library. A mechanism that's highly comparable to .NET Reflection, the type library plays the exact same role as .NET metadata does.

The ProxyStubClsId32 registry key inside the Interface key contains the CLSID of that component. You'll very commonly find {00000320-0000-0000-C000-000000000046} there, that's the system provided marshaller that uses the type library.

Regasm doesn't write the interface keys, it sets the ThreadingModel key for a .NET [ComVisible] class to "Both". So that the methods can be called both from an STA as well as an MTA thread without having to be marshaled. That's very optimistic and very rarely tested, writing thread-safe .NET code isn't that easy.

2 Comments

Thanks Hans for all the background information. Indeed DCOM is an interesting use-case for registration of interfaces. And yes working with COM in .Net hides a lot of plumbing and is almost a breeze (except when it breaks in low level layers you are not used to deal with).
Standard marshaling refers to the one where you use MIDL to generate a proxy/stub, or any similar technique that generates similar declarations and code, so {00020424-0000-0000-C000-000000000046} is the type library marshaler. Note also that {00000320-0000-0000-C000-000000000046} is ole32.dll's own marshaler, it should not be used for anything else.
2

Regarding your first question, if the interface is not supposed to be used across COM contexts, or if the interface derives from IDispatch and you only use late-binding, you don't need to register it.

However, if you use early-binding, or if the interface is supposed to be used across COM contexts, you need to register it.

Just registering an interface doesn't enable marshaling, all argument types and return types must be marshalable too, i.e. not HANDLE or alike.

Regarding your second question, my hope is that you can answer yourself after reading the answer thus far. If not,

if you don't register an interface, you can't use it directly across COM contexts. If it derives from some registered interface, you can use that interface, such as the case of IDispatch-based interfaces.

However, very few interfaces are as general as IDispatch, so for any other base interface, you won't be able to use your derived interface's new methods.

In type libraries, if you don't register event dispinterfaces, then development tools (typically IDEs) won't be able to show you which events can be fired, or any event at all. The only other option is to implement the dispinterfaces by hand, if your programming language has that option, which requires documentation equivalent to the missing IDL in the first place.

One common extreme of this is to have all objects simply implement IDispatch and no other interface, but again this will hinder any effort a development tool might do towards method listing, code completion and/or argument choice (e.g. IntelliSense). Note that sometimes this is enough, such as when implementing a window.external object for IE's JScript, but it's a bit of lazyness when done in more general objects.

In general, if you're required very few extra effort to have interfaces registered, given you're already targeting COM, do so.

1 Comment

Thanks for these precisions Paulo. Just to be clear in our case VBA uses early-binding based on the metadata from the TLB I guess. As for COM registration things are not always simple, the main issue being non-admin users because AFAIK RegAsm is not able to register only in the current user's registry hive but registers globally which forces to create the registration information by hand. I've not found any simple solution to this issue, you must be careful to ensure .reg are kept in sync with current API.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.