text: /*
   *	VALCOPY.C
   *
   *	Utility functions for validating, copying, and (yeech) relocating
   *	complex MAPI structures.
   *
   *	For each data type there are three functions:
   *		ScCountXXX address-checks and calculates the size
   *		ScCopyXXX copies to a contiguous block of memory, which
   *			must be pre-allocated
   *		ScRelocXXX adjusts pointers, assuming that a structure in a
   *			contiguous block of memory has been moved
   *
   *	Data types supported:
   *		NOTIFICATION (and array of), in ScCountNotifications etc.
   *		SPropValue (and array of), in ScCountProps etc.
   *
   *	//$ SIZE Returning the byte count from ScRelocXXX may not be necessary.
   */
  
  #include <_apipch.h>
  
  
  
  #if defined (_AMD64_) || defined (_IA64_)
  #define AlignProp(_cb)	Align8(_cb)
  #else
  #define AlignProp(_cb)	(_cb)
  #endif
  
  #define ALIGN_RISC		8
  #define ALIGN_X86		1
  
  
  //	Pointer manipulation macros for use in the Reloc functions
  #ifdef WIN16
  #define SEG(_fp)	HIWORD((DWORD)_fp)
  #define OFF(_fp)	LOWORD((DWORD)_fp)
  
  #define PvRelocPv(_p,_baseOld,_baseNew) \
  	((LPVOID)MAKELONG(OFF(_p) - OFF(_baseOld) + OFF(_baseNew), SEG(_baseNew)))
  #else
  #define PvRelocPv(_p,_baseOld,_baseNew)	\
  	((LPVOID)((LPBYTE)(_p) - (LPBYTE)(_baseOld) + (LPBYTE)(_baseNew)))
  #endif
  
  
  #ifdef NOTIFICATIONS    // save this for notifications
  STDAPI_(SCODE)
  ScCountNotifications(int cntf, LPNOTIFICATION rgntf, ULONG FAR *pcb)
  {
  	ULONG			cb;
  	ULONG			cbT;
  	LPNOTIFICATION	pntf;
  	SCODE			sc = S_OK;
  
  	// validate parameters
  
  	AssertSz(!cntf || !IsBadReadPtr(rgntf, sizeof(NOTIFICATION) * cntf),
  			 TEXT("rgntf fails address check"));
  
  	AssertSz(!pcb || !IsBadWritePtr(pcb, sizeof(ULONG)),
  		 TEXT("pcb fails address check"));
  
  	for (cb = 0, pntf = rgntf; cntf--; ++pntf)
  	{
  		if (IsBadReadPtr(pntf, sizeof(NOTIFICATION)))
  		{
  			DebugTraceArg(ScCountNotification,  TEXT("pntf fails address check"));
  			goto badNotif;
  		}
  		cb += sizeof(NOTIFICATION);
  
  		switch (HIWORD(pntf->ulEventType))
  		{
  		case (fnevExtended >> 16):
  //		case (fnevSpooler >> 16):
  			//	fnevSpooler and fnevExtended both use the EXTENDED_NOTIFICATION
  			//	structure for their parameters
  			if (pntf->info.ext.cb &&
  				IsBadReadPtr(pntf->info.ext.pbEventParameters, (UINT)pntf->info.ext.cb))
  			{
  				DebugTraceArg(ScCountNotification,  TEXT("ext.pbEventParameters fails address check"));
  				goto badNotif;
  			}
  			cb += AlignProp(pntf->info.ext.cb);
  			break;
  
  		case 0:
  		{
  			switch (LOWORD(pntf->ulEventType))
  			{
  			case (USHORT)fnevCriticalError:
  			{
  				ERROR_NOTIFICATION FAR *perr = &pntf->info.err;
  
  				if ( IsBadReadPtr( perr->lpEntryID, (UINT)perr->cbEntryID ) )
  				{
  					DebugTraceArg( ScCountNotification,  TEXT("lpEntryID fails address check") );
  					goto badNotif;
  				}
  
  				cb += AlignProp(((UINT)perr->cbEntryID));
  
  				if (perr->lpMAPIError)
  				{
  					cb += AlignProp(sizeof( MAPIERROR ));
  
  #if defined(_WINNT) && !defined(MAC)
  					if (perr->ulFlags & MAPI_UNICODE)
  					{
  						//$	No error check in WIN16
  						if (IsBadStringPtrW((LPWSTR)perr->lpMAPIError->lpszError, INFINITE))
  						{
  							DebugTraceArg(ScCountNotification,  TEXT("err.MapiError.lpszError (UNICODE) fails address check"));
  							goto badNotif;
  						}
  
  						cb += AlignProp(((lstrlenW((LPWSTR)perr->lpMAPIError->lpszError) + 1)
  							* sizeof(WCHAR)));
  
  						if ( perr->lpMAPIError->lpszComponent )
  						{
  							if (IsBadStringPtrW((LPWSTR)perr->lpMAPIError->lpszComponent, INFINITE))
  							{
  								DebugTraceArg(ScCountNotification,  TEXT("err.MapiError.lpszComponent (UNICODE) fails address check"));
  								goto badNotif;
  							}
  
  							cb += AlignProp(((lstrlenW((LPWSTR)perr->lpMAPIError->lpszComponent) + 1)
  								* sizeof(WCHAR)));
  
  						}
  					}
  					else
  #endif
  					{
  						if (IsBadStringPtrA((LPSTR)perr->lpMAPIError->lpszError, INFINITE))
  						{
  							DebugTraceArg(ScCountNotification,  TEXT("err.MapiError.lpszError (ASCII) fails address check"));
  							goto badNotif;
  						}
  
  						cb += AlignProp((lstrlenA((LPSTR)perr->lpMAPIError->lpszError) + 1));
  
  						if ( perr->lpMAPIError->lpszComponent )
  						{
  							if (IsBadStringPtrA((LPSTR)perr->lpMAPIError->lpszComponent, INFINITE))
  							{
  								DebugTraceArg(ScCountNotification,  TEXT("err.MapiError.lpszError (ASCII) fails address check"));
  								goto badNotif;
  							}
  
  							cb += AlignProp((lstrlenA((LPSTR)perr->lpMAPIError->lpszComponent) + 1));
  						}
  					}
  				}
  				break;
  			}
  
  			case (USHORT)fnevNewMail:
  			{
  				NEWMAIL_NOTIFICATION FAR *pnew = &pntf->info.newmail;
  
  				if (IsBadReadPtr(pnew->lpEntryID, (UINT)pnew->cbEntryID))
  				{
  					DebugTraceArg(ScCountNotification,  TEXT("lpEntryID fails address check"));
  					goto badNotif;
  				}
  				cb += AlignProp(((UINT)pnew->cbEntryID));
  				if (IsBadReadPtr(pnew->lpParentID, (UINT)pnew->cbParentID))
  				{
  					DebugTraceArg(ScCountNotification,  TEXT("lpParentID fails address check"));
  					goto badNotif;
  				}
  				cb += AlignProp(((UINT)pnew->cbParentID));
  				if (pnew->lpszMessageClass)
  				{
  					if (pnew->ulFlags & MAPI_UNICODE)
  					{
  #if defined(_WINNT) && !defined(MAC)
  						//$	No error check in WIN16
  						if (IsBadStringPtrW((LPWSTR)pnew->lpszMessageClass, INFINITE))
  						{
  							DebugTraceArg(ScCountNotification,  TEXT("newmail.lpszMessageClass (UNICODE) fails address check"));
  							goto badNotif;
  						}
  #endif
  						cb += AlignProp(((lstrlenW((LPWSTR)pnew->lpszMessageClass) + 1)
  							* sizeof(WCHAR)));
  					}
  					else
  					{
  						if (IsBadStringPtrA((LPSTR)pnew->lpszMessageClass, INFINITE))
  						{
  							DebugTraceArg(ScCountNotification,  TEXT("newmail.lpszMessageClass (ASCII) fails address check"));
  							goto badNotif;
  						}
  						cb += AlignProp((lstrlenA((LPSTR)pnew->lpszMessageClass) + 1));
  					}
  				}
  
  				break;
  			}
  
  			case (USHORT)fnevObjectCreated:
  			case (USHORT)fnevObjectDeleted:
  			case (USHORT)fnevObjectModified:
  			case (USHORT)fnevObjectMoved:
  			case (USHORT)fnevObjectCopied:
  			case (USHORT)fnevSearchComplete:
  			{
  				OBJECT_NOTIFICATION FAR *pobj = &pntf->info.obj;
  
  				if (pobj->cbEntryID)
  				{
  					if (IsBadReadPtr(pobj->lpEntryID, (UINT)pobj->cbEntryID))
  					{
  						DebugTraceArg(ScCountNotifications,  TEXT("obj.lpEntryID fails address check"));
  						goto badNotif;
  					}
  					cb += AlignProp(pobj->cbEntryID);
  				}
  				if (pobj->cbParentID)
  				{
  					if (IsBadReadPtr(pobj->lpParentID, (UINT)pobj->cbParentID))
  					{
  						DebugTraceArg(ScCountNotifications,  TEXT("obj.lpParentID fails address check"));
  						goto badNotif;
  					}
  					cb += AlignProp(pobj->cbParentID);
  				}
  				if (pobj->cbOldID)
  				{
  					if (IsBadReadPtr(pobj->lpOldID, (UINT)pobj->cbOldID))
  					{
  						DebugTraceArg(ScCountNotifications,  TEXT("obj.lpOldID fails address check"));
  						goto badNotif;
  					}
  					cb += AlignProp(pobj->cbOldID);
  				}
  				if (pobj->cbOldParentID)
  				{
  					if (IsBadReadPtr(pobj->lpOldParentID, (UINT)pobj->cbOldParentID))
  					{
  						DebugTraceArg(ScCountNotifications,  TEXT("obj.lpOldParentID fails address check"));
  						goto badNotif;
  					}
  					cb += AlignProp(pobj->cbOldParentID);
  				}
  				if (pobj->lpPropTagArray)
  				{
  					if (IsBadReadPtr(pobj->lpPropTagArray, sizeof(ULONG)) ||
  						IsBadReadPtr(pobj->lpPropTagArray,
  							offsetof(SPropTagArray, aulPropTag) +
  								(UINT)pobj->lpPropTagArray->cValues * sizeof(ULONG)))
  					{
  						DebugTraceArg(ScCountNotifications,  TEXT("obj.lpPropTagArray fails address check"));
  						goto badNotif;
  					}
  					cb += AlignProp(offsetof(SPropTagArray, aulPropTag) +
  						pobj->lpPropTagArray->cValues * sizeof(ULONG));
  				}
  				break;
  			}
  
  			case (USHORT)fnevTableModified:
  			{
  				TABLE_NOTIFICATION FAR *ptn = &pntf->info.tab;
  				UINT	n = (UINT) ptn->ulTableEvent;
  
  				if (n != TABLE_CHANGED &&
  					n != TABLE_RELOAD &&
  					n != TABLE_ERROR &&
  					n != TABLE_ROW_ADDED &&
  					n != TABLE_ROW_DELETED &&
  					n != TABLE_ROW_MODIFIED &&
  					n != TABLE_SORT_DONE &&
  					n != TABLE_RESTRICT_DONE &&
  					n != TABLE_SETCOL_DONE)
  				{
  					DebugTraceArg(ScCountNotifications,  TEXT("invalid tab.ulTableEvent"));
  					goto badNotif;
  				}
  
  				if (ptn->propIndex.ulPropTag)
  				{
  					if (sc = ScCountProps(1, &ptn->propIndex, &cbT))
  						goto ret;
  					cb += cbT;
  				}
  				if (ptn->propPrior.ulPropTag)
  				{
  					if (sc = ScCountProps(1, &ptn->propPrior, &cbT))
  						goto ret;
  					cb += cbT;
  				}
  				if (ptn->row.cValues)
  				{
  					if (sc = ScCountProps((int)ptn->row.cValues, ptn->row.lpProps,
  							&cbT))
  						goto ret;
  					cb += cbT;
  				}
  				else if (ptn->row.lpProps)
  				{
  					DebugTraceArg(ScCountNotifications,  TEXT("non-NULL row.lpProps with zero row.cValues in table notification"));
  					goto badNotif;
  				}
  				break;
  			}
  
  			case (USHORT)fnevStatusObjectModified:
  			{
  				STATUS_OBJECT_NOTIFICATION FAR *pstat = &pntf->info.statobj;
  
  				if (pstat->cbEntryID)
  				{
  					if (IsBadReadPtr(pstat->lpEntryID, (UINT)pstat->cbEntryID))
  					{
  						DebugTraceArg(ScCountNotifications,  TEXT("statobj.lpEntryID fails address check"));
  						goto badNotif;
  					}
  					cb += AlignProp(pstat->cbEntryID);
  				}
  				if (pstat->cValues)
  				{
  					if (sc = ScCountProps((int)pstat->cValues,
  							pstat->lpPropVals, &cbT))
  						goto ret;
  					cb += cbT;
  				}
  				break;
  			}
  
  			default:
  				DebugTraceArg(ScCountNotification,  TEXT("invalid ulEventType"));
  				goto badNotif;
  			}
  			break;
  		}
  		default:
  			DebugTraceArg(ScCountNotification,  TEXT("invalid ulEventType"));
  			goto badNotif;
  		}
  	}
  
  	if (pcb)
  		*pcb = cb;
  
  ret:
  	DebugTraceSc(ScCountNotifications, sc);
  	return sc;
  
  badNotif:
  	//	trace already issued
  	return E_INVALIDARG;
  }
  
  STDAPI_(SCODE)
  ScCopyNotifications(int cntf, LPNOTIFICATION rgntf, LPVOID pvDst,
  	ULONG FAR *pcb)
  {
  	LPBYTE			pb = pvDst;
  	ULONG			cb = 0;
  	ULONG			cbT;
  	LPNOTIFICATION	pntf;
  	LPNOTIFICATION	pntfDst;
  	SCODE			sc = S_OK;
  
  	// validate parameters
  
  	AssertSz(!cntf || !IsBadReadPtr(rgntf, sizeof(NOTIFICATION) * cntf),
  			 TEXT("rgntf fails address check"));
  
  	AssertSz(!cntf || !IsBadWritePtr(pvDst, sizeof(NOTIFICATION) * cntf),
  			 TEXT("pvDst fails address check"));
  
  	AssertSz(!pcb || !IsBadWritePtr(pcb, sizeof(ULONG)),
  			 TEXT("pcb fails address check"));
  
  	cb = cntf * sizeof(NOTIFICATION);
  	MemCopy(pvDst, rgntf, (UINT)cb);
  	pb = (LPBYTE)pvDst + cb;
  
  	for (pntf = rgntf, pntfDst = (LPNOTIFICATION)pvDst;
  		cntf--;
  			++pntf, ++pntfDst)
  	{
  		switch (HIWORD(pntf->ulEventType))
  		{
  		case (fnevExtended >> 16):
  //     case (fnevSpooler >> 16):
  			if (pntf->info.ext.cb)
  			{
  				pntfDst->info.ext.pbEventParameters = pb;
  				cbT = pntf->info.ext.cb;
  				MemCopy(pb, pntf->info.ext.pbEventParameters, (UINT)cbT);
  				pb += AlignProp(cbT);
  				cb += AlignProp(cbT);
  			}
  			break;
  
  		case 0:
  		{
  			switch (LOWORD(pntf->ulEventType))
  			{
  			case (USHORT)fnevCriticalError:
  			{
  				ERROR_NOTIFICATION FAR *perr = &pntf->info.err;
  
  				if ( perr->cbEntryID )
  				{
  					pntfDst->info.err.lpEntryID = (LPENTRYID)pb;
  					cbT = perr->cbEntryID;
  					MemCopy(pb, perr->lpEntryID, (UINT)cbT);
  					pb += AlignProp(cbT);
  					cb += AlignProp(cbT);
  				}
  
  
  				if ( perr->lpMAPIError )
  				{
  					pntfDst->info.err.lpMAPIError = (LPMAPIERROR)pb;
  					cbT = sizeof(MAPIERROR);
  					MemCopy( pb, perr->lpMAPIError, sizeof( MAPIERROR ) );
  					pb += AlignProp(cbT);
  					cb += AlignProp(cbT);
  
  					if (perr->lpMAPIError->lpszError)
  					{
  #ifdef _WINNT
  						if (perr->ulFlags & MAPI_UNICODE)
  						{
  							cbT = (lstrlenW((LPWSTR)perr->lpMAPIError->lpszError)
  									+ 1) * sizeof(WCHAR);
  						}
  						else
  #endif
  						{
  							cbT = lstrlenA((LPSTR)perr->lpMAPIError->lpszError) + 1;
  
  						}
  
  						pntfDst->info.err.lpMAPIError->lpszError = (LPSTR)pb;
  						MemCopy(pb, perr->lpMAPIError->lpszError, (UINT)cbT);
  						pb += AlignProp(cbT);
  						cb += AlignProp(cbT);
  					}
  
  					if (perr->lpMAPIError->lpszComponent)
  					{
  #ifdef _WINNT
  						if (perr->ulFlags & MAPI_UNICODE)
  						{
  							cbT = (lstrlenW((LPWSTR)perr->lpMAPIError->lpszComponent)
  									+ 1) * sizeof(WCHAR);
  						}
  						else
  #endif
  						{
  							cbT = lstrlenA((LPSTR)perr->lpMAPIError->lpszComponent) + 1;
  
  						}
  
  						pntfDst->info.err.lpMAPIError->lpszComponent = pb;
  						MemCopy(pb, perr->lpMAPIError->lpszComponent, (UINT)cbT);
  						pb += AlignProp(cbT);
  						cb += AlignProp(cbT);
  					}
  				}
  
  				break;
  			}
  
  			case (USHORT)fnevNewMail:
  			{
  				NEWMAIL_NOTIFICATION FAR *pnew = &pntf->info.newmail;
  
  				if (pnew->cbEntryID)
  				{
  					pntfDst->info.newmail.lpEntryID = (LPENTRYID)pb;
  					cbT = pnew->cbEntryID;
  					MemCopy(pb, pnew->lpEntryID, (UINT)cbT);
  					pb += AlignProp(cbT);
  					cb += AlignProp(cbT);
  				}
  
  				if (pnew->cbParentID)
  				{
  					pntfDst->info.newmail.lpParentID = (LPENTRYID)pb;
  					cbT = pnew->cbParentID;
  					MemCopy(pb, pnew->lpParentID, (UINT)cbT);
  					pb += AlignProp(cbT);
  					cb += AlignProp(cbT);
  				}
  
  				if (pnew->lpszMessageClass)
  				{
  					if (pnew->ulFlags & MAPI_UNICODE)
  						cbT = (lstrlenW((LPWSTR)pnew->lpszMessageClass) + 1)
  							* sizeof(WCHAR);
  					else
  						cbT = lstrlenA((LPSTR)pnew->lpszMessageClass) + 1;
  					pntfDst->info.newmail.lpszMessageClass = (LPTSTR)pb;
  					MemCopy(pb, pnew->lpszMessageClass, (UINT)cbT);
  					pb += AlignProp(cbT);
  					cb += AlignProp(cbT);
  				}
  				break;
  			}
  
  			case (USHORT)fnevObjectCreated:
  			case (USHORT)fnevObjectDeleted:
  			case (USHORT)fnevObjectModified:
  			case (USHORT)fnevObjectMoved:
  			case (USHORT)fnevObjectCopied:
  			case (USHORT)fnevSearchComplete:
  			{
  				OBJECT_NOTIFICATION FAR *pobj = &pntf->info.obj;
  
  				if (pobj->cbEntryID)
  				{
  					pntfDst->info.obj.lpEntryID = (LPENTRYID)pb;
  					cbT = pobj->cbEntryID;
  					MemCopy(pb, pobj->lpEntryID, (UINT)cbT);
  					pb += AlignProp(cbT);
  					cb += AlignProp(cbT);
  				}
  				if (pobj->cbParentID)
  				{
  					pntfDst->info.obj.lpParentID = (LPENTRYID)pb;
  					cbT = pobj->cbParentID;
  					MemCopy(pb, pobj->lpParentID, (UINT)cbT);
  					pb += AlignProp(cbT);
  					cb += AlignProp(cbT);
  				}
  				if (pobj->cbOldID)
  				{
  					pntfDst->info.obj.lpOldID = (LPENTRYID)pb;
  					cbT = pobj->cbOldID;
  					MemCopy(pb, pobj->lpOldID, (UINT)cbT);
  					pb += AlignProp(cbT);
  					cb += AlignProp(cbT);
  				}
  				if (pobj->cbOldParentID)
  				{
  					pntfDst->info.obj.lpOldParentID = (LPENTRYID)pb;
  					cbT = pobj->cbOldParentID;
  					MemCopy(pb, pobj->lpOldParentID, (UINT)cbT);
  					pb += AlignProp(cbT);
  					cb += AlignProp(cbT);
  				}
  				if (pobj->lpPropTagArray)
  				{
  					cbT = offsetof(SPropTagArray, aulPropTag) +
  						pobj->lpPropTagArray->cValues * sizeof(ULONG);
  					pntfDst->info.obj.lpPropTagArray = (LPSPropTagArray)pb;
  					MemCopy(pb, pobj->lpPropTagArray, (UINT)cbT);
  					pb += AlignProp(cbT);
  					cb += AlignProp(cbT);
  				}
  				break;
  			}
  
  			case (USHORT)fnevTableModified:
  			{
  				TABLE_NOTIFICATION FAR *ptn = &pntf->info.tab;
  
  				if (ptn->propIndex.ulPropTag)
  				{
  					//	Wastes 16 bytes when the property doesn't have a tail
  					if (sc = ScCopyProps(1, &ptn->propIndex, pb, &cbT))
  						goto ret;
                      //
                      //  This was once a straight structure assignment.  However, on RISC platforms
                      //  if pntfDst is not on an 8-byte boundary, this raises a Datatype
                      //  Misalignment Exception.  Changed this to a memcpy in order to not worry
                      //  about alignment and not cause any extra exception handling.
                      //
  					memcpy(&(pntfDst->info.tab.propIndex), (LPSPropValue)pb, sizeof(SPropValue));
  					pb += AlignProp(cbT);
  					cb += AlignProp(cbT);
  				}
  
  				if (ptn->propPrior.ulPropTag)
  				{
  					//	Wastes 16 bytes when the property doesn't have a tail
  					if (sc = ScCopyProps(1, &ptn->propPrior, pb, &cbT))
  						goto ret;
                      //
                      //  This was once a straight structure assignment.  However, on RISC platforms
                      //  if pntfDst is not on an 8-byte boundary, this raises a Datatype
                      //  Misalignment Exception.  Changed this to a memcpy in order to not worry
                      //  about alignment and not cause any extra exception handling.
                      //
  					memcpy(&(pntfDst->info.tab.propPrior), (LPSPropValue)pb, sizeof(SPropValue));
  					pb += AlignProp(cbT);
  					cb += AlignProp(cbT);
  				}
  
  				if (ptn->row.cValues)
  				{
  					pntfDst->info.tab.row.lpProps = (LPSPropValue)pb;
  					if (sc = ScCopyProps((int)ptn->row.cValues, ptn->row.lpProps, pb, &cbT))
  						goto ret;
  					pb += AlignProp(cbT);
  					cb += AlignProp(cbT);
  				}
  				break;
  			}
  
  			case (USHORT)fnevStatusObjectModified:
  			{
  				STATUS_OBJECT_NOTIFICATION FAR *pstat = &pntf->info.statobj;
  
  				if (pstat->cbEntryID)
  				{
  					pntfDst->info.statobj.lpEntryID = (LPENTRYID)pb;
  					cbT = pstat->cbEntryID;
  					MemCopy(pb, pstat->lpEntryID, (UINT)cbT);
  					pb += AlignProp(cbT);
  					cb += AlignProp(cbT);
  				}
  				if (pstat->cValues)
  				{
  					pntfDst->info.statobj.lpPropVals = (LPSPropValue)pb;
  					if (sc = ScCopyProps((int)pstat->cValues,
  							pstat->lpPropVals, pb, &cbT))
  						goto ret;
  					pb += cbT;
  					cb += cbT;
  				}
  				break;
  			}
  
  			default:
  				DebugTraceArg(ScCopyNotification,  TEXT("invalid ulEventType"));
  				goto badNotif;
  			}
  			break;
  		}
  		default:
  			DebugTraceArg(ScCopyNotification,  TEXT("invalid ulEventType"));
  			goto badNotif;
  		}
  	}
  
  	if (pcb)
  		*pcb = (ULONG)cb;
  
  ret:
  	DebugTraceSc(ScCopyNotifications, sc);
  	return sc;
  
  badNotif:
  	//	trace already issued
  	return E_INVALIDARG;
  
  #undef COPY
  }
  
  STDAPI_(SCODE)
  ScRelocNotifications(int cntf, LPNOTIFICATION rgntf, LPVOID pvBaseOld,
  	LPVOID pvBaseNew, ULONG FAR *pcb)
  {
  	ULONG			cb;
  	ULONG			cbT;
  	LPNOTIFICATION	pntf;
  	SCODE			sc = S_OK;
  
  	AssertSz(!cntf || !IsBadReadPtr(rgntf, sizeof(NOTIFICATION) * cntf),
  			 TEXT("rgntf fails address check"));
  
  	AssertSz(pvBaseOld,  TEXT("pvBaseOld fails address check"));
  
  	AssertSz(!IsBadWritePtr(pvBaseNew, sizeof(LPVOID)),
  			 TEXT("pvBaseNew fails address check"));
  
  	AssertSz(!pcb || !IsBadWritePtr(pcb, sizeof(ULONG)),
  			 TEXT("pcb fails address check"));
  
  	cb = cntf * sizeof(NOTIFICATION);
  	for (pntf = rgntf; cntf--; ++pntf)
  	{
  		switch (HIWORD(pntf->ulEventType))
  		{
  		case (fnevExtended >> 16):
   //      case (fnevSpooler >> 16):
  			if (pntf->info.ext.cb)
  			{
  				pntf->info.ext.pbEventParameters =
  					PvRelocPv(pntf->info.ext.pbEventParameters, pvBaseOld,
  						pvBaseNew);
  				cb += AlignProp(pntf->info.ext.cb);
  			}
  			break;
  
  		case 0:
  		{
  			switch (LOWORD(pntf->ulEventType))
  			{
  			case (USHORT)fnevCriticalError:
  			{
  				ERROR_NOTIFICATION FAR *perr 	= &pntf->info.err;
  
  				if ( perr->lpEntryID )
  				{
  					perr->lpEntryID = PvRelocPv( perr->lpEntryID, pvBaseOld,
  							pvBaseNew );
  					cb += AlignProp( (UINT)perr->cbEntryID );
  				}
  
  				if ( perr->lpMAPIError )
  				{
  					perr->lpMAPIError = PvRelocPv( perr->lpMAPIError, pvBaseOld,
  							pvBaseNew );
  
  					if (perr->lpMAPIError->lpszError)
  					{
  						perr->lpMAPIError->lpszError = PvRelocPv(
  								perr->lpMAPIError->lpszError, pvBaseOld,
  								pvBaseNew );
  
  #ifdef _WINNT
  						if (perr->ulFlags & MAPI_UNICODE)
  							cb += AlignProp((lstrlenW((LPWSTR)perr->lpMAPIError->lpszError)
  									+ 1) * sizeof(WCHAR));
  						else
  #endif
  							cb += AlignProp(lstrlenA((LPSTR)perr->lpMAPIError->lpszError) + 1);
  					}
  
  					if (perr->lpMAPIError->lpszComponent)
  					{
  						perr->lpMAPIError->lpszComponent = PvRelocPv(
  								perr->lpMAPIError->lpszComponent, pvBaseOld,
  								pvBaseNew );
  #ifdef _WINNT
  						if (perr->ulFlags & MAPI_UNICODE)
  							cb += AlignProp((lstrlenW((LPWSTR)perr->lpMAPIError->lpszComponent) + 1)
  								* sizeof(WCHAR));
  						else
  #endif
  							cb += AlignProp(lstrlenA((LPSTR)perr->lpMAPIError->lpszComponent) + 1);
  					}
  				}
  			}
  
  			case (USHORT)fnevNewMail:
  			{
  				NEWMAIL_NOTIFICATION FAR *pnew = &pntf->info.newmail;
  
  				if (pnew->lpEntryID)
  				{
  					pnew->lpEntryID = PvRelocPv(pnew->lpEntryID, pvBaseOld,
  						pvBaseNew);
  					cb += AlignProp((UINT)pnew->cbEntryID);
  				}
  
  				if (pnew->lpParentID)
  				{
  					pnew->lpParentID = PvRelocPv(pnew->lpParentID, pvBaseOld,
  						pvBaseNew);
  					cb += AlignProp((UINT)pnew->cbParentID);
  				}
  
  				if (pnew->lpszMessageClass)
  				{
  					pnew->lpszMessageClass = PvRelocPv(pnew->lpszMessageClass,
  						pvBaseOld, pvBaseNew);
  					if (pnew->ulFlags & MAPI_UNICODE)
  						cbT = (lstrlenW((LPWSTR)pnew->lpszMessageClass) + 1)
  							* sizeof(WCHAR);
  					else
  						cbT = lstrlenA((LPSTR)pnew->lpszMessageClass) + 1;
  					cb += AlignProp(cbT);
  				}
  				break;
  			}
  
  			case (USHORT)fnevObjectCreated:
  			case (USHORT)fnevObjectDeleted:
  			case (USHORT)fnevObjectModified:
  			case (USHORT)fnevObjectMoved:
  			case (USHORT)fnevObjectCopied:
  			case (USHORT)fnevSearchComplete:
  			{
  				OBJECT_NOTIFICATION FAR *pobj = &pntf->info.obj;
  
  				if (pobj->lpEntryID)
  				{
  					pobj->lpEntryID = PvRelocPv(pobj->lpEntryID, pvBaseOld,
  						pvBaseNew);
  					cb += AlignProp(pobj->cbEntryID);
  				}
  				if (pobj->lpParentID)
  				{
  					pobj->lpParentID = PvRelocPv(pobj->lpParentID, pvBaseOld,
  						pvBaseNew);
  					cb += AlignProp(pobj->cbParentID);
  				}
  				if (pobj->lpOldID)
  				{
  					pobj->lpOldID = PvRelocPv(pobj->lpOldID, pvBaseOld,
  						pvBaseNew);
  					cb += AlignProp(pobj->cbOldID);
  				}
  				if (pobj->lpOldParentID)
  				{
  					pobj->lpOldParentID = PvRelocPv(pobj->lpOldParentID, pvBaseOld,
  						pvBaseNew);
  					cb += AlignProp(pobj->cbOldParentID);
  				}
  				if (pobj->lpPropTagArray)
  				{
  					pobj->lpPropTagArray = PvRelocPv(pobj->lpPropTagArray,
  						pvBaseOld, pvBaseNew);
  					cb += offsetof(SPropTagArray, aulPropTag) +
  						pobj->lpPropTagArray->cValues * sizeof(ULONG);
  				}
  				break;
  			}
  
  			case (USHORT)fnevTableModified:
  			{
  				TABLE_NOTIFICATION FAR *ptn = &pntf->info.tab;
  
  				Assert (FIsAligned (&ptn->propIndex));
  				if (ptn->propIndex.ulPropTag)
  				{
  					if (sc = ScRelocProps(1, &ptn->propIndex, pvBaseOld,
  							pvBaseNew, &cbT))
  						goto ret;
  					cb += cbT;
  				}
  				Assert (FIsAligned (&ptn->propPrior));
  				if (ptn->propPrior.ulPropTag)
  				{
  					if (sc = ScRelocProps(1, &ptn->propPrior, pvBaseOld,
  							pvBaseNew, &cbT))
  						goto ret;
  					cb += cbT;
  				}
  				if (ptn->row.cValues)
  				{
  					Assert (FIsAligned (ptn->row.lpProps));
  					ptn->row.lpProps = PvRelocPv(ptn->row.lpProps,
  						pvBaseOld, pvBaseNew);
  					if (sc = ScRelocProps((int)ptn->row.cValues, ptn->row.lpProps,
  							pvBaseOld, pvBaseNew, &cbT))
  						goto ret;
  					cb += cbT;
  				}
  				break;
  			}
  
  			case (USHORT)fnevStatusObjectModified:
  			{
  				STATUS_OBJECT_NOTIFICATION FAR *pstat = &pntf->info.statobj;
  
  				if (pstat->lpEntryID)
  				{
  					pstat->lpEntryID = PvRelocPv(pstat->lpEntryID, pvBaseOld,
  						pvBaseNew);
  
  					//	Whoa, this is not sufficient to buffer the size of
  					//	the entryid.  If the entryid is not aligned, then the
  					//	the properties that follow will not be aligned either.
  					//
  					Assert (FIsAligned (pstat->lpEntryID));
  					cb += AlignProp(pstat->cbEntryID);
  				}
  				if (pstat->cValues)
  				{
  					pstat->lpPropVals = PvRelocPv(pstat->lpPropVals,
  						pvBaseOld, pvBaseNew);
  					Assert (FIsAligned (pstat->lpPropVals));
  					if (sc = ScRelocProps((int)pstat->cValues, pstat->lpPropVals,
  							pvBaseOld, pvBaseNew, &cbT))
  						goto ret;
  					cb += cbT;
  				}
  				break;
  			}
  
  			default:
  				DebugTraceArg(ScRelocNotification,  TEXT("invalid ulEventType"));
  				goto badNotif;
  			}
  			break;
  		}
  		default:
  			DebugTraceArg(ScRelocNotification,  TEXT("invalid ulEventType"));
  			goto badNotif;
  		}
  	}
  
  	if (pcb)
  		*pcb = cb;
  
  ret:
  	DebugTraceSc(ScRelocNotifications, sc);
  	return sc;
  
  badNotif:
  	//	trace already issued
  	return E_INVALIDARG;
  }
  #endif // NOTIFICATIONS
  
  STDAPI_(LPSPropValue)
  LpValFindProp( ULONG ulPropTag, ULONG cprop, LPSPropValue rgprop)
  {
  	//	Mutate the property tag to a property ID
  	ulPropTag = PROP_ID(ulPropTag);
  
  	while (cprop--)
  	{
  		Assert( !IsBadReadPtr( rgprop, sizeof(SPropValue)));
  
  		if (PROP_ID(rgprop->ulPropTag) == ulPropTag)
  		{
  			return rgprop;
  		}
  
  		rgprop++;
  	}
  
  	// No match was found so return NULL.
  	return NULL;
  }
  
  /*
   * 	ScCountPropsEx()
   *
   * 	Internal routine that computes the size required
   * 	to hold a given propval array based on specified alignment
   */
  
  SCODE
  ScCountPropsEx(int cprop, LPSPropValue rgprop, ULONG ulAlign, ULONG FAR *pcb)
  {
  	LPSPropValue	pprop;
  	ULONG			cb = 0;
  	ULONG			cbMV;
  	int				iValue;
  
  #define Align(_cb)	((ULONG)( ((DWORD_PTR) ((_cb) + (ulAlign-1))) & ~(((DWORD_PTR) ulAlign-1))))
  
  	// validate parameters
  
  	AssertSz(ulAlign && ulAlign <= ALIGN_RISC,
  			 TEXT("invalid alignment value"));
  
  	AssertSz(!pcb || !IsBadWritePtr(pcb, sizeof(ULONG)),
  			 TEXT("pcb fails address check"));
  
  	//$ SIZE Some of the multi-valued cases could be collapsed if we don't
  	//$	mind assuming that the counts and pointers are in the same place.
  
  	if (   (rgprop && !cprop)
  		|| IsBadReadPtr(rgprop, cprop*sizeof(SPropValue)))
  	{
  		DebugTraceArg(ScCountProps,  TEXT("rgprop fails address check"));
  		return MAPI_E_INVALID_PARAMETER;
  	}
  
  	for (pprop = rgprop; cprop--; ++pprop)
  	{
  		ULONG	ulID = PROP_ID(pprop->ulPropTag);
  		ULONG	ulType = PROP_TYPE(pprop->ulPropTag);
  
  		//	Check for valid PROP_ID
  		if (   (ulID == PROP_ID_INVALID)
  			|| ((ulType == PT_NULL) && (ulID != PROP_ID_NULL))
  			|| ((ulID == PROP_ID_NULL) && (ulType != PT_NULL) && (ulType != PT_ERROR)))
  			return MAPI_E_INVALID_PARAMETER;
  
  		//	Check for valid PROP_TYPE and count memory consumed
  		cb += sizeof(SPropValue);
  		switch ( PROP_TYPE(pprop->ulPropTag) )
  		{
  			case PT_UNSPECIFIED:
  			default:
  				DebugTrace( TEXT("ScCountProps: Unknown property type %s (index %d)\n"), SzDecodeUlPropTag(pprop->ulPropTag), pprop - rgprop);
  				return MAPI_E_INVALID_PARAMETER;
  
  			case PT_I2:
  			case PT_LONG:
  			case PT_R4:
  			case PT_APPTIME:
  			case PT_DOUBLE:
  			case PT_BOOLEAN:
  			case PT_CURRENCY:
  			case PT_SYSTIME:
  			case PT_I8:
  			case PT_ERROR:
  			case PT_OBJECT:
  			case PT_NULL:
  				break;
  
  			case PT_CLSID:
  				if (IsBadReadPtr(pprop->Value.lpguid, sizeof(GUID)))
  					goto badProp;
  				cb += Align(sizeof(GUID));
  				break;
  
  			case PT_BINARY:
  				//$Hack:  IsBadReadPtr works funny under Win16.
  				//$Hack:  It doesn't handle the case of 0 cb, and
  				//$Hack:  non-0 lpb.
  				if (pprop->Value.bin.cb && IsBadReadPtr( pprop->Value.bin.lpb
  								, (UINT) (pprop->Value.bin.cb)))
  					goto badProp;
  
  				cb += Align(pprop->Value.bin.cb);
  				break;
  
  			case PT_STRING8:
  				if (IsBadStringPtrA(pprop->Value.lpszA, INFINITE))
  					goto badProp;
  				cb += Align((lstrlenA( pprop->Value.lpszA ) + 1) * sizeof(CHAR));
  
  				break;
  
  			case PT_UNICODE:
  #if defined(WIN32) && !defined(MAC)
  				//$	No validation code available on Win16
  				if (IsBadStringPtrW(pprop->Value.lpszW, INFINITE))
  					goto badProp;
  #endif
  				cb += Align((lstrlenW( pprop->Value.lpszW ) + 1) * sizeof(WCHAR));
  				break;
  
  
              //	Note!	MVxxx.cValues may NOT be zero (DCR 2789).
  
  			case PT_MV_I2:
  				if (   !(cbMV = pprop->Value.MVi.cValues * sizeof(short int))
  					|| IsBadReadPtr(pprop->Value.MVi.lpi, (UINT) cbMV))
  					goto badProp;
  				cb += Align(cbMV);
  				break;
  
  			case PT_MV_LONG:
  				if (   !(cbMV = pprop->Value.MVl.cValues * sizeof(LONG))
  					|| IsBadReadPtr(pprop->Value.MVl.lpl, (UINT) cbMV))
  					goto badProp;
  				cb += Align(cbMV);
  				break;
  
  			case PT_MV_R4:
  				if (   !(cbMV = pprop->Value.MVflt.cValues * sizeof(float))
  					|| IsBadReadPtr(pprop->Value.MVflt.lpflt, (UINT) cbMV))
  					goto badProp;
  				cb += Align(cbMV);
  				break;
  
  			case PT_MV_APPTIME:
  				if (   !(cbMV = pprop->Value.MVat.cValues * sizeof(double))
  					|| IsBadReadPtr(pprop->Value.MVat.lpat, (UINT) cbMV))
  					goto badProp;
  				cb += cbMV;
  				break;
  
  			case PT_MV_DOUBLE:
  				if (   !(cbMV = pprop->Value.MVdbl.cValues * sizeof(double))
  					|| IsBadReadPtr(pprop->Value.MVdbl.lpdbl, (UINT) cbMV))
  					goto badProp;
  				cb += cbMV;
  				break;
  
  			case PT_MV_CURRENCY:
  				if (   !(cbMV = pprop->Value.MVcur.cValues * sizeof(CURRENCY))
  					|| IsBadReadPtr(pprop->Value.MVcur.lpcur, (UINT) cbMV))
  					goto badProp;
  				cb += cbMV;
  				break;
  
  			case PT_MV_SYSTIME:
  				if (   !(cbMV = pprop->Value.MVft.cValues * sizeof(FILETIME))
  					|| IsBadReadPtr(pprop->Value.MVft.lpft, (UINT) cbMV))
  					goto badProp;
  				cb += cbMV;
  				break;
  
  			case PT_MV_CLSID:
  				if (   !(cbMV = pprop->Value.MVguid.cValues * sizeof(GUID))
  					|| IsBadReadPtr(pprop->Value.MVguid.lpguid, (UINT) cbMV))
  					goto badProp;
  				cb += cbMV;
  				break;
  
  			case PT_MV_I8:
  				if (   !(cbMV = pprop->Value.MVli.cValues * sizeof(LARGE_INTEGER))
  					|| IsBadReadPtr(pprop->Value.MVli.lpli, (UINT) cbMV))
  					goto badProp;
  				cb += cbMV;
  				break;
  
  
  			case PT_MV_BINARY:
  				if (   !(cbMV = pprop->Value.MVbin.cValues * sizeof(SBinary))
  					|| IsBadReadPtr(pprop->Value.MVbin.lpbin, (UINT) cbMV))
  					goto badProp;
  
  				Assert(Align(cbMV) == cbMV);
  				cb += cbMV;
  
  				for ( iValue = 0;
  				  	(ULONG)iValue < pprop->Value.MVbin.cValues;
  					  	iValue++ )
  				{
  					if (IsBadReadPtr(pprop->Value.MVbin.lpbin[iValue].lpb,
  							(UINT)pprop->Value.MVbin.lpbin[iValue].cb))
  						goto badProp;
  					cb += Align(pprop->Value.MVbin.lpbin[iValue].cb);
  				}
  
  				break;
  
  			case PT_MV_STRING8:
  				if (   !(cbMV = pprop->Value.MVszA.cValues * sizeof(LPVOID))
  					|| IsBadReadPtr(pprop->Value.MVszA.lppszA, (UINT) cbMV))
  					goto badProp;
  
  				cb += cbMV;
  
  				for ( iValue = 0;
  				  	(ULONG)iValue < pprop->Value.MVszA.cValues;
  					  	iValue++ )
  				{
  					if (IsBadStringPtrA(pprop->Value.MVszA.lppszA[iValue],
  										INFINITE))
  						goto badProp;
  					cb += lstrlenA(pprop->Value.MVszA.lppszA[iValue]) + 1;
  				}
  
  				cb = Align(cb);
  
  				break;
  
  			case PT_MV_UNICODE:
  				if (   !(cbMV = pprop->Value.MVszW.cValues * sizeof(LPVOID))
  					|| IsBadReadPtr(pprop->Value.MVszW.lppszW, (UINT) cbMV))
  					goto badProp;
  
  				cb += cbMV;
  
  				for ( iValue = 0;
  				  	(ULONG)iValue < pprop->Value.MVszW.cValues;
  					  	iValue++ )
  				{
  #if defined(WIN32) && !defined(MAC)
  					//$	No validation on Win16
  					if (IsBadStringPtrW(pprop->Value.MVszW.lppszW[iValue], INFINITE))
  						goto badProp;
  #endif
  					cb += (lstrlenW(pprop->Value.MVszW.lppszW[iValue]) + 1)
  						  * sizeof(WCHAR);
  				}
  
  				cb = Align(cb);
  
  				break;
  		}
  	}
  
  	if (pcb)
  	{
  		Assert(!IsBadWritePtr(pcb, sizeof(ULONG)));
  		*pcb = cb;
  	}
  	return S_OK;
  
  badProp:
  	DebugTrace( TEXT("ScCountProps: Unreadable property %s (index %d)\n"), SzDecodeUlPropTag(pprop->ulPropTag), pprop - rgprop);
  	return MAPI_E_INVALID_PARAMETER;
  
  #undef Align
  }
  
  STDAPI_(SCODE)
  ScCountProps(int cprop, LPSPropValue rgprop, ULONG FAR *pcb)
  {
  #if defined (_AMD64_) || defined(_IA64_)
  	return ScCountPropsEx( cprop, rgprop, ALIGN_RISC, pcb );
  #else
  	return ScCountPropsEx( cprop, rgprop, ALIGN_X86, pcb );
  #endif
  }
  
  STDAPI_(SCODE)
  ScCopyProps(int cprop, LPSPropValue rgprop, LPVOID pvDst, ULONG FAR *pcb)
  {
  	LPSPropValue	pprop;
  	LPSPropValue	ppropDst;
  	ULONG			cb;
  	ULONG			cbMV;
  	LPBYTE			pb;
  	UINT			cbT;
  	int				iValue;
  
  	// validate parameters
  
  	AssertSz(!cprop || !IsBadReadPtr(rgprop, sizeof(SPropValue) * cprop),
  			 TEXT("rgprop fails address check"));
  
  	AssertSz(!cprop || !IsBadWritePtr(pvDst, sizeof(SPropValue) * cprop),
  			 TEXT("pvDst fails address check"));
  
  	AssertSz(!pcb || !IsBadWritePtr(pcb, sizeof(ULONG)),
  			 TEXT("pcb fails address check"));
  
  	//$ SIZE Some of the multi-valued cases could be collapsed if we don't
  	//$	mind assuming that the counts and pointers are in the same place.
  
  	cb = cprop * sizeof(SPropValue);
  	MemCopy(pvDst, rgprop, (UINT)cb);
  	pb = (LPBYTE)pvDst + cb;
  
  	for (pprop = rgprop, ppropDst = pvDst; cprop--; ++pprop, ++ppropDst)
  	{
  		//	Tricky: common code after the switch increments pb and cb
  		//	by the amount copied. If no increment is necessary, the case
  		//	uses 'continue' rather than 'break' to exit the switch, thus
  		//	skipping the increment -- AND any other code which may be
  		//	added after the switch.
  
  		switch ( PROP_TYPE(pprop->ulPropTag) )
  		{
  			default:
  				DebugTrace( TEXT("ScCopyProps: Unknown property type %s (index %d)\n"), SzDecodeUlPropTag(pprop->ulPropTag), pprop - rgprop);
  				return E_INVALIDARG;
  
  			case PT_I2:
  			case PT_LONG:
  			case PT_R4:
  			case PT_APPTIME:
  			case PT_DOUBLE:
  			case PT_BOOLEAN:
  			case PT_CURRENCY:
  			case PT_SYSTIME:
  			case PT_I8:
  			case PT_ERROR:
  			case PT_OBJECT:
  			case PT_NULL:
  				continue;	//	nothing to add
  
  			case PT_CLSID:
  				ppropDst->Value.lpguid = (LPGUID) pb;
  				cbT = sizeof(GUID);
  				MemCopy(pb, (LPBYTE) pprop->Value.lpguid, cbT);
  				break;
  
  			case PT_BINARY:
  				ppropDst->Value.bin.lpb = pb;
  				cbT = (UINT)pprop->Value.bin.cb;
  				MemCopy(pb, pprop->Value.bin.lpb, cbT);
  				break;
  
  			case PT_STRING8:
  				ppropDst->Value.lpszA = (LPSTR)pb;
  				cbT = lstrlenA( pprop->Value.lpszA ) + 1;
  				MemCopy(pb, pprop->Value.lpszA, cbT);
  				break;
  
  			case PT_UNICODE:
  				ppropDst->Value.lpszW = (LPWSTR)pb;
  				cbT = (lstrlenW( pprop->Value.lpszW ) + 1) * sizeof(WCHAR);
  				MemCopy(pb, pprop->Value.lpszW, cbT);
  				break;
  
  			case PT_MV_I2:
  				ppropDst->Value.MVi.lpi = (short int FAR *)pb;
  				cbT = (UINT)pprop->Value.MVi.cValues * sizeof(short int);
  				MemCopy(pb, pprop->Value.MVi.lpi, cbT);
  				break;
  
  			case PT_MV_LONG:
  				ppropDst->Value.MVl.lpl = (LONG FAR *)pb;
  				cbT = (UINT)pprop->Value.MVl.cValues * sizeof(LONG);
  				MemCopy(pb, pprop->Value.MVl.lpl, cbT);
  				break;
  
  			case PT_MV_R4:
  				ppropDst->Value.MVflt.lpflt = (float FAR *)pb;
  				cbT = (UINT)pprop->Value.MVflt.cValues * sizeof(float);
  				MemCopy(pb, pprop->Value.MVflt.lpflt, cbT);
  				break;
  
  			case PT_MV_APPTIME:
  				ppropDst->Value.MVat.lpat = (double FAR *)pb;
  				cbT = (UINT)pprop->Value.MVat.cValues * sizeof(double);
  				MemCopy(pb, pprop->Value.MVat.lpat, cbT);
  				break;
  
  			case PT_MV_DOUBLE:
  				ppropDst->Value.MVdbl.lpdbl = (double FAR *)pb;
  				cbT = (UINT)pprop->Value.MVdbl.cValues * sizeof(double);
  				MemCopy(pb, pprop->Value.MVdbl.lpdbl, cbT);
  				break;
  
  			case PT_MV_CURRENCY:
  				ppropDst->Value.MVcur.lpcur = (CURRENCY FAR *)pb;
  				cbT = (UINT)pprop->Value.MVcur.cValues * sizeof(CURRENCY);
  				MemCopy(pb, pprop->Value.MVcur.lpcur, cbT);
  				break;
  
  			case PT_MV_SYSTIME:
  				ppropDst->Value.MVft.lpft = (FILETIME FAR *)pb;
  				cbT = (UINT)pprop->Value.MVft.cValues * sizeof(FILETIME);
  				MemCopy(pb, pprop->Value.MVft.lpft, cbT);
  				break;
  
  			case PT_MV_CLSID:
  				ppropDst->Value.MVguid.lpguid = (GUID FAR *)pb;
  				cbT = (UINT)pprop->Value.MVguid.cValues * sizeof(GUID);
  				MemCopy(pb, pprop->Value.MVguid.lpguid, cbT);
  				break;
  
  			case PT_MV_I8:
  				ppropDst->Value.MVli.lpli = (LARGE_INTEGER FAR *)pb;
  				cbT = (UINT)pprop->Value.MVli.cValues * sizeof(LARGE_INTEGER);
  				MemCopy(pb, pprop->Value.MVli.lpli, cbT);
  				break;
  
  			case PT_MV_BINARY:
  				ppropDst->Value.MVbin.lpbin = (SBinary *) pb;
  				cbMV = pprop->Value.MVbin.cValues * sizeof(SBinary);
  				pb += cbMV;
  				cb += cbMV;
  				for (iValue = 0;
  				  	(ULONG)iValue < pprop->Value.MVbin.cValues;
  					  	iValue++)
  				{
  					ppropDst->Value.MVbin.lpbin[iValue].lpb = pb;
  					cbT = (UINT)pprop->Value.MVbin.lpbin[iValue].cb;
  					ppropDst->Value.MVbin.lpbin[iValue].cb = (ULONG)cbT;
  					MemCopy(pb, pprop->Value.MVbin.lpbin[iValue].lpb, cbT);
  					cbT = AlignProp(cbT);
  					cb += cbT;
  					pb += cbT;
  				}
  				continue;	//	already updated, don't do it again
  
  			case PT_MV_STRING8:
  				ppropDst->Value.MVszA.lppszA = (LPSTR *) pb;
  				cbMV = pprop->Value.MVszA.cValues * sizeof(LPSTR);
  				pb += cbMV;
  				cb += cbMV;
  				for (iValue = 0;
  				  	(ULONG)iValue < pprop->Value.MVszA.cValues;
  					  	iValue++)
  				{
  					ppropDst->Value.MVszA.lppszA[iValue] = (LPSTR)pb;
  					cbT = lstrlenA(pprop->Value.MVszA.lppszA[iValue]) + 1;
  					MemCopy(pb, pprop->Value.MVszA.lppszA[iValue], cbT);
  					pb += cbT;
  					cb += cbT;
  				}
  				cbT = (UINT)AlignProp(cb);
  				pb += cbT - cb;
  				cb  = cbT;
  				continue;	//	already updated, don't do it again
  
  			case PT_MV_UNICODE:
  				ppropDst->Value.MVszW.lppszW = (LPWSTR *) pb;
  				cbMV = pprop->Value.MVszW.cValues * sizeof(LPWSTR);
  				pb += cbMV;
  				cb += cbMV;
  				for (iValue = 0;
  				  	(ULONG)iValue < pprop->Value.MVszW.cValues;
  					  	iValue++)
  				{
  					ppropDst->Value.MVszW.lppszW[iValue] = (LPWSTR)pb;
  					cbT = (lstrlenW(pprop->Value.MVszW.lppszW[iValue]) + 1)
  						* sizeof(WCHAR);
  					MemCopy(pb, pprop->Value.MVszW.lppszW[iValue], cbT);
  					pb += cbT;
  					cb += cbT;
  				}
  				cbT = (UINT)AlignProp(cb);
  				pb += cbT - cb;
  				cb  = cbT;
  				continue;	//	already updated, don't do it again
  		}
  
  		//	Advance pointer and total count by the amount copied
  		cbT = AlignProp(cbT);
  		pb += cbT;
  		cb += cbT;
  	}
  
  	if (pcb)
  	{
  		Assert(!IsBadWritePtr(pcb, sizeof(ULONG)));
  		*pcb = cb;
  	}
  	return S_OK;
  }
  
  #ifdef NOTIFICATIONS
  STDAPI_(SCODE)
  ScRelocProps(	int cprop,
  				LPSPropValue rgprop,
  				LPVOID pvBaseOld,
  				LPVOID pvBaseNew,
  				ULONG FAR *pcb)
  {
  	LPSPropValue	pprop;
  	ULONG			cb;
  	UINT			cbT;
  	LPVOID			pvT;
  	int				iValue;
  	BOOL			fBaseNewValid = !IsBadReadPtr (pvBaseNew, sizeof (LPVOID));
  
  	// validate parameters
  
  	AssertSz(!cprop || !IsBadWritePtr(rgprop, sizeof(SPropValue) * cprop),
  			 TEXT("rgprop fails address check"));
  
  	AssertSz(!pcb || !IsBadWritePtr(pcb, sizeof(ULONG)),
  			 TEXT("pcb fails address check"));
  
  	// The old behavior of this code assumed that pvBaseNew was a usable
  	// pointer and that there would be no relocation to or from an unusable
  	// pointer. We've changed this so that you may relocate to or from an
  	// unusable pointer -- but logic to figure out whether to use the
  	// original or new pointer to fixup internal pointers was added.
  	// What we mean by this is that things like strlens and mv prop arrays
  	// need to be computed based on where the data ** CURRENTLY ** lives.
  	// The old rules allowed us to assume that the NEW location was always
  	// the right place. The new rules make us figure it out based on the
  	// validity of the two pointers pvBaseNew/Old, that are passed in.
  	//
  	// In order to preserve the old behavior, we try to use the new pointer
  	// (the one that was always used before) as the basis for internal
  	// pointer fixup. If it is bad (for example if we are relocating from
  	// something to zero), we will use the old pointer.
  	//
  	// A new wrinkle in the behavior of this code is a return of
  	// MAPI_E_INVALID_PARAMETER if both addresses appear invalid. This is
  	// to help protect this code for the mv or strlen case (though all
  	// other cases would have worked OK).
  
  	if (!fBaseNewValid && IsBadReadPtr (pvBaseOld, sizeof (LPVOID)))
  	{
  		DebugTrace ( TEXT("pvBaseOld and pvBaseNew failed address check"));
  		DebugTraceSc (ScRelocProps, MAPI_E_INVALID_PARAMETER);
  		return MAPI_E_INVALID_PARAMETER;
  	}
  
  	//$ SIZE Some of the multi-valued cases could be collapsed if we don't
  	//$	mind assuming that the counts and pointers are in the same place.
  
  	cb = cprop * sizeof(SPropValue);
  
  	for (pprop = rgprop; cprop--; ++pprop)
  	{
  		//	Tricky: common code after the switch increments cb.
  		//	If no increment is necessary, the case
  		//	uses 'continue' rather than 'break' to exit the switch, thus
  		//	skipping the increment -- AND any other code which may be
  		//	added after the switch.
  
  		switch ( PROP_TYPE(pprop->ulPropTag) )
  		{
  			default:
  				DebugTrace(	 TEXT("ScRelocProps: Unknown property type %s")
  							 TEXT(" (index %d)\n"),
  							SzDecodeUlPropTag(pprop->ulPropTag), pprop - rgprop);
  				return E_INVALIDARG;
  
  			case PT_I2:
  			case PT_LONG:
  			case PT_R4:
  			case PT_APPTIME:
  			case PT_DOUBLE:
  			case PT_BOOLEAN:
  			case PT_CURRENCY:
  			case PT_SYSTIME:
  			case PT_I8:
  			case PT_ERROR:
  			case PT_OBJECT:
  			case PT_NULL:
  				continue;	//	nothing to add or relocate
  
  			case PT_CLSID:
  				pprop->Value.lpguid = PvRelocPv(pprop->Value.lpguid,
  					pvBaseOld, pvBaseNew);
  				cbT = sizeof(GUID);
  				break;
  
  			case PT_BINARY:
  				pprop->Value.bin.lpb = PvRelocPv(pprop->Value.bin.lpb,
  					pvBaseOld, pvBaseNew);
  				cbT = (UINT)pprop->Value.bin.cb;
  				break;
  
  			case PT_STRING8:
  
  				// If we're assuming that the old pointer is OK (this
  				// means that we determined that the new one is not OK),
  				// save the current lpszA value in a temp variable. After
  				// the relocation, if the reverse is true, we'll put the
  				// relocated lpszA value into the temp variable.
  				//
  				// We then use the strlen of the string we hope the temp
  				// variable is pointing to, in order to compute the amount
  				// of space in the blob which is occupied by the string.
  
  				if (!fBaseNewValid)
  					pvT = pprop->Value.lpszA;
  
  				pprop->Value.lpszA = PvRelocPv(pprop->Value.lpszA,
  					pvBaseOld, pvBaseNew);
  
  				if (fBaseNewValid)
  					pvT = pprop->Value.lpszA;
  
  				cbT = lstrlenA((LPSTR)pvT) + 1;
  
  				break;
  
  			case PT_UNICODE:
  
  				// If we're assuming that the old pointer is OK (this
  				// means that we determined that the new one is not OK),
  				// save the current lpszW value in a temp variable. After
  				// the relocation, if the reverse is true, we'll put the
  				// relocated lpszW value into the temp variable.
  				//
  				// We then use the strlen of the string we hope the temp
  				// variable is pointing to, in order to compute the amount
  				// of space in the blob which is occupied by the string.
  
  				if (!fBaseNewValid)
  					pvT = pprop->Value.lpszW;
  
  				pprop->Value.lpszW = PvRelocPv(pprop->Value.lpszW,
  					pvBaseOld, pvBaseNew);
  
  				if (fBaseNewValid)
  					pvT = pprop->Value.lpszW;
  
  				cbT = (lstrlenW((LPWSTR)pvT) + 1) * sizeof(WCHAR);
  
  				break;
  
  			case PT_MV_I2:
  				pprop->Value.MVi.lpi = PvRelocPv(pprop->Value.MVi.lpi,
  					pvBaseOld, pvBaseNew);
  				cbT = (UINT)pprop->Value.MVi.cValues * sizeof(short int);
  				break;
  
  			case PT_MV_LONG:
  				pprop->Value.MVl.lpl = PvRelocPv(pprop->Value.MVl.lpl,
  					pvBaseOld, pvBaseNew);
  				cbT = (UINT)pprop->Value.MVl.cValues * sizeof(LONG);
  				break;
  
  			case PT_MV_R4:
  				pprop->Value.MVflt.lpflt = PvRelocPv(pprop->Value.MVflt.lpflt,
  					pvBaseOld, pvBaseNew);
  				cbT = (UINT)pprop->Value.MVflt.cValues * sizeof(float);
  				break;
  
  			case PT_MV_APPTIME:
  				pprop->Value.MVat.lpat = PvRelocPv(pprop->Value.MVat.lpat,
  					pvBaseOld, pvBaseNew);
  				cbT = (UINT)pprop->Value.MVat.cValues * sizeof(double);
  				break;
  
  			case PT_MV_DOUBLE:
  				pprop->Value.MVdbl.lpdbl = PvRelocPv(pprop->Value.MVdbl.lpdbl,
  					pvBaseOld, pvBaseNew);
  				cbT = (UINT)pprop->Value.MVdbl.cValues * sizeof(double);
  				break;
  
  			case PT_MV_CURRENCY:
  				pprop->Value.MVcur.lpcur = PvRelocPv(pprop->Value.MVcur.lpcur,
  					pvBaseOld, pvBaseNew);
  				cbT = (UINT)pprop->Value.MVcur.cValues * sizeof(CURRENCY);
  				break;
  
  			case PT_MV_SYSTIME:
  				pprop->Value.MVft.lpft = PvRelocPv(pprop->Value.MVft.lpft,
  					pvBaseOld, pvBaseNew);
  				cbT = (UINT)pprop->Value.MVft.cValues * sizeof(FILETIME);
  				break;
  
  			case PT_MV_CLSID:
  				pprop->Value.MVguid.lpguid = PvRelocPv(pprop->Value.MVguid.lpguid,
  					pvBaseOld, pvBaseNew);
  				cbT = (UINT)pprop->Value.MVguid.cValues * sizeof(GUID);
  				break;
  
  			case PT_MV_I8:
  				pprop->Value.MVli.lpli = PvRelocPv(pprop->Value.MVli.lpli,
  					pvBaseOld, pvBaseNew);
  				cbT = (UINT)pprop->Value.MVli.cValues * sizeof(LARGE_INTEGER);
  				break;
  
  			case PT_MV_BINARY:
  			{
  				LPSBinary lpsbT = pprop->Value.MVbin.lpbin;
  
  				pprop->Value.MVbin.lpbin = PvRelocPv(lpsbT, pvBaseOld, pvBaseNew);
  
  				// We've already set up a temporary variable to point to the
  				// pvBaseOld memory location. If pvBaseNew was OK, then we'll
  				// redirect the temp variable to the relocated memory before
  				// using it to correct the pointers in the MVbin array.
  
  				if (fBaseNewValid)
  					lpsbT = pprop->Value.MVbin.lpbin;
  
  				for (iValue = 0;
  				  	(ULONG)iValue < pprop->Value.MVbin.cValues;
  					  	iValue++)
  				{
  					lpsbT[iValue].lpb = PvRelocPv(lpsbT[iValue].lpb, pvBaseOld, pvBaseNew);
  					cb += (UINT)AlignProp(lpsbT[iValue].cb);
  				}
  				continue;	//	already updated, don't do it again
  			}
  
  			case PT_MV_STRING8:
  			{
  				LPSTR * lppszT = pprop->Value.MVszA.lppszA;
  
  				pprop->Value.MVszA.lppszA = PvRelocPv(lppszT, pvBaseOld, pvBaseNew);
  
  				// We've already set up a temporary variable to point to the
  				// pvBaseOld memory location. If pvBaseNew was OK, then we'll
  				// redirect the temp variable to the relocated memory before
  				// using it to correct the pointers in the MVszA array.
  
  				if (fBaseNewValid)
  					   lppszT = pprop->Value.MVszA.lppszA;
  
  				for (iValue = 0;
  				  	(ULONG)iValue < pprop->Value.MVszA.cValues;
  					  	iValue++)
  				{
  					// If we're assuming that the old pointer is OK (this
  					// means that we determined that the new one is not OK),
  					// save the current lppszT value in a temp variable. After
  					// the relocation, if the reverse is true, we'll put the
  					// relocated lppszT value into the temp variable.
  					//
  					// We then use the strlen of the string we hope the temp
  					// variable is pointing to, in order to compute the amount
  					// of space in the blob which is occupied by the string.
  
  					if (!fBaseNewValid)
  						pvT = lppszT[iValue];
  
  					lppszT[iValue] = PvRelocPv(lppszT[iValue], pvBaseOld, pvBaseNew);
  
  					if (fBaseNewValid)
  						pvT = lppszT[iValue];
  
  					cb += lstrlenA((LPSTR)pvT) + 1;
  				}
  				cb = AlignProp(cb);
  				continue;	//	already updated, don't do it again
  			}
  
  			case PT_MV_UNICODE:
  			{
  				LPWSTR * lppszwT = pprop->Value.MVszW.lppszW;
  
  				pprop->Value.MVszW.lppszW = PvRelocPv(lppszwT, pvBaseOld, pvBaseNew);
  
  				// We've already set up a temporary variable to point to the
  				// pvBaseOld memory location. If pvBaseNew was OK, then we'll
  				// redirect the temp variable to the relocated memory before
  				// using it to correct the pointers in the MVszW array.
  
  				if (fBaseNewValid)
  					   lppszwT = pprop->Value.MVszW.lppszW;
  
  				for (iValue = 0;
  				  	(ULONG)iValue < pprop->Value.MVszW.cValues;
  					  	iValue++)
  				{
  					// If we're assuming that the old pointer is OK (this
  					// means that we determined that the new one is not OK),
  					// save the current lppszwT value in a temp variable. After
  					// the relocation, if the reverse is true, we'll put the
  					// relocated lppszwT value into the temp variable.
  					//
  					// We then use the strlen of the string we hope the temp
  					// variable is pointing to, in order to compute the amount
  					// of space in the blob which is occupied by the string.
  
  					if (!fBaseNewValid)
  						pvT = lppszwT[iValue];
  
  					lppszwT[iValue] = PvRelocPv(lppszwT[iValue], pvBaseOld, pvBaseNew);
  
  					if (fBaseNewValid)
  						pvT = lppszwT[iValue];
  
  					cb += (lstrlenW(lppszwT[iValue]) + 1) * sizeof(WCHAR);
  				}
  				cb = AlignProp(cb);
  				continue;	//	already updated, don't do it again
  			}
  		}
  
  		//	Advance total count
  		cb += AlignProp(cbT);
  	}
  
  	if (pcb)
  	{
  		Assert(!IsBadWritePtr(pcb, sizeof(ULONG)));
  		*pcb = cb;
  	}
  	return S_OK;
  }
  #endif
  
  /*
   *	Wrapper function to just duplicate a property value array
   *	into a single block of MAPI memory.
   */
  STDAPI_(SCODE)
  ScDupPropset(int cprop, LPSPropValue rgprop, LPALLOCATEBUFFER palloc,
  	LPSPropValue FAR *prgprop)
  {
  	ULONG		cb;
  	SCODE		sc;
  
  	// validate parameters
  
  	AssertSz(!cprop || !IsBadReadPtr(rgprop, sizeof(SPropValue) * cprop),
  			 TEXT("rgprop fails address check"));
  
  	AssertSz(!IsBadCodePtr((FARPROC)palloc),  TEXT("palloc fails address check"));
  
  	AssertSz(!IsBadWritePtr(prgprop, sizeof(LPSPropValue)),
  			 TEXT("prgprop fails address check"));
  
  	//	Find out how much memory we need
  	if (sc = ScCountProps(cprop, rgprop, &cb))
  		goto ret;
  	//	Obtain memory
  	if (sc = (*palloc)(cb, (LPVOID *)prgprop))
  		goto ret;
  	//	Copy the properties
  	if (sc = ScCopyProps(cprop, rgprop, *prgprop, &cb))
  		goto ret;
  
  ret:
  	DebugTraceSc(ScDupPropset, sc);
  	return sc;
  }
  