MSPOBJ.C
/* 
 * M S P O B J . C 
 * 
 * Common code for implementation of objects in the sample message store 
 * provider. 
 * 
 * Copyright (C) 1992-1995 Microsoft Corporation. All Rights Reserved. 
 */ 
 
#include "msp.h" 
 
/* Object Type to Neuter Function Map -------------------------------------- */ 
 
LPFNNEUTER rgfnNeuter[] = 
{ 
    (LPFNNEUTER) 0,             /* IMSProvider */ 
    (LPFNNEUTER) 0,             /* IMSLogon */ 
    (LPFNNEUTER) IMS_Neuter,    /* IMsgStore */ 
    (LPFNNEUTER) IFLD_Neuter,   /* IMAPIFolder */ 
    (LPFNNEUTER) IMSG_Neuter,   /* IMessage */ 
    (LPFNNEUTER) IATCH_Neuter,  /* IAttach */ 
    (LPFNNEUTER) 0,             /* IStream */ 
    (LPFNNEUTER) 0,             /* IMAPITable */ 
}; 
 
/* Object Type to Interfaces Supported Map --------------------------------- */ 
 
REFIID MSP_IIDs[] = 
{ 
    &IID_IMSProvider, 
    &IID_IUnknown 
}; 
 
REFIID MSL_IIDs[] = 
{ 
    &IID_IMSLogon, 
    &IID_IUnknown 
}; 
 
REFIID MS_IIDs[] = 
{ 
    &IID_IMsgStore, 
    &IID_IMAPIProp, 
    &IID_IUnknown 
}; 
 
REFIID FLD_IIDs[] = 
{ 
    &IID_IMAPIFolder, 
    &IID_IMAPIContainer, 
    &IID_IMAPIProp, 
    &IID_IUnknown 
}; 
 
REFIID MSG_IIDs[] = 
{ 
    &IID_IMessage, 
    &IID_IMAPIProp, 
    &IID_IUnknown 
}; 
 
REFIID ATCH_IIDs[] = 
{ 
    &IID_IAttachment, 
    &IID_IMAPIProp, 
    &IID_IUnknown 
}; 
 
REFIID STM_IIDs[] = 
{ 
    &IID_IStream, 
    &IID_IUnknown 
}; 
 
REFIID TBL_IIDs[] = 
{ 
    &IID_IMAPITable, 
    &IID_IUnknown 
}; 
 
REFIID *rgpriid[] = 
{ 
    MSP_IIDs, 
    MSL_IIDs, 
    MS_IIDs, 
    FLD_IIDs, 
    MSG_IIDs, 
    ATCH_IIDs, 
    STM_IIDs, 
    TBL_IIDs 
}; 
 
#define OBJ_IsInvalid(pobj, f)                                  \ 
    (IsBadWritePtr(pobj, sizeof(OBJ))                           \ 
    || IsBadReadPtr((pobj)->lpVtbl, sizeof(OBJ_Vtbl))           \ 
    || (pobj)->lpVtbl->f != OBJ_##f                             \ 
    || (pobj)->wType >= OT_MAX) 
 
/* OBJ_QueryInterface ------------------------------------------------------ */ 
 
BOOL  
FQueryInterface(int wType, REFIID riid) 
{ 
    REFIID *priid = rgpriid[wType]; 
 
    while (1) 
    { 
        if (IsEqualGUID(riid, *priid)) 
            return (TRUE); 
 
        if (*priid == &IID_IUnknown) 
            break; 
 
        priid += 1; 
    } 
 
    return (FALSE); 
} 
 
STDMETHODIMP  
OBJ_QueryInterface(POBJ pobj, REFIID riid, LPVOID * ppvObj) 
{ 
    if (    OBJ_IsInvalid(pobj, QueryInterface) 
        ||  IsBadReadPtr(riid, sizeof(IID)) 
        ||  IsBadWritePtr(ppvObj, sizeof(LPVOID))) 
        return ResultFromScode(E_INVALIDARG); 
 
    /* Even if an error is returned, must zero *ppvObj */ 
    *ppvObj = 0; 
 
    if (FQueryInterface(pobj->wType, riid)) 
    { 
        UlAddRef(pobj); 
        *ppvObj = pobj; 
        return (0); 
    } 
 
    return (ResultFromScode(E_NOINTERFACE)); 
} 
 
/* OBJ_AddRef -------------------------------------------------------------- */ 
 
STDMETHODIMP_(ULONG) OBJ_AddRef(POBJ pobj) 
{ 
    LONG cRef; 
 
    if (OBJ_IsInvalid(pobj, AddRef)) 
    { 
        TraceSz1("Sample MS: OBJ_AddRef(pobj=%08lX): Object is invalid and " 
            "is being ignored", pobj); 
        return (0); 
    } 
 
    OBJ_EnterCriticalSection(pobj); 
 
#ifdef DEBUG 
    if (pobj->wType == OT_MSGSTORE) 
        Assert(!OBJ_TestFlag(pobj, MSF_BEINGDESTROYED)); 
#endif 
 
    AssertSz1(      pobj->wType == OT_MSGSTORE 
              ||    pobj->cRef >= 1, "OBJ_AddRef(): Bogus cRef (%08lX)", pobj->cRef); 
 
    cRef = ++pobj->cRef; 
 
    OBJ_LeaveCriticalSection(pobj); 
 
    return (cRef); 
} 
 
