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 that objects are static. In other words, since objects do not change during run time, type information can fully describe them at compile time. Dynamic run-time models that are 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 makes the following assumption:
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:
This JScript code in the function test() does the following:
The full HTML code is:
<HTML> <BODY> <SCRIPT LANGUAGE="JScript"> function cat() { // 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 = cat; // 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 a dispatch pointer to the script engines from the browser. The control could then implement the function test():
<HTML> <BODY> <SCRIPT LANGUAGE="JScript"> function cat() { // 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, 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:
The code for the test control:
BOOL test(IDispatchEx *pdexScript) { HRESULT hr; VARIANT var; BSTR bstrName; DISPID dispid, putid; IDispatchEx *pdexObj; IDispatch *pdispObj, *pdispCat; DISPPARAMS dispparams, dispparamsNoArgs = {NULL, NULL, 0, 0}; // Get dispatch pointer for "cat" bstrName = SysAllocString(OLESTR("cat")); pdexScript->GetDispID(bstrName, 0, &dispid); SysFreeString(bstrName); pdexScript->InvokeEx(dispid, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispparamsNoArgs, &var, NULL, NULL); pdispCat = 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 "cat" dispatch pointer to element putid = DISPID_PROPERTYPUT; var.vt = VT_DISPATCH; var.pdispVal = pdispCat; 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; }