//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1997.
//
//  File:       cdispmgr.cxx
//
//  Contents:   The dispatch manager -- a class to manage
//		multiple IDispatch-callable interfaces.
//
//  History:    ??-???-??   KrishnaG   created
//		07-Sep-97   t-blakej   Commented, cleaned up, made
//                                     independent of ADSI.
//
//----------------------------------------------------------------------------

//
// Dispatch manager description:
//

//
// The dispatch manager is a way to invoke methods on an object that has
// more than one COM interface.  The regular GetIDsOfNames and Invoke
// methods assume only a single ITypeInfo to look up and call names from;
// if an object has more than one interface, it has multiple ITypeInfo's,
// and it has to explicitly check each one.  The dispatch manager implements
// the IDispatch methods, and keeps track of as many ITypeInfos as necessary.
//
// To use the dispatch manager, an object should store a pointer to a
// CAggregatorDispMgr object and delegate all IDispatch calls to it, or perhaps
// inherit from it directly.  The method
//
//   HRESULT CAggregatorDispMgr::LoadTypeInfoEntry(
//       REFIID libid, REFIID iid, void *pIntf, DISPID SpecialId)
//
// is used to load the type information for the object into the dispatch
// manager.  The arguments to this method are:
//
//   REFIID libid		- IID of the type library to load from
//   REFIID iid			- IID of the interface to load
//   void *pIntf		- pointer to the interface on the containing
//				  object
//   DISPID SpecialId		- DISPID_REGULAR for most things (see below);
//				  DISPID_VALUE for interfaces which implement
//				    the containing object's "value" property;
//				  DISPID_NEWENUM for interfaces which implement
//				    the containing object's "NewEnum" method
//
// DISPID_REGULAR is defined to be 1 by all ADSI providers, but not in
// any top-level include file.  So non-ADSI users of the dispatch manager will 
// probably have to define it explicitly.
//
// The LoadTypeInfoEntry method should be called at constructor time of
// the containing object.  After all the type information is loaded, the
// dispatch manager can start servicing GetIDsOfNames and Invoke calls.
//
//
// For ADSI, there are two other calls to load information into the dispatch
// manager:
//
//   void CAggregatorDispMgr::RegisterPropertyCache(IPropertyCache *pPropertyCache);
//   void CAggregatorDispMgr::RegisterBaseDispatchPtr(IDispatch *pDispatch);
//
// The first method registers a property cache of the containing object;
// this is used in the ADSI providers to cache attributes of a directory
// server object.  See iprops.hxx for more information.
//
// The second method is a hack used to get around a lack of inheritance.
// If an object A implements IDispatch and some other dual interfaces,
// and an object B derives from A and also implements IDispatch and some
// other dual interfaces, this method can be used in object B to use A's
// dispatch manager as a "backup" to its own.  This way B doesn't have to
// load the type information about the interfaces it inherits from A.
// (Also, if A has an ADSI property cache, then callers of B's IDispatch
// methods can get at the underlying property cache.)
//
// The function
//
//   void FreeTypeInfoTable();
//
// should be called at global destructor or library-unload time.  Currently,
// all the ADSI libraries do it separately.
//
//
// The DISPIDs returned by GetIDsOfNames are 32 bit values laid out
// as follows:
//
//    3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
//    1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
//   +-+-------------+---------------+-------------------------------+
//   |X|  DispMgrId  |  TypeInfoId   |              DispId           |
//   +-+-------------+---------------+-------------------------------+
//
// where
//
//     X - is reserved and never set.  This would turn the value negative
//         (which would overlap with some Automation-reserved DISPIDs.)
//
//     DispMgrId - identifies the dispatch manager (used when stringing
//         dispatch managers together via RegisterBaseDispatchPtr).
//
//     TypeInfoId - uniquely identifies the interface within this
//         dispatch manager.
//
//     DispId - uniquely identifies the name within the interface.
//
// So if an object uses the dispatch manager, it shouldn't try to only use
// it for just GetIDsOfNames or just Invoke, since the DISPIDs returned are
// not necessarily the ones in the type library.
//
//////////////////////////////////////////////////////////////////////////////

