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;
}
}
}