This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.


MIND


This article assumes you're familiar with COM and Visual C++
Download the code (42KB)

Dynamic Object Composition Using IDispatchEx
Joe Graf

Sometimes a predefined COM component just doesn't fit your needs. With IDispatchEx, you can complete your interfaces at runtime.
Most Web application developers are familiar with the document object model of their favorite browser. These object models are dynamically built from the elements in the Web pages themselves, giving the programmer full access to any edit boxes or radio buttons on the page. This gives the programmer a way to validate form input, calculate new field values from entered values, or generate new HTML based on the objects and their content (using DHTML). These powerful features are possible due to the runtime composition of the object model and late binding of the script code to the objects.
      Late binding of objects is not new to the Internet. It has been around since the inception of Visual Basic® and OLE Automation (now just known as Automation). Automation provides a mechanism for coding to a set of object semantics without having to know the calling convention or the exact location of the method in an object; these are queried at runtime. With early binding languages such as C/C++, all of the object's methods and properties must be known at compile time.
      Scripting languages take the concept of late binding to its extreme: properties or methods of an object are queried just before they are executed. If the object does not support the requested member, an error is generated (Visual Basic returns errors, too). JScript® even allows members to be added to an object at runtime. This means that if a property does not exist, it can be added so that the line of source is not in error. You can also add JScript functions as methods to an object to build object-oriented script code. You can take advantage of these dynamic object features using your favorite language—except if your favorite is Visual Basic and VBScript.

Why Dynamic Objects?
      Why build objects at runtime? First, this method of object creation and composition provides a very elegant mechanism for creating object hierarchies that can only be determined at runtime, like the forms contained on an HTML page. Second, it provides a more concise method of accessing child objects, such as


 var objChild = objRoot.Child1;
instead of

 var objChild = objRoot.Items("Child1");
      It also allows the consumer of an object to add dynamic data or behavior to the object that is context-specific or determined at runtime. For instance, in this code

 function AddProps(objItem)
 {
     if( objItem.Type == 1 )
     {
         objItem.NewProp = "Object Type 1";
     }
     else
     {
         objItem.AnotherNewProp = nGlobalUserCount * 4;
     }
 }
      NewProp did not previously exist for the object and is of type string, and AnotherNewProp did not exist and is of type integer. The object programmer does not know about these properties, but the object carries them around to be used as the script programmer sees fit.
      If the script programmer has access to a purely dynamic object, he can build objects out of script code that can then be passed to other objects or scripts for use.

 function Test()
 {
     alert(this.Name);
 }
 
 // Create a purely dynamic object
 var objNew = new Object;
 // Give the new object a property
 objNew.Name = "My name is John Doe";
 // Assign a function pointer
 objNew.Show = Test;
 // Now call the display function
 objNew.Show();
This code—add it to an HTML page to see it in action—creates a new object that is empty of properties or methods. It assigns the text "My name is John Doe" to the property Name, creating the property in the process. The Show method is added by assigning the function Test to it. This is essentially the same thing as assigning a function pointer. For the scripting engines, a function pointer is an LPDISPATCH that uses the DISPID of zero to invoke it. Notice this.Name being used as an argument from within the function Test. This dereferences the object to which the Test method is assigned, giving it the dynamically added property Name. If you call the Test function directly, the message box contains the text "undefined" since there is no valid this pointer.
      Now that I've shown you why you would want to use dynamic objects, and some examples of using them within JScript, let's take the next step and discover how to create COM objects that support dynamic composition.

