//////////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 1999 - 2000 Microsoft Corporation
//
//  Module Name:
//      CITracker.H
//
//  Description:
//      Implementation of a COM interface tracker.
//
//  [Documentation:]
//      Debugging.PPT - A power-point presentation of the debug utilities.
//
//  Maintained By:
//      Geoffrey Pease (gpease) 19-NOV-1999
//
//  Notes:
//      This is X86 specific for now. It can be adapted for other platforms
//      as required. Since today, most of our developement is done on the
//      X86 platform, there is not a need to do this (yet).
//
//      Define NO_TRACE_INTERFACES to disable interface tracking in DEBUG
//      builds.
//
//      Define TRACE_INTERFACES_ENABLED to enable interface tracking in RETAIL
//      builds.
//
//////////////////////////////////////////////////////////////////////////////

#pragma once

//
// Combine this complex expression in to one simple #define.
//
#if ( DBG==1 || defined( _DEBUG ) ) && !defined( NO_TRACE_INTERFACES )
#define TRACE_INTERFACES_ENABLED
#endif

#if defined( _X86_ )
///////////////////////////////////////
//
// BEGIN _X86_
//

#if defined( TRACE_INTERFACES_ENABLED )
///////////////////////////////////////
//
// BEGIN DEBUG INTERFACE TRACKING
//
#pragma message("BUILD: Interface Tracking Enabled")

///////////////////////////////////////
//
// TraceInterface definitions
//
typedef struct {
    const struct _GUID *    riid;
    LPCTSTR                 pszName;
    ULONG                   cFunctions;
} INTERFACE_TABLE[], INTERFACE_TABLE_ENTRY, * LPINTERFACE_TABLE_ENTRY;

//
// Interface Table
//
// This table is used in builds in which interface tracking was turned on. It
// is used to map an name with a particular IID. It also helps the CITracker
// determine the size of the interfaces Vtable to mimic (haven't figured out 
// a runtime or compile time way to do this).
// 
extern const INTERFACE_TABLE g_itTable;

//////////////////////////////////////////////////////////////////////////////
//
// MACRO
// _interface *
// TraceInterface(
//      _nameIn,
//      _interface,
//      _punkIn,
//      _addrefIn
//      )
//
// Description:
//      This is the macro wrapper for DebugTrackInterface( ) which is only
//      defined in DEBUG builds. Using the TraceInterface( ) macro eliminates
//      the need for specifying compile time parameters as well as the
//      #ifdef/#endif definitions around the call.
//
//      This "routine" creates a CITracker for the interface specified by
//      _interface and returns a new punk to the interface. You specify the
//      initial ref count on the interface using the _addrefIn parameter. You
//      can assign a name to the object that the interface refereneces in the
//      _nameIn parameter. The returned punk will be cast to the _interface
//      parameter.
//
//      If there is insufficent memory to create the CITracker, the _punkIn
//      will be returned instead. There is no need to check the output for
//      failures as they are all handle internally to provide worry-free use.
//
//      If you are getting an AV after adding tracing to the interface, this
//      usually indicates that the Interface Table entry for that interface
//      is incorrect. Double check the number of methods on the interface
//      against the tables.
//
// Arguments:
//      _nameIn     - Name of the object this interface references (string).
//      _interface  - Name of the interface (typedef).
//      _punkIn     - Pointer to interface to track
//      _addrefIn   - Initial reference count on the interface.
//
// Return Values:
//      a VALID _interface pointer
//          Points to the CITracker that can be used as if it were the
//          orginal _punkIn. If there was insufficent memory, the orginal
//          _punkIn will be returned.
//
//  NOTES:
//      _addrefIn should be 0 if your going to be giving out the interface 
//      from your QueryInterface routine if you AddRef( ) before giving it 
//      out (typical QueryInterface( ) routines do this ).
//
//////////////////////////////////////////////////////////////////////////////
#define  TraceInterface( _nameIn, _interface, _punkIn, _addrefIn ) \
    reinterpret_cast<_interface*>( \
        DebugTrackInterface( TEXT(__FILE__), \
                             __LINE__, \
                             __MODULE__, \
                             _nameIn, \
                             IID_##_interface, \
                             static_cast<_interface*>( _punkIn), \
                             _addrefIn \
                             ) )

