IDispatchEx

IDispatchEx


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.

arrowg.gifDescription

arrowg.gifIDispatchEx Methods

arrowg.gifExamples

Description

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:

IDispatchEx Methods

DeleteMemberByDispID
DeleteMemberByName
GetDispID
GetMemberName
GetMemberProperties
GetNameSpaceParent
GetNextDispID
InvokeEx

IDispatch methods

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.

IDispatchEx::DeleteMemberByDispID
HRESULT DeleteMemberByDispID(
    DISPID id
);

Deletes a member by DISPID.

id
Member identifier. Use GetDispID or GetNextDispID to obtain the dispatch identifier.

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);
IDispatchEx::DeleteMemberByName
HRESULT DeleteMemberByName(
   BSTR bstrName,
   DWORD grfdex
);

Deletes a member by name.

bstrName
Name of member to be deleted.
grfdex
Determines if the member name is case sensitive. This can be one of the following values:
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);
IDispatchEx::GetDispID
 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.

bstrName
Passed in name to be mapped.
grfdex
Determines the options for obtaining the member identifier. This can be a combination of the following values:
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.
pid
Pointer to caller-allocated location to receive DISPID result. If an error occurs, pid contains DISPID_UNKNOWN.

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
IDispatchEx::GetMemberName
HRESULT GetMemberName(
   DISPID id,
   BSTR *pbstrName
);

Retrieves the name of a member.

id
Identifies the member. Use GetDispID or GetNextDispID to obtain the dispatch identifier.
pbstrName
Address of a BSTR that receives the name of the member. The calling application is responsible for freeing this value.

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);
}
IDispatchEx::GetMemberProperties
HRESULT GetMemberProperties(
   DISPID id,
   DWORD grfdexFetch,
   DWORD *pgrfdex
);

Retrieves a member's properties.

id
Identifies the member. Use GetDispID or GetNextDispID to obtain the dispatch identifier.
grfdexFetch
Determines which properties to retrieve. This can be a combination of the values listed under pgrfdex and/or a combination of the following values:
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.
pgrfdex
Address of a DWORD that receives the requested properties. This can be a combination of the following values:
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
IDispatchEx::GetNameSpaceParent
HRESULT GetNameSpaceParent(
   IUnknown **ppunk
);

Retrieves the interface for the namespace parent of an object.

ppunk
Address of an IUnknown interface pointer that receives the namespace parent's interface.
IDispatchEx::GetNextDispID
HRESULT GetNextDispID(
   DWORD grfdex,
   DISPID id,
   DISPID *pid
);

Enumerates the members of the object.

grfdex
Determines which set of item will be enumerated. This can be a combination of the following values:
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.
id
Identifies the current member. GetNextDispID will retrieve the item in the enumeration after this one. Use GetDispID or a previous call to GetNextDispID to obtain this identifier. Use the DISPID_STARTENUM value to obtain the first identifier of the first item.
pid
Address of a DISPID variable that receives the identifier of the next item in the enumeration.

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);
   }
IDispatchEx::InvokeEx
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.

id
Identifies the member. Use GetDispID or GetNextDispID to obtain the dispatch identifier.
lcid
The locale context in which to interpret arguments. The lcid is passed to InvokeEx to allow the object to interpret its arguments specific to a locale.
wFlags
Flags describing the context of the InvokeEx call:
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:
DISPATCH_PROPERTYGET
DISPATCH_METHOD
DISPATCH_PROPERTYGET | DISPATCH_METHOD
DISPATCH_PROPERTYPUT
DISPATCH_PROPERTYPUTREF
DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF
DISPATCH_CONSTRUCT
pdp
Pointer to a structure containing an array of arguments, an array of argument DISPIDs for named arguments, and counts for the number of elements in the arrays. See the IDispatch documentation for a full description of the DISPPARAMS structure.
pVarRes
Pointer to the location where the result is to be stored, or Null if the caller expects no result. This argument is ignored if DISPATCH_PROPERTYPUT or DISPATCH_PROPERTYPUTREF is specified.
pei
Pointer to a structure that contains exception information. This structure should be filled in if DISP_E_EXCEPTION is returned. Can be Null. See the IDispatch documentation for a full description of the EXCEPINFO structure.
pspCaller
Pointer to a service provider object supplied by the caller which allows the object to obtain services from the caller. Can be Null.

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);

Examples

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.