Figure 3   FUNCDESC Structure Members


MEMBERID       memid
SCODE *        lprgscode
ELEMDESC *     lprgelemdescParam
INVOKEKIND     invkind
CALLCONV       callconv
short          cParams
short          cParamsOpt
short          oVft
short          cScodes
ELEMDESC       elemdescFunc
WORD           wFuncFlags

Figure 4   COMTypeLibDump


//==================================================
// COMTypeLibDump - Matt Pietrek 1999
// Microsoft Systems Journal, March 1999
// FILE: COMTypeLibDump.CPP
//==================================================
#include <windows.h>
#include <ole2.h>
#include <tchar.h>
//============================================================================
LPCTSTR g_szHelpText =  _T( "COMTypeLibDump - Matt Pietrek 1999 for MSJ\n" )
                        _T( "  Syntax: COMTypeLibDump <filename>\n" );
//============================================================================
void DisplayTypeLib( LPCTSTR pszFileName );
void EnumTypeLib( LPTYPELIB pITypeLib );
void DisplayTypeInfo( LPTYPEINFO pITypeInfo );
void EnumTypeInfoMembers( LPTYPEINFO pITypeInfo, LPTYPEATTR pTypeAttr);
LPCTSTR GetTypeKindName( TYPEKIND typekind );
LPCTSTR GetInvokeKindName( INVOKEKIND invkind );
//============================================================================
extern "C" int _tmain( int argc, LPCTSTR * argv )
{
    CoInitialize( 0 );
    if ( 2 != argc )
    {
        _tprintf( g_szHelpText );
        return 0;
    }
    DisplayTypeLib( argv[1] );
    CoUninitialize();
    return 0;
}

void DisplayTypeLib( LPCTSTR pszFileName )
{
    LPTYPELIB pITypeLib;
    HRESULT hr = LoadTypeLib( pszFileName, &pITypeLib );
    if ( S_OK != hr )
    {
        _tprintf( _T("LoadTypeLib failed on file %s\n"), pszFileName );
        return;
    }
    EnumTypeLib( pITypeLib );
    pITypeLib->Release();
}

void EnumTypeLib( LPTYPELIB pITypeLib )
{
    UINT tiCount = pITypeLib->GetTypeInfoCount();
    for ( UINT i = 0; i < tiCount; i++ )
    {
        LPTYPEINFO pITypeInfo;
        HRESULT hr = pITypeLib->GetTypeInfo( i, &pITypeInfo );
        if ( S_OK == hr )
        {
            DisplayTypeInfo( pITypeInfo );
            pITypeInfo->Release();
        }
    }
}

void DisplayTypeInfo( LPTYPEINFO pITypeInfo )
{
    HRESULT hr;
    BSTR pszTypeInfoName;
    hr = pITypeInfo->GetDocumentation(MEMBERID_NIL, &pszTypeInfoName, 0, 0, 0);
    if ( S_OK != hr ) return;
    TYPEATTR * pTypeAttr;
    hr = pITypeInfo->GetTypeAttr( &pTypeAttr );
    if ( S_OK != hr )
    {
        SysFreeString( pszTypeInfoName );
        return;
    }
    _tprintf( _T("%ls - %s\n"), pszTypeInfoName,
                GetTypeKindName(pTypeAttr->typekind) );
    EnumTypeInfoMembers( pITypeInfo, pTypeAttr );
    _tprintf( _T("\n") );
    SysFreeString( pszTypeInfoName );
    pITypeInfo->ReleaseTypeAttr( pTypeAttr );
}

