Handling Design-Time Changes

At design time, an end user can add properties and methods to an ActiveX designer or remove them from the designer. When this occurs, your designer code must change the existing type information and lay out the new type information.

The action you have to take depends on the kind of object you're working with. To change the type information for a VTBL interface, you treat properties and methods as functions. Use the AddFuncDesc method of the ICreateTypeInfo interface to add a property or method, and DeleteFuncDesc (or DeleteFuncDescByMemId) in the ICreateTypeInfo2 interface to remove a property or method.

When you change the type information for a dispatch interface, you treat properties as variables and methods as functions. To add and remove properties, use the AddVarDesc and DeleteVarDesc (or DeleteVarDescByMemId) methods. To add and remove methods, use the AddFuncDesc and DeleteFuncDesc (or DeleteFuncDescByMemId) methods.

After adding or removing the properties or methods, call ITypeInfo::LayOut, which assigns a dispatch identifier (DISPID) and a VTBL offset to the new subobject. A brief discussion of these methods follows; see the Automation Programmer's Reference in the Platform Software Development Kit (SDK) for complete details.

Whenever your code adds or deletes a function or variable descriptor, Automation triggers an ITypeChangeEvents notification to the host. This notification makes the host aware that it should call the ActiveX designer's GetDynamicClassInfo method to get the new type information.

Adding Type Information

To add type information, use the AddFuncDesc or AddVarDesc method of the ICreateTypeInfo interface.

Call AddFuncDesc to add a method to a dispatch interface or to add a property or method to a VTBL interface. Call AddVarDesc to add a property to a dispatch interface.

Each of these methods takes two parameters: an index for the new variable or function in the type description; and a pointer to a FUNCDESC or VARDESC structure that describes the new property or method.

The values of the fields in the function and variable descriptors depend on the type of interface you're working with and the property or method you're adding. For AddFuncDesc, build a function descriptor according to the values in the following table. See the Automation Programmer's Reference in the Platform Software Development Kit (SDK) for additional details.

Field Description Suggested Value
Memid Member identifier for the function; same as the DISPID. MEMBERID_NIL. The ID will be assigned later by the LayOut method.
LprgScode Valid return values for the function. User-defined.
LprgElemdescParam Array of parameter types. User-defined.
Funckind Type of function. FUNC_PUREVIRTUAL or FUNC_DISPATCH
Invkind Invocation kind. INVOKE_PROPERTYGET or INVOKE_FUNC
Callconv Calling convention. CC_STDCALL
CParams Total number of parameters. User-defined.
CParamsOpt Number of optional parameters. User-defined.
OVft Offset into virtual function table. 0. The VTBL address will be assigned later by the LayOut method.
CScodes Number of return values in lprgScode. User-defined.
ElemdescFunc ELEMDESC structure that gives the return type for the function. User-defined.
WFuncFlags Bitmask containing FUNCFLAGS enumerators that describe the function. Use FUNCFLAG_FSOURCE if the function is a source of events.

For AddVarDesc, build a variable descriptor according to the values in the following table. See the Automation Programmer's Reference in the Platform Software Development Kit (SDK) for additional details.

Field Description Suggested Value
Memid Member identifier for the property; same as the DISPID. MEMBERID_NIL. The ID will be assigned later by the LayOut method.
ElemdescVar ELEMDESC structure that contains the type information for the property. User-defined.
WVarFlags Bitmask containing VARFLAGS enumerators describing the property. Use VARFLAG_FSOURCE if the property is a source of events.
Varkind VARKIND enumeration that identifies the property. VAR_DISPATCH

For both a function and a variable, specify an index of zero to place the description at the front of the type information.

Example–Adding a Property

The following example builds a variable descriptor and adds a property to an object described by a dispatch interface. In the example, the property is one of a group of subobjects that the designer can create. The variable vd is a structure of type VARDESC, and td is a structure of type TYPEDESC.

First, the example sets up some constants in the VARDESC structure. It specifies VAR_DISPATCH to indicate that it applies to a dispatch interface, and sets variable flags to show that it is a source of events and is read-only. This example also specifies its own DISPIDs rather than allowing the Layout method to assign them:

vd.memid = index + DISPID_CUSTOMOFFSET;
vd.varkind = VAR_DISPATCH;
vd.wVarFlags = VARFLAG_FSOURCE | VARFLAG_FREADONLY;

Next, it adds a reference to the new property's type information:

hr = m_state.pRuntimeDispinterface->AddRefTypeInfo(
      m_state.pObjectTI, &href);  

With the handle to the referenced type information in href, the rest of the variable and type descriptors can be filled in. The variable descriptor is then passed to AddVarDesc to add the new type information:

td.vt = VT_USERDEFINED;
td.hreftype = href;
vd.elemdescVar.tdesc.lptdesc = &td;
vd.elemdescVar.tdesc.vt = VT_PTR;
hr = m_state.pRuntimeDispinterface->AddVarDesc(0, &vd);

After adding the property, you should set its name. The code fragment below stores the names of the subobjects in the global structure m_state, and retrieves a name from the structure to pass to the SetVarName method:

bstrName = m_state.rgObjects[index].bstrName;
hr =  m_state.pRuntimeDispinterface->SetVarName(0, bstrName);

Finally, you must lay out the new type information. See "Laying Out the Type Information" later in this chapter.

Removing a Type Description

When the end user deletes a method or property, use the DeleteFuncDesc (or DeleteFuncDescByMemId) or DeleteVarDesc (or DeleteVarDescByMemId) method in the ICreateTypeInfo2 interface to remove its type description. See the Automation Programmer's Reference in the Platform Software Development Kit (SDK) for descriptions of these methods.

If you know the index for the property or method in the type information, use DeleteFuncDesc or DeleteVarDesc. These methods take one parameter, index, which supplies the index into the type information.

If you don't know the index but know the name of the property or method, use DeleteFuncDescByMemId or DeleteVarDescByMemId instead. These two methods require the member identifier (MEMBERID) of the property or method and its INVKIND, which is either INVOKE_PROPERTYGET for a property or INVOKE_FUNC for a method.

The MEMBERID is available through the object's ITypeComp interface, as in the following example. (Error handling is omitted for brevity.)

hr = m_pRuntimeTypeInfo->QueryInterface(IID_ITypeInfo,(void **)&ptiTemp);
hr = ptiTemp->GetTypeComp(&pTypeComp);

After getting a pointer to the object's ITypeComp interface, use the Bind method to map the subobject's name to its type information. In the following code fragment, the variable pwsz represents the name of the subobject and bp is the BINDPTR, computed by the Bind method and returned to the caller:

hr = pTypeComp->Bind(pwsz, LHashValOfNameSys(SYS_WIN32,
   g_lcidLocale, pwsz), INVOKE_PROPERTYGET, &ptiTemp, &dk, &bp);

Next, get the MEMBERID from the function descriptor and release the reference to ITypeComp:

memid = bp.lpfuncdesc->memid;
pTypeComp->Release();

Finally, with the MEMBERID in memid, call DeleteFuncDescByMemId to delete the function:

hr = m_pRuntimeTypeInfo->DeleteFuncDescByMemId(memid,
                                    INVOKE_PROPERTYGET);

Laying Out the Type Information

Each time you add or remove an object from the type information, you must call ICreateTypeInfo::LayOut. The LayOut method assigns VTBL offsets and member identifiers (which are the same as DISPIDs) to the objects in the type information:

hr = m_pRuntimeTypeInfo->LayOut();

After LayOut returns, the VTBL offsets and member identifiers are available through the ITypeInfo structure for the object.