Figure 2   IDispatchEx Members

Member Name
Description
GetDispID
Used to map a member name to its DISPID. This method can also be used to add members to the object. This method can be case-sensitive.
InvokeEx
Invokes a member of the object. The flag DISPATCH_CONSTRUCT indicates that the member is being used as a constructor. A this pointer may be passed to the member via the named parameter DISPID_THIS.
DeleteMemberByName
Removes a member from the object by name.
DeleteMemberByDispID
Removes a member from the object by DISPID.
GetMemberProperties
Describes the member's capabilities, whether it can fire events, is a property or method, and so on.
GetMemberName
Returns the name of the member mapped to a specified DISPID.
GetNextDispID
Used to enumerate the DISPIDs of the object's members. The caller can request all members or the default set (determined by the object).
GetNameSpaceParent
Allows the caller to get the IUnknown of the object's parent namespace.


Figure 3   IDispatchEx Features

Dynamic Composition
Adding (GetDispID) members to the object using the fdexNameEnsure option.
Deleting members by their name (DeleteMemberByName) or DISPID (DeleteMemberByDispID).
Member Introspection
Enumerating a member's DISPIDs (GetNextDispID).
Determining a member's properties (GetMemberProperties).
Looking up a member's name via the DISPID (GetMemberName).
Case-sensitivity when determining the DISPID of a member (GetDispID).
Member Invocation (InvokeEx)
Passing a this pointer to an invoked member via the named parameter DISPID_THIS.
Object construction via the DISPATCH_CONSTRUCT flag.
Namespaces
Querying the namespace of an object (GetNameSpaceParent), which is used in dynamic object models.


Figure 5   IDispatchExImpl Method Descriptions

Method
Description
GetEntryByName
Finds the CDynamicDispatchEntry for a given name. The search can be either case-sensitive or case-insensitive. Defaults to case-insensitive.
CreateNewEntry
Creates and populates a CDynamicDispatchEntry with the member attributes specified. Adds the new entry to the map classes. Determines what the next DISPID should be for a new dynamic member.
GetStartDispID
Finds the first nondeleted entry in the dynamic member map and retrieves its DISPID.
GetNextDispID
Gets the DISPID of the next valid (nondeleted) entry in the map.
GetVariantByDispID
Copies the contents of a dispatch entry with the supplied DISPID into an outbound VARIANT. This can be either a property or a function pointer (LPDISPATCH). If this is a static member (a member that is part of an interface that derives from IDispatchEx, one that is not added dynamically), then the call returns S_FALSE, telling the InvokeEx method to use the ITypeInfo interface to process the request.
SetVariantByDispID
Copies the contents of the supplied VARIANT into the dispatch entry specified by the DISPID. This also returns S_FALSE for an ITypeInfo item and processes as above.
AddTypeInfoDispIDs
Using the ITypeInfo interface pointer, enumerates the static members (ones that are not added dynamically) of the derived interface, creates a new dynamic dispatch entry, and adds it to the map classes. This method is not present if the macro IDISPATCHEX_NO_TYPEINFO is defined.
InvokeByDispID
This method is responsible for forwarding calls onto dynamic methods. This is done by calling Invoke on the LPDISPATCH housed in the dynamic entry's VARIANT with a DISPID of zero.
Get
This method is not supplied in either IDispatch or IDispatchEx. It is there to allow access to dynamic members via their name, as in objVar = objVar2.Get-("IndexCollection"). This method is not supplied if the IDISPATCHEX_NO_TYPEINFO macro is defined.
put_Set
See the description for Get. The funny naming convention is used because left-hand items (on the left side of the = operator) are considered "put" properties. This method allows the setting of an item by name, as in objVar.Set("Name") = "John Doe".
IDispatchExImpl
This is the default constructor, which initializes the ITypeInfo pointer to NULL and sets the starting point for dynamic DISPIDs. The ITypeInfo pointer is not a member of the class if the IDISPATCHEX_NO_TYPEINFO macro is defined.
~IDispatchExImpl
Walks the dynamic dispatch map, freeing the allocated CDynamicDispatchEntries. It releases the ITypeInfo pointer if declared and not NULL.
GetTypeInfoCount
This IDispatch method returns the number of ITypeInfo interfaces supported by this object. This is 1 or zero depending on whether the class has ITypeInfo support turned on.
GetTypeInfo
Fetches the ITypeInfo pointer of the derived class or NULL depending on whether type information is enabled.
GetIDsOfNames
Determines the DISPIDs for a list of member names by calling the IDispatchEx::GetDispID method for each name in the array.
Invoke
This call is forwarded to the IDispatchEx::InvokeEx method.
DeleteMemberByDispID
Marks a dynamic entry in the map as deleted. Any property held within the VARIANT of the entry is cleared (released for interface pointers). Calls to that DISPID will return DISP_E_MEMBERNOTFOUND until the member becomes valid again by the consumer adding it again.
DeleteMemberByName
Similar to DeleteMemberByDispID, but searches for the member by name instead of DISPID. This may or may not be case-sensitive.
GetDispID
Returns the DISPID of the specified member. If there is not a member with the given name and the consumer has indicated fdexNameEnsure, a new dynamic dispatch is created. If there is a deleted member that matches the name and the caller passed in fdexNameEnsure, the DISPID of the entry is returned. Otherwise, DISP_E_UNKNOWNNAME is returned and the outgoing DISPID is set to DISPID_UNKNOWN.
GetMemberName
Returns the name of the member that maps to the passed in DISPID.
GetMemberProperties
This method returns grfdexPropCanAll for any member DISPID.
GetNameSpaceParent
This method is not used by this class and returns E_NOTIMPL.
GetNextDispID
If the start DISPID specified by the caller is DISPID_STARTENUM, this function returns the first active DISPID in the map. Otherwise, it returns the next active DISPID in the map.
InvokeEx
Provides the processing of member calls. If the caller has specified DISPATCH_PROPERTYGET, InvokeEx calls the Get­VariantBy­- DispID helper method to copy the contents of the VARIANT held by the entry. If the caller specifies DISPATCH_PROPERTYPUT or DISPATCH_PROPERTYPUTREF, the method calls SetVariantBy­- DispID to copy the contents of the first VARIANT in the DISPPARAMS structure into the dynamic entry's VARIANT. If the caller specifies DISPATCH_METHOD, the call is forwarded to InvokeByDispID. If any of the three helper functions return S_FALSE, the InvokeEx call is forwarded to the ITypeInfo interface for processing.