void EnumTypeInfoMembers( LPTYPEINFO pITypeInfo, LPTYPEATTR pTypeAttr  )
{
    if ( pTypeAttr->cFuncs )
    {
        _tprintf( _T("  Functions:\n") );
        for ( unsigned i = 0; i < pTypeAttr->cFuncs; i++ )
        {
            FUNCDESC * pFuncDesc;
            pITypeInfo->GetFuncDesc( i, &pFuncDesc );
            BSTR pszFuncName;        
            pITypeInfo->GetDocumentation(pFuncDesc->memid, &pszFuncName,0,0,0);
            _tprintf( _T("    %-32ls"), pszFuncName );
            _tprintf( _T(" (%ls)\n"), GetInvokeKindName(pFuncDesc->invkind) );
            pITypeInfo->ReleaseFuncDesc( pFuncDesc );                        
            SysFreeString( pszFuncName );
        }
    }
    if ( pTypeAttr->cVars )
    {
        _tprintf( _T("  Variables:\n") );
        for ( unsigned i = 0; i < pTypeAttr->cVars; i++ )
        {
            VARDESC * pVarDesc;
            pITypeInfo->GetVarDesc( i, &pVarDesc );
            BSTR pszVarName;        
            pITypeInfo->GetDocumentation(pVarDesc->memid, &pszVarName,0,0,0);
            _tprintf( _T("    %ls\n"), pszVarName );
            pITypeInfo->ReleaseVarDesc( pVarDesc );                        
            SysFreeString( pszVarName );
        }
    }
}

#define CASE_STRING( x ) case x: s = _T(#x); break;
LPCTSTR GetTypeKindName( TYPEKIND typekind )
{
    LPTSTR s = _T("<unknown>");
    switch( typekind )
    {
        CASE_STRING( TKIND_ENUM )
        CASE_STRING( TKIND_RECORD )
        CASE_STRING( TKIND_MODULE )
        CASE_STRING( TKIND_INTERFACE )
        CASE_STRING( TKIND_DISPATCH )
        CASE_STRING( TKIND_COCLASS )
        CASE_STRING( TKIND_ALIAS )
        CASE_STRING( TKIND_UNION )
    }
    return s;
}

LPCTSTR GetInvokeKindName( INVOKEKIND invkind )
{
    LPTSTR s = _T("<unknown>");
    switch( invkind )
    {
        CASE_STRING( INVOKE_FUNC )
        CASE_STRING( INVOKE_PROPERTYGET )
        CASE_STRING( INVOKE_PROPERTYPUT )
        CASE_STRING( INVOKE_PROPERTYPUTREF )
    }    
    return s;
}

Figure 5   Running COMTypeLibDump


VbVarType - TKIND_ENUM
  Variables:
    vbEmpty
    vbNull
    vbInteger
    vbLong
    vbSingle
    vbDouble
    vbCurrency
    vbDate
    vbString
    vbObject
    vbError
    vbBoolean
    vbVariant
    vbDataObject
    vbDecimal
    vbByte
    vbUserDefinedType
    vbArray

VbMsgBoxStyle - TKIND_ENUM
  Variables:
    vbOKOnly
    vbOKCancel
    vbAbortRetryIgnore
    vbYesNoCancel
    vbYesNo
    vbRetryCancel
    vbCritical
    vbQuestion
    vbExclamation
    vbInformation
    vbDefaultButton1
    vbDefaultButton2
    vbDefaultButton3
    vbDefaultButton4
    vbApplicationModal
    vbSystemModal
    vbMsgBoxHelpButton
    vbMsgBoxRight
    vbMsgBoxRtlReading
    vbMsgBoxSetForeground

FileSystem - TKIND_MODULE
  Functions:
    ChDir                            (INVOKE_FUNC)
    ChDrive                          (INVOKE_FUNC)
    EOF                              (INVOKE_FUNC)
    FileAttr                         (INVOKE_FUNC)
    FileCopy                         (INVOKE_FUNC)
    FileDateTime                     (INVOKE_FUNC)
    FileLen                          (INVOKE_FUNC)
    GetAttr                          (INVOKE_FUNC)
    Kill                             (INVOKE_FUNC)
    Loc                              (INVOKE_FUNC)
    LOF                              (INVOKE_FUNC)
    MkDir                            (INVOKE_FUNC)
    Reset                            (INVOKE_FUNC)
    RmDir                            (INVOKE_FUNC)
    Seek                             (INVOKE_FUNC)
    SetAttr                          (INVOKE_FUNC)
    _B_str_CurDir                    (INVOKE_FUNC)
    _B_var_CurDir                    (INVOKE_FUNC)
    FreeFile                         (INVOKE_FUNC)
    Dir                              (INVOKE_FUNC)

