Why does WScript.ConnectObject not always work?

I've had a tiny handful of entries in my VBScript quiz. I know from my logs that 2600+ people have read it. Come on people, there are fabulous prizes at stake here! Go for it!

I'm going to keep the contest open until Friday morning, at which point I'll start posting answers and analysis.

****************************************************

Earlier today someone asked me why it is that sometimes

WScript.ConnectObject does not connect an object to an event sink even if the object sources events. That reminded me that back in June I said that I'd explain this at some point, and I never did.

WScript.ConnectObject

only works if the object implements IProvideClassInfo or IProvideMultipleClassInfo. Not all objects do.

Why?

An object can implement multiple interfaces both incoming and outgoing. Incoming interfaces are the interfaces you call an object on. Outgoing interfaces are interfaces that they call you on. That's how events work: you tell the object that you want a particular method to be called on a particular outgoing interface when a particular event occurs.

Each interface supported by an object, whether incoming or outgoing, can have its own interface typeinfo describing it. For a given object all these interfaces are "summarized" by one "parent" type info called the "coclass type info". Once you have the coclass type info, you can figure out everything about the object's incoming and outgoing interfaces.

Clearly, in order to build an event sink for an object, you need to know what events the event source is going to source! You need the outgoing interface typeinfo, or, equivalently, the coclass typeinfo.

The flaw in the design of the automation interfaces is this: given an

IDispatch object, there is no way to discover what its coclass typeinfo is! You can get a type info for the dispatch interface -- that is, the incoming call interface -- by calling GetTypeInfo. But there is no way to get the coclass type info, and therefore no way to determine what the outgoing interfaces are on the object, and therefore no way to bind its events.

When that omission was realized,

IProvideClassInfo was invented. (And for aggregate objects, which may have aggregated multiple coclasses, IProvideMultipleClassInfo was invented.) IProvideClassInfo returns the coclass type info. That's why WScript.ConnectObject requires IProvideClassInfo -- it just has the object and knows nothing about its class.

You may then wonder how it is that

WScript.CreateObject does event binding on objects that do not implement IProvideClassInfo. That's easy -- WScript.CreateObject is given enough information to find the coclass type info. WScript.CreateInstance is passed the progid. It uses that to look up the class id in the registry. It obtains the incoming interface typeinfo from the newly-created dispatch object, obtains the typelibrary from the typeinfo, and searchs the typelibrary for a coclass type info which matches the class id.

This also explains how it is that Visual Basic can do event binding on objects which do not implement

IProvideClassInfo. VB has compile-time information about the progid of the object and can look up the outgoing interface in the same way.

We considered creating another version of WScript.ConnectObject which took a classid or progid, but we decided that it wouldn't be useful in very many situations, and would be confusing and hard to document well.