///////////////////////////////////////
//
// IID --> Name lookup stuff
//
#if defined( DEBUG )
static const cchGUID_STRING_SIZE = sizeof("{12345678-1234-1234-1234-123456789012}");

#define PszTraceFindInterface( _riid, _szguid ) \
    ( g_tfModule ? PszDebugFindInterface( _riid, _szguid ) : TEXT("riid") )

LPCTSTR
PszDebugFindInterface(
    REFIID      riidIn,
    LPTSTR      pszGuidBufOut
    );
#endif // DEBUG

///////////////////////////////////////
//
// CITracker Structures
//
typedef HRESULT (CALLBACK *LPFNQUERYINTERFACE)(
    LPUNKNOWN punk, 
    REFIID    riid, 
    LPVOID*   ppv );

typedef ULONG (CALLBACK *LPFNADDREF)(
    LPUNKNOWN punk );

typedef ULONG (CALLBACK *LPFNRELEASE)( 
    LPUNKNOWN punk );

typedef struct __vtbl {
    LPFNQUERYINTERFACE lpfnQueryInterface;
    LPFNADDREF         lpfnAddRef;
    LPFNRELEASE        lpfnRelease;
} VTBL, *LPVTBL;

typedef struct __vtbl2 {
    ULONG              cRef;
    LPUNKNOWN          punk;
    LPCTSTR            pszInterface;
    DWORD              dwSize;
    LPVTBL             pNewVtbl;
    // These must be last and in this order: QI, AddRef, Release.
    LPFNQUERYINTERFACE lpfnQueryInterface;
    LPFNADDREF         lpfnAddRef;
    LPFNRELEASE        lpfnRelease;
    // additional vtbl entries hang off the end
} VTBL2, *LPVTBL2;

#define VTBL2OFFSET ( sizeof( VTBL2 ) - ( 3 * sizeof(LPVOID) ) )

///////////////////////////////////////
//
// CITracker Functions
//
LPUNKNOWN
DebugTrackInterface(
    LPCTSTR   pszFileIn,
    const int nLineIn,
    LPCTSTR   pszModuleIn,
    LPCTSTR   pszNameIn,
    REFIID    riidIn,
    LPUNKNOWN pvtblIn,
    LONG      cRefIn
    );
    
///////////////////////////////////////
//
// interface IUnknownTracker
//
//
class
IUnknownTracker :
    public IUnknown
{
public:
    STDMETHOD(QueryInterface)( REFIID riid, LPVOID *ppv );
    STDMETHOD_(ULONG, AddRef)( void );
    STDMETHOD_(ULONG, Release)( void );
    STDMETHOD_(void, Stub3 )( void );
    STDMETHOD_(void, Stub4 )( void );
    STDMETHOD_(void, Stub5 )( void );
    STDMETHOD_(void, Stub6 )( void );
    STDMETHOD_(void, Stub7 )( void );
    STDMETHOD_(void, Stub8 )( void );
    STDMETHOD_(void, Stub9 )( void );
    STDMETHOD_(void, Stub10 )( void );
    STDMETHOD_(void, Stub11 )( void );
    STDMETHOD_(void, Stub12 )( void );
    STDMETHOD_(void, Stub13 )( void );
    STDMETHOD_(void, Stub14 )( void );
    STDMETHOD_(void, Stub15 )( void );
    STDMETHOD_(void, Stub16 )( void );
    STDMETHOD_(void, Stub17 )( void );
    STDMETHOD_(void, Stub18 )( void );
    STDMETHOD_(void, Stub19 )( void );
    STDMETHOD_(void, Stub20 )( void );
    STDMETHOD_(void, Stub21 )( void );
    STDMETHOD_(void, Stub22 )( void );
    STDMETHOD_(void, Stub23 )( void );
    STDMETHOD_(void, Stub24 )( void );
    STDMETHOD_(void, Stub25 )( void );
    STDMETHOD_(void, Stub26 )( void );
    STDMETHOD_(void, Stub27 )( void );
    STDMETHOD_(void, Stub28 )( void );
    STDMETHOD_(void, Stub29 )( void );
    STDMETHOD_(void, Stub30 )( void );

}; //*** interface IUnknownTracker

