Windows2003-3790/inetcore/outlookexpress/wabw/wabapi/proputil.c
2020-09-30 16:53:55 +02:00

1440 lines
42 KiB
C

/*
* MAPI 1.0 property handling routines
*
*
* PROPUTIL.C -
*
* Useful routines for manipulating and comparing property values
*/
#include <_apipch.h>
//
//
// WARNING! WARNING! WARNING! 32-bit Intel Specific!
//
//
#define SIZEOF_FLOAT 4
#define SIZEOF_DOUBLE 8
#define SIZEOF_LONG_DOUBLE 8
// Make linker happy.
BOOL _fltused;
//
//
//
//
#define cchBufMax 256
#if defined (_AMD64_) || defined(_IA64_)
#define AlignProp(_cb) Align8(_cb)
#else
#define AlignProp(_cb) (_cb)
#endif
#ifdef OLD_STUFF
//#ifdef OLDSTUFF_DBCS
ULONG ulchStrCount (LPTSTR, ULONG, LANGID);
ULONG ulcbStrCount (LPTSTR, ULONG, LANGID);
//#endif // DBCS
#endif //OLD_STUFF
// $MAC - Mac 68K compiler bug
#ifdef _M_M68K
#pragma optimize( TEXT(""), off)
#endif
/*
- PropCopyMore()
-
*
* Copies a property pointed to by lpSPropValueSrc into the property pointed
* to by lpSPropValueDst. No memory allocation is done unless the property
* is one of the types that do not fit within a SPropValue, eg. STRING8
* For these large properties, memory is allocated using
* the AllocMore function passed as a parameter.
*/
STDAPI_(SCODE)
PropCopyMore( LPSPropValue lpSPropValueDst,
LPSPropValue lpSPropValueSrc,
ALLOCATEMORE * lpfAllocateMore,
LPVOID lpvObject )
{
SCODE sc;
ULONG ulcbValue;
LPBYTE lpbValueSrc;
UNALIGNED LPBYTE * lppbValueDst;
// validate parameters
AssertSz( lpSPropValueDst && !IsBadReadPtr( lpSPropValueDst, sizeof( SPropValue ) ),
TEXT("lpSPropValueDst fails address check") );
AssertSz( lpSPropValueSrc && !IsBadReadPtr( lpSPropValueSrc, sizeof( SPropValue ) ),
TEXT("lpSPropValueDst fails address check") );
AssertSz( !lpfAllocateMore || !IsBadCodePtr( (FARPROC)lpfAllocateMore ),
TEXT("lpfAllocateMore fails address check") );
AssertSz( !lpvObject || !IsBadReadPtr( lpvObject, sizeof( LPVOID ) ),
TEXT("lpfAllocateMore fails address check") );
// Copy the part that fits in the SPropValue struct (including the tag).
// This is a little wasteful for complicated properties
// because it copies more than is strictly necessary, but
// it saves time for small properties and saves code in general
MemCopy( (BYTE *) lpSPropValueDst,
(BYTE *) lpSPropValueSrc,
sizeof(SPropValue) );
switch ( PROP_TYPE(lpSPropValueSrc->ulPropTag) )
{
// Types whose values fit in the 64-bit Value of the property
// or whose values aren't anything PropCopyMore can interpret
case PT_UNSPECIFIED:
case PT_NULL:
case PT_OBJECT:
case PT_I2:
case PT_LONG:
case PT_R4:
case PT_DOUBLE:
case PT_CURRENCY:
case PT_ERROR:
case PT_BOOLEAN:
case PT_SYSTIME:
case PT_APPTIME:
case PT_I8:
return SUCCESS_SUCCESS;
case PT_BINARY:
ulcbValue = lpSPropValueSrc->Value.bin.cb;
lpbValueSrc = lpSPropValueSrc->Value.bin.lpb;
lppbValueDst = (LPBYTE *) &lpSPropValueDst->Value.bin.lpb;
break;
case PT_STRING8:
ulcbValue = (lstrlenA(lpSPropValueSrc->Value.lpszA) + 1) * sizeof(CHAR);
lpbValueSrc = (LPBYTE) lpSPropValueSrc->Value.lpszA;
lppbValueDst = (LPBYTE *) &lpSPropValueDst->Value.lpszA;
break;
case PT_UNICODE:
ulcbValue = (lstrlenW(lpSPropValueSrc->Value.lpszW) + 1) * sizeof(WCHAR);
lpbValueSrc = (LPBYTE) lpSPropValueSrc->Value.lpszW;
lppbValueDst = (LPBYTE *) &lpSPropValueDst->Value.lpszW;
break;
case PT_CLSID:
ulcbValue = sizeof(GUID);
lpbValueSrc = (LPBYTE) lpSPropValueSrc->Value.lpguid;
lppbValueDst = (LPBYTE *) &lpSPropValueDst->Value.lpguid;
break;
case PT_MV_CLSID:
ulcbValue = lpSPropValueSrc->Value.MVguid.cValues * sizeof(GUID);
lpbValueSrc = (LPBYTE) lpSPropValueSrc->Value.MVguid.lpguid;
lppbValueDst = (LPBYTE *) &lpSPropValueDst->Value.MVguid.lpguid;
break;
case PT_MV_I2:
ulcbValue = lpSPropValueSrc->Value.MVi.cValues * sizeof(short int);
lpbValueSrc = (LPBYTE) lpSPropValueSrc->Value.MVi.lpi;
lppbValueDst = (LPBYTE *) &lpSPropValueDst->Value.MVi.lpi;
break;
case PT_MV_LONG:
ulcbValue = lpSPropValueSrc->Value.MVl.cValues * sizeof(LONG);
lpbValueSrc = (LPBYTE) lpSPropValueSrc->Value.MVl.lpl;
lppbValueDst = (LPBYTE *) &lpSPropValueDst->Value.MVl.lpl;
break;
case PT_MV_R4:
ulcbValue = lpSPropValueSrc->Value.MVflt.cValues * SIZEOF_FLOAT;
lpbValueSrc = (LPBYTE) lpSPropValueSrc->Value.MVflt.lpflt;
lppbValueDst = (LPBYTE *) &lpSPropValueDst->Value.MVflt.lpflt;
break;
case PT_MV_DOUBLE:
case PT_MV_APPTIME:
ulcbValue = lpSPropValueSrc->Value.MVdbl.cValues * SIZEOF_DOUBLE;
lpbValueSrc = (LPBYTE) lpSPropValueSrc->Value.MVdbl.lpdbl;
lppbValueDst = (LPBYTE *) &lpSPropValueDst->Value.MVdbl.lpdbl;
break;
case PT_MV_CURRENCY:
ulcbValue = lpSPropValueSrc->Value.MVcur.cValues * sizeof(CURRENCY);
lpbValueSrc = (LPBYTE) lpSPropValueSrc->Value.MVcur.lpcur;
lppbValueDst = (LPBYTE *) &lpSPropValueDst->Value.MVcur.lpcur;
break;
case PT_MV_SYSTIME:
ulcbValue = lpSPropValueSrc->Value.MVat.cValues * sizeof(FILETIME);
lpbValueSrc = (LPBYTE) lpSPropValueSrc->Value.MVat.lpat;
lppbValueDst = (LPBYTE *) &lpSPropValueDst->Value.MVat.lpat;
break;
case PT_MV_I8:
ulcbValue = lpSPropValueSrc->Value.MVli.cValues * sizeof(LARGE_INTEGER);
lpbValueSrc = (LPBYTE) lpSPropValueSrc->Value.MVli.lpli;
lppbValueDst = (LPBYTE *) &lpSPropValueDst->Value.MVli.lpli;
break;
case PT_MV_BINARY:
{
// Multi-valued binaries are copied in memory into a single
// allocated buffer in the following way:
//
// cb1, pb1 ... cbn, pbn, b1,0, b1,1 ... b2,0 b2,1 ...
//
// The cbn and pbn parameters form the SBinary array that
// will be pointed to by lpSPropValueDst->Value.MVbin.lpbin.
// The remainder of the allocation is used to store the binary
// data for each of the elements of the array. Thus pb1 points
// to the b1,0, etc.
UNALIGNED SBinaryArray * pSBinaryArray = (UNALIGNED SBinaryArray * ) (&lpSPropValueSrc->Value.MVbin);
ULONG uliValue;
UNALIGNED SBinary * pSBinarySrc;
UNALIGNED SBinary * pSBinaryDst;
LPBYTE pbData;
ulcbValue = pSBinaryArray->cValues * sizeof(SBinary);
for ( uliValue = 0, pSBinarySrc = pSBinaryArray->lpbin;
uliValue < pSBinaryArray->cValues;
uliValue++, pSBinarySrc++ )
ulcbValue += AlignProp(pSBinarySrc->cb);
// Allocate a buffer to hold it all
lppbValueDst = (LPBYTE *) &lpSPropValueDst->Value.MVbin.lpbin;
sc = (*lpfAllocateMore)( ulcbValue,
lpvObject,
(LPVOID *) lppbValueDst );
if ( sc != SUCCESS_SUCCESS )
{
DebugTrace( TEXT("PropCopyMore() - OOM allocating space for dst PT_MV_BINARY property") );
return sc;
}
// And copy it all in
pbData = (LPBYTE) ((LPSBinary) *lppbValueDst + pSBinaryArray->cValues);
for ( uliValue = 0,
pSBinarySrc = pSBinaryArray->lpbin,
pSBinaryDst = (LPSBinary) *lppbValueDst;
uliValue < pSBinaryArray->cValues;
uliValue++, pSBinarySrc++, pSBinaryDst++ )
{
pSBinaryDst->cb = pSBinarySrc->cb;
pSBinaryDst->lpb = pbData;
MemCopy( pbData, pSBinarySrc->lpb, (UINT) pSBinarySrc->cb );
pbData += AlignProp(pSBinarySrc->cb);
}
return SUCCESS_SUCCESS;
}
case PT_MV_STRING8:
{
// Multi-valued STRING8 properties are copied into a single
// allocated block of memory in the following way:
//
// | Allocated buffer |
// |---------------------------------------|
// | pszA1, pszA2 ... | szA1[], szA2[] ... |
// |------------------|--------------------|
// | LPSTR array | String data |
//
// Where pszAn are the elements of the LPSTR array pointed
// to by lpSPropValueDst->Value.MVszA. Each pszAn points
// to its corresponding string, szAn, stored later in the
// buffer. The szAn are stored starting at the first byte
// past the end of the LPSTR array.
UNALIGNED SLPSTRArray * pSLPSTRArray = (UNALIGNED SLPSTRArray *) (&lpSPropValueSrc->Value.MVszA);
ULONG uliValue;
LPSTR * pszASrc;
LPSTR * pszADst;
LPBYTE pbSzA;
ULONG ulcbSzA;
// Figure out the size of the buffer we need
ulcbValue = pSLPSTRArray->cValues * sizeof(LPSTR);
for ( uliValue = 0, pszASrc = pSLPSTRArray->lppszA;
uliValue < pSLPSTRArray->cValues;
uliValue++, pszASrc++ )
ulcbValue += (lstrlenA(*pszASrc) + 1) * sizeof(CHAR);
// Allocate the buffer to hold the strings
lppbValueDst = (LPBYTE *) &lpSPropValueDst->Value.MVszA.lppszA;
sc = (*lpfAllocateMore)( ulcbValue,
lpvObject,
(LPVOID *) lppbValueDst );
if ( sc != SUCCESS_SUCCESS )
{
DebugTrace( TEXT("PropCopyMore() - OOM allocating space for dst PT_MV_STRING8 property") );
return sc;
}
// Copy the strings into the buffer and set pointers
// to them in the LPSTR array at the beginning of the buffer
for ( uliValue = 0,
pszASrc = pSLPSTRArray->lppszA,
pszADst = (LPSTR *) *lppbValueDst,
pbSzA = (LPBYTE) (pszADst + pSLPSTRArray->cValues);
uliValue < pSLPSTRArray->cValues;
uliValue++, pszASrc++, pszADst++ )
{
ulcbSzA = (lstrlenA(*pszASrc) + 1) * sizeof(CHAR);
*pszADst = (LPSTR) pbSzA;
MemCopy( pbSzA, (LPBYTE) *pszASrc, (UINT) ulcbSzA );
pbSzA += ulcbSzA;
}
return SUCCESS_SUCCESS;
}
case PT_MV_UNICODE:
{
// Multi-valued UNICODE properties are copied into a single
// allocated block of memory in the following way:
//
// | Allocated buffer |
// |---------------------------------------|
// | pszW1, pszW2 ... | szW1[], szW2[] ... |
// |------------------|--------------------|
// | LPWSTR array | String data |
//
// Where pszWn are the elements of the LPWSTR array pointed
// to by lpSPropValueDst->Value.MVszW. Each pszWn points
// to its corresponding string, szWn, stored later in the
// buffer. The szWn are stored starting at the first byte
// past the end of the LPWSTR array.
UNALIGNED SWStringArray * pSWStringArray = (UNALIGNED SWStringArray *) (&lpSPropValueSrc->Value.MVszW);
ULONG uliValue;
UNALIGNED LPWSTR * pszWSrc;
UNALIGNED LPWSTR * pszWDst;
LPBYTE pbSzW;
ULONG ulcbSzW;
// Figure out the size of the buffer we need
ulcbValue = pSWStringArray->cValues * sizeof(LPWSTR);
for ( uliValue = 0, pszWSrc = pSWStringArray->lppszW;
uliValue < pSWStringArray->cValues;
uliValue++, pszWSrc++ )
ulcbValue += (lstrlenW(*pszWSrc) + 1) * sizeof(WCHAR);
// Allocate the buffer to hold the strings
lppbValueDst = (LPBYTE *) &lpSPropValueDst->Value.MVszW.lppszW;
sc = (*lpfAllocateMore)( ulcbValue,
lpvObject,
(LPVOID *) lppbValueDst );
if ( sc != SUCCESS_SUCCESS )
{
DebugTrace( TEXT("PropCopyMore() - OOM allocating space for dst PT_MV_UNICODE property") );
return sc;
}
// Copy the strings into the buffer and set pointers
// to them in the LPWSTR array at the beginning of the buffer
for ( uliValue = 0,
pszWSrc = pSWStringArray->lppszW,
pszWDst = (LPWSTR *) *lppbValueDst,
pbSzW = (LPBYTE) (pszWDst + pSWStringArray->cValues);
uliValue < pSWStringArray->cValues;
uliValue++, pszWSrc++, pszWDst++ )
{
ulcbSzW = (lstrlenW(*pszWSrc) + 1) * sizeof(WCHAR);
*((UNALIGNED LPWSTR *) pszWDst) = (LPWSTR) pbSzW;
Assert(ulcbSzW < 0xFfff);
MemCopy( pbSzW, (LPBYTE) *pszWSrc, (UINT) ulcbSzW );
pbSzW += ulcbSzW;
}
return SUCCESS_SUCCESS;
}
default:
DebugTrace( TEXT("PropCopyMore() - Unsupported/Unimplemented property type 0x%04x"), PROP_TYPE(lpSPropValueSrc->ulPropTag) );
return MAPI_E_NO_SUPPORT;
}
sc = (*lpfAllocateMore)( ulcbValue, lpvObject, (LPVOID *) lppbValueDst );
if ( sc != SUCCESS_SUCCESS )
{
DebugTrace( TEXT("PropCopyMore() - OOM allocating space for dst property") );
return sc;
}
MemCopy( *lppbValueDst, lpbValueSrc, (UINT) ulcbValue );
return SUCCESS_SUCCESS;
}
// $MAC - Mac 68K compiler bug
#ifdef _M_M68K
#pragma optimize( TEXT(""), on)
#endif
/*
- UlPropSize()
*
* Returns the size of the property pointed to by lpSPropValue
*/
STDAPI_(ULONG)
UlPropSize( LPSPropValue lpSPropValue )
{
// parameter validation
AssertSz( lpSPropValue && !IsBadReadPtr( lpSPropValue, sizeof( SPropValue ) ),
TEXT("lpSPropValue fails address check") );
switch ( PROP_TYPE(lpSPropValue->ulPropTag) )
{
case PT_I2: return sizeof(short int);
case PT_LONG: return sizeof(LONG);
case PT_R4: return SIZEOF_FLOAT;
case PT_APPTIME:
case PT_DOUBLE: return SIZEOF_DOUBLE;
case PT_BOOLEAN: return sizeof(unsigned short int);
case PT_CURRENCY: return sizeof(CURRENCY);
case PT_SYSTIME: return sizeof(FILETIME);
case PT_CLSID: return sizeof(GUID);
case PT_I8: return sizeof(LARGE_INTEGER);
case PT_ERROR: return sizeof(SCODE);
case PT_BINARY: return lpSPropValue->Value.bin.cb;
case PT_STRING8: return (lstrlenA( lpSPropValue->Value.lpszA ) + 1) * sizeof(CHAR);
case PT_UNICODE: return (lstrlenW( lpSPropValue->Value.lpszW ) + 1) * sizeof(WCHAR);
case PT_MV_I2: return lpSPropValue->Value.MVi.cValues * sizeof(short int);
case PT_MV_LONG: return lpSPropValue->Value.MVl.cValues * sizeof(LONG);
case PT_MV_R4: return lpSPropValue->Value.MVflt.cValues * SIZEOF_FLOAT;
case PT_MV_APPTIME:
case PT_MV_DOUBLE: return lpSPropValue->Value.MVdbl.cValues * SIZEOF_DOUBLE;
case PT_MV_CURRENCY: return lpSPropValue->Value.MVcur.cValues * sizeof(CURRENCY);
case PT_MV_SYSTIME: return lpSPropValue->Value.MVat.cValues * sizeof(FILETIME);
case PT_MV_I8: return lpSPropValue->Value.MVli.cValues * sizeof(LARGE_INTEGER);
case PT_MV_BINARY:
{
ULONG ulcbSize = 0;
ULONG uliValue;
for ( uliValue = 0;
uliValue < lpSPropValue->Value.MVbin.cValues;
uliValue++ )
ulcbSize += AlignProp((lpSPropValue->Value.MVbin.lpbin + uliValue)->cb);
return ulcbSize;
}
case PT_MV_STRING8:
{
ULONG ulcbSize = 0;
ULONG uliValue;
for ( uliValue = 0;
uliValue < lpSPropValue->Value.MVszA.cValues;
uliValue++ )
ulcbSize += (lstrlenA(*(lpSPropValue->Value.MVszA.lppszA + uliValue)) + 1) * sizeof(CHAR);
return ulcbSize;
}
case PT_MV_UNICODE:
{
ULONG ulcbSize = 0;
ULONG uliValue;
for ( uliValue = 0;
uliValue < lpSPropValue->Value.MVszW.cValues;
uliValue++ )
ulcbSize += (lstrlenW(*(lpSPropValue->Value.MVszW.lppszW + uliValue)) + 1) * sizeof(WCHAR);
return ulcbSize;
}
}
return 0;
}
/**************************************************************************
* GetInstance
*
* Purpose
* Fill in an SPropValue with an instance of an MV propvalue
*
* Parameters
* pvalMv The Mv property
* pvalSv The Sv propery to fill
* uliInst The instance with which to fill pvalSv
*/
STDAPI_(void)
GetInstance(LPSPropValue pvalMv, LPSPropValue pvalSv, ULONG uliInst)
{
switch (PROP_TYPE(pvalSv->ulPropTag))
{
case PT_I2:
pvalSv->Value.li = pvalMv->Value.MVli.lpli[uliInst];
break;
case PT_LONG:
pvalSv->Value.l = pvalMv->Value.MVl.lpl[uliInst];
break;
case PT_R4:
pvalSv->Value.flt = pvalMv->Value.MVflt.lpflt[uliInst];
break;
case PT_DOUBLE:
pvalSv->Value.dbl = pvalMv->Value.MVdbl.lpdbl[uliInst];
break;
case PT_CURRENCY:
pvalSv->Value.cur = pvalMv->Value.MVcur.lpcur[uliInst];
break;
case PT_APPTIME :
pvalSv->Value.at = pvalMv->Value.MVat.lpat[uliInst];
break;
case PT_SYSTIME:
pvalSv->Value.ft = pvalMv->Value.MVft.lpft[uliInst];
break;
case PT_STRING8:
pvalSv->Value.lpszA = pvalMv->Value.MVszA.lppszA[uliInst];
break;
case PT_BINARY:
pvalSv->Value.bin = pvalMv->Value.MVbin.lpbin[uliInst];
break;
case PT_UNICODE:
pvalSv->Value.lpszW = pvalMv->Value.MVszW.lppszW[uliInst];
break;
case PT_CLSID:
pvalSv->Value.lpguid = &pvalMv->Value.MVguid.lpguid[uliInst];
break;
default:
DebugTrace( TEXT("GetInstance() - Unsupported/unimplemented property type 0x%08lx"), PROP_TYPE(pvalMv->ulPropTag) );
pvalSv->ulPropTag = PT_NULL;
return;
}
}
LPTSTR
PszNormalizePsz(LPTSTR pszIn, BOOL fExact)
{
LPTSTR pszOut = NULL;
UINT cb = 0;
if (fExact)
return pszIn;
cb = sizeof(CHAR) * (lstrlen(pszIn) + 1);
if (FAILED(MAPIAllocateBuffer(cb, (LPVOID *)&pszOut)))
return NULL;
MemCopy(pszOut, pszIn, cb);
#if defined(WIN16) || defined(WIN32)
CharUpper(pszOut);
#else
//$TODO: This should be inlined in the mapinls.h for non WIN
//$ but I didn't want to do all the cases of CharUpper.
//$DRM What about other languages?
{
CHAR *pch;
for (pch = pszOut; *pch; pch++)
{
if (*pch >= 'a' && *pch <= 'z')
*pch = (CHAR)(*pch - 'a' + 'A');
}
}
#endif
return pszOut;
}
#ifdef TABLES
/*
- FPropContainsProp()
-
* Compares two properties to see if one TEXT("contains") the other
* according to a fuzzy level heuristic.
*
* The method of comparison depends on the type of the properties
* being compared and the fuzzy level:
*
* Property types Fuzzy Level Comparison
* -------------- ----------- ----------
* PT_STRING8 FL_FULLSTRING Returns TRUE if the value of the source
* PT_BINARY and target string are equivalent. With
* no other flags is equivalent to
* RES_PROPERTY with RELOP_EQ
* returns FALSE otherwise.
*
* PT_STRING8 FL_SUBSTRING Returns TRUE if Pattern is contained
* PT_BINARY as a substring in Target
* returns FALSE otherwise.
*
* PT_STRING8 FL_IGNORECASE All comparisons are done case insensitively
*
* PT_STRING8 FL_IGNORENONSPACE THIS IS NOT (YET?) IMPLEMENTED
* All comparisons ignore what in unicode are
* called TEXT("non-spacing characters") such as
* diacritics.
*
* PT_STRING8 FL_LOOSE Provider adds value by doing as much of
* FL_IGNORECASE and FL_IGNORESPACE as he wants
*
* PT_STRING8 FL_PREFIX Pattern and Target are compared only up to
* PT_BINARY the length of Pattern
*
* PT_STRING8 any other Ignored
*
*
* PT_BINARY any not defined Returns TRUE if the value of the property
* above pointed to by lpSPropValueTarget contains the
* sequence of bytes which is the value of
* the property pointed to by lpSPropValuePattern;
* returns FALSE otherwise.
*
* Error returns:
*
* FALSE If the properties being compared are not both of the same
* type, or if one or both of those properties is not one
* of the types listed above, or if the fuzzy level is not
* one of those listed above.
*/
STDAPI_(BOOL)
FPropContainsProp( LPSPropValue lpSPropValueTarget,
LPSPropValue lpSPropValuePattern,
ULONG ulFuzzyLevel )
{
SPropValue sval;
ULONG uliInst;
LCID lcid = GetUserDefaultLCID();
DWORD dwCSFlags = ((!(ulFuzzyLevel & FL_IGNORECASE)-1) & (NORM_IGNORECASE | NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH)) |
((!(ulFuzzyLevel & FL_IGNORENONSPACE)-1) & NORM_IGNORENONSPACE);
// Validate parameters
AssertSz( lpSPropValueTarget && !IsBadReadPtr( lpSPropValueTarget, sizeof( SPropValue ) ),
TEXT("lpSPropValueTarget fails address check") );
AssertSz( lpSPropValuePattern && !IsBadReadPtr( lpSPropValuePattern, sizeof( SPropValue ) ),
TEXT("lpSPropValuePattern fails address check") );
if (ulFuzzyLevel & FL_LOOSE)
dwCSFlags |= NORM_IGNORECASE | NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH;
if ( !(lpSPropValuePattern->ulPropTag & MV_FLAG)
&& lpSPropValueTarget->ulPropTag & MV_FLAG)
{
sval.ulPropTag = lpSPropValueTarget->ulPropTag & ~MV_FLAG;
uliInst = lpSPropValueTarget->Value.MVbin.cValues;
while (uliInst-- > 0)
{
GetInstance(lpSPropValueTarget, &sval, uliInst);
if (FPropContainsProp(&sval, lpSPropValuePattern, ulFuzzyLevel))
return TRUE;
}
return FALSE;
}
if ( PROP_TYPE(lpSPropValuePattern->ulPropTag) !=
PROP_TYPE(lpSPropValueTarget->ulPropTag) )
return FALSE;
switch ( PROP_TYPE(lpSPropValuePattern->ulPropTag) )
{
case PT_STRING8:
// [PaulHi] 2/16/99 single byte string version
if (ulFuzzyLevel & FL_SUBSTRING)
{
return FRKFindSubpsz(lpSPropValueTarget->Value.lpszA,
lstrlenA(lpSPropValueTarget->Value.lpszA),
lpSPropValuePattern->Value.lpszA,
lstrlenA(lpSPropValuePattern->Value.lpszA),
ulFuzzyLevel);
}
else // FL_PREFIX or FL_FULLSTRING
{
UINT cch;
if (ulFuzzyLevel & FL_PREFIX)
{
cch = (UINT)lstrlenA(lpSPropValuePattern->Value.lpszA);
if (cch > (UINT)lstrlenA(lpSPropValueTarget->Value.lpszA))
return(FALSE);
}
else
cch = (UINT)-1;
return CompareStringA(lcid, dwCSFlags,
lpSPropValueTarget->Value.lpszA, cch,
lpSPropValuePattern->Value.lpszA, cch) == 2;
}
case PT_UNICODE:
// [PaulHi] 2/16/99 double byte string version
if (ulFuzzyLevel & FL_SUBSTRING)
{
LPSTR lpszTarget = ConvertWtoA(lpSPropValueTarget->Value.lpszW);
LPSTR lpszPattern = ConvertWtoA(lpSPropValuePattern->Value.lpszW);
BOOL bRtn = FALSE;
if (lpszTarget && lpszPattern)
{
bRtn = FRKFindSubpsz(lpszTarget,
lstrlenA(lpszTarget),
lpszPattern,
lstrlenA(lpszPattern),
ulFuzzyLevel);
}
LocalFreeAndNull(&lpszTarget);
LocalFreeAndNull(&lpszPattern);
return bRtn;
}
else // FL_PREFIX or FL_FULLSTRING
{
UINT cch;
if (ulFuzzyLevel & FL_PREFIX)
{
cch = (UINT)lstrlen(lpSPropValuePattern->Value.lpszW);
if (cch > (UINT)lstrlen(lpSPropValueTarget->Value.lpszW))
return(FALSE);
}
else
cch = (UINT)-1;
return CompareString(lcid, dwCSFlags,
lpSPropValueTarget->Value.lpszW, cch,
lpSPropValuePattern->Value.lpszW, cch) == 2;
}
break;
case PT_BINARY:
if (ulFuzzyLevel & FL_SUBSTRING)
return FRKFindSubpb(lpSPropValueTarget->Value.bin.lpb,
lpSPropValueTarget->Value.bin.cb,
lpSPropValuePattern->Value.bin.lpb,
lpSPropValuePattern->Value.bin.cb);
else if (ulFuzzyLevel & FL_PREFIX)
{
if (lpSPropValuePattern->Value.bin.cb > lpSPropValueTarget->Value.bin.cb)
return FALSE;
}
else // FL_FULLSTRING
if (lpSPropValuePattern->Value.bin.cb != lpSPropValueTarget->Value.bin.cb)
return FALSE;
return !memcmp(lpSPropValuePattern->Value.bin.lpb,
lpSPropValueTarget->Value.bin.lpb,
(UINT) lpSPropValuePattern->Value.bin.cb);
case PT_MV_STRING8:
{
SPropValue spvT, spvP;
ULONG i;
// [PaulHi] 2/16/99 single byte string version
// To do MV_STRING we will break up the individual strings in the target
// into single STRING prop values and pass them recursively back into this
// function.
// We expect the pattern MV prop to contain exactly one string. It's kind
// of hard to decide what the behavior should be otherwise.
if (lpSPropValuePattern->Value.MVszA.cValues != 1)
{
DebugTrace( TEXT("FPropContainsProp() - PT_MV_STRING8 of pattern must have cValues == 1\n"));
return(FALSE);
}
// Turn off the MV flag and pass in each string seperately
spvP.ulPropTag = spvT.ulPropTag = lpSPropValuePattern->ulPropTag & ~MV_FLAG;
spvP.Value.lpszA = *lpSPropValuePattern->Value.MVszA.lppszA;
for (i = 0; i < lpSPropValueTarget->Value.MVszA.cValues; i++)
{
spvT.Value.lpszA = lpSPropValueTarget->Value.MVszA.lppszA[i];
if (FPropContainsProp(&spvT,
&spvP,
ulFuzzyLevel))
{
return(TRUE);
}
}
return(FALSE);
}
break;
case PT_MV_UNICODE:
{
SPropValue spvT, spvP;
ULONG i;
// [PaulHi] 2/16/99 double byte string version
// To do MV_STRING we will break up the individual strings in the target
// into single STRING prop values and pass them recursively back into this
// function.
// We expect the pattern MV prop to contain exactly one string. It's kind
// of hard to decide what the behavior should be otherwise.
if (lpSPropValuePattern->Value.MVszW.cValues != 1)
{
DebugTrace( TEXT("FPropContainsProp() - PT_MV_UNICODE of pattern must have cValues == 1\n"));
return(FALSE);
}
// Turn off the MV flag and pass in each string seperately
spvP.ulPropTag = spvT.ulPropTag = lpSPropValuePattern->ulPropTag & ~MV_FLAG;
spvP.Value.lpszW = *lpSPropValuePattern->Value.MVszW.lppszW;
for (i = 0; i < lpSPropValueTarget->Value.MVszW.cValues; i++)
{
spvT.Value.lpszW = lpSPropValueTarget->Value.MVszW.lppszW[i];
if (FPropContainsProp(&spvT,
&spvP,
ulFuzzyLevel))
{
return(TRUE);
}
}
return(FALSE);
}
break;
default:
DebugTrace( TEXT("FPropContainsProp() - Unsupported/unimplemented property type 0x%08lx\n"), PROP_TYPE(lpSPropValuePattern->ulPropTag) );
return FALSE;
} // end switch(ulPropTag)
}
/*
- FPropCompareProp()
-
* Compares the property pointed to by lpSPropValue1 with the property
* pointed to by lpSPropValue2 using the binary relational operator
* specified by ulRelOp. The order of comparison is:
*
* Property1 Operator Property2
*/
STDAPI_(BOOL)
FPropCompareProp( LPSPropValue lpSPropValue1,
ULONG ulRelOp,
LPSPropValue lpSPropValue2 )
{
SPropValue sval;
ULONG uliInst;
// Validate parameters
AssertSz( lpSPropValue1 && !IsBadReadPtr( lpSPropValue1, sizeof( SPropValue ) ),
TEXT("lpSPropValue1 fails address check") );
AssertSz( lpSPropValue2 && !IsBadReadPtr( lpSPropValue2, sizeof( SPropValue ) ),
TEXT("lpSPropValue2 fails address check") );
if ( !(lpSPropValue2->ulPropTag & MV_FLAG)
&& lpSPropValue1->ulPropTag & MV_FLAG)
{
sval.ulPropTag = lpSPropValue1->ulPropTag & ~MV_FLAG;
uliInst = lpSPropValue1->Value.MVbin.cValues;
while (uliInst-- > 0)
{
GetInstance(lpSPropValue1, &sval, uliInst);
if (FPropCompareProp(&sval, ulRelOp, lpSPropValue2))
return TRUE;
}
return FALSE;
}
// If the prop types don't match then the properties are not
// equal but otherwise uncomparable
//
if (PROP_TYPE(lpSPropValue1->ulPropTag) !=
PROP_TYPE(lpSPropValue2->ulPropTag))
return (ulRelOp == RELOP_NE);
switch ( ulRelOp )
{
case RELOP_LT:
return LPropCompareProp( lpSPropValue1, lpSPropValue2 ) < 0;
case RELOP_LE:
return LPropCompareProp( lpSPropValue1, lpSPropValue2 ) <= 0;
case RELOP_GT:
return LPropCompareProp( lpSPropValue1, lpSPropValue2 ) > 0;
case RELOP_GE:
return LPropCompareProp( lpSPropValue1, lpSPropValue2 ) >= 0;
case RELOP_EQ:
return LPropCompareProp( lpSPropValue1, lpSPropValue2 ) == 0;
case RELOP_NE:
return LPropCompareProp( lpSPropValue1, lpSPropValue2 ) != 0;
case RELOP_RE:
return FALSE;
}
DebugTrace( TEXT("FPropCompareProp() - Unknown relop 0x%08lx"), ulRelOp );
return FALSE;
}
/*
- LPropCompareProp()
-
* Description:
*
* Compares two properties to determine the ordering
* relation between the two. For property types which
* have no intrinsic ordering (eg. BOOLEAN, ERROR, etc.)
* this function simply determines if the two are equal
* or not equal. If they are not equal, the returned
* value is not defined, but it will be non-zero and
* will be consistent across calls.
*
*
* Returns:
*
* < 0 if property A is TEXT("less than") property B
* > 0 if property A is TEXT("greater than") property B
* 0 if property A TEXT("equals") property B
*
*/
STDAPI_(LONG)
LPropCompareProp( LPSPropValue lpSPropValueA,
LPSPropValue lpSPropValueB )
{
ULONG uliinst;
ULONG ulcinst;
LONG lRetval;
LCID lcid = GetUserDefaultLCID();
// Validate parameters
AssertSz( lpSPropValueA && !IsBadReadPtr( lpSPropValueA, sizeof( SPropValue ) ),
TEXT("lpSPropValueA fails address check") );
AssertSz( lpSPropValueB && !IsBadReadPtr( lpSPropValueB, sizeof( SPropValue ) ),
TEXT("lpSPropValueB fails address check") );
Assert( PROP_TYPE(lpSPropValueA->ulPropTag) ==
PROP_TYPE(lpSPropValueB->ulPropTag) );
if (lpSPropValueA->ulPropTag & MV_FLAG)
{
ulcinst = min(lpSPropValueA->Value.MVi.cValues, lpSPropValueB->Value.MVi.cValues);
for (uliinst = 0; uliinst < ulcinst; uliinst++)
{
switch (PROP_TYPE(lpSPropValueA->ulPropTag))
{
case PT_MV_I2:
if (lRetval = lpSPropValueA->Value.MVi.lpi[uliinst] - lpSPropValueB->Value.MVi.lpi[uliinst])
return lRetval;
break;
case PT_MV_LONG:
if (lRetval = lpSPropValueA->Value.MVl.lpl[uliinst] - lpSPropValueB->Value.MVl.lpl[uliinst])
return lRetval;
break;
case PT_MV_R4:
if (lpSPropValueA->Value.MVflt.lpflt[uliinst] != lpSPropValueB->Value.MVflt.lpflt[uliinst])
return lpSPropValueA->Value.MVflt.lpflt[uliinst] < lpSPropValueB->Value.MVflt.lpflt[uliinst] ? -1 : 1;
break;
case PT_MV_DOUBLE:
if (lpSPropValueA->Value.MVdbl.lpdbl[uliinst] != lpSPropValueB->Value.MVdbl.lpdbl[uliinst])
return lpSPropValueA->Value.MVdbl.lpdbl[uliinst] < lpSPropValueB->Value.MVdbl.lpdbl[uliinst] ? -1 : 1;
break;
case PT_MV_SYSTIME:
lRetval = lpSPropValueA->Value.MVft.lpft[uliinst].dwHighDateTime == lpSPropValueB->Value.MVft.lpft[uliinst].dwHighDateTime ?
(lpSPropValueA->Value.MVft.lpft[uliinst].dwLowDateTime != lpSPropValueB->Value.MVft.lpft[uliinst].dwLowDateTime ?
(lpSPropValueA->Value.MVft.lpft[uliinst].dwLowDateTime < lpSPropValueB->Value.MVft.lpft[uliinst].dwLowDateTime ?
-1 : 1) : 0) : (lpSPropValueA->Value.MVft.lpft[uliinst].dwHighDateTime < lpSPropValueB->Value.MVft.lpft[uliinst].dwHighDateTime ? -1 : 1);
if (lRetval)
return lRetval;
break;
case PT_MV_BINARY:
lRetval = lpSPropValueA->Value.MVbin.lpbin[uliinst].cb != lpSPropValueB->Value.MVbin.lpbin[uliinst].cb ?
(lpSPropValueA->Value.MVbin.lpbin[uliinst].cb < lpSPropValueB->Value.MVbin.lpbin[uliinst].cb ?
-1 : 1) : memcmp(lpSPropValueA->Value.MVbin.lpbin[uliinst].lpb,
lpSPropValueB->Value.MVbin.lpbin[uliinst].lpb,
(UINT) lpSPropValueA->Value.MVbin.lpbin[uliinst].cb);
if (lRetval)
return lRetval;
break;
case PT_MV_STRING8:
lRetval = CompareStringA(lcid, NORM_IGNORECASE | NORM_IGNORENONSPACE | NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH,
lpSPropValueA->Value.MVszA.lppszA[uliinst], -1,
lpSPropValueB->Value.MVszA.lppszA[uliinst], -1) - 2;
if (lRetval)
return lRetval;
break;
case PT_MV_UNICODE:
lRetval = CompareStringW(lcid, NORM_IGNORECASE | NORM_IGNORENONSPACE | NORM_IGNOREKANATYPE,
lpSPropValueA->Value.MVszW.lppszW[uliinst], -1,
lpSPropValueB->Value.MVszW.lppszW[uliinst], -1) - 2;
if (lRetval)
return lRetval;
break;
case PT_MV_I8:
case PT_MV_CURRENCY:
lRetval = lpSPropValueA->Value.MVli.lpli[uliinst].HighPart == lpSPropValueB->Value.MVli.lpli[uliinst].HighPart ?
(lpSPropValueA->Value.MVli.lpli[uliinst].LowPart != lpSPropValueB->Value.MVli.lpli[uliinst].LowPart ?
(lpSPropValueA->Value.MVli.lpli[uliinst].LowPart < lpSPropValueB->Value.MVli.lpli[uliinst].LowPart ?
-1 : 1) : 0) : (lpSPropValueA->Value.MVli.lpli[uliinst].HighPart < lpSPropValueB->Value.MVli.lpli[uliinst].HighPart ? -1 : 1);
if (lRetval)
return lRetval;
break;
case PT_MV_CLSID:
lRetval = memcmp(&lpSPropValueA->Value.MVguid.lpguid[uliinst],
&lpSPropValueB->Value.MVguid.lpguid[uliinst],
sizeof(GUID));
break;
case PT_MV_APPTIME: //$ NYI
default:
DebugTrace( TEXT("PropCompare() - Unknown or NYI property type 0x%08lx. Assuming equal"), PROP_TYPE(lpSPropValueA->ulPropTag) );
return 0;
}
}
return lpSPropValueA->Value.MVi.cValues - lpSPropValueB->Value.MVi.cValues;
}
else
{
switch ( PROP_TYPE(lpSPropValueA->ulPropTag) )
{
case PT_NULL:
//$ By definition any PT_NULL property is equal to
//$ every other PT_NULL property. (Is this right?)
return 0;
case PT_LONG:
case PT_ERROR:
return (lpSPropValueA->Value.l == lpSPropValueB->Value.l) ? 0 :
(lpSPropValueA->Value.l > lpSPropValueB->Value.l) ? 1 : -1;
case PT_BOOLEAN:
return (LONG) !!lpSPropValueA->Value.b - (LONG) !!lpSPropValueB->Value.b;
case PT_I2:
return (LONG) lpSPropValueA->Value.i - (LONG) lpSPropValueB->Value.i;
case PT_I8:
case PT_CURRENCY:
return lpSPropValueA->Value.li.HighPart == lpSPropValueB->Value.li.HighPart ?
(lpSPropValueA->Value.li.LowPart != lpSPropValueB->Value.li.LowPart ?
(lpSPropValueA->Value.li.LowPart < lpSPropValueB->Value.li.LowPart ?
-1 : 1) : 0) : (lpSPropValueA->Value.li.HighPart < lpSPropValueB->Value.li.HighPart ? -1 : 1);
case PT_SYSTIME:
return lpSPropValueA->Value.ft.dwHighDateTime == lpSPropValueB->Value.ft.dwHighDateTime ?
(lpSPropValueA->Value.ft.dwLowDateTime != lpSPropValueB->Value.ft.dwLowDateTime ?
(lpSPropValueA->Value.ft.dwLowDateTime < lpSPropValueB->Value.ft.dwLowDateTime ?
-1 : 1) : 0) : (lpSPropValueA->Value.ft.dwHighDateTime < lpSPropValueB->Value.ft.dwHighDateTime ? -1 : 1);
case PT_R4:
return lpSPropValueA->Value.flt != lpSPropValueB->Value.flt ?
(lpSPropValueA->Value.flt < lpSPropValueB->Value.flt ?
-1 : 1) : 0;
case PT_DOUBLE:
case PT_APPTIME:
return lpSPropValueA->Value.dbl != lpSPropValueB->Value.dbl ?
(lpSPropValueA->Value.dbl < lpSPropValueB->Value.dbl ?
-1 : 1) : 0;
case PT_BINARY:
// The following tediousness with assignment de-ICEs WIN16SHP
{
LPBYTE pbA = lpSPropValueA->Value.bin.lpb;
LPBYTE pbB = lpSPropValueB->Value.bin.lpb;
lRetval = min(lpSPropValueA->Value.bin.cb, lpSPropValueB->Value.bin.cb);
lRetval = memcmp(pbA, pbB, (UINT) lRetval);
}
if (lRetval != 0)
return lRetval;
else if (lpSPropValueA->Value.bin.cb == lpSPropValueB->Value.bin.cb)
return 0L;
else if (lpSPropValueA->Value.bin.cb < lpSPropValueB->Value.bin.cb)
return -1L;
else
return 1L;
case PT_UNICODE:
//$ REVIEW: If we NORM_IGNORENONSPACE then our sorts will look
//$ REVIEW: wrong for languages which define an ordering for
//$ REVIEW: diacritics.
return CompareStringW(lcid, NORM_IGNORECASE | NORM_IGNOREKANATYPE,
lpSPropValueA->Value.lpszW, -1, lpSPropValueB->Value.lpszW, -1) - 2;
case PT_STRING8:
//$ REVIEW: If we NORM_IGNORENONSPACE then our sorts will look
//$ REVIEW: wrong for languages which define an ordering for
//$ REVIEW: diacritics.
return CompareStringA(lcid, NORM_IGNORECASE | NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH,
lpSPropValueA->Value.lpszA, -1, lpSPropValueB->Value.lpszA, -1) - 2;
case PT_CLSID:
{
GUID UNALIGNED *lpguidA = lpSPropValueA->Value.lpguid;
GUID UNALIGNED *lpguidB = lpSPropValueB->Value.lpguid;
return memcmp(lpguidA, lpguidB, sizeof(GUID));
}
case PT_OBJECT: // Not supported
case PT_UNSPECIFIED: // Not supported
default:
DebugTrace( TEXT("PropCompare() - Unknown or NYI property type 0x%08lx. Assuming equal"), PROP_TYPE(lpSPropValueA->ulPropTag) );
return 0;
}
}
}
/**************************************************************************
* HrAddColumns
*
* Purpose
* Add space for the properties in ptaga to the column set for the table.
* The specified properties will be the first properties returned on
* subsequent QueryRows calls.
* Any properties that were already in the column set, but weren't
* in the new array will be placed at the end of the new column
* set. This call is most often used on RECIPIENT tables.
*
* Parameters
* pmt A pointer to an LPMAPITABLE
* ptaga counted array of props to be moved up front or added
* lpfnAllocBuf pointer to MAPIAllocateBuffer
* lpfnFreeBuf pointer to MAPIFreeBuffer
*/
STDAPI_(HRESULT)
HrAddColumns( LPMAPITABLE pmt,
LPSPropTagArray ptaga,
LPALLOCATEBUFFER lpfnAllocBuf,
LPFREEBUFFER lpfnFreeBuf)
{
HRESULT hr;
hr = HrAddColumnsEx(pmt, ptaga, lpfnAllocBuf, lpfnFreeBuf, NULL);
DebugTraceResult(HrAddColumns, hr);
return hr;
}
/**************************************************************************
* HrAddColumnsEx
*
* Purpose
* add space for the properties in ptaga to the columns set for the pmt.
* The specified properties will be the first properties returned on
* subsequent QueryRows calls. Any properties that were already in the
* column set, but weren't in the new array will be placed at the end
* of the new column set. This call is most often used on RECIPIENT
* tables. The extended version of this call allows the caller to
* filter the original proptags (e.g., to force UNICODE to STRING8).
*
* Parameters
* pmt pointer to an LPMAPITABLE
* ptagaIn counted array of properties to be moved up front
* or added
* lpfnAllocBuf pointer to MAPIAllocateBuffer
* lpfnFreeBuf pointer to MAPIFreeBuffer
* lpfnFilterColumns callback function applied to the table's column set
*/
STDAPI_(HRESULT)
HrAddColumnsEx( LPMAPITABLE pmt,
LPSPropTagArray ptagaIn,
LPALLOCATEBUFFER lpfnAllocBuf,
LPFREEBUFFER lpfnFreeBuf,
void (FAR *lpfnFilterColumns)(LPSPropTagArray ptaga))
{
HRESULT hr = hrSuccess;
SCODE sc = S_OK;
LPSPropTagArray ptagaOld = NULL; /* old, original columns on pmt */
LPSPropTagArray ptagaExtend = NULL; /* extended columns on pmt */
ULONG ulcPropsOld;
ULONG ulcPropsIn;
ULONG ulcPropsFinal;
UNALIGNED ULONG *pulPTEnd;
UNALIGNED ULONG *pulPTOld;
UNALIGNED ULONG *pulPTOldMac;
// Do some parameter checking.
AssertSz(!FBadUnknown((LPUNKNOWN) pmt),
TEXT("HrAddColumnsEx: bad table object"));
AssertSz( !IsBadReadPtr(ptagaIn, CbNewSPropTagArray(0))
&& !IsBadReadPtr(ptagaIn, CbSPropTagArray(ptagaIn)),
TEXT("Bad Prop Tag Array given to HrAddColumnsEx."));
AssertSz(!IsBadCodePtr((FARPROC) lpfnAllocBuf),
TEXT("HrAddColumnsEx: lpfnAllocBuf fails address check"));
AssertSz(!IsBadCodePtr((FARPROC) lpfnFreeBuf),
TEXT("HrAddColumnsEx: lpfnFreeBuf fails address check"));
AssertSz(!lpfnFilterColumns || !IsBadCodePtr((FARPROC) lpfnFilterColumns),
TEXT("HrAddColumnsEx: lpfnFilterColumns fails address check"));
// Find out which columns are already set on the table.
//
hr = pmt->lpVtbl->QueryColumns(pmt, TBL_ALL_COLUMNS, &ptagaOld);
if (HR_FAILED(hr))
goto exit;
AssertSz( !IsBadReadPtr( ptagaOld, CbNewSPropTagArray(0))
&& !IsBadReadPtr( ptagaOld, CbSPropTagArray(ptagaOld)),
TEXT("Bad Prop Tag Array returned from QueryColumns."));
// Give the caller an opportunity to filter the source column set,
// for instance, to force UNICODE to STRING8
//
if (lpfnFilterColumns)
{
(*lpfnFilterColumns)(ptagaOld);
}
ulcPropsOld = ptagaOld->cValues;
ulcPropsIn = ptagaIn->cValues;
// Allocate space for the maximum possible number of new columns.
//
sc = (lpfnAllocBuf)(CbNewSPropTagArray(ulcPropsOld + ulcPropsIn),
(LPVOID *)&ptagaExtend);
if (FAILED(sc))
{
hr = ResultFromScode(sc);
goto exit;
}
// Fill in the front of the extended prop tag array with the set
// of properties which must be at known locations in the array.
//
MemCopy(ptagaExtend, ptagaIn, CbSPropTagArray(ptagaIn));
// If one of the old columns isn't in the given array, then put it after
// the given tags.
ulcPropsFinal = ptagaIn->cValues;
pulPTEnd = &(ptagaExtend->aulPropTag[ulcPropsFinal]);
pulPTOld = ptagaOld->aulPropTag;
pulPTOldMac = pulPTOld + ulcPropsOld;
while (pulPTOld < pulPTOldMac)
{
UNALIGNED ULONG *pulPTIn;
UNALIGNED ULONG *pulPTInMac;
pulPTIn = ptagaIn->aulPropTag;
pulPTInMac = pulPTIn + ulcPropsIn;
while ( pulPTIn < pulPTInMac
&& *pulPTOld != *pulPTIn)
++pulPTIn;
if (pulPTIn >= pulPTInMac)
{
// This property is not one of the input ones so put it in the next
// available position after the input ones.
//
*pulPTEnd = *pulPTOld;
++pulPTEnd;
++ulcPropsFinal;
}
++pulPTOld;
}
// Set the total number of prop tags in the extended tag array.
//
ptagaExtend->cValues = ulcPropsFinal;
// Tell the table to return the extended column set.
//
hr = pmt->lpVtbl->SetColumns(pmt, ptagaExtend, 0L);
exit:
(lpfnFreeBuf)(ptagaExtend);
(lpfnFreeBuf)(ptagaOld);
DebugTraceResult(HrAddColumnsEx, hr);
return hr;
}
#endif