/* OBJ_Release ------------------------------------------------------------- */ 
 
/* Used by Message Store, Message, and Attachment objects. All other objects */ 
/* have their own implementations of Release. */ 
 
STDMETHODIMP_(ULONG) OBJ_Release(POBJ pobj) 
{ 
    LONG cRef; 
 
    if (!pobj) 
        return (0); 
 
    if (OBJ_IsInvalid(pobj, Release)) 
    { 
        TraceSz1("SampleMS: OBJ_Release(pobj=%08lX): Object is invalid and is " 
            "being ignored", pobj); 
        return (0); 
    } 
 
    OBJ_EnterCriticalSection(pobj); 
 
    AssertSz(pobj->cRef > 0, "OBJ_Release(): Too many releases"); 
 
    cRef = --pobj->cRef; 
 
    if (cRef == 0) 
    { 
        if (pobj->wType != OT_MSGSTORE) 
            pobj->lpVtbl = 0; 
 
        if (pobj->pobjHead == 0) 
        { 
            OBJ_Destroy(pobj); 
            return (0); 
        } 
    } 
 
    OBJ_LeaveCriticalSection(pobj); 
 
    return (cRef); 
} 
 
/* OBJ_Enqueue / OBJ_Dequeue / OBJ_Destroy --------------------------------- */ 
 
void OBJ_Enqueue(POBJ pobj, POBJ pobjParent) 
{ 
    pobj->pobjParent = pobjParent; 
    pobj->pobjNext = pobjParent->pobjHead; 
    pobjParent->pobjHead = pobj; 
} 
 
void OBJ_Dequeue(POBJ pobj) 
{ 
    if (pobj->pobjParent != NULL) 
    { 
        POBJ *ppobj = &pobj->pobjParent->pobjHead; 
 
        while (TRUE) 
        { 
            POBJ pobjCur = *ppobj; 
 
            if (pobjCur == NULL) 
                break; 
 
            if (pobjCur == pobj) 
            { 
                *ppobj = pobj->pobjNext; 
                break; 
            } 
 
            ppobj = &pobjCur->pobjNext; 
        } 
 
        pobj->pobjParent = 0; 
    } 
} 
 
/*  
 * OBJ_Destroy 
 *  
 * Destroy an object. If this object was the last thing causing the parent to 
 * exist, then we should destroy the parent, and so on up the chain. There are 
 * two actual critical sections in the sample store. One is in the msp (message 
 * store provider) object. The other is in the msl (message store logon) object. 
 * All other objects in the sample store contain a pointer to the msl object's  
 * critical section (they all share it). When we arrive at this routine, we  
 * should have the object's critical section locked, i.e., the msl critical 
 * section should be locked. That's why there are calls to leave a critical 
 * section that aren't balanced with an enter. 
 */ 
 
void OBJ_Destroy(POBJ pobj) 
{ 
    PIMS pims; 
    POBJ pobjParent; 
    LPFNNEUTER lpfnNeuter; 
    LPMAPISUP psup; 
 
    pims = pobj->pims; 
 
    while (1) 
    { 
        /* Call a routine to make the object free any memory */ 
        /* or other structures it has. (We call this "neutering" the object.) */ 
 
        if ((lpfnNeuter = rgfnNeuter[pobj->wType]) != 0) 
            lpfnNeuter(pobj); 
 
        pobjParent = pobj->pobjParent; 
 
        if (pobj == (POBJ) pims) 
        { 
            if (pobjParent != NULL) 
            { 
                /* The parent in this case is the msp (message store provider) */ 
                /* object. We need to get its critical section in order to */ 
                /* safely dequeue the message store object. */ 
 
                OBJ_EnterCriticalSection(pobjParent); 
                OBJ_Dequeue(pobj); 
                OBJ_LeaveCriticalSection(pobjParent); 
            } 
 
            pobjParent = (POBJ) pims->pmsl; 
            psup = pims->psup; 
 
            pobj->lpVtbl = 0; 
            LMFree(&pims->lmr, pobj); 
 
            /* This leave balances the enter in the calling function. */ 
            /* Since we just freed the message store object, we can't leave */ 
            /* critical section using that pointer. Therefore, use the msl */ 
            /* (message store logon) object's critical section instead. They */ 
            /* are the same (see header comment at top of function). */ 
 
            OBJ_LeaveCriticalSection(pobjParent); 
 
            UlRelease(psup);    /* do this last */ 
            break; 
        } 
 
        OBJ_Dequeue(pobj); 
 
        pobj->lpVtbl = 0; 
        LMFree(&pims->lmr, pobj); 
 
        pobj = pobjParent; 
 
        if (pobj == 0 || pobj->cRef || pobj->pobjHead) 
        { 
            /* This leave balances the enter in the calling function. */ 
 
            OBJ_LeaveCriticalSection((POBJ) pims); 
            break; 
        } 
    } 
}