IDispatch and IDispatchEx
      The original COM interface for performing late binding to objects is IDispatch. This interface is the foundation of Automation, as it lets a consumer of an object determine at runtime what members and their corresponding parameters are available for use. Although it's a powerful interface, it lacks some of the features required by scripting languages such as JScript. For example, IDispatch does not support dynamically created members. According to the IDispatch documentation, "The member and parameter DISPIDs must remain constant for the lifetime of the object. This allows a client to obtain DISPIDs once, and cache them for later use."
      IDispatch also makes no provision for case-sensitive access to members, as opposed to JScript. The interface also does not allow for the enumeration of members so that a consumer can "discover" what the object supports.
      To remedy these deficiencies, Microsoft created the IDispatchEx interface. This interface allows for enumeration of members, the addition/deletion of members, and case-sensitive calling of members. IDispatchEx inherits from IDispatch so that consumers that do not take advantage of the dynamic nature of the objects can still make calls on the object.
      IDispatchEx was created to support JScript within Microsoft® Internet Explorer. To be usable by VBScript and other scripting languages, IDispatchEx is derived from IDispatch. Note that VBScript will call QueryInterface for the IDispatchEx interface, but it will not take advantage of the dynamic object capabilities. Figure 1 shows the interface inheritance hierarchy for IDispatchEx, including the methods required for each interface.

Figure 1: IDispatchEx Hierarchy
      Figure 1: IDispatchEx Hierarchy

      As you can see from the interface definition, a number of new features are available with this interface. IDispatch is not as flexible in its processing of DISPIDs as IDispatchEx. The ability to iterate through the members of an object, determine a single DISPID from a name, or determine the name of a member by DISPID makes it easier for a consumer of an object to determine its capabilities. Of course, you can do the same things using the IDispatch interface, but it requires getting the ITypeInfo interface and using that to iterate through the members. The IDispatch approach works, but is more cumbersome. Aside from the member discovery features of IDispatchEx (see Figure 2 for a description of each member), it also implements a number of additional features as described in Figure 3.
      There are a few caveats surrounding the deletion of members. Though the IDispatchEx interface allows an object's set of members to be dynamic, there is a contract between a given name and its corresponding DISPID: once a name has been assigned a DISPID, it can never change or be reused by a member with a different name. This means that a deleted member's DISPID cannot be assigned to a newly added member. It also means that if the member is added to the object again, the DISPID previously assigned to it must be reissued. These requirements are due to the possibility of a consumer caching DISPIDs via the IDispatch interface (commonly done for performance reasons).
      Another consideration is that all of the DISPIDs passed out via the IDispatchEx interface must be valid for the IDispatch interface of that object. The easiest way to ensure this is to implement the IDispatch interface as part of your IDispatchEx implementation. Next I'll describe a template-based class that does just that.

IDispatchExImpl
      So far, I've focused on what features COM objects and their consumers gain by supporting IDispatchEx. But how do you actually implement a COM object that supports IDispatchEx? Rather than just implement a single COM object, a template-based class that can be reused by any COM object is even better. IDispatchExImpl provides just such functionality (see Figure 4).

