[Previous] [Contents] [Next]

Obtaining Type Information

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

The ITypeLib Interface

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

The ITypeInfo Interface

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

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

Reading Type Information Using High-Level Languages

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