///////////////////////////////////////
//
// interface IDeadObjTracker
//
class
IDeadObjTracker
{
private: // Members
    VTBL2 m_vtbl;

public:
    STDMETHOD( Stub0 )( LPVOID* punk );
    STDMETHOD( Stub1 )( LPVOID* punk );
    STDMETHOD( Stub2 )( LPVOID* punk );
    STDMETHOD( Stub3 )( LPVOID* punk );
    STDMETHOD( Stub4 )( LPVOID* punk );
    STDMETHOD( Stub5 )( LPVOID* punk );
    STDMETHOD( Stub6 )( LPVOID* punk );
    STDMETHOD( Stub7 )( LPVOID* punk );
    STDMETHOD( Stub8 )( LPVOID* punk );
    STDMETHOD( Stub9 )( LPVOID* punk );
    STDMETHOD( Stub10 )( LPVOID* punk );
    STDMETHOD( Stub11 )( LPVOID* punk );
    STDMETHOD( Stub12 )( LPVOID* punk );
    STDMETHOD( Stub13 )( LPVOID* punk );
    STDMETHOD( Stub14 )( LPVOID* punk );
    STDMETHOD( Stub15 )( LPVOID* punk );
    STDMETHOD( Stub16 )( LPVOID* punk );
    STDMETHOD( Stub17 )( LPVOID* punk );
    STDMETHOD( Stub18 )( LPVOID* punk );
    STDMETHOD( Stub19 )( LPVOID* punk );
    STDMETHOD( Stub20 )( LPVOID* punk );
    STDMETHOD( Stub21 )( LPVOID* punk );
    STDMETHOD( Stub22 )( LPVOID* punk );
    STDMETHOD( Stub23 )( LPVOID* punk );
    STDMETHOD( Stub24 )( LPVOID* punk );
    STDMETHOD( Stub25 )( LPVOID* punk );
    STDMETHOD( Stub26 )( LPVOID* punk );
    STDMETHOD( Stub27 )( LPVOID* punk );
    STDMETHOD( Stub28 )( LPVOID* punk );
    STDMETHOD( Stub29 )( LPVOID* punk );
    STDMETHOD( Stub30 )( LPVOID* punk );

}; //*** interface IDeadObject


///////////////////////////////////////
//
// CITracker Class
//
class
CITracker:
    public IUnknownTracker
{
private: // Members
    VTBL2 m_vtbl;

private: // Methods
    CITracker( );
    ~CITracker( );
    STDMETHOD(Init)( LPUNKNOWN * ppunkOut, 
                     LPUNKNOWN punkIn, 
                     const INTERFACE_TABLE_ENTRY * piteIn, 
                     LPCTSTR pszNameIn, 
                     LONG cRefIn 
                     );

public: // Methods
    friend LPUNKNOWN DebugTrackInterface( LPCTSTR    pszFileIn,
                                          const int  nLineIn,
                                          LPCTSTR    pszModuleIn,
                                          LPCTSTR    pszNameIn, 
                                          REFIID     riidIn, 
                                          LPUNKNOWN  pvtblIn, 
                                          LONG      cRefIn
                                          );

    // IUnknown
    STDMETHOD(QueryInterface)( REFIID riid, LPVOID *ppv );
    STDMETHOD_(ULONG, AddRef)(void);
    STDMETHOD_(ULONG, Release)(void);

}; //*** class CITracker

//
// END DEBUG INTERFACE TRACKING
//
///////////////////////////////////////
#else // !TRACE_INTERFACES_ENABLED
///////////////////////////////////////
//
// BEGIN DEBUG WITHOUT INTERFACE TRACKING
//

#define  TraceInterface( _nameIn, _interface, _punkIn, _faddrefIn ) static_cast<##_interface *>( _punkIn );
#define  PszDebugFindInterface( _riid, _szGuid ) L"<unknown>"

//
// END DEBUG INTERFACE TRACKING
//
///////////////////////////////////////
#endif // TRACE_INTERFACES_ENABLED

//
// END _X86_
//
///////////////////////////////////////
#else   // !_X86_
///////////////////////////////////////
//
// BEGIN !_X86_
//

#define  TraceInterface( _nameIn, _riidIn, _punkIn, _faddrefIn ) _punkIn
#define  PszDebugFindInterface( _riid, _szGuid ) L"<unknown>"
//
// END !_X86_
//
///////////////////////////////////////
#endif // _X86_
