Quite a while back I said that I'd write a post describing why we invented IDispatchEx. I already covered the part about using it to probe the stack for security reasons, but what other reasons did we implement that thing? The documentation is pretty much accurate, but sparse in places. (According to the doc, GetNameSpaceParent, uh, gets the namespace parent
.) I won't go into the flag-for-flag level of detail that the doc does well, but I thought I might describe what features motivated the various methods on IDispatchEx. The features are:
In most objects, the methods you've got are the ones you get. But JScript objects and some IE DOM objects can have arbitrary, user-defined properties and methods added to them at will. We call such objects "expando" objects.
IDispatchEx is not required to implement an expando object. You could have an implementation of IDispatch which added a new named field every time GetIdsOfNames was called, and everything would work just fine. What IDispatchEx gives you is the ability to pass in a flag (fdexNameEnsure) that specifically requests expando semantics. An object model could choose to, say, only create new fields when the flag was passed in and error out if an unknown name was passed in without the flag. This would then allow the caller to probe for the existence of a property without creating it as a side effect. (Those semantics are up to the callee though -- callers cannot assume that not passing the flag means no expando semantics!)
GetDispID method to access this feature.
JScript has a
delete operator which does not work at all like C++'s delete operator. Rather, JScript's delete operator removes an expando field from an object. It's pretty useless actually, because doing so does not free up any memory beyond simply clearing the field. Why not? Because implementers must ensure that if the property is re-added that it gets the same dispatch identifier the second time; someone might be caching the dispid. That means that the property bucket and its name has to be kept around, and worse, that every property bucket needs a flag that marks whether it's deleted or not. (The implementer must also ensure that property enumeration continues to work even if the enumerator -- see below -- is presently sitting on a property that was just deleted.) Use DeleteMemberByName or DeleteMemberByDispID in the unlikely event that you want to use this feature.
Visual Basic is case-insensitive, and
IDispatch was built by the VBA team back in the day, so they never implemented support for case-sensitive languages. JScript is case sensitive, so IDispatchEx lets you pass in flags (fdexNameCaseSensitive, fdexNameCaseInsensitive) on GetDispID that control the case-sensitivity of the lookup.
In the non-expando world, the property set of an object is stable, so there's little need to enumerate it. If you did need to enumerate it, you could always just look at the static typeinfo. But constructing dynamic typeinfos in a world with expando objects is difficult and expensive, so instead we added the ability to enumerate the valid dispatch identifiers (
GetNextDispID) and turn the identifier back into a name (GetMemberName). This is how JScript's for-in loop is implemented.
JScript functions may be called as constructors, which is a little weird and has
different semantics from calling a regular function. IDispatchEx adds a DISPATCH_CONSTRUCT flag that can be passed to InvokeEx to let the callee know that it is being invoked as a constructor.
JScript supports some pretty intense scope resolution semantics, well beyond the local/class/global
To make the
object semantics that you guys were arguing about the other day work, if you call a function that has a this argument, and it was invoked with the fdexImplicitParents flag, then we run up the GetNameSpaceParent chain to get all the parent scope objects and stuff them into the scope chain for the function invocation. Don't try this at home, kids; I barely remember how any of this stuff works.
Finally IDispatchEx has a GetMemberProperties method which is never used by JScript. Rather, it's used by the script debugger so that the debugger can tell you whether a given field is a method, property, etc.
So there you go -- a whole lot of new features that were difficult to shoehorn into IDispatch, so we implemented a new interface.