
IDispatchEx, an extension of the IDispatch interface, supports features appropriate for dynamic languages such as scripting languages. This section describes the IDispatchEx interface itself, the differences between IDispatch and IDispatchEx, and the rationale for the extensions. It is expected that readers are familiar with IDispatch and have access to the IDispatch documentation.
IDispatch was developed essentially for Microsoft® Visual Basic®. The primary limitation of IDispatch is that it assumes objects are static, that is, they do not change during run time and thus can be fully described at compile time by type information. Dynamic run time models as found in scripting languages such as Visual Basic Scripting Edition (VBScript) and JScript and object models such as Dynamic HTML require a more flexible interface.
IDispatchEx was developed to provide all the services of IDispatch as well as some extensions that are appropriate for more dynamic late-bound languages such as scripting languages. The additional features of IDispatchEx beyond those provided by IDispatch are:
Objects that support IDispatchEx might also support IDispatch for backward compatibility. The dynamic nature of objects that support IDispatchEx has a few implications for the IDispatch interface of those objects. For example, IDispatch assumes that "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." Since IDispatchEx allows the addition and deletion of members, the set of valid DISPIDs does not remain constant. However, IDispatchEx requires that the mapping between DISPID and member name remain constant. This means that if a member is deleted:
| DeleteMemberByDispID |
| DeleteMemberByName |
| GetDispID |
| GetMemberName |
| GetMemberProperties |
| GetNameSpaceParent |
| GetNextDispID |
| InvokeEx |
IDispatchEx is a subclass of IDispatch therefore, any object that supports the IDispatchEx interface also has implementations of the IDispatch methods:
These methods behave as defined by IDispatch except that they might provide additional support as defined by IDispatchEx. For example, IDispatchEx::InvokeEx should support the same values as IDispatch::Invoke (for example, DISPID_THIS, DISPATCH_CONSTRUCT). This allows an implementation of IDispatch::Invoke to simply delegate calls to IDispatchEx::InvokeEx. An object that supports both IDispatch and IDispatchEx might also choose to provide this additional support in IDispatch::Invoke.
HRESULT DeleteMemberByDispID(
DISPID id
);
Deletes a member by DISPID.
| S_OK | Success. |
| S_FALSE | Member exists but cannot be deleted. |
If the member is deleted, the DISPID needs to remain valid for GetNextDispID.
If a member with a given name is deleted and later a member with the same name is recreated, the DISPID should be the same. (Whether member names that differ only by case are the "same" is object-dependent.)
Example
BSTR bstrName; DISPID dispid; IDispatch *pdex; // Assign to pdex and bstrName pdex->GetDispID(bstrName, fdexNameCaseSensitive, &dispid); pdex->DeleteMemberByDispID(dispid);
HRESULT DeleteMemberByName( BSTR bstrName, DWORD grfdex );
Deletes a member by name.
| S_OK | Success. |
| S_FALSE | Member exists but cannot be deleted. |
| fdexNameCaseSensitive | Request that the name lookup be done in a case-sensitive manner. Can be ignored by object that does not support case-sensitive lookup. |
| fdexNameCaseInsensitive | Request that the name lookup be done in a case-insensitive manner. Can be ignored by object that does not support case-insensitive lookup. |
If the member is deleted, the DISPID needs to remain valid for GetNextDispID.
If a member with a given name is deleted and later a member with the same name is recreated, the DISPID should be the same. (Whether members that differ only by case are the "same" is object-dependent.)
Example
BSTR bstrName; IDispatch *pdex; // Assign to pdex and bstrName pdex->DeleteMemberByName(bstrName, fdexNameCaseSensitive);
HRESULT GetDispID( BSTR bstrName, DWORD grfdex, DISPID *pid );
Maps a single member name to its corresponding DISPID, which can then be used on subsequent calls to IDispatchEx::InvokeEx.
| S_OK | Success. |
| E_OUTOFMEMORY | Out of Memory. |
| DISP_E_UNKNOWNNAME | The name was not known. |
| fdexNameCaseSensitive | Request that the name lookup be done in a case-sensitive manner. May be ignored by object that does not support case-sensitive lookup. |
| fdexNameEnsure | Request that the member be created if it does not already exist. The new member should be created with the value VT_EMPTY. |
| fdexNameImplicit | Indicates that the caller is searching object(s) for a member of a particular name, when the base object is not explicitly specified. |
| fdexNameCaseInsensitive | Request that the name lookup be done in a case-insensitive manner. May be ignored by object that does not support case-insensitive lookup. |
GetDispID can be used instead of GetIDsOfNames to obtain the DISPID for a given member.
Because IDispatchEx allows the addition and deletion of members, the set of DISPIDs does not remain constant for the lifetime of an object.
The unused riid parameter in IDispatch::GetIDsOfNames has been removed.
Example
BSTR bstrName; DISPID dispid; IDispatch *pdex; // Assign to pdex and bstrName pdex->GetDispID(bstrName, fdexNameCaseSensitive, &dispid); // Use dispid
HRESULT GetMemberName( DISPID id, BSTR *pbstrName );
Retrieves the name of a member.
| S_OK | Success. |
| DISP_E_MEMBERNOTFOUND | The requested member does not exist. |
Example
HRESULT hr;
BSTR bstrName;
DISPID dispid;
IDispatch *pdex;
// Assign to pdex
hr = pdex->GetNextDispID(fdexEnumAll, DISPID_STARTENUM, &dispid);
while (hr != S_FALSE)
{
pdex->GetMemberName(dispid, &bstrName);
if (!wcscmp(bstrName, OLESTR("Bar")))
return TRUE;
SysFreeString(bstrName);
hr = pdexObj->GetNextDispID(fdexEnumAll, dispid, &dispid);
}
HRESULT GetMemberProperties( DISPID id, DWORD grfdexFetch, DWORD *pgrfdex );
Retrieves a member's properties.
| S_OK | Success. |
| DISP_E_MEMBERNOTFOUND | The requested member does not exist. |
| grfdexPropCanAll | Combines fdexPropCanGet, fdexPropCanPut, fdexPropCanPutRef, fdexPropCanCall, fdexPropCanConstruct and fdexPropCanSourceEvents. |
| grfdexPropCannotAll | Combines fdexPropCannotGet, fdexPropCannotPut, fdexPropCannotPutRef, fdexPropCannotCall, fdexPropCannotConstruct and fdexPropCannotSourceEvents. |
| grfdexPropExtraAll | Combines fdexPropNoSideEffects and fdexPropDynamicType. |
| grfdexPropAll | Combines grfdexPropCanAll, grfdexPropCannotAll and grfdexPropExtraAll. |
| fdexPropCanGet | The member can be obtained using DISPATCH_PROPERTYGET. |
| fdexPropCannotGet | The member cannot be obtained using DISPATCH_PROPERTYGET. |
| fdexPropCanPut | The member can be set using DISPATCH_PROPERTYPUT. |
| fdexPropCannotPut | The member cannot be set using DISPATCH_PROPERTYPUT. |
| fdexPropCanPutRef | The member can be set using DISPATCH_PROPERTYPUTREF. |
| fdexPropCannotPutRef | The member cannot be set using DISPATCH_PROPERTYPUTREF. |
| fdexPropNoSideEffects | The member does not have any side effects. For example, a debugger could safely get/set/call this member without changing the state of the script being debugged. |
| fdexPropDynamicType | The member is dynamic and can change during the lifetime of the object. |
| fdexPropCanCall | The member can be called as a method using DISPATCH_METHOD. |
| fdexPropCannotCall | The member cannot be called as a method using DISPATCH_METHOD. |
| fdexPropCanConstruct | The member can be called as a constructor using DISPATCH_CONSTRUCT. |
| fdexPropCannotConstruct | The member cannot be called as a constructor using DISPATCH_CONSTRUCT. |
| fdexPropCanSourceEvents | The member can fire events. |
| fdexPropCannotSourceEvents | The member cannot fire events. |
Example
BSTR bstrName;
DISPID dispid;
IDispatch *pdex;
DWORD dwProps;
// Assign to pdex and bstrName
pdex->GetDispID(bstrName, fdexNameCaseSensitive, &dispid);
pdex->GetMemberProperties(dispid, grfdexPropAll, &dwProps);
if (dwProps & fdexPropCanPut)
// Assign to member
HRESULT GetNameSpaceParent( IUnknown **ppunk );
Retrieves the interface for the namespace parent of an object.
HRESULT GetNextDispID( DWORD grfdex, DISPID id, DISPID *pid );
Enumerates the members of the object.
| S_OK | Success. |
| S_FALSE | Enumeration is done. |
| fdexEnumDefault | Request that the object enumerate the default elements. The object is allowed to enumerate any set of elements. |
| fdexEnumAll | Request that the object enumerate all of the elements. The object is allowed to enumerate any set of elements. |
If a member is deleted by DeleteMemberByName or DeleteMemberByDispID, the DISPID needs to remain valid for GetNextDispID.
Example
HRESULT hr;
BSTR bstrName;
DISPID dispid;
IDispatch *pdex;
// Assign to pdex
hr = pdex->GetNextDispID(fdexEnumAll, DISPID_STARTENUM, &dispid);
while (hr != S_FALSE)
{
pdex->GetMemberName(dispid, &bstrName);
if (!wcscmp(bstrName, OLESTR("Bar")))
return TRUE;
SysFreeString(bstrName);
hr = pdexObj->GetNextDispID(fdexEnumAll, dispid, &dispid);
}
HRESULT InvokeEx( DISPID id, LCID lcid, WORD wFlags, DISPARAMS *pdp, VARIANT *pVarRes, EXCEPINFO *pei, IServiceProvider *pspCaller );
Provides access to properties and methods exposed by an IDispatchEx object.
| S_OK | Success. |
| DISP_E_BADPARAMCOUNT | The number of elements provided to DISPPARAMS is different from the number of arguments accepted by the method or property. |
| DISP_E_BADVARTYPE | One of the arguments in rgvarg is not a valid variant type. |
| DISP_E_EXCEPTION | The application needs to raise an exception. In this case, the structure passed in pei should be filled in. |
| DISP_E_MEMBERNOTFOUND | The requested member does not exist, or the call to InvokeEx tried to set the value of a read-only property. |
| DISP_E_NONAMEDARGS | This implementation of IDispatch does not support named arguments. |
| DISP_E_OVERFLOW | One of the arguments in rgvarg could not be coerced to the specified type. |
| DISP_E_PARAMNOTFOUND | One of the parameter DISPIDs does not correspond to a parameter on the method. |
| DISP_E_TYPEMISMATCH | One or more of the arguments could not be coerced. |
| DISP_E_UNKNOWNLCID | The member being invoked interprets string arguments according to the LCID, and the LCID is not recognized. If the LCID is not needed to interpret arguments, this error should not be returned. |
| DISP_E_PARAMNOTOPTIONAL | A required parameter was omitted. |
| DISPATCH_METHOD | The member is invoked as a method. If a property has the same name, both this and the DISPATCH_PROPERTYGET flag may be set (defined by IDispatch). | |||||||
| DISPATCH_PROPERTYGET | The member is retrieved as a property or data member (defined by IDispatch). | |||||||
| DISPATCH_PROPERTYPUT | The member is changed as a property or data member (defined by IDispatch). | |||||||
| DISPATCH_PROPERTYPUTREF | The member is changed by a reference assignment, rather than a value assignment. This flag is valid only when the property accepts a reference to an object (defined by IDispatch). | |||||||
| DISPATCH_CONSTRUCT | The member is being used as a constructor (this is a new value defined by IDispatchEx). The legal values for wFlags are:
|
IDispatchEx::InvokeEx provides all of the same features as IDispatch::Invoke and adds a few extensions:
| DISPATCH_CONSTRUCT | Indicates that the item is being used as a constructor. |
| pspCaller | The pspCaller allows the object access to services provided by the caller. Specific services may be handled by the caller itself or delegated to callers further up the call chain. For example, if a script engine inside a browser makes an InvokeEx call to an external object, the object can follow the pspCaller chain to obtain services from the script engine or browser. (Note that the call chain is not the same as the creation chain a.k.a. container chain or site chain the creation chain may be available through some other mechanism such as IObjectWithSite.) |
| "this" pointer | When DISPATCH_METHOD is set in wFlags, there may be a "named parameter" for the "this" value. The DISPID will be DISPID_THIS and it must be the first named parameter.
The unused riid parameter in IDispatch::Invoke has been removed. The puArgArr parameter in IDispatch::Invoke has been removed. |
See the IDispatch::Invoke documentation for the following examples:
Example
VARIANT var;
BSTR bstrName;
DISPID dispid;
IDispatch *pdex;
DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
// Assign to pdex and bstrName
pdex->GetDispID(bstrName, fdexNameCaseSensitive, &dispid);
pdex->InvokeEx(dispid, LOCALE_USER_DEFAULT,
DISPATCH_PROPERTYGET, &dispparamsNoArgs,
&var, NULL, NULL);
This JScript code in the function test() does the following:
<HTML>
<BODY>
<SCRIPT LANGUAGE="JScript">
function foo()
{
// Create new element and assign the value 10
this.Bar = 10;
}
function test()
{
// Construct new object
Obj = new Object();
// Create new element and assign function pointer
Obj.Elem = foo;
// Call Elem method ("this" == Obj)
Obj.Elem();
// Obj.Bar now exists
}
test();
</SCRIPT>
</BODY>
</HTML>
A control placed on this same web page could obtain from the browser a dispatch pointer to the script engines. The control could then implement the function test():
<HTML>
<BODY>
<SCRIPT LANGUAGE="JScript">
function foo()
{
// Create new element and assign the value 10
this.Bar = 10;
}
</SCRIPT>
<OBJECT ID="test" <CLASSID="CLSID:9417DB5D-FA2A-11D0-8CB3-00C04FC2B085">>
</OBJECT>
</BODY>
</HTML>
Code from the control "test" that does the same thing as the JScript function test(). Note that these dispatch calls are made into the 'running' JScript engine and change the state of the engine:
BOOL test(IDispatchEx *pdexScript)
{
HRESULT hr;
VARIANT var;
BSTR bstrName;
DISPID dispid, putid;
IDispatchEx *pdexObj;
IDispatch *pdispObj, *pdispFoo;
DISPPARAMS dispparams, dispparamsNoArgs = {NULL, NULL, 0, 0};
// Get dispatch pointer for "foo"
bstrName = SysAllocString(OLESTR("foo"));
pdexScript->GetDispID(bstrName, 0, &dispid);
SysFreeString(bstrName);
pdexScript->InvokeEx(dispid, LOCALE_USER_DEFAULT,
DISPATCH_PROPERTYGET, &dispparamsNoArgs,
&var, NULL, NULL);
pdispFoo = var.pdispVal;
// Create object by calling "Object" constructor
bstrName = SysAllocString(OLESTR("Object"));
pdexScript->GetDispID(bstrName, 0, &dispid);
SysFreeString(bstrName);
pdexScript->InvokeEx(dispid, LOCALE_USER_DEFAULT,
DISPATCH_CONSTRUCT, &dispparamsNoArgs,
&var, NULL, NULL);
pdispObj = var.pdispVal;
pdispObj->QueryInterface(IID_IDispatchEx, (void **)&pdexObj);
// Create new element in object
bstrName = SysAllocString(OLESTR("Elem"));
pdexObj->GetDispID(bstrName, fdexNameEnsure, &dispid);
SysFreeString(bstrName);
// Assign "foo" dispatch pointer to element
putid = DISPID_PROPERTYPUT;
var.vt = VT_DISPATCH;
var.pdispVal = pdispFoo;
dispparams.rgvarg = &var;
dispparams.rgdispidNamedArgs = &putid;
dispparams.cArgs = 1;
dispparams.cNamedArgs = 1;
pdexObj->InvokeEx(dispid, LOCALE_USER_DEFAULT,
DISPATCH_PROPERTYPUTREF, &dispparams,
NULL, NULL, NULL);
// Invoke method with "this" pointer
putid = DISPID_THIS;
var.vt = VT_DISPATCH;
var.pdispVal = pdispObj;
dispparams.rgvarg = &var;
dispparams.rgdispidNamedArgs = &putid;
dispparams.cArgs = 1;
dispparams.cNamedArgs = 1;
pdexObj->InvokeEx(dispid, LOCALE_USER_DEFAULT,
DISPATCH_METHOD, &dispparams,
NULL, NULL, NULL);
// Confirm that new element "Bar" is in object
hr = pdexObj->GetNextDispID(fdexEnumAll, DISPID_STARTENUM, &dispid);
while (hr != S_FALSE)
{
pdexObj->GetMemberName(dispid, &bstrName);
if (!wcscmp(bstrName, OLESTR("Bar")))
return TRUE;
SysFreeString(bstrName);
hr = pdexObj->GetNextDispID(fdexEnumAll, dispid, &dispid);
}
return FALSE;
}
© 1997 Microsoft Corporation. All rights reserved. Terms of Use.