You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1439 lines
42 KiB
1439 lines
42 KiB
/*
|
|
* 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
|