// Forward declarations:
struct IPropertyCache;
/*
typedef struct _typeinfoentry
{
    LONG TypeInfoId;
    void *ptypeinfo;
    void *pInterfacePointer;
    struct _typeinfoentry *pNext;
} TYPEINFOENTRY, *PTYPEINFOENTRY;*/

class CAggregatorDispMgr
{
public:
    CAggregatorDispMgr::CAggregatorDispMgr();
    CAggregatorDispMgr::~CAggregatorDispMgr();

    //
    // The IDispatch methods are the main interface of the Dispatch Manager.
    //
    STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo);

    STDMETHOD(GetTypeInfo)(THIS_ UINT itinfo, LCID lcid, ITypeInfo **pptinfo);

    STDMETHOD(GetIDsOfNames)(THIS_ REFIID riid, LPWSTR *rgszNames,
        UINT cNames, LCID lcid, DISPID *rgdispid);

    STDMETHOD(Invoke)(THIS_ DISPID dispidMember, REFIID riid, LCID lcid,
        WORD wFlags, DISPPARAMS *pdispparams, VARIANT *pvarResult,
        EXCEPINFO *pexcepinfo, UINT *puArgErr);

    //
    // Methods for initializing the dispatch manager.
    //
    void
    CAggregatorDispMgr::RegisterPropertyCache(IPropertyCache* pPropertyCache);

    void
    CAggregatorDispMgr::RegisterBaseDispatchPtr(IDispatch *pDispatch);

    HRESULT
    CAggregatorDispMgr::LoadTypeInfoEntry(
	REFIID libid,
	REFIID iid,
	void * pIntf,
	DISPID SpecialId
	);

private:
    void *
    CAggregatorDispMgr::getInterfacePtr(LONG TypeInfoId);

    ITypeInfo *
    CAggregatorDispMgr::getTypeInfo(LONG TypeInfoId);

    PTYPEINFOENTRY
    CAggregatorDispMgr::FindTypeInfoEntry(LONG TypeInfoId);

    HRESULT
    CAggregatorDispMgr::AddTypeInfo(void FAR *ptypeinfo, void * pIntfptr);


    STDMETHODIMP
    CAggregatorDispMgr::TypeInfoInvoke(DISPID dispidMember, REFIID iid, LCID lcid,
            unsigned short wFlags, DISPPARAMS FAR* pdispparams,
            VARIANT FAR* pvarResult, EXCEPINFO FAR* pexcepinfo,
            unsigned int FAR* puArgErr);

    HRESULT
    CAggregatorDispMgr::MarkAsNewEnum(void *pTypeInfo);

    HRESULT
    CAggregatorDispMgr::MarkAsItem(void *pTypeInfo);

    PTYPEINFOENTRY
    CAggregatorDispMgr::FindTypeInfo(void *pTypeInfo);

    LONG
    CAggregatorDispMgr::gentypeinfoid();

protected:

    LONG        _dwTypeInfoId;
    PTYPEINFOENTRY _pTypeInfoEntry;
    PTYPEINFOENTRY _pDispidNewEnum;
    PTYPEINFOENTRY _pDispidValue;

    IPropertyCache *_pPropertyCache;
    LONG _dwPropCacheID;
};

#define BAIL_IF_ERROR(hr) if (FAILED(hr)) { goto cleanup; }

// deprecated
HRESULT
LoadTypeInfoEntry(
    CAggregatorDispMgr * pDispMgr,
    REFIID libid,
    REFIID iid,
    void * pIntf,
    DISPID SpecialId
    );

void
FreeTypeInfoTable();


HRESULT
DynamicDispidInvoke(
    IPropertyCache * pPropertyCache,
    DISPID  dispid,
    unsigned short wFlags,
    DISPPARAMS *pdispparams,
    VARIANT * pvarResult
    );

BOOL
AggregatorDllCanUnload();