Figure 4: IDispatchExImpl Hierarchy
      Figure 4: IDispatchExImpl Hierarchy

      IDispatchExImpl implements both the IDispatch and IDispatchEx interfaces. It uses the CDynamicDispatchEntry class to hold the state information for a given dispatch member. The CDynamicDispatchEntry is aggregated in three MFC map classes for fast lookups of member entries. One of the maps provides case-sensitive lookups, another case-insensitive lookups, and the third provides for lookups based on DISPIDs. DISPIDs start from zero, although this default is easily changed by defining the macro DISPID_START n, where n is the DISPID to start with, before including IDispatchExImpl.h.
       Figure 5 lists all of the IDispatchExImpl members. The next available DISPID is contained in the member m_dispidNext and is used by the CreateNewEntry method to determine the DISPID of the new entry. Entries deleted by consumers are not actually removed from the map, but are marked as deleted. This allows for the revival of the same member later if the consumer desires (as required by the interface contract). Calls to the IDispatch members GetIDsOfNames and Invoke are forwarded under the covers to the IDispatchEx implementations of GetDispID and InvokeEx. This class is also thread-safe for use in freethreaded applications.
      IDispatchExImpl is implemented as a template class. This is in keeping with the tradition of ATL. Yes, MFC is used instead of STL for the map classes as some would prefer, but this is a matter of personal taste. Beyond just being ATL-like, implementing the class as a template has some real benefits: the ability to support interface inheritance, such as interfaces that are derived from IDispatchEx and contain their own members. The IDispatchImpl provided by ATL does this for dual COM interfaces and was the source of inspiration for this feature. By passing type information as template parameters, the class is able to support a derived class's members as dynamic dispatch entries. Any dynamic dispatch entries that are from a child interface are invoked using the ITypeInfo:: Invoke method. This allows the ITypeInfo interface to validate parameters and call the correct function in the vtable rather than having to hand-code this processing, which would be very complex. If you delve into ATL's IDispatchImpl, you will find that it does the same thing via CComTypeInfoHolder.
      To have the type information loaded into the object at creation time, a custom class factory (CDispatchExClassFactory) class is used. Unlike ATL's IDispatchImpl class, which loads type information the first time its CComTypeInfoHolder pointer is accessed, this class is preloaded to avoid using extra cycles to check whether the type information is valid. This is done automatically in the overloaded CreateInstance of the CDispatchExClassFactory by calling TypeInfoInit on the newly created object. The type information is read into an intermediate ITypeLib interface from the registry using the parameters passed into the template.
      From ITypeLib, the ITypeInfo interface is requested using the IID of the COM object's derived interface. With the ITypeInfo interface in hand, an internal helper function, AddTypeInfoDispIDs, is called. This method iterates through all of the members in ITypeInfo, adding them to the dynamic dispatch maps so that they can be called via the IDispatchEx interface. This process also sets the starting DISPID for dynamic members to be one greater than the largest DISPID in the ITypeInfo interface, preventing DISPID collisions between static (part of an interface that derives from IDispatchEx) and dynamic members. However, if you want a pure IDispatchEx COM object, just declare the macro IDISPATCHEX_NO_TYPEINFO and all type information will be compiled out of the class. An object of this type will have no default properties or methods.

Using IDispatchExImpl
      Let's put IDispatchExImpl into action by implementing a pure IDispatchEx object, an object that inherits from IDispatchEx, and the JScript code that manipulates them.
      The sample project TestDispatchEx contains two COM objects: CDynamicOnly is a pure dynamic object, and CTestDispatchEx inherits from IDispatchEx. These two objects implement the ITestDispatchEx and IDynamicOnly interfaces, respectively. IDynamicOnly derives from IDispatchEx, providing no custom methods. The second interface adds some custom members. You can download the project and source files from the link at the top of this article.
      The CDynamicOnly COM object is very tiny. All of its implementation details are fulfilled by the template classes it inherits from, the exception being the ATL Wizard-generated ISupportsErrorInfo::InterfaceSupportsErrorInfo method. This method is boilerplate code that the wizard spits out. Figure 6 shows the complete definition for the class.
      Before the IDispatchExImpl.h file is included, the macro IDISPATCHEX_NO_TYPEINFO is defined to strip out any type information support. The DECLARE_CLASSFACTORY_DISPATCHEX macro specifies that the CDispatchExClassFactory is used to create new instances of CDynamicOnly. Because this object is purely dynamic, it is a good way to see the IDispatchEx interface in action. The following JScript code exercises the IDispatchEx interface:


 var objDynamicOnly = new ActiveXObject("TestDispatchEx.DynamicOnly");
 // Add a dynamic property named LastName
 objDynamicOnly.LastName = "Doe";
 // Add a lower case property
 objDynamicOnly.firstname = "John";
 // Add a dynamic method
 objDynamicOnly.Show = Display;
 // Call the object's method now
 objDynamicOnly.Show();

 function Display()
 {
     alert(objDynamicOnly.LastName + ", " + objDynamicOnly.firstname);
     // This should display "Doe, undefined"
     alert(objDynamicOnly.LastName + ", " + objDynamicOnly.FirstName);
 }
      This code creates an instance of the DynamicOnly object. Two properties are then supplied: one is in mixed case and the other is all lowercase. A method is added to the object by means of a function pointer. The newly added function is executed to illustrate calling a JScript function from within a C++ COM object. This method displays the two properties, last name followed by first. There is an intentional mistake on the second alert call to show what happens when a member cannot be found due to case inconsistencies.
      The second COM object sample provides the same functionality as DynamicOnly, and also adds its own custom methods to provide an example of calls being forwarded to ITypeInfo.

