Implementing IProvideDynamicClassInfo

The host calls the IProvideDynamicClassInfo interface when either of the following occurs:

The interface has two methods. GetDynamicClassInfo provides a pointer to the type information object. FreezeShape freezes the dynamically changing type information object.

Providing Type Information

The GetDynamicClassInfo method gives the host access to dynamic type information for the objects an ActiveX designer creates. You should implement this method if you expect any changes to the type information for the visual designer. The method is defined as follows:

HRESULT GetDynamicClassInfo(
   ITypeInfo ** ppTI, 
   DWORD * pdwCookie
);

GetDynamicClassInfo returns two parameters: ppTypeInfoOut, an indirect pointer to a type information object that describes a coclass; and pdwCookie, a handle that the host saves in its own cache along with the type information.

The remainder of this section steps through tasks a designer might need to perform to create and return a dynamic type information object with GetDynamicClassInfo. Assume that the following declarations have been made:

ICreateTypeInfo *pctiDispinterface = NULL;
ICreateTypeInfo *pctiCoClass = NULL;
ICreateTypeLib2 *pCreateTypeLib = NULL;
ITypeInfo *ptiEvents = NULL, *ptiDispinterface;
HREFTYPE hreftype;
HRESULT  hr;

Error handling has been omitted for brevity throughout this section.

Creating the Type Library

The dynamic type information object must be in a new format type library.

To create a new type library, use the ICreateTypeLib2 interface or the CreateTypeLib2 Application Programming Interface (API), as in the following example:

hr = CreateTypeLib2(SYS_WIN32, pwszFile, &pCreateTypeLib);

// Set the guid, help string DLL, and name.
hr = pCreateTypeLib->SetGuid(m_state.guidTypeLib);
hr = pCreateTypeLib->SetHelpStringDll(L"");
hr = pCreateTypeLib->SetName(L"MyDesignerTypeLib");

In the example, pwszFile contains a temporary name for the type library, and m_state is a global structure that holds useful information for the designer, including its GUID. You must set the name for the new type library.

Creating the Type Information Object

After creating the type library, you can create the type information object. The easiest way is to use an existing static type information object as a template. If the existing static object describes a virtual function table (VTBL) interface (or a dual interface), simply copy the object; Automation handles the necessary inheritance. Then return an indirect pointer to your copy as the type information object (see "Returning Parameters," later in this section).

For a dispatch interface, however, the procedure is more complicated. You must copy the entire object and set up the inheritance yourself, in code. The following code fragment shows how:

// Get the dispatch interface to use as a template.
hr = LoadRegTypeLib(*g_pLibid, (USHORT)VERSIONOFOBJECT(m_ObjectType),0,
         LANG_NEUTRAL, &pTypeLibStatic);

hr = pTypeLibStatic->GetTypeInfoOfGuid(DIID_DMyDesigner,
               &ptiDispinterface);
if (SUCCEEDED(hr)) {
   // Now get the type information of event interface.
   hr = pTypeLibStatic->GetTypeInfoOfGuid(
                  DIID_DMyDesignerEvents,ppTypeInfo);
   if (SUCCEEDED(hr)) {
     // And get the type information of control object.
     hr = pTypeLibStatic->GetTypeInfoOfGuid(CLSID_MyObject,                            &m_state.pMyObjectTI);
   }
 }
pTypeLibStatic->Release();

The preceding code fragment gets an existing type information object that describes a dispatch interface.

The next few lines create a new dispatch type information object and set its GUID. The local function _GetIDispatchTypeInfo returns the dispatch type information for the existing object. Calls to AddRefTypeInfo and AddImplTypeInfo set up inheritance.

hr = pTypeLib->CreateTypeInfo(L"DMyDesignerObject", TKIND_DISPATCH,
            &pctiDispinterface);
hr = pctiDispinterface->SetGuid(m_state.guidTypeInfo);

hr = _GetIDispatchTypeInfo(&ptiDispatch);
hr = pctiDispinterface->AddRefTypeInfo(ptiDispatch, &hreftype);
hr = pctiDispinterface->AddImplType(0, hreftype);

Finally, copy the existing type information from *ptiDispInterface to the new object, *pctiDispInterface, and lay out the type information. In this example, CopyDispInterfaceTypeInfo is a local function that copies the information:

hr = CopyDispinterfaceTypeInfo(ptiDispinterface, pctiDispinterface);
pctiDispinterface->LayOut();
pctiDispinterface->AddRef();

Now pctiDispInterface points to a TKIND_DISPATCH type information object to be used as a template for the new dynamic type information object. The new object must contain type information for a coclass, so you have to create a blank object of TKIND_COCLASS:

hr = pCreateTypeLib->CreateTypeInfo(L"MyDesigner", TKIND_COCLASS,
                           &pctiCoClass);

Next, get the type information for the existing interfaces and events from the template in pctiDispinterface, add these to the new type information in ptiCoClass, and set the implementation type flags:

hr = pctiDispinterface->QueryInterface(IID_ITypeInfo,
               (void **)&ptiDispinterface);
// Information on the interface.
hr = pctiCoClass->AddRefTypeInfo(ptiDispinterface, &hreftype);
ptiDispinterface->Release();
hr = pctiCoClass->AddImplType(0, hreftype);

// Information on the events.
hr = pctiCoClass->AddRefTypeInfo(ptiEvents, &hreftype);
hr = pctiCoClass->AddImplType(1, hreftype);

// Now set up the flags for these two items.
hr = pctiCoClass->SetImplTypeFlags(0, IMPLTYPEFLAG_FDEFAULT);
hr = pctiCoClass->SetImplTypeFlags(1, IMPLTYPEFLAG_FDEFAULT
             | IMPLTYPEFLAG_FSOURCE);

When this is done, the type information object is set up. Only two tasks remain: setting up the output parameter and incrementing the cookie.

Returning Parameters

As previously described, GetDynamicClassInfo returns two parameters: ppITypeInfo and pdwCookie.

The ppTypeInfo output parameter is an indirect pointer to an ITypeInfo interface. Because pctiCoClass represents an ICreateTypeInfo interface, you have to query for the correct interface and cast the resulting pointer:

pctiCoClass->QueryInterface(IID_ITypeInfo, (void **)ppTypeInfo)

Finally, pass the current value of the cookie, which the example stores in a global variable:

if (pdwCookie)
    *pdwCookie = m_dwTICookie;

The designer should initialize *pdwCookie in the InitNew or Load method of its persistence interfaces, and increment it if an unrecoverable error occurs when the persistent data are loaded.

Each time the host is reloaded, it checks the cookie to determine whether its cached version of the type information is up to date. If the cached information is accurate, the host can use it without having to reload the persistent state. If the cache is outdated, the host must reload the designer's persistent state.

Freezing Type Information

When the user requests that the application be run or built, the host calls the FreezeShape method to freeze the dynamic type information. After FreezeShape has been called, no additional changes can take place.

The FreezeShape method is simple to implement with a global flag. The flag is set to True when FreezeShape is called, as in the following example:

STDMETHODIMP CMyDesigner::FreezeShape
(
  void
)
{
   m_fFreezeShape = TRUE;
  return S_OK;
}

The flag is reset to False in the class factory for the visual designer.

Whether or not the host calls FreezeShape, a designer must not permit changes to its type information at run time.