/* * e x o . c p p * * Purpose: * Base Exchange COM Object * * Any Exchange object that implements one or more COM interfaces * should 'derive' from EXObject using the macros below. * * Originator: * JohnKal * Owner: * BeckyAn * * Copyright (C) Microsoft Corp 1993-1997. All rights reserved. */ #pragma warning(disable:4201) /* nameless struct/union */ #pragma warning(disable:4514) /* unreferenced inline function */ #include #include #include #include #include "exo.h" #ifndef NCHAR #define NCHAR CHAR #endif // !NCHAR // Debugging traces. // Change the function name in the define to match your system's debug calls. #ifdef DBG BOOL g_fExoDebugTraceOn = -1; #define ExoDebugTrace (!g_fExoDebugTraceOn)?0:DebugTrace #else // !DBG #define ExoDebugTrace 1?0:DebugTrace #endif // DBG, else // Forward function declarations //////////////////////////////////////// #ifdef DBG // EXO supporting debug structures. #define IID_PAIR(_iid) { (IID *) & IID_ ## _iid, #_iid } const struct { IID * piid; LPSTR szIidName; } c_rgiidpair[] = { IID_PAIR(IUnknown), IID_PAIR(IDataObject), // IID_PAIR(IMAPITable), IID_PAIR(IOleObject), IID_PAIR(IOleInPlaceObject), IID_PAIR(IPersist), // IID_PAIR(IPersistMessage), IID_PAIR(IPersistStorage), IID_PAIR(IStorage), IID_PAIR(IStream), IID_PAIR(IViewObject), IID_PAIR(IViewObject2), // IID_PAIR(IMAPIForm), // IID_PAIR(IMAPIFormAdviseSink), // IID_PAIR(IMAPISession), IID_PAIR(IMoniker), IID_PAIR(IROTData), { 0, 0 } // end of table marker. }; // EXO supporting debug function. // Gets a name for a given IID. LPCSTR NszFromIid(REFIID riid) { int i = 0; static NCHAR rgnch[80]; //$ REVIEW: not thread safe. while (c_rgiidpair[i].piid) { if (*c_rgiidpair[i].piid == riid) return c_rgiidpair[i].szIidName; ++i; } wsprintfA(rgnch, "{%08lx-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x}", riid.Data1, riid.Data2, riid.Data3, riid.Data4[0],riid.Data4[1],riid.Data4[2],riid.Data4[3], riid.Data4[4],riid.Data4[5],riid.Data4[6],riid.Data4[7]); return rgnch; } #endif // DBG // EXO base class: implementation //////////////////////////////////////// // EXO's own interface mapping table & class info definitions. BEGIN_INTERFACE_TABLE(EXO) END_INTERFACE_TABLE(EXO); DECLARE_EXOCLSINFO(EXO) = EXOCLSINFO_CONTENT_EX( EXO, NULL, exotypNonserver, &CLSID_NULL, NULL ); EXO::EXO() : m_cRef(1) { } EXO::~EXO() { // must have this or linker whines. } /* * EXO::InternalQueryInterface * * Purpose: * Given an interface ID, check to see if the object implements the interface. * If the interface is supported, return a pointer to it. * Otherwise, return E_NOINTERFACE. * This function scans the object's classinfo chain, starting at the lowest * level, looking for the requested IID. The lowest classinfo struct is * obtained by calling a virtual function. * * Arguments: * riid in IID of the requested interface. * ppvOut out Returned interface pointer. * * Returns: * S_OK or E_NOINTERFACE. * * Notes: * This needs to be virtual for several reasons: * 1) so classes derived from EXO can aggregate other objects and still have EXO routines * access the interfaces of the aggregated object, * 2) so that EXOA_UNK members can call it * All QI work should be routed here using EXO[A]_INCLASS_DECL(). * (The only exception is if you are an aggregator -- you have a kid -- * and then you should still CALL this function to search throught your own interfaces.) * Notes: * Note that pexoclsinfo is looked up using the virtual call to GetEXOClassInfo instead of using the object's m_pexoclsinfo. This is so * that a derived object's IIDINFO table need only contain interfaces new to that object instead of * all the interfaces of the derived object and all the interfaces of it's parent. * A class's InternalQueryInterface() method general calls this method explicitly passing * the classes EXOCLSINFO (*not* m_pexoclsinfo) and if that fails calls it's parent's * InternalQueryInterface(). */ HRESULT EXO::InternalQueryInterface(REFIID riid, LPVOID * ppvOut) { UINT ciidinfo; const EXOCLSINFO * pexoclsinfo; const IIDINFO * piidinfo; #ifdef DBG #ifdef UNICODE UsesMakeANSI; LPCSTR szClassName = MakeANSI(GetEXOClassInfo()->szClassName); #else // !UNICODE LPCSTR szClassName = GetEXOClassInfo()->szClassName; #endif // !UNICODE ExoDebugTrace("%s::QueryInterface(%08lx): being asked for %s\n", szClassName, this, NszFromIid(riid)); #endif // DBG Assert(ppvOut); *ppvOut = NULL; // Get the lowest (leaf) classinfo for this object. pexoclsinfo = GetEXOClassInfo(); // Search up the classinfo chain. EXO's parent classinfo pointer is NULL, // and will terminate this loop. while (pexoclsinfo) { // Get the interface mapping table from the classinfo struct. ciidinfo = pexoclsinfo->ciidinfo; piidinfo = pexoclsinfo->rgiidinfo; // Search through this interface mapping table. for ( ; ciidinfo--; ++piidinfo) { // If the iid is found. if (*piidinfo->iid == riid) { // Apply the offset for this iid. IUnknown * const punk = EXOApplyDbCast(IUnknown, this, piidinfo->cbDown, piidinfo->cbUp); #ifdef DBG // Uses a debug-only variable. ExoDebugTrace("%s::QueryInterface(%08lx): cRef: %d -> %d\n", szClassName, this, m_cRef, m_cRef+1); #endif // DBG // Need to AddRef the resulting object. This ref is for the caller. *ppvOut = punk; punk->AddRef(); return S_OK; } } // Fetch the next classinfo struct up the chain. pexoclsinfo = pexoclsinfo->pexoclsinfoParent; } // No support for the requested inteface. #ifdef DBG // Uses a debug-only variable. ExoDebugTrace("%s::QueryInterface(%08lx): E_NOINTERFACE\n", szClassName, this); #endif // DBG return E_NOINTERFACE; } /* * EXO::InternalAddRef * * Purpose: * Increments the reference count on the object. * * Arguments: * None. * * Returns: * The new reference count. */ ULONG EXO::InternalAddRef() { #ifdef DBG #ifdef UNICODE UsesMakeANSI; ExoDebugTrace("%s::AddRef(%08lx): cRef: %ld->%ld\n", MakeANSI(GetEXOClassInfo()->szClassName), this, m_cRef, m_cRef+1); #else // !UNICODE ExoDebugTrace("%s::AddRef(%08lx): cRef: %ld->%ld\n", GetEXOClassInfo()->szClassName, this, m_cRef, m_cRef+1); #endif // !UNICODE #endif // DBG // NOTE: On Win95 or NT3.51, this won't return the exact m_cRef.... // (People shouldn't depend on the value returned from AddRef anyway!!!) // return InterlockedIncrement(&m_cRef); } /* * EXO::InternalRelease * * Purpose: * Decrements the reference count on the object. If the reference * count reaches zero, we destroy the object. * * Arguments: * None. * * Returns: * The new reference count, 0 if the object is destroyed. */ ULONG EXO::InternalRelease() { ULONG cRef; #ifdef DBG #ifdef UNICODE UsesMakeANSI; ExoDebugTrace("%s::Release(%08lx): cRef: %ld->%ld\n", MakeANSI(GetEXOClassInfo()->szClassName), this, m_cRef, m_cRef-1); #else // !UNICODE ExoDebugTrace("%s::Release(%08lx): cRef: %ld->%ld\n", GetEXOClassInfo()->szClassName, this, m_cRef, m_cRef-1); #endif // !UNICODE #endif // DBG AssertSz(m_cRef > 0, "cRef is already 0!"); cRef = InterlockedDecrement(&m_cRef); if (0 == cRef) { delete this; return 0; } return cRef; } // Implementation of EXOA::EXOA_UNK methods ////////////////// /* * EXOA::EXOA_UNK::QueryInterface * * Purpose: * Given an interface ID 'riid', first calls out through a virtual * function EXOA::AggregatorQueryInterface() to allow an aggregated * aggregator to QI any of his children for the interface pointer. * If that doesn't result in an interface, we then perform the * EXO::QueryInterface() scan on the derived object itself. * * Arguments: * riid in IID of the requested interface. * ppvOut out Returned interface pointer. * * Returns: * S_OK or E_NOINTERFACE. */ STDMETHODIMP EXOA::EXOA_UNK::QueryInterface(REFIID riid, LPVOID * ppvOut) { // We need to preserve object identity when IUnknown is requested. if (IID_IUnknown == riid) { *ppvOut = this; AddRef(); return S_OK; } return m_pexoa->InternalQueryInterface(riid, ppvOut); } /* * EXOA::EXOA_UNK::AddRef * * Purpose: * Increases the reference count on the object, by deferring * to EXO::AddRef(). Note that we're _not_ calling EXOA::Addref() * as that would release the refcount of the aggregate, not this * object. * * Arguments: * None. * * Returns: * The new reference count. */ STDMETHODIMP_(ULONG) EXOA::EXOA_UNK::AddRef() { return m_pexoa->InternalAddRef(); } /* * EXOA::EXOA_UNK::Release * * Purpose: * Decreases the reference count on the object, by deferring * to EXO::Release(). Note that we're _not_ calling EXOA::Release() * as that would release the refcount of the aggregate, not this * object. * * Arguments: * None. * * Returns: * The new reference count. */ STDMETHODIMP_(ULONG) EXOA::EXOA_UNK::Release() { return m_pexoa->InternalRelease(); } // Implementation of EXOA methods //////////////////////////////////////// EXOA::EXOA(IUnknown * punkOuter) { m_exoa_unk.m_pexoa = this; m_punkOuter = (punkOuter) ? punkOuter : &m_exoa_unk; } EXOA::~EXOA() { // must have this or linker whines. } // end of exo.cpp ////////////////////////////////////////