Inheritance from IDispatchEx
      In real-world use, a COM object that supports IDispatchEx is going to have custom methods that it wants to expose through that interface. CTestDispatchEx is an example of an object doing that. It has one property that holds a numeric value and supplies a method that squares that property. A Get method and Set property are also provided for named access to members (see Figure 5 for a complete description). The IDL for this interface is:


 interface ITestDispatchEx : IDispatchEx
 {
     [id(1), helpstring("method Square")]
     HRESULT Square(void);
     [propget, id(2), helpstring("property Number")]
     HRESULT Number([out, retval] VARIANT *pVal);
     [propput, id(2), helpstring("property Number")]
     HRESULT Number([in] VARIANT newVal);
     [id(3), helpstring("method Get")]
     HRESULT Get([in] BSTR bstrVarName,[out,retval] VARIANT* pvtVar);
     [propput, id(4), helpstring("method Set")]
     HRESULT Set([in] BSTR bstrVarName,[in] VARIANT vtVar);
 };
      This object illustrates the use of static and dynamic members on the same object. It also provides an example of using the ITypeInfo interface to forward calls to the correct function in the vtable. This COM object uses CDispatchExClassFactory to automatically process the type information at creation time. If the IDispatchExImpl class is unable to load the type information, the object will fail to create.
      If you are using this implementation of IDispatchEx in your object and you internally use CComObject::CreateInstance to create the object, you need to call the TypeInfoInit method or the code will assert when the ITypeInfo pointer is accessed. This is necessary since the CComObject::CreateInstance bypasses the class factory of the object. The same holds true if you create the object with the new operator.
      When the script code invokes one of the static methods, the call is forwarded to the ITypeInfo pointer. Here is a snippet from the IDispatchExImpl::InvokeEx method showing how this is done:

 // Forward to the type info
 hr = m_pTypeInfo->Invoke(this,dispid,wFlags,
	pDispParams,pvtResult,
     pExcepInfo,&nErrArg);
Notice the first parameter is the object's this pointer. The parameter provides to the ITypeInfo a pointer to the vtable of the object that is being called. The ITypeInfo interface does the offset calculation and invokes the function at the correct vtable index using the specified pointer. With some clever coding, that pointer need not be an object's this pointer. It could be an array of function pointers that was built dynamically, though the code to do this is tricky. However, doing this would allow a programmer to aggregate methods of multiple objects and expose them as one object. The code to accomplish this is beyond the scope of this article.
      Now that we can forward the calls to the ITypeInfo interface, let's look at some sample JScript code that uses this feature. The code in Figure 7 shows both DynamicOnly and TestDispatchEx objects. Named properties on the TestDispatchEx object are modified using the Get and put_Set custom methods. Note that the methods are case-insensitive for compatibility with VBScript. Some dynamic properties are added to the objects, including the setting of a function pointer. When this function is invoked, more dynamic properties are added, which are displayed upon return from the function. The call to the function pointer illustrates the passing of parameters to a dynamic method. Finally, the static members of the TestDispatchEx object are called.
      Run this sample in the browser and set a breakpoint in the InvokeEx method. Interrogate the method parameters to see how the scripting engine packages everything for you. Set breakpoints in CTestDispatchEx to see the forwarding of the calls in action. By experimenting with these samples provided with the project, you can explore the range of features that IDispatchEx gives you.

Conclusion
      I've covered what the IDispatchEx interface gives you, created a template class allowing for easy support of this interface in your COM objects, and shown how the script engine interacts with this interface. This template class effectively yields a dual interface—custom, IDispatch, and IDispatchEx—that allows for dynamic object composition. You can build dynamic objects (and object hierarchies) in your C++ code, then expose them via IDispatch or IDispatchEx. It's another useful tool to add to your Automation utility belt.

MSDN
http://msdn.microsoft.com/library/partbook/dhtml/theruntimeengineforserverscriptlets.htm
and
http://msdn.microsoft.com/library/techart/msdn_xmlscripts.htm


From the October 1999 issue of Microsoft Internet Developer.