So far, we've examined how type information can be synthesized at run time. In comparison, obtaining access to predefined type information is relatively easy. Normally, a client can find the type library of a coclass simply by looking in the registry. The type library identifier (LIBID) of the class is stored in the TypeLib subkey beneath the CLSID key. With this LIBID, you can obtain type information about the class by calling the LoadRegTypeLib function. However, not all objects have a CLSID and the complementary registry entries—for example, subobjects such as connection points do not have a CLSID. You cannot obtain type information about such objects unless the object supports the IProvideClassInfo interface. Objects that do not have a CLSID but still want to expose type information to their clients must implement this interface. Adding support for the IProvideClassInfo interface is not particularly difficult. The lone method of this interface, GetClassInfo, retrieves the type information of the class. Here is the IProvideClassInfo interface in IDL notation:
interface IProvideClassInfo : IUnknown { // Get a pointer to the type information for this CLSID. HRESULT GetClassInfo([out] ITypeInfo** ppTI); } |
How the GetClassInfo method is implemented depends entirely on where the type information for the object is stored. If the type information is created dynamically and stored only in memory, the method should simply return a pointer to the ITypeInfo interface. If the type information is available in a type library file, call LoadRegTypeLib and then call ITypeLib::GetTypeInfoOfGuid, as shown here:
HRESULT CInsideCOM::GetClassInfo(ITypeInfo** pTypeInfo) { ITypeLib* pTypeLib; LoadRegTypeLib(LIBID_Component, 1, 0, LANG_NEUTRAL, &pTypeLib); HRESULT hr = pTypeLib->GetTypeInfoOfGuid(CLSID_InsideCOM, &pTypeInfo); pTypeLib->Release(); return hr; } |
Now the client can simply call QueryInterface for the IProvideClassInfo interface and follow that with a call to IProvideClassInfo::GetClassInfo. This is an equal opportunity interface: every coclass can expose IProvideClassInfo regardless of whether it has a CLSID. For objects with a CLSID, the IProvideClassInfo interface allows clients to use the standard QueryInterface mechanism instead of having to perform a registry lookup to locate the LIBID.
The IProvideClassInfo2 interface offers a further improvement; its definition is shown here:
interface IProvideClassInfo2 : IProvideClassInfo { // Get the IID of this object's default source interface. HRESULT GetGUID( [in] DWORD dwGuidKind, [out] GUID * pGUID); } |
As you can see, this interface derives from IProvideClassInfo; the only addition is the GetGUID method, which makes it quick and easy for a client to retrieve the interface identifier (IID) of an object's default source interface. The dwGuidKind parameter can be one of the values in the GUIDKIND enumeration. Currently, GUIDKIND_DEFAULT_SOURCE_DISP_IID is the only value defined. Using this value, the client requests the IID of the object's default source dispinterface. Because the sample source interfaces shown in Chapter 8 are not based on IDispatch, officially we should return an error. But never mind that. This is how an implementation of IProvideClassInfo2::GetGUID might look:
HRESULT CInsideCOM::GetGUID(DWORD dwGuidKind, GUID* pGUID) { if(pGUID == NULL) return E_INVALIDARG; *pGUID = IID_IOutGoing; return S_OK; } |
Regardless of the technique you use to obtain type information, the end result is an ITypeLib or ITypeInfo interface pointer. The ITypeLib interface provides a container for the type information stored in a type library, as well as general information and flags that apply to the type library as a whole. The ITypeLib interface is shown below in IDL notation:
interface ITypeLib : IUnknown { // Returns the number of type descriptions in the // type library UINT GetTypeInfoCount(void); // Retrieves the specified type description in the library HRESULT GetTypeInfo([in] UINT index, [out] ITypeInfo** ppTInfo); // Retrieves the type of a type description HRESULT GetTypeInfoType([in] UINT index, [out] TYPEKIND* pTKind); // Retrieves the type description that corresponds to the // specified GUID HRESULT GetTypeInfoOfGuid([in] REFGUID guid, [out] ITypeInfo** ppTinfo); // Retrieves the structure that contains the // library's attributes HRESULT GetLibAttr([out] TLIBATTR** ppTLibAttr); // Enables a client compiler to bind to a library's types, // variables, constants, and global functions HRESULT GetTypeComp([out] ITypeComp ** ppTComp); // Retrieves the library's documentation string, the // complete Help file name and path, and the context // identifier for the library Help topic in the Help file HRESULT GetDocumentation([in] INT index, [out] BSTR* pBstrName, [out] BSTR* pBstrDocString, [out] DWORD* pdwHelpContext, [out] BSTR* pBstrHelpFile); // Indicates whether a passed-in string contains the name // of a type or member described in the library HRESULT IsName([in, out] LPOLESTR szNameBuf, [in] ULONG lHashVal, [out] BOOL* pfName); // Finds occurrences of a type description in a type // library. This can be used to quickly verify that a name // exists in a type library. HRESULT FindName([in, out] LPOLESTR szNameBuf, [in] ULONG lHashVal, [out, size_is(*pcFound), length_is(*pcFound)] ITypeInfo** ppTInfo, [out, size_is(*pcFound), length_is(*pcFound)] MEMBERID* rgMemId, [in, out] USHORT* pcFound); // Releases the TLIBATTR originally obtained from // ITypeLib::GetLibAttr void ReleaseTLibAttr([in] TLIBATTR* pTLibAttr); } |
The ITypeLib interface was extended to produce the ITypeLib2 interface; the enhancements allow you to retrieve custom data values and localized help file information. To obtain a pointer to the ITypeLib2 interface, you simply call QueryInterface on the ITypeLib interface pointer. The ITypeLib2 interface is shown below in IDL notation:
interface ITypeLib2 : ITypeLib { // Gets custom data HRESULT GetCustData([in] REFGUID guid, [out] VARIANT* pVarVal); // Returns statistics about a type library that are required // for efficient sizing of hash tables HRESULT GetLibStatistics([out] ULONG* pcUniqueNames, [out] ULONG* pcchUniqueNames); // Gets localized documentation information about the // type library HRESULT GetDocumentation2([in] INT index, [in] LCID lcid, [out] BSTR* pbstrHelpString, [out] DWORD* pdwHelpStringContext, [out] BSTR* pbstrHelpStringDll); // Gets all custom data items for the library HRESULT GetAllCustData([out] CUSTDATA* pCustData); } |
You use the ITypeInfo interface to obtain type information about an object. Generally speaking, an application obtains access to type information by calling the ITypeLib::GetTypeInfo or ITypeLib::GetTypeInfoOfGuid methods, both of which return an ITypeInfo interface pointer. The ITypeInfo interface is shown below in IDL notation:
interface ITypeInfo : IUnknown { // Retrieves a TYPEATTR structure that contains the // attributes of the type description HRESULT GetTypeAttr([out] TYPEATTR** ppTypeAttr); // Retrieves the ITypeComp interface for the type // description, which enables a client compiler to bind // to the type description's members HRESULT GetTypeComp([out] ITypeComp** ppTComp); // Retrieves the FUNCDESC structure for a specified function HRESULT GetFuncDesc([in] UINT index, [out] FUNCDESC** ppFuncDesc); // Retrieves a VARDESC structure that describes the // specified variable HRESULT GetVarDesc([in] UINT index, [out] VARDESC** ppVarDesc); // Retrieves the variable with the specified member ID (or // the name of the property or method and its parameters) // that corresponds to the specified function ID HRESULT GetNames([in] MEMBERID memid, [out, size_is(cMaxNames), length_is(*pcNames)] BSTR* rgBstrNames, [in] UINT cMaxNames, [out] UINT* pcNames); // Retrieves the type description of the implemented // interface types for a coclass. For an interface, it // returns the type information for inherited interfaces, // if any. HRESULT GetRefTypeOfImplType([in] UINT index, [out] HREFTYPE* pRefType); // Retrieves the IMPLTYPEFLAGS an interface in a // type description HRESULT GetImplTypeFlags([in] UINT index, [out] INT* pImplTypeFlags); // Maps between member names and member IDs and between // parameter names and parameter IDs HRESULT GetIDsOfNames( [in, size_is(cNames)] LPOLESTR* rgszNames, [in] UINT cNames, [out, size_is(cNames)] MEMBERID* pMemId); // Invokes a method defined by the type description HRESULT Invoke([in] PVOID pvInstance, [in] MEMBERID memid, [in] WORD wFlags, [in, out] DISPPARAMS* pDispParams, [out] VARIANT* pVarResult, [out] EXCEPINFO* pExcepInfo, [out] UINT* puArgErr); // Retrieves the documentation information for a // type description HRESULT GetDocumentation([in] MEMBERID memid, [out] BSTR* pBstrName, [out] BSTR* pBstrDocString, [out] DWORD* pdwHelpContext, [out] BSTR* pBstrHelpFile); // Retrieves a description or specification of an entry // point for a function in a DLL HRESULT GetDllEntry([in] MEMBERID memid, [in] INVOKEKIND invKind, [out] BSTR* pBstrDllName, [out] BSTR* pBstrName, [out] WORD* pwOrdinal); // Retrieves the referenced type descriptions HRESULT GetRefTypeInfo([in] HREFTYPE hRefType, [out] ITypeInfo** ppTInfo); // Gets the addresses of static functions or variables, // such as those defined in a DLL HRESULT AddressOfMember([in] MEMBERID memid, [in] INVOKEKIND invKind, [out] PVOID* ppv); // Creates a new instance of a type that describes a coclass HRESULT CreateInstance([in] IUnknown* pUnkOuter, [in] REFIID riid, [out, iid_is(riid)] PVOID* ppvObj); // Retrieves marshaling information HRESULT GetMops([in] MEMBERID memid, [out] BSTR* pBstrMops); // Retrieves the containing type library and the index of // the type description within that type library HRESULT GetContainingTypeLib([out] ITypeLib** ppTLib, [out] UINT* pIndex); // Releases a TYPEATTR previously returned by GetTypeAttr void ReleaseTypeAttr([in] TYPEATTR* pTypeAttr); // Releases a FUNCDESC previously returned by GetFuncDesc void ReleaseFuncDesc([in] FUNCDESC* pFuncDesc); // Releases a VARDESC previously returned by GetVarDesc void ReleaseVarDesc([in] VARDESC* pVarDesc); } |
The enhanced version of ITypeInfo, ITypeInfo2, permits clients to obtain localized help file information as well as custom data items stored in the type library. To obtain a pointer to the ITypeInfo2 interface, you simply call QueryInterface from any ITypeInfo interface pointer. The ITypeInfo2 interface is shown below in IDL notation:
interface ITypeInfo2 : ITypeInfo { // Returns the TYPEKIND enumeration quickly, without doing // any allocations HRESULT GetTypeKind([out] TYPEKIND* pTypeKind); // Returns the type flags without any allocations HRESULT GetTypeFlags([out] ULONG* pTypeFlags); // Binds to a specific member based on a known DISPID, // where the member name is not known HRESULT GetFuncIndexOfMemId([in] MEMBERID memid, [in] INVOKEKIND invKind, [out] UINT* pFuncIndex); // Binds to a specific member based on a known DISPID, // where the member name is not known HRESULT GetVarIndexOfMemId([in] MEMBERID memid, [out] UINT* pVarIndex); // Gets the custom data HRESULT GetCustData([in] REFGUID guid, [out] VARIANT* pVarVal); // Gets the custom data from the specified function HRESULT GetFuncCustData([in] UINT index, [in] REFGUID guid, [out] VARIANT *pVarVal); // Gets the specified custom data parameter HRESULT GetParamCustData([in] UINT indexFunc, [in] UINT indexParam, [in] REFGUID guid, [out] VARIANT* pVarVal); // Gets the variable for the custom data HRESULT GetVarCustData([in] UINT index, [in] REFGUID guid, [out] VARIANT* pVarVal); // Gets the implementation type of the custom data HRESULT GetImplTypeCustData([in] UINT index, [in] REFGUID guid, [out] VARIANT* pVarVal); // Gets localized documentation information for the // type description HRESULT GetDocumentation2([in] MEMBERID memid, [in] LCID lcid, [out] BSTR* pbstrHelpString, [out] DWORD* pdwHelpStringContext, [out] BSTR* pbstrHelpStringDll); // Gets all custom data items for the library HRESULT GetAllCustData([out] CUSTDATA* pCustData); // Gets all custom data from the specified function HRESULT GetAllFuncCustData([in] UINT index, [out] CUSTDATA* pCustData); // Gets all of the custom data for the specified // function parameter HRESULT GetAllParamCustData([in] UINT indexFunc, [in] UINT indexParam, [out] CUSTDATA* pCustData); // Gets the variable for the custom data HRESULT GetAllVarCustData([in] UINT index, [out] CUSTDATA* pCustData); // Gets all custom data for the specified // implementation type HRESULT GetAllImplTypeCustData([in] UINT index, [out] CUSTDATA* pCustData); } |
The ITypeComp interface, an optimization for use by compilers, is implemented in oleaut32.dll. This interface enables clients to quickly locate a certain piece of type information in a type library without having to navigate through many of the methods of ITypeLib and ITypeInfo. The ITypeComp interface is shown below in IDL notation:
interface ITypeComp : IUnknown { HRESULT Bind( [in] LPOLESTR szName, [in] ULONG lHashVal, [in] WORD wFlags, [out] ITypeInfo ** ppTInfo, [out] DESCKIND * pDescKind, [out] BINDPTR * pBindPtr ); HRESULT BindType( [in] LPOLESTR szName, [in] ULONG lHashVal, [out] ITypeInfo ** ppTInfo, [out] ITypeComp ** ppTComp ); } |
While type information is usually created and read automatically by the run-time support provided for high-level languages, type information can also be obtained programmatically in languages such as Visual Basic, Java, or even script via the TypeLib Information (tlbinf32.dll) component. This component encapsulates the type information interfaces offered by COM+—ITypeLib(2) and ITypeInfo(2)—in high-level dual interfaces. The Visual Basic code below reads and displays the type information from the type library we created earlier in this chapter.
Private Sub Command1_Click() Dim refTypeLibInfo As TLI.TypeLibInfo ' Load the type library from the registry based on ' the LIBID. Set refTypeLibInfo = _ TLI.TLIApplication.TypeLibInfoFromRegistry( _ "{10000003-0000-0000-0000-000000000001}", 1, 0, 0) Dim refCoClassInfo As TLI.CoClassInfo For Each refCoClassInfo In refTypeLibInfo.CoClasses ' Print the coclass name. Print "Coclass: " & refCoClassInfo.Name ' Display info about all interfaces implemented by ' the coclass. PrintInterfaces refCoClassInfo.Interfaces Next End Sub Sub PrintInterfaces(refInterfaces As TLI.Interfaces) Dim refInterfaceInfo As TLI.InterfaceInfo For Each refInterfaceInfo In refInterfaces ' Print interface name. Print "Interface: " & refInterfaceInfo.Name; ' Print interface IID. Print " " & refInterfaceInfo.Guid Dim refMemberInfo As TLI.MemberInfo For Each refMemberInfo In refInterfaceInfo.Members ' Print the return type. Select Case refMemberInfo.ReturnType Case TliVarType.VT_HRESULT Print vbTab & "HRESULT "; Case TliVarType.VT_INT Print vbTab & "int "; Case TliVarType.VT_UI4 Print vbTab & "ULONG "; End Select ' Print method name. Print refMemberInfo.Name & "("; Dim refParameterInfo As TLI.ParameterInfo For Each refParameterInfo In _ refMemberInfo.Parameters ' Print parameter direction. Select Case refParameterInfo.Flags Case ParamFlags.PARAMFLAG_FIN Print "[in] "; Case ParamFlags.PARAMFLAG_FOUT Print "[out] "; Case ParamFlags.PARAMFLAG_FRETVAL Print "[retval] "; End Select ' Print parameter type. Select Case refParameterInfo.VarTypeInfo.VarType Case TliVarType.VT_INT Print "int "; Case TliVarType.VT_VOID Print "void "; End Select ' Print parameter name. Print refParameterInfo.Name & ", "; Next Print ")" Next ' Recursive calls to print all base interfaces, ' including IUnknown PrintInterfaces refInterfaceInfo.ImpliedInterfaces Next End Sub |