Interaction - TKIND_MODULE
  Functions:
    AppActivate                      (INVOKE_FUNC)
    Beep                             (INVOKE_FUNC)
    CreateObject                     (INVOKE_FUNC)
    DoEvents                         (INVOKE_FUNC)
    GetObject                        (INVOKE_FUNC)
    InputBox                         (INVOKE_FUNC)
    MacScript                        (INVOKE_FUNC)
    MsgBox                           (INVOKE_FUNC)
    SendKeys                         (INVOKE_FUNC)
    Shell                            (INVOKE_FUNC)
    Partition                        (INVOKE_FUNC)
    Choose                           (INVOKE_FUNC)
    _B_var_Environ                   (INVOKE_FUNC)
    _B_str_Environ                   (INVOKE_FUNC)
    Switch                           (INVOKE_FUNC)
    _B_var_Command                   (INVOKE_FUNC)
    _B_str_Command                   (INVOKE_FUNC)
    IIf                              (INVOKE_FUNC)
    GetSetting                       (INVOKE_FUNC)
    SaveSetting                      (INVOKE_FUNC)
    DeleteSetting                    (INVOKE_FUNC)
    GetAllSettings                   (INVOKE_FUNC)
    CallByName                       (INVOKE_FUNC)

_ErrObject - TKIND_DISPATCH
  Functions:
    QueryInterface                   (INVOKE_FUNC)
    AddRef                           (INVOKE_FUNC)
    Release                          (INVOKE_FUNC)
    GetTypeInfoCount                 (INVOKE_FUNC)
    GetTypeInfo                      (INVOKE_FUNC)
    GetIDsOfNames                    (INVOKE_FUNC)
    Invoke                           (INVOKE_FUNC)
    Number                           (INVOKE_PROPERTYGET)
    Number                           (INVOKE_PROPERTYPUT)
    Source                           (INVOKE_PROPERTYGET)
    Source                           (INVOKE_PROPERTYPUT)
    Description                      (INVOKE_PROPERTYGET)
    Description                      (INVOKE_PROPERTYPUT)
    HelpFile                         (INVOKE_PROPERTYGET)
    HelpFile                         (INVOKE_PROPERTYPUT)
    HelpContext                      (INVOKE_PROPERTYGET)
    HelpContext                      (INVOKE_PROPERTYPUT)
    Raise                            (INVOKE_FUNC)
    Clear                            (INVOKE_FUNC)
    LastDllError                     (INVOKE_PROPERTYGET)

ErrObject - TKIND_COCLASS

Figure 6    CoClassSymsCallouts.H


#ifdef __cplusplus
extern "C" {
#endif

BOOL __stdcall CoClassSymsBeginSymbolCallouts( PSTR pszExecutable );

BOOL __stdcall  CoClassSymsAddSymbol(
    unsigned short section,
    unsigned long offset,
    PSTR pszSymbolName );

BOOL __stdcall CoClassSymsSymbolsFinished( void );


typedef BOOL (__stdcall * PFNCCSCALLOUTBEGIN)( PSTR pszExecutable );
typedef BOOL (__stdcall * PFNCCSADDSYMBOL)(unsigned short, unsigned long,PSTR);
typedef BOOL (__stdcall * PFNCCSFINISHED)(void);

#ifdef __cplusplus
}
#endif

Figure 7   Excerpts from CoClassSyms.CPP


•
•
•
//============================================================================
// Top level handling code for a single ITypeInfo extracted from a typelib
//============================================================================
void ProcessTypeInfo( LPTYPEINFO pITypeInfo )
{
    HRESULT hr;
        
    LPTYPEATTR pTypeAttr;
    hr = pITypeInfo->GetTypeAttr( &pTypeAttr );
    if ( S_OK != hr )
        return;
    
    if ( TKIND_COCLASS == pTypeAttr->typekind )
    {
        for ( unsigned short i = 0; i < pTypeAttr->cImplTypes; i++ )
        {
            HREFTYPE hRefType;
            
            hr = pITypeInfo->GetRefTypeOfImplType( i, &hRefType );
            
            if ( S_OK == hr )
                ProcessReferencedTypeInfo( pITypeInfo, pTypeAttr, hRefType );
         }
    }
    
    pITypeInfo->ReleaseTypeAttr( pTypeAttr );
}