Figure 6   CDynamicOnly

class CDynamicOnly : 
    public IDispatchExImpl<CDynamicOnly,IDynamicOnly,
        &IID_IDynamicOnly,&LIBID_TESTDISPATCHEXLib>,
    public ISupportErrorInfo,
    public CComObjectRoot,
    public CComCoClass<CDynamicOnly,&CLSID_DynamicOnly>
{
public:
    CDynamicOnly() {}
BEGIN_COM_MAP(CDynamicOnly)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(IDispatchEx)
    COM_INTERFACE_ENTRY(IDynamicOnly)
    COM_INTERFACE_ENTRY(ISupportErrorInfo)
END_COM_MAP()
DECLARE_NOT_AGGREGATABLE(CDynamicOnly) 
DECLARE_CLASSFACTORY_DISPATCHEX(CDynamicOnly)

DECLARE_REGISTRY_RESOURCEID(IDR_DynamicOnly)
// ISupportsErrorInfo
    STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

// IDynamicOnly
public:
};

Figure 7   DynamicOnly & TestDispatchEx


var objTest = new ActiveXObject("TestDispatchEx.TestDispatchEx");
var objDynamicOnly = new ActiveXObject("TestDispatchEx.DynamicOnly");

// Test the processing of properties by name
objTest.Set("Number") = 5;
alert(objTest.Get("number"));

// Add a new property to it
objTest.Foo = "Foo";
alert(objTest.Foo);
// Set and call the function pointer
// Change this value to one of the three methods below to see the
// effect
objTest.FuncPtr = TestFunc2;
objTest.FuncPtr("This is a cool function pointer of ");
alert(objTest.AnotherProp);
// Check to see if the prop is set
alert(objDynamicOnly.DynamicProp);

// Call the ITestDispatchEx members
alert(objTest.Number);
objTest.Square();
alert(objTest.Number);
objTest.Number = 4;
objTest.Square();
alert(objTest.Number);

function TestFunc2(strTest)
{
    objTest.AnotherProp = strTest + "TestFunc2";
    objDynamicOnly.DynamicProp = "A purely dynamic property";
}