If a client wants to browse an object's type information without instantiating the object, the client can use the object's registry entries to load the library itself and obtain an ITypeLib pointer. The only complication here is that the client has to know the CLSID of the object in question, and it then has to navigate through the library to find the ITypeInfo pointer for the object's coclass. If the client already has a running instance of that class, it would make sense to simply ask the object for its coclass type information directly.
This is the purpose of the IProvideClassInfo interface, which an object can implement if it wants to provide this feature. The only types of objects that are currently required to implement this interface are those with custom event sets, such as OLE controls. For other objects, it's merely a convenience for interested clients, but it is an interface I strongly recommend.9
IProvideClassInfo is actually one of the simplest interfaces you'll find. Besides its IUnknown members, it has only one additional function, named GetClassInfo, which takes a single out-parameter in which it returns the ITypeInfo pointer for the object's coclass information:
interface IProvideClassInfo : IUnknown
{
HRESULT GetClassInfo(ITypeInfo **ppITI);
}
The presence of this interface frees the client from having to load and navigate a type library itself, which is, however, what the object will generally do using code somewhat like the code on the following page to obtain the ITypeInfo pointer to return:
//Try loading from registry information.
if (FAILED(LoadRegTypeLib(LIBID_MyTypeLibrary, 1, 0
, LANG_NEUTRAL, &pITypeLib)))
{
//Try loading directly, fixing registry information.
if (FAILED(LoadTypeLib(TEXT("MYTYPES.TLB"), &pITypeLib)))
return FALSE;
}
//Find ITypeInfo for coclass.
pITypeLib->GetTypeInfoOfGuid(CLSID_MyObject, &m_pITI);
pITypeLib->Release();
In the following code, m_pITI would end up with the ITypeInfo pointer for IProvideClassInfo::GetClassInfo:
STDMETHODIMP CMyObject::GetClassInfo(ITypeInfo **ppITI)
{
if (NULL==ppITI)
return ResultFromScode(E_POINTER);
*ppITI=m_pITI;
if (NULL!=m_pITI)
{
m_pITI->AddRef();
return NOERROR;
}
return ResultFromScode(E_FAIL);
}
In a sense, IProvideClassInfo represents the ability of an object to describe its interfaces and types when everything else about it is unknown. As an example, if a client received through, say, a drag-and-drop operation some arbitrary IUnknown pointer, it would query for IProvideClassInfo, and if that succeeded, it could programmatically analyze the available incoming and outgoing interfaces that the object supports. Based on this analysis, the client might determine what could be done with the object: Is it a control? A compound document content object? An OLE Automation object? Something else? What abilities does it have?
There is one deficiency in this interface that you should note: there is no way to ask the object for type information in a particular language. In other words, the interface assumes that the object will have already loaded type information appropriate to the user's international settings on the current machine. In some cases, however, this may not be adequate for the needs of all clients, so there may, in the future, be something like an IProvideClassInfo2 that would have an additional function that takes an LCID as an argument. For the time being, however, this remains a single-locale interface.
9 This interface was first defined as part of the OLE Controls specification itself and at the time of writing it is not widely used outside this realm. In addition the initial support for this interface which was the OLE Controls Development Kit (with Microsoft Visual C++ 2.0) did not provide any marshaling support (proxy and stub) for IProvideClassInfo and as a consequence it can be imple-mented only on in-process objects unless you are willing to write your own proxy or stub DLL. I fully expect that this limitation will not be present by the time this book is published or shortly thereafter. |