//============================================================================
// Given a TKIND_COCLASS ITypeInfo, get the ITypeInfo that describes the
// referenced (HREFTYPE) TKIND_DISPATCH or TKIND_INTERFACE.  Pass that
// ITypeInfo to EnumTypeInfoMembers.
//============================================================================
void ProcessReferencedTypeInfo( LPTYPEINFO pITypeInfo_CoClass,
                                LPTYPEATTR pTypeAttr,
                                HREFTYPE hRefType )
{
    LPTYPEINFO pIRefTypeInfo;
    
    HRESULT hr = pITypeInfo_CoClass->GetRefTypeInfo(hRefType, &pIRefTypeInfo);
    if ( S_OK != hr )
        return;

    LPTYPEATTR pRefTypeAttr;
    pIRefTypeInfo->GetTypeAttr( &pRefTypeAttr );

    LPUNKNOWN pIUnknown = 0;

    hr = CoCreateInstance(  pTypeAttr->guid,
                            0,                    // pUnkOuter
                            CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER,
                            pRefTypeAttr->guid,
                            (LPVOID *)&pIUnknown );

    if ( (S_OK == hr) && pIUnknown )
    {
        EnumTypeInfoMembers( pIRefTypeInfo, pRefTypeAttr, pIUnknown );

        pIUnknown->Release();
    }
                        
    pIRefTypeInfo->ReleaseTypeAttr( pRefTypeAttr );
    pIRefTypeInfo->Release();
}

//============================================================================
// Enumerate through each member of an ITypeInfo.  Send the method name and
// address to the CoClassSymsAddSymbol function.
//=============================================================================
void EnumTypeInfoMembers( LPTYPEINFO pITypeInfo,    // The ITypeInfo to enum.
                          LPTYPEATTR pTypeAttr,     // The associated TYPEATTR.
                          LPUNKNOWN lpUnknown       // From CoCreateInstance.
                        )
{
    // Only call CoClassSymsBeginSymbolCallout once    
    static BOOL fCalledBeginCallout = FALSE;
    if ( FALSE == fCalledBeginCallout )
    {
        char szFileName[MAX_PATH];
        wcstombs( szFileName, g_pszFileName, MAX_PATH );
        
        fCalledBeginCallout = g_pfnCoClassSymsBeginSymbolCallouts(szFileName);
    }

    // Make a pointer to the vtable.    
    PBYTE pVTable = (PBYTE)*(PDWORD)(lpUnknown);

    if ( 0 == pTypeAttr->cFuncs )    // Make sure at least one method!
        return;

    // Get the name of the ITypeInfo, to use as the interface name in the
    // symbol names we'll be constructing.
    TCHAR pszInterfaceName[256];
    GetTypeInfoName( pITypeInfo, pszInterfaceName );

    // Enumerate through each method, obtain its name, address, and ship the
    // info off to CoClassSymsAddSymbol()
    for ( unsigned i = 0; i < pTypeAttr->cFuncs; i++ )
    {
        FUNCDESC * pFuncDesc;
        
        pITypeInfo->GetFuncDesc( i, &pFuncDesc );
        
        TCHAR pszMemberName[256];        
        GetTypeInfoName( pITypeInfo, pszMemberName, pFuncDesc->memid );

        // Index into the vtable to retrieve the method's virtual address
        DWORD pFunction = *(PDWORD)(pVTable + pFuncDesc->oVft);

        // Created the basic form of the symbol name in interface::method
        // form using ANSI characters
        char pszMungedName[512];
        wsprintfA(  pszMungedName,"%ls::%ls",
                    pszInterfaceName,pszMemberName );

        INVOKEKIND invkind = pFuncDesc->invkind;

        // If it's a property "get" or "put", append a meaningful ending.
        // The "put" and "get" will have identical names, so we want to
        // make them into unique names
        if ( INVOKE_PROPERTYGET == invkind )
            strcat( pszMungedName, "_get" );
        else if ( INVOKE_PROPERTYPUT == invkind )
            strcat( pszMungedName, "_put" );
        else if ( INVOKE_PROPERTYPUTREF == invkind )
            strcat( pszMungedName, "_putref" );
                    

        // Convert the virtual address to a logical address
        unsigned short section;
        unsigned long offset;
        
        if ( VAToSectionOffset((PVOID)pFunction, section, offset) )
            g_pfnCoClassSymsAddSymbol( section, offset, pszMungedName );
        
        pITypeInfo->ReleaseFuncDesc( pFuncDesc );                        
    }
}
•
•
•

Figure 8   Generating .MAP Files


#include <windows.h>
#include <imagehlp.h>
#include <stdio.h>
#include <stdlib.h>

//=========================== Global Variables ===============================

LOADED_IMAGE g_loadedImage;
FILE * g_pMapFile;

//============================================================================

BOOL __stdcall CoClassSymsBeginSymbolCallouts( LPCSTR pszExecutable )
{
    if ( !MapAndLoad( (LPSTR)pszExecutable, 0, &g_loadedImage, FALSE, TRUE ) )
    {
        printf( "Unable to access or load executable\n" );
        return 0;
    }

    char szExeBaseName[MAX_PATH];
    char szMapFileName[MAX_PATH];
    _splitpath( pszExecutable, 0, 0, szExeBaseName, 0 );
    sprintf( szMapFileName, "%s.MAP", szExeBaseName );
    
    g_pMapFile = fopen( szMapFileName, "wt" );
    if ( !g_pMapFile )
        return FALSE;

    fprintf( g_pMapFile,
            " Start         Length     Name                   Class\n" );

    PIMAGE_SECTION_HEADER pSectHdr = g_loadedImage.Sections;
            
    for (   unsigned i=1;
            i <= g_loadedImage.NumberOfSections;
            i++, pSectHdr++ )
    {
        fprintf(     g_pMapFile,
                    " %04X:00000000 %08XH %-23.8hs %s\n",
                    i, pSectHdr->Misc.VirtualSize, pSectHdr->Name,
                    pSectHdr->Characteristics & IMAGE_SCN_CNT_CODE
                    ? "CODE" : "DATA" );
    }

    fprintf( g_pMapFile, 
        "\n  Address         Publics by Value              Rva+Base\n\n");    

    return TRUE;
}

BOOL __stdcall CoClassSymsAddSymbol(
    unsigned short section,
    unsigned long offset,
    PSTR pszSymbolName )
{
    if ( !g_pMapFile )
        return FALSE;

    fprintf( g_pMapFile, " %04X:%08X       %-32s\n",
             section, offset, pszSymbolName );
                
    return true;
}
        
BOOL __stdcall CoClassSymsSymbolsFinished( void )
{
    if ( !g_pMapFile )
        return FALSE;
    
    DWORD entryRVA =
        g_loadedImage.FileHeader->OptionalHeader.AddressOfEntryPoint;
    
    PIMAGE_SECTION_HEADER pSectHdr;
    
    pSectHdr = ImageRvaToSection(   g_loadedImage.FileHeader,
                                    g_loadedImage.MappedAddress,
                                    entryRVA );
    if ( pSectHdr )
    {
        // Pointer math below!!!
        WORD section = (WORD)(pSectHdr - g_loadedImage.Sections) +1;
        DWORD offset = entryRVA - pSectHdr->VirtualAddress;
        
        fprintf( g_pMapFile, "\n entry point at        %04X:%08X\n",
                 section, offset );
    }
    
    fclose( g_pMapFile );

    UnMapAndLoad( &g_loadedImage );        // Undo the MapAndLoad call
    
    return TRUE;
}