Leaked source code of windows server 2003
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.
 
 
 
 
 
 

5196 lines
133 KiB

/*============================================================================
*
* ITABLE.C
*
* MAPI 1.0 In-memory MAPI Table DLL (MAPIU.DLL)
*
* Copyright (C) 1993 and 1994 Microsoft Corporation
*
*
* Hungarian shorthand:
* To avoid excessively long identifier names, the following
* shorthand expressions are used:
*
* LPSPropTagArray lppta
* LPSRestriction lpres
* LPSPropValue lpprop
* LPSRow lprow
* LPSRowSet lprows
* LPSSortOrder lpso
* LPSSortOrderSet lpsos
*
* Known bugs:
* - Restriction evaulation/copying is recursive
* - Uses hinst derived from static module name (Raid 1263)
*/
#include "_apipch.h"
void FixupColsWA(LPSPropTagArray lpptaCols, BOOL bUnicodeTable);
/*============================================================================
* TAD (table data class)
*
* Implementes in-memory table data object.
*/
TAD_Vtbl vtblTAD =
{
VTABLE_FILL
(TAD_QueryInterface_METHOD FAR *) UNKOBJ_QueryInterface,
(TAD_AddRef_METHOD FAR *) UNKOBJ_AddRef,
TAD_Release,
TAD_HrGetView,
TAD_HrModifyRow,
TAD_HrDeleteRow,
TAD_HrQueryRow,
TAD_HrEnumRow,
TAD_HrNotify, //$PERFORMANCE
TAD_HrInsertRow,
TAD_HrModifyRows,
TAD_HrDeleteRows
};
LPIID rglpiidTAD[2] =
{
(LPIID)&IID_IMAPITableData,
(LPIID)&IID_IUnknown
};
/*============================================================================
* VUE (table view class)
*
* Implementes in-memory IMAPITable class on top of TADs
*/
VUE_Vtbl vtblVUE =
{
VTABLE_FILL
(VUE_QueryInterface_METHOD FAR *) UNKOBJ_QueryInterface,
(VUE_AddRef_METHOD FAR *) UNKOBJ_AddRef,
VUE_Release,
(VUE_GetLastError_METHOD FAR *) UNKOBJ_GetLastError,
VUE_Advise,
VUE_Unadvise,
VUE_GetStatus,
VUE_SetColumns,
VUE_QueryColumns,
VUE_GetRowCount,
VUE_SeekRow,
VUE_SeekRowApprox,
VUE_QueryPosition,
VUE_FindRow,
VUE_Restrict,
VUE_CreateBookmark,
VUE_FreeBookmark,
VUE_SortTable,
VUE_QuerySortOrder,
VUE_QueryRows,
VUE_Abort,
VUE_ExpandRow,
VUE_CollapseRow,
VUE_WaitForCompletion,
VUE_GetCollapseState,
VUE_SetCollapseState
};
LPIID rglpiidVUE[2] =
{
(LPIID)&IID_IMAPITable,
(LPIID)&IID_IUnknown
};
/*============================================================================
- CreateTable()
-
*
* ulFlags - 0 or MAPI_UNICODE
*/
//
// BUGBUG
// [PaulHi] 4/5/99 @bug
// A zero is passed in to CreateTableData() ulFlags parameter. This means that the
// requested table is ALWAYS ANSI and CANNOT be UNICODE, regardless of the properties
// passed in through the LPSPropTagArray. The CreateTableData() function will forcibly
// set the property types to PT_STRING8 or PT_UNICODE depending on the ulFlags and since
// the ulFlags is hard coded to zero this means always STRING8 string properties.
//
STDAPI_(SCODE)
CreateTable(LPCIID lpiid,
ALLOCATEBUFFER FAR * lpfAllocateBuffer,
ALLOCATEMORE FAR * lpfAllocateMore,
FREEBUFFER FAR * lpfFreeBuffer,
LPVOID lpvReserved,
ULONG ulTableType,
ULONG ulPropTagIndexCol,
LPSPropTagArray lpptaCols,
LPTABLEDATA FAR * lplptad)
{
return(CreateTableData(lpiid,
lpfAllocateBuffer,
lpfAllocateMore,
lpfFreeBuffer,
lpvReserved,
ulTableType,
ulPropTagIndexCol,
lpptaCols,
NULL, // lpvDataSource
0, // cbDataSource
NULL,
0, // ulFlags, includes MAPI_UNICODE, which is hard coded to ANSI!!!
lplptad));
}
/*
-
- CreateTableData
*
* ulFlags - 0 | MAPI_UNICODE | WAB_PROFILE_CONTENTS | WAB_ENABLE_PROFILES
*
*/
STDAPI_(SCODE)
CreateTableData(LPCIID lpiid,
ALLOCATEBUFFER FAR * lpfAllocateBuffer,
ALLOCATEMORE FAR * lpfAllocateMore,
FREEBUFFER FAR * lpfFreeBuffer,
LPVOID lpvReserved,
ULONG ulTableType,
ULONG ulPropTagIndexCol,
LPSPropTagArray lpptaCols,
LPVOID lpvDataSource,
ULONG cbDataSource,
LPSBinary pbinContEID,
ULONG ulFlags,
LPTABLEDATA FAR * lplptad)
{
LPTAD lptad = NULL;
SCODE sc;
ULONG ulIndexType = PROP_TYPE(ulPropTagIndexCol);
#if !defined(NO_VALIDATION)
if ( lpiid && IsBadReadPtr(lpiid,sizeof(IID)) ||
IsBadCodePtr((FARPROC)lpfAllocateBuffer) ||
IsBadCodePtr((FARPROC)lpfAllocateMore) ||
IsBadCodePtr((FARPROC)lpfFreeBuffer) ||
(ulTableType != TBLTYPE_SNAPSHOT &&
ulTableType != TBLTYPE_KEYSET &&
ulTableType != TBLTYPE_DYNAMIC) ||
!PROP_ID(ulPropTagIndexCol) ||
(ulIndexType == PT_UNSPECIFIED) ||
(ulIndexType == PT_NULL) ||
(ulIndexType == PT_ERROR) ||
(ulIndexType & MV_FLAG) ||
FBadColumnSet(lpptaCols) ||
IsBadWritePtr(lplptad,sizeof(LPTABLEDATA)) )
{
DebugTrace(TEXT("CreateTable() - Bad parameter(s) passed\n") );
return MAPI_E_INVALID_PARAMETER;
}
#endif
// Verify caller wants an IMAPITableData interface
if ( lpiid && memcmp(lpiid, &IID_IMAPITableData, sizeof(IID)) )
{
DebugTrace(TEXT("CreateTable() - Unknown interface ID passed\n") );
return MAPI_E_INTERFACE_NOT_SUPPORTED;
}
// Instantiate a new table data object
if ( FAILED(sc = lpfAllocateBuffer(sizeof(TAD), (LPVOID FAR *) &lptad)) )
{
DebugTrace(TEXT("CreateTable() - Error instantiating new TAD (SCODE = 0x%08lX)\n"), sc );
goto err;
}
MAPISetBufferName(lptad, TEXT("ITable TAD object"));
ZeroMemory(lptad, sizeof(TAD));
if (lpvDataSource) {
if (cbDataSource) {
LPTSTR lpNew;
if (! (lpNew = LocalAlloc(LPTR, cbDataSource))) {
DebugTrace(TEXT("CreateTable:LocalAlloc(%u) -> %u\n"), cbDataSource, GetLastError());
sc = MAPI_E_NOT_ENOUGH_MEMORY;
goto err;
}
CopyMemory(lpNew, lpvDataSource, cbDataSource);
lptad->lpvDataSource = lpNew;
} else {
lptad->lpvDataSource = lpvDataSource; // no size, just a pointer. DON'T Free!
}
lptad->cbDataSource = cbDataSource;
} else {
lptad->cbDataSource = 0;
lptad->lpvDataSource = NULL;
}
lptad->pbinContEID = pbinContEID;
//if(!pbinContEID || (!pbinContEID->cb && !pbinContEID->lpb)) // This is the PAB container
// The caller will send in container EIDs which will be:
// if PAB = User Folder, cont EID won't be NULL - return folder contents only
// if PAB = Virtual Folder, cont EID will have 0 and NULL in it - return all WAB contents
// if WAB_PROFILE_CONTENTS is specified, just return all the contents of all the folders in the profile
if(ulFlags & WAB_PROFILE_CONTENTS)
lptad->bAllProfileContents = TRUE; // this forces folder contents only
if(ulFlags & MAPI_UNICODE)
lptad->bMAPIUnicodeTable = TRUE;
if(pbinContEID && pbinContEID->cb && pbinContEID->lpb) // This is not the Virtual PAB container
lptad->bContainerContentsOnly = (ulFlags & WAB_ENABLE_PROFILES);
else
lptad->bContainerContentsOnly = FALSE;
lptad->inst.lpfAllocateBuffer = lpfAllocateBuffer;
lptad->inst.lpfAllocateMore = lpfAllocateMore;
lptad->inst.lpfFreeBuffer = lpfFreeBuffer;
#ifdef MAC
lptad->inst.hinst = hinstMapiX;//GetCurrentProcess();
#else
lptad->inst.hinst = hinstMapiX;//HinstMapi();
#ifdef DEBUG
if (lptad->inst.hinst == NULL)
TraceSz1( TEXT("ITABLE: GetModuleHandle failed with error %08lX"),
GetLastError());
#endif /* DEBUG */
#endif /* MAC */
if (FAILED(sc = UNKOBJ_Init( (LPUNKOBJ) lptad
, (UNKOBJ_Vtbl FAR *) &vtblTAD
, sizeof(vtblTAD)
, rglpiidTAD
, sizeof(rglpiidTAD)/sizeof(REFIID)
, &lptad->inst)))
{
DebugTrace(TEXT("CreateTable() - Error initializing object (SCODE = 0x%08lX)\n"), sc );
goto err;
}
lptad->ulTableType = ulTableType;
lptad->ulPropTagIndexCol = ulPropTagIndexCol;
if ( FAILED(sc = ScCOAllocate(lptad,
CbNewSPropTagArray(lpptaCols->cValues),
&lptad->lpptaCols)) )
{
DebugTrace(TEXT("CreateTable() - Error duping initial column set (SCODE = 0x%08lX)\n"), sc );
goto err;
}
lptad->ulcColsMac = lpptaCols->cValues;
CopyMemory(lptad->lpptaCols,
lpptaCols,
(size_t) (CbNewSPropTagArray(lpptaCols->cValues)));
// [PaulHi] 4/5/99 @comment Shouldn't
// clients of the WAB request ANSI/UNICODE based on property tags in the
// column array, rather than doing mass conversions?
// Seems like the fix is to create two versions of column property arrays,
// an ANSI and Unicode version.
FixupColsWA(lptad->lpptaCols, (ulFlags & MAPI_UNICODE));
// And return it
*lplptad = (LPTABLEDATA) lptad;
ret:
DebugTraceSc(CreateTable, sc);
return sc;
err:
UlRelease(lptad);
goto ret;
}
/*============================================================================
- TAD::Release()
-
*/
STDMETHODIMP_(ULONG)
TAD_Release( LPTAD lptad )
{
ULONG ulcRef;
LPSRow * plprow;
#if !defined(NO_VALIDATION)
if ( BAD_STANDARD_OBJ(lptad,TAD_,Release,lpVtbl) )
{
TraceSz( TEXT("TAD::Release() - Invalid parameter passed as TAD object"));
return !0;
}
#endif
LockObj(lptad);
if (ulcRef = lptad->ulcRef)
{
ulcRef = --lptad->ulcRef;
}
if ( ulcRef == 0 && !lptad->lpvueList )
{
UnlockObj(lptad); //$ Do we need this?
COFree(lptad, lptad->lpptaCols);
if (lptad->cbDataSource && lptad->lpvDataSource) {
LocalFreeAndNull(&lptad->lpvDataSource);
}
plprow = lptad->parglprowAdd + lptad->ulcRowsAdd;
while ( plprow-- > lptad->parglprowAdd )
ScFreeBuffer(lptad, *plprow);
COFree(lptad, lptad->parglprowAdd);
COFree(lptad, lptad->parglprowIndex);
UNKOBJ_Deinit((LPUNKOBJ) lptad);
ScFreeBuffer(lptad, lptad);
}
else
{
#if DEBUG
if ( ulcRef == 0 && lptad->lpvueList )
{
TraceSz( TEXT("TAD::Release() - TAD object still has open views"));
}
#endif // DEBUG
UnlockObj(lptad);
}
return ulcRef;
}
/*============================================================================
- TAD::HrGetView()
-
* A NULL lpsos means that rows will be in the order that they were added
* to the TAD.
*/
STDMETHODIMP
TAD_HrGetView(
LPTAD lptad,
LPSSortOrderSet lpsos,
CALLERRELEASE FAR * lpfReleaseCallback,
ULONG ulReleaseData,
LPMAPITABLE FAR * lplpmt )
{
SCODE sc;
LPVUE lpvue = NULL;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lptad,TAD_,HrGetView,lpVtbl);
if ( (lpsos && FBadSortOrderSet(lpsos)) ||
(lpfReleaseCallback && IsBadCodePtr((FARPROC) lpfReleaseCallback)) ||
IsBadWritePtr(lplpmt, sizeof(LPMAPITABLE)) )
{
DebugTrace(TEXT("TAD::HrGetView() - Invalid parameter(s) passed\n") );
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
#endif
// Can't support categories
if (lpsos && lpsos->cCategories)
{
DebugTrace(TEXT("TAD::GetView() - No support for categories\n") );
return ResultFromScode(MAPI_E_TOO_COMPLEX);
}
LockObj(lptad);
// Instantiate a new table view
if ( FAILED(sc = lptad->inst.lpfAllocateBuffer(sizeof(VUE),
(LPVOID FAR *) &lpvue)) )
{
DebugTrace(TEXT("ScCreateView() - Error instantiating VUE on TAD (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
MAPISetBufferName(lpvue, TEXT("ITable VUE object"));
ZeroMemory(lpvue, sizeof(VUE));
if (FAILED(sc = UNKOBJ_Init( (LPUNKOBJ) lpvue
, (UNKOBJ_Vtbl FAR *) &vtblVUE
, sizeof(vtblVUE)
, rglpiidVUE
, sizeof(rglpiidVUE)/sizeof(REFIID)
, lptad->pinst)))
{
DebugTrace(TEXT("ScCreateView() - Error initializing VUE object (SCODE = 0x%08lX)\n"), sc );
// don't try to release the vue since it wasn't initialized yet
lptad->inst.lpfFreeBuffer(lpvue);
goto ret;
}
// Link the view to the TAD and AddRef the TAD.
lpvue->lpvueNext = lptad->lpvueList;
lptad->lpvueList = lpvue;
lpvue->lptadParent = lptad;
UlAddRef(lptad);
// Identifier for this table
lpvue->cbDataSource = lptad->cbDataSource;
lpvue->lpvDataSource = lptad->lpvDataSource;
// Initialize the predefined bookmarks
lpvue->bkBeginning.dwfBKS = dwfBKSValid;
lpvue->bkCurrent.dwfBKS = dwfBKSValid;
lpvue->bkEnd.dwfBKS = dwfBKSValid;
#ifdef NOTIFICATIONS
// Burn up a MUID for the notification key for this view
if ( FAILED(sc = ScGenerateMuid(&lpvue->mapiuidNotif)) )
{
DebugTrace(TEXT("TAD::HrGetView() - Error generating MUID for notification key (SCODE = 0x%08lX)\n"), sc );
goto err;
}
#endif
// Make a copy of the initial sort order for the VUE
if ( lpsos
&& FAILED(sc = ScDupRgbEx( (LPUNKOBJ) lptad
, CbSSortOrderSet(lpsos)
, (LPBYTE) lpsos
, 0
, (LPBYTE FAR *) &(lpvue->lpsos))) )
{
DebugTrace(TEXT("TAD::GetView() - Error duping sort order set (SCODE = 0x%08lX)\n"), sc );
goto err;
}
MAPISetBufferName(lpvue->lpsos, TEXT("ITable: dup sort order set"));
// Load the view's initial row set in sorted order
if ( FAILED(sc = ScLoadRows(lpvue->lptadParent->ulcRowsAdd,
lpvue->lptadParent->parglprowAdd,
lpvue,
NULL,
lpvue->lpsos)) )
{
DebugTrace(TEXT("TAD::HrGetView() - Error loading view's initial row set (SCODE = 0x%08lX)\n"), sc );
goto err;
}
lpvue->bMAPIUnicodeTable = lptad->bMAPIUnicodeTable;
// Set the view's initial column set
if ( FAILED(sc = GetScode(VUE_SetColumns(lpvue, lptad->lpptaCols, 0))) )
{
DebugTrace(TEXT("TAD::HrGetView() - Error setting view's initial column set (SCODE = 0x%08lX)\n"), sc );
goto err;
}
lpvue->lpfReleaseCallback = lpfReleaseCallback;
lpvue->ulReleaseData = ulReleaseData;
*lplpmt = (LPMAPITABLE) lpvue;
ret:
UnlockObj(lptad);
return ResultFromScode(sc);
err:
// This will unlink and release the parent TAD.
UlRelease(lpvue);
goto ret;
}
/*============================================================================
- TAD::HrModifyRow()
-
*/
STDMETHODIMP
TAD_HrModifyRow(
LPTAD lptad,
LPSRow lprow )
{
SizedSRowSet( 1, rowsetIn);
rowsetIn.cRows = 1;
rowsetIn.aRow[0] = *lprow;
return TAD_HrModifyRows(lptad, 0, (LPSRowSet) &rowsetIn);
}
/*============================================================================
- TAD::HrModifyRows()
-
*/
STDMETHODIMP
TAD_HrModifyRows(
LPTAD lptad,
ULONG ulFlags,
LPSRowSet lprowsetIn )
{
ULONG cRowsCopy = 0;
LPSRow * parglprowSortedCopy = NULL;
LPSRow * parglprowUnsortedCopy = NULL;
ULONG cRowsOld = 0;
LPSRow * parglprowOld = NULL;
ULONG cNewTags = 0;
SCODE sc;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lptad,TAD_,HrModifyRows,lpVtbl);
if ( IsBadReadPtr( lprowsetIn, CbNewSRowSet(0))
|| IsBadWritePtr( lprowsetIn, CbSRowSet(lprowsetIn)))
{
DebugTrace(TEXT("TAD::HrModifyRows() - Invalid parameter(s) passed\n") );
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
// Validation of the actual rows input will be done at the same time that
// we make our internal copy.
#endif
if (ulFlags)
{
DebugTrace(TEXT("TAD::HrModifyRows() - Unknown flags passed\n") );
// return ResultFromScode(MAPI_E_UNKNOWN_FLAGS);
}
LockObj(lptad);
// Make a copy of the rows for our own use.
// Add new columns to the TAD's column set.
//
// Move the index column to the front.
// Filter out PT_ERROR and PT_NULL columns.
// Validate the input rows.
// Note - two rows with the same index property is invalid.
if ( FAILED(sc = ScCopyTadRowSet( lptad
, lprowsetIn
, &cNewTags
, &cRowsCopy
, &parglprowUnsortedCopy
, &parglprowSortedCopy)) )
{
DebugTrace(TEXT("TAD::HrModifyRows() - Error duping row set to modify\n") );
goto ret;
}
// Replace/add the copied row to the table data. We pass in the unsorted
// Set in order to maintain the FIFO behaviour on unsorted views
// Note! This call MUST replace all (SUCCESS) or none (FAILURE)!
if ( FAILED(sc = ScReplaceRows( lptad
, cRowsCopy
, parglprowUnsortedCopy
, &cRowsOld
, &parglprowOld)) )
{
DebugTrace(TEXT("TAD::HrModifyRows() - Error adding rows (SCODE = 0x%08lX)\n"), sc );
goto err;
}
// Update the views with the modified rows.
// NOTE! Failure to update a view CANNOT leave a view pointing to an
// old row!
UpdateViews( lptad
, cRowsOld
, parglprowOld
, cRowsCopy
, parglprowUnsortedCopy
, parglprowSortedCopy);
// Free the old rows.
if (parglprowOld)
{
LPSRow * plprowTmp = parglprowOld;
while (cRowsOld)
{
ScFreeBuffer( lptad, *(plprowTmp++));
cRowsOld--;
}
}
ret:
// Free the tables of row pointers
ScFreeBuffer( lptad, parglprowSortedCopy);
ScFreeBuffer( lptad, parglprowUnsortedCopy);
ScFreeBuffer(lptad, parglprowOld);
UnlockObj(lptad);
return ResultFromScode(sc);
err:
// Reset the TAD columns
lptad->lpptaCols->cValues -= cNewTags;
// On error the all copied rows are freed...
if (parglprowSortedCopy)
{
LPSRow * plprowTmp = parglprowSortedCopy;
while (cRowsCopy)
{
ScFreeBuffer( lptad, *(plprowTmp++));
cRowsCopy--;
}
}
goto ret;
}
/*============================================================================
- TAD::HrDeleteRows()
-
*/
STDMETHODIMP
TAD_HrDeleteRows(
LPTAD lptad,
ULONG ulFlags,
LPSRowSet lprowsetToDelete,
ULONG FAR * lpcRowsDeleted )
{
SCODE sc = S_OK;
LPSRow lprowDelete;
LPSRow * plprowIn;
LPSRow * plprowOut;
ULONG cRowsDeleted = 0;
LPSRow * parglprowOld = NULL;
LPSRow * * pargplprowOld;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lptad,TAD_,HrDeleteRow,lpVtbl);
if ( ((ulFlags & TAD_ALL_ROWS) && lprowsetToDelete)
|| ( !(ulFlags & TAD_ALL_ROWS)
&& ( IsBadReadPtr( lprowsetToDelete, CbNewSRowSet(0))
|| IsBadWritePtr( lprowsetToDelete
, CbSRowSet(lprowsetToDelete))))
|| (lpcRowsDeleted && IsBadWritePtr( lpcRowsDeleted, sizeof(ULONG))))
{
DebugTrace(TEXT("TAD::HrDeleteRows() - Invalid parameter(s) passed\n") );
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
#endif
if (ulFlags & ~TAD_ALL_ROWS)
{
DebugTrace(TEXT("TAD::HrModifyRows() - Unknown flags passed\n") );
// return ResultFromScode(MAPI_E_UNKNOWN_FLAGS);
}
LockObj(lptad);
if (ulFlags & TAD_ALL_ROWS)
{
cRowsDeleted = lptad->ulcRowsAdd;
//
// If there are any rows to delete
//
if (cRowsDeleted)
{
//
// And they delete cleanly
//
if (FAILED(sc = ScDeleteAllRows( lptad)))
{
DebugTrace(TEXT("TAD::HrDeleteRows() - ScDeleteAllRows returned error (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
}
if (lpcRowsDeleted)
{
*lpcRowsDeleted = cRowsDeleted;
}
goto ret;
}
if (!lprowsetToDelete->cRows)
{
goto ret;
}
// Not allowed to delete rows from non-dynamic tables with open views
if ( lptad->ulTableType != TBLTYPE_DYNAMIC && lptad->lpvueList )
{
DebugTrace(TEXT("TAD::HrDeleteRows() - Operation not supported on non-dynamic TAD with open views\n") );
sc = MAPI_E_CALL_FAILED;
goto ret;
}
// Allocate the list of old rows now so we won't fail after we start
// adding rows.
if (FAILED(sc = ScAllocateBuffer( lptad
, lprowsetToDelete->cRows * sizeof(LPSRow)
, &parglprowOld)))
{
DebugTrace(TEXT("ScAddRows() - Error creating old row list (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
MAPISetBufferName(parglprowOld, TEXT("ITable old row list"));
// First we will try to find each row in the index sorted row list.
// We won't delete them on the first pass since we must verify that
// there aren't dups in the row set and that each row has an Index
// property before we actually delete any rows.
// Keep a list of pointers to the index sorted slot (argplprow) so that
// we don't have to search again when we finally delete the rows.
pargplprowOld = (LPSRow * *) parglprowOld;
for ( lprowDelete = lprowsetToDelete->aRow + lprowsetToDelete->cRows
; lprowDelete-- > lprowsetToDelete->aRow
; )
{
LPSRow * plprow = NULL;
LPSPropValue lppropIndex;
if (FBadRow(lprowDelete))
{
DebugTrace(TEXT("TAD::HrDeleteRows() - Invalid row(s) passed\n") );
sc = MAPI_E_INVALID_PARAMETER;
goto ret;
}
if (!(lppropIndex = PpropFindProp( lprowDelete->lpProps
, lprowDelete->cValues
, lptad->ulPropTagIndexCol)))
{
sc = MAPI_E_INVALID_PARAMETER;
DebugTrace(TEXT("TAD::HrDeleteRows() - Row has no Index property.\n") );
goto ret;
}
sc = ScFindRow(lptad, lppropIndex, &plprow);
if (sc == MAPI_E_NOT_FOUND)
{
// Don't try to delete rows that aren't in the table
continue;
}
else if (FAILED(sc))
{
// Something bad happened in ScFindRow so fail the call
DebugTrace(TEXT("TAD::HrDeleteRows() - Error from ScFindRow.\n") );
goto ret;
}
// The row is valid and in the TAD so put its plprow in the table
// to delete
*(pargplprowOld++) = plprow;
}
sc = S_OK;
// Now that we have completely validated the input row set and have made
// a list of plprow's to delete from the Index sorted set, we can
// actually delete them from the unsorted set and the index sorted set.
// turn the list of plprows into a list of lprows for UpdateViews
// The call is not allowed to fail after this point!
cRowsDeleted = (ULONG) (((LPSRow *) pargplprowOld) - parglprowOld);
while (((LPSRow *) pargplprowOld--) > parglprowOld)
{
LPSRow * plprow;
LPSRow lprow = **pargplprowOld;
// Remove the row from the unsorted row set
if (plprow = PlprowByLprow( lptad->ulcRowsAdd
, lptad->parglprowAdd
, lprow))
{
--lptad->ulcRowsAdd;
MoveMemory( plprow
, plprow + 1
, (size_t)
( (BYTE *)(lptad->parglprowAdd + lptad->ulcRowsAdd)
- (BYTE *)(plprow)));
}
// The row should be in the unsorted set.
Assert(plprow);
// Remove the row from the Index sorted row set by
// setting it to NULL. We'll squish the NULLs out later since we
// don't know now what order they are in.
**pargplprowOld = NULL;
// Turn the plprow into an lprow to use in UpdateViews
(LPSRow) (*pargplprowOld) = lprow;
}
// Remove the NULL pointers that were left in the Index sorted set
for ( plprowOut = plprowIn = lptad->parglprowIndex
; (plprowIn < lptad->parglprowIndex + lptad->ulcRowsIndex)
; plprowIn++)
{
if (*plprowIn)
{
*plprowOut = *plprowIn;
plprowOut++;
}
}
lptad->ulcRowsIndex = (ULONG) (plprowOut - lptad->parglprowIndex);
// Update and notify any affected views
// using the converted argplprowOld (arglprowOld)
UpdateViews(lptad, cRowsDeleted, parglprowOld, 0, NULL, NULL);
if (lpcRowsDeleted)
{
*lpcRowsDeleted = cRowsDeleted;
}
ret:
// Free the old rows.
if (parglprowOld)
{
LPSRow * plprowOld;
for ( plprowOld = parglprowOld + cRowsDeleted
; plprowOld-- > parglprowOld
; )
{
ScFreeBuffer( lptad, *plprowOld);
}
ScFreeBuffer(lptad, parglprowOld);
}
UnlockObj(lptad);
return ResultFromScode(sc);
}
/*============================================================================
- TAD::HrDeleteRow()
-
*/
STDMETHODIMP
TAD_HrDeleteRow (
LPTAD lptad,
LPSPropValue lpprop )
{
HRESULT hResult;
SizedSRowSet(1, rowsetToDelete);
ULONG cRowsDeleted;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lptad,TAD_,HrDeleteRow,lpVtbl);
// Validation of lpprop done by TAD_HrDeleteRows
#endif
rowsetToDelete.cRows = 1;
rowsetToDelete.aRow[0].cValues = 1;
rowsetToDelete.aRow[0].lpProps = lpprop;
if (HR_FAILED(hResult = TAD_HrDeleteRows( lptad
, 0
, (LPSRowSet) &rowsetToDelete
, &cRowsDeleted)))
{
DebugTrace(TEXT("TAD::HrDeleteRow() - Failed to delete rows.\n") );
goto ret;
}
Assert((cRowsDeleted == 1) || !cRowsDeleted);
if (!cRowsDeleted)
{
DebugTrace(TEXT("TAD::HrDeleteRow() - Couldn't find row to delete.\n") );
hResult = ResultFromScode(MAPI_E_NOT_FOUND);
}
ret:
return hResult;
}
/*============================================================================
- TAD::HrQueryRow()
-
*/
STDMETHODIMP
TAD_HrQueryRow(
LPTAD lptad,
LPSPropValue lpprop,
LPSRow FAR * lplprow,
ULONG * puliRow)
{
LPSRow * plprow = NULL;
SCODE sc;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lptad,TAD_,HrQueryRow,lpVtbl);
if ( FBadProp(lpprop) ||
IsBadWritePtr(lplprow, sizeof(LPSRow)) ||
(puliRow && IsBadWritePtr(puliRow, sizeof(*puliRow))) )
{
DebugTrace(TEXT("TAD::HrQueryRow() - Invalid parameter(s) passed\n") );
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
#endif
LockObj(lptad);
// Find the row
if (FAILED(sc = ScFindRow(lptad, lpprop, &plprow)))
{
goto ret;
}
Assert(plprow);
// Copy the row to return. Don't try to add new tags to the column set.
if ( FAILED(sc = ScCopyTadRow( lptad, *plprow, NULL, lplprow )) )
{
DebugTrace(TEXT("TAD::HrQueryRow() - Error making copy of row (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
if (puliRow)
{
// Find the row from the unsorted row set
plprow = PlprowByLprow( lptad->ulcRowsAdd,
lptad->parglprowAdd,
*plprow);
// If the row was in the Index sorted set then it should be in
// the unsorted set.
Assert(plprow);
*puliRow = (ULONG) (plprow - lptad->parglprowAdd);
}
ret:
UnlockObj(lptad);
return ResultFromScode(sc);
}
/*============================================================================
- TAD::HrEnumRow()
-
*/
STDMETHODIMP
TAD_HrEnumRow(
LPTAD lptad,
ULONG uliRow,
LPSRow FAR * lplprow )
{
SCODE sc;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lptad,TAD_,HrEnumRow,lpVtbl);
if ( IsBadWritePtr(lplprow, sizeof(LPSRow)) )
{
DebugTrace(TEXT("TAD::HrEnumRow() - Invalid parameter(s) passed\n") );
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
#endif
LockObj(lptad);
if ( uliRow < lptad->ulcRowsAdd )
{
// Copy the row to return.
if ( FAILED(sc = ScCopyTadRow( lptad
, lptad->parglprowAdd[uliRow]
, NULL // Don't try to add new columns.
, lplprow )) )
{
DebugTrace(TEXT("TAD::HrEnumRow() - Error making copy of row (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
}
else
{
// Return NULL if row index is out of range
*lplprow = NULL;
sc = S_OK;
}
ret:
UnlockObj(lptad);
return ResultFromScode(sc);
}
/*============================================================================
- TAD_HrNotify
-
* Parameters:
* lptad in the table object
* ulFlags in flags (unused)
* cValues in number of property values
* lpsv in property value array to be compared
*/
STDMETHODIMP
TAD_HrNotify(
LPTAD lptad,
ULONG ulFlags,
ULONG cValues,
LPSPropValue lpspv)
{
#ifdef NOTIFICATION
NOTIFICATION notif;
VUENOTIFKEY vuenotifkey;
ULONG uliRow;
ULONG ulNotifFlags;
ULONG uliProp;
LPSRow lprow;
SCODE sc;
LPVUE lpvue;
#endif // NOTIFICATIONS
#if !defined(NO_VALIDATION)
if ( BAD_STANDARD_OBJ(lptad,TAD_,HrNotify,lpVtbl) )
{
DebugTrace(TEXT("TAD::HrNotify() - Invalid parameter passed as TAD object\n") );
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
if (cValues > UINT_MAX/sizeof(SPropValue) || IsBadReadPtr(lpspv,((UINT)cValues*sizeof(SPropValue))))
{
DebugTrace(TEXT("TAD::HrNotify() - Invalid parameter passed as prop value array\n") );
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
#endif
if (cValues==0)
{
DebugTrace(TEXT("TAD::HrNotify() - zero props in prop value array?? \n") );
return hrSuccess;
}
#ifdef NOTIFICATIONS
ZeroMemory(&notif, sizeof(NOTIFICATION));
notif.ulEventType = fnevTableModified;
notif.info.tab.ulTableEvent = TABLE_ROW_MODIFIED;
LockObj(lptad);
for ( lpvue = (LPVUE) lptad->lpvueList;
lpvue != NULL;
lpvue = (LPVUE) lpvue->lpvueNext )
{
AssertSz( !IsBadWritePtr(lpvue, sizeof(VUE)) &&
lpvue->lpVtbl == &vtblVUE,
TEXT("Bad lpvue in TAD vue List.") );
for (uliRow=0; uliRow < lpvue->bkEnd.uliRow; uliRow++)
{
lprow = lpvue->parglprows[uliRow];
// does the row contain matching properties?
if (!FRowContainsProp(lprow,cValues,lpspv))
continue; // it doesn't so go on to next row
// copy the row for the client
sc=ScCopyVueRow(lpvue,lpvue->lpptaCols,lprow,&notif.info.tab.row);
if (FAILED(sc))
{
DebugTrace(TEXT("TAD_HrNotify() - VUE_ScCopyRow return %s\n"), SzDecodeScode(sc));
continue;
}
notif.info.tab.propIndex=*lpspv;
// Fill in index property of row previous to row
// modified. If row modified was first
// row, fill in 0 for proptag of index property of
// previous row.
if (uliRow == 0)
{
ZeroMemory(&notif.info.tab.propPrior, sizeof(SPropValue));
notif.info.tab.propPrior.ulPropTag = PR_NULL;
}
else
{
// point to previous row
lprow = lpvue->parglprows[uliRow-1];
for (uliProp=0; uliProp < lprow->cValues; uliProp++)
{
if (lprow->lpProps[uliProp].ulPropTag==lpspv->ulPropTag)
break;
}
// should have found the index property
Assert(uliProp < lprow->cValues);
notif.info.tab.propPrior = lprow->lpProps[uliProp];
}
// Kick off notifications to all the notifications open on the view
vuenotifkey.ulcb = sizeof(MAPIUID);
vuenotifkey.mapiuid = lpvue->mapiuidNotif;
ulNotifFlags = 0;
(void) HrNotify((LPNOTIFKEY) &vuenotifkey,
1,
&notif,
&ulNotifFlags);
// Free the notification's copy of the modified row
ScFreeBuffer(lpvue, notif.info.tab.row.lpProps);
}
}
UnlockObj(lptad);
return hrSuccess;
#endif // NOTIFICATIONS
return(ResultFromScode(MAPI_E_NO_SUPPORT));
}
/*============================================================================
- TAD::HrInsertRow()
-
*/
STDMETHODIMP
TAD_HrInsertRow(
LPTAD lptad,
ULONG uliRow,
LPSRow lprow )
{
LPSRow lprowCopy = NULL;
SizedSSortOrderSet( 1, sosIndex) = { 1, 0, 0 };
ULONG cTagsAdded = 0;
LPSRow * plprow;
SCODE sc;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lptad,TAD_,HrInsertRow,lpVtbl);
// lprow is validated by ScCopyTadRow()
if (uliRow > lptad->ulcRowsAdd)
{
DebugTrace(TEXT("TAD::HrInsertRow() - Invalid parameter(s) passed\n") );
DebugTrace(TEXT("TAD::HrInsertRow() - uliRow is zero or greater thna row count\n") );
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
#endif
LockObj(lptad);
// Make a copy of the row for our own use filtering
// out PT_ERROR and PT_NULL columns and moving
// the index column to the front
if ( FAILED(sc = ScCopyTadRow(lptad, lprow, &cTagsAdded, &lprowCopy)) )
{
DebugTrace(TEXT("TAD::HrInsertRow() - Error duping row to modify\n") );
goto ret;
}
sosIndex.aSort[0].ulPropTag = lptad->ulPropTagIndexCol;
sosIndex.aSort[0].ulOrder = TABLE_SORT_ASCEND;
// Find out where the row would collate on the Index sorted set
// NOTE! We collate before the first occurance so that we will end
// up pointing at the row with this index if it exists.
plprow = PlprowCollateRow(lptad->ulcRowsIndex,
lptad->parglprowIndex,
(LPSSortOrderSet) &sosIndex,
FALSE,
lprowCopy);
// If a row with the same Index value exists then we can't insert!
if ( lptad->ulcRowsIndex
&& (plprow < (lptad->parglprowIndex + lptad->ulcRowsIndex))
&& !LPropCompareProp( lprowCopy->lpProps
, (*plprow)->lpProps))
{
sc = MAPI_E_INVALID_PARAMETER;
goto err;
}
// Insert it to the end of the unsorted row set
if ( FAILED(sc = ScAddRow((LPUNKOBJ) lptad,
NULL, // No sort order
lprowCopy,
uliRow,
&lptad->ulcRowsAdd,
&lptad->ulcRowMacAdd,
&lptad->parglprowAdd,
NULL)) )
{
DebugTrace(TEXT("TAD::ScInsertRow() - Error appending new row to TAD (SCODE = 0x%08lX)\n"), sc );
goto err;
}
// Insert the row into the Index sorted row set
if ( FAILED(sc = ScAddRow((LPUNKOBJ) lptad,
NULL, // We already collated the row
lprowCopy,
(ULONG) (plprow - lptad->parglprowIndex),
&lptad->ulcRowsIndex,
&lptad->ulcRowMacIndex,
&lptad->parglprowIndex,
NULL)) )
{
DebugTrace(TEXT("TAD::ScInsertRow() - Error appending new row to TAD (SCODE = 0x%08lX)\n"), sc );
goto err;
}
UpdateViews(lptad, 0, NULL, 1, &lprowCopy, &lprowCopy);
ret:
UnlockObj(lptad);
return ResultFromScode(sc);
err:
// Reset the TAD columns
lptad->lpptaCols->cValues -= cTagsAdded;
goto ret;
}
/*============================================================================
- ScCopyTadRowSet()
-
* Validates and makes a copy of the specified row set using MAPI memory
* allocation filtering out PT_ERROR and PT_NULL columns and moving the
* index column to the first column.
*
* if lplpptaNew is not NULL then a list of columns which are new to
* the TAD are returned.
*
*
* Parameters:
* lptad in Table data object
* lprowsetIn in Row set to copy
* pcNewTags out Number of columns new to the TAD
* pcRows out Count of rows in the two row sets
* pparglprowUnsortedCopy out Pointer to copied rows (Unsorted)
* pparglprowSortedCopy out Pointer to copied rows (Sorted on Index)
*/
SCODE
ScCopyTadRowSet(
LPTAD lptad,
LPSRowSet lprowsetIn,
ULONG * pcNewTags,
ULONG * pcRows,
LPSRow * * pparglprowUnsortedCopy,
LPSRow * * pparglprowSortedCopy )
{
SCODE sc = S_OK;
ULONG cRows = 0;
LPSRow lprowIn;
LPSRow lprowCopy = NULL;
ULONG ulcRowsCopy = 0;
ULONG ulcRowsMacCopy;
LPSRow FAR * parglprowSortedCopy = NULL;
LPSRow FAR * parglprowUnsortedCopy = NULL;
ULONG cNewTags = 0;
SizedSSortOrderSet( 1, sosIndex) = { 1, 0, 0 };
// Assert Itable internal parameters are valid.
Assert( !pcNewTags
|| !IsBadWritePtr( pcNewTags, sizeof(ULONG)));
Assert( !IsBadWritePtr( pcRows, sizeof(ULONG)));
Assert( !IsBadWritePtr( pparglprowUnsortedCopy, sizeof(LPSRow *)));
Assert( !IsBadWritePtr( pparglprowSortedCopy, sizeof(LPSRow *)));
// Validate Itable API parameters (ie lprowsetIn)
// Note! The actual rows will be validated by ScCopyTadRow later.
if ( IsBadReadPtr( lprowsetIn, sizeof(SRowSet))
|| IsBadReadPtr( lprowsetIn->aRow
, (UINT) (lprowsetIn->cRows * sizeof(SRow))))
{
DebugTrace(TEXT("TAD::ScCopyTadRowSet() - Bad row set In!\n") );
return MAPI_E_INVALID_PARAMETER;
}
// Allocate space the Index sorted list of copied rows
if ( FAILED(sc = ScAllocateBuffer( lptad,
sizeof(LPSRow) * lprowsetIn->cRows,
&parglprowSortedCopy)) )
{
DebugTrace(TEXT("TAD::ScCopyTadRowSet() - Error allocating parglprowSortedCopy (SCODE = 0x%08lX)\n"), sc );
goto err;
}
MAPISetBufferName(parglprowSortedCopy, TEXT("ITable copied Index sorted row list"));
// Allocate space the unsorted list of copied rows
if ( FAILED(sc = ScAllocateBuffer( lptad,
sizeof(LPSRow) * lprowsetIn->cRows,
&parglprowUnsortedCopy)) )
{
DebugTrace(TEXT("TAD::ScCopyTadRowSet() - Error allocating parglprowUnsortedCopy (SCODE = 0x%08lX)\n"), sc );
goto err;
}
MAPISetBufferName(parglprowUnsortedCopy, TEXT("ITable copied unsorted row list"));
// Set each LPSRow to NULL so we can easily free on error
ZeroMemory( parglprowSortedCopy, (UINT) (sizeof(LPSRow) * lprowsetIn->cRows));
ZeroMemory( parglprowUnsortedCopy, (UINT) (sizeof(LPSRow) * lprowsetIn->cRows));
// Sort the copied rows by IndexCol. The code to add the rows
// to the TAD relies on this (for speed). This code relies on the sort
// to find duplicate index columns.
sosIndex.aSort[0].ulPropTag = lptad->ulPropTagIndexCol;
sosIndex.aSort[0].ulOrder = TABLE_SORT_ASCEND;
// Mark our temporary Index Sorted Set as empty.
ulcRowsCopy = 0;
ulcRowsMacCopy = lprowsetIn->cRows;
for ( lprowIn = lprowsetIn->aRow, cRows = ulcRowsMacCopy
; cRows
; lprowIn++, cRows--)
{
LPSRow * plprow;
ULONG cTagsAdded = 0;
// Make a copy of the row for our own use filtering
// out PT_ERROR and PT_NULL columns and moving
// the index column to the front
//
// Also adds any new tags to the TADs column set;
if ( FAILED(sc = ScCopyTadRow( lptad
, lprowIn
, (pcNewTags) ? &cTagsAdded : NULL
, &lprowCopy)) )
{
DebugTrace(TEXT("TAD::HrInsertRow() - Error duping row\n") );
goto err;
}
cNewTags += cTagsAdded;
// Find out where the row would collate in our Index sorted copy
// NOTE! We collate before the first occurance so that we will end
// up pointing at the row with this index if it exists.
plprow = PlprowCollateRow( ulcRowsCopy
, parglprowSortedCopy
, (LPSSortOrderSet) &sosIndex
, FALSE
, lprowCopy);
// If a row with the same Index value exists then we can't insert!
if ( ulcRowsCopy
&& (plprow < (parglprowSortedCopy + ulcRowsCopy))
&& !LPropCompareProp( lprowCopy->lpProps
, (*plprow)->lpProps))
{
sc = MAPI_E_INVALID_PARAMETER;
DebugTrace(TEXT("TAD::ScCopyTadRowSet() - The same row index occurred on more than one row.\n") );
goto err;
}
// Append the row to the Unsorted row set
// This is done before inserting it into the Index Sorted set to make
// sure ulcRowsCopy is not incremented
parglprowUnsortedCopy[ulcRowsCopy] = lprowCopy;
// Insert the row into the Index sorted row set last
if ( FAILED(sc = ScAddRow( (LPUNKOBJ) lptad
, NULL // We already collated the row
, lprowCopy
, (ULONG) (plprow - parglprowSortedCopy)
, &ulcRowsCopy
, &ulcRowsMacCopy
, &parglprowSortedCopy
, NULL)) )
{
DebugTrace(TEXT("TAD::ScCopyTadRowSet() - Error appending new row to RowSet copy (SCODE = 0x%08lX)\n"), sc );
goto err;
}
lprowCopy = NULL;
}
*pparglprowUnsortedCopy = parglprowUnsortedCopy;
*pparglprowSortedCopy = parglprowSortedCopy;
*pcRows = ulcRowsCopy;
if (pcNewTags)
{
*pcNewTags = cNewTags;
}
ret:
return sc;
err:
// Reset the TAD columns
lptad->lpptaCols->cValues -= cNewTags;
// We loop through the SORTED row set to free rows because we know that
// if lprowCopy is not NULL then it hasn't been added to the SORTED set.
// This prevents a double FreeBuffer on lprowCopy!
if (parglprowSortedCopy)
{
LPSRow * plprowTmp = parglprowSortedCopy;
while (ulcRowsCopy)
{
ScFreeBuffer( lptad, *(plprowTmp++));
ulcRowsCopy--;
}
ScFreeBuffer( lptad, parglprowSortedCopy);
}
ScFreeBuffer( lptad, parglprowUnsortedCopy);
ScFreeBuffer( lptad, lprowCopy);
goto ret;
}
/*============================================================================
- ScCopyTadRow()
-
* Validates and makes a copy of the specified row using MAPI memory
* allocation filtering out PT_ERROR and PT_NULL columns and moving the
* index column to the first column.
*
* Iff lpcNewTags is not NULL then any new columns are added to the END
* of TAD's column set and the count of columns added is returned in
* *lpcNewTags. Tags are added to the end so that the caller can
* back out changes if necessary.
*
* Note! Unlike ScCopyVueROW it is the SRow structure that is
* allocated and which must be subsequently freed to free the row.
*
* Parameters:
* lptad in Table data object
* lprow in Row set to copy
* lpcTagsAdded out Number of columns added to the TAD's col set
* lplprow out Pointer to copied row
*/
SCODE
ScCopyTadRow(
LPTAD lptad,
LPSRow lprow,
ULONG FAR * lpcTagsAdded,
LPSRow FAR * lplprowCopy )
{
ULONG ulcCols = 0;
LONG iIndexCol;
CMB cmb;
SPropValue propTmp;
LPSPropValue lppropDst;
LPSPropValue lppropSrc;
ULONG cTagsAdded = 0;
LPSRow lprowCopy = NULL;
SCODE sc = S_OK;
Assert( !lpcTagsAdded || !IsBadWritePtr( lpcTagsAdded, sizeof(ULONG)));
Assert( !IsBadWritePtr( lplprowCopy, sizeof(LPSRow)));
// Validate the input row structure
if (FBadRow( lprow))
{
sc = MAPI_E_INVALID_PARAMETER;
DebugTrace(TEXT("ScCopyTadRow() - Bad input row\n") );
return sc;
}
// The CMB (CopyMore Buffer) is used so that we do a single MAPI allocation
// for PropCopyMore to use. It is used in conjuntion with the very
// special ScBufAllocateMore to keep track of the chunks of memory which
// would have otherwise been allocated with MAPI - AllocateMore.
ZeroMemory(&cmb, sizeof(CMB));
// Figure out how many columns to copy and how much
// additional memory they'll need to be copied.
iIndexCol = -1;
for ( lppropSrc = lprow->lpProps;
lppropSrc < lprow->lpProps + lprow->cValues;
lppropSrc++ )
{
// Ignore PT_ERROR and PT_NULL properties
if ( PROP_TYPE(lppropSrc->ulPropTag) == PT_ERROR ||
PROP_TYPE(lppropSrc->ulPropTag) == PT_NULL )
continue;
// If this column is the index column, remember its
// location in the copied (dst) row
// so it can be moved to the first column in the copy
if ( lppropSrc->ulPropTag == lptad->ulPropTagIndexCol )
iIndexCol = ulcCols;
// If it's a new property and the caller asked us to (lpcTagsAdded)
// add the tag to the TAD's column set
else if ( lpcTagsAdded
&& !FFindColumn( lptad->lpptaCols, lppropSrc->ulPropTag))
{
// Realloc the column only if there is no room
if (lptad->lpptaCols->cValues >= lptad->ulcColsMac)
{
sc = ScCOReallocate( lptad
, CbNewSPropTagArray( lptad->ulcColsMac
+ COLUMN_CHUNK_SIZE)
, &lptad->lpptaCols);
if (FAILED(sc))
{
DebugTrace(TEXT("TAD::ScCopyTadRow() - Error resizing default column set (SCODE = 0x%08lX)\n"), sc );
goto err;
}
lptad->ulcColsMac += COLUMN_CHUNK_SIZE;
}
// Add the column to the end of the existing column set
lptad->lpptaCols->aulPropTag[lptad->lpptaCols->cValues++]
= lppropSrc->ulPropTag;
cTagsAdded++;
}
// Add in the size of the column
cmb.ulcb += UlcbPropToCopy(lppropSrc);
++ulcCols;
}
// Make sure the row to copy had an index column
if ( iIndexCol == -1 )
{
DebugTrace(TEXT("TAD::ScCopyTadRow() - Row doesn't have an index column!\n") );
sc = MAPI_E_INVALID_PARAMETER;
goto err;
}
// Allocate space for the entire row (including all allocated values)
if ( FAILED(sc = ScAllocateBuffer( lptad,
sizeof(SRow) + 4 + // +4 to start lpProp at 8-byte bndry
ulcCols * sizeof(SPropValue) +
cmb.ulcb,
&lprowCopy)) )
{
DebugTrace(TEXT("TAD::ScCopyTadRow() - Error allocating row copy (SCODE = 0x%08lX)\n"), sc );
goto err;
}
MAPISetBufferName(lprowCopy, TEXT("ITable copy of entire row"));
// Fill in the allocated SRow structure
// WARNING! The allocate SRow structre MUST always be the first
// structure in the memory allocation and the property
// array MUST always immediately follow the SRow structure!
// Itable code which passes internal rows around counts on
// this.
lprowCopy->cValues = ulcCols;
lprowCopy->lpProps = (LPSPropValue)(((LPBYTE)lprowCopy) + sizeof(SRow)+4);
// Set the initial pointer in our special CopyMoreBuffer. This buffer
// will be used by our special AllocateMore routine to allocate
// strings, bins, etc in PropCopyMore below.
cmb.lpv = lprowCopy->lpProps + ulcCols;
// Copy the row properties
lppropDst = lprowCopy->lpProps + ulcCols;
lppropSrc = lprow->lpProps + lprow->cValues;
while ( lppropSrc-- > lprow->lpProps )
{
// Strip out properties of type PT_ERROR and PT_NULL
if ( PROP_TYPE(lppropSrc->ulPropTag) == PT_ERROR
|| PROP_TYPE(lppropSrc->ulPropTag) == PT_NULL )
{
continue;
}
// Copy the property
SideAssert( PropCopyMore( --lppropDst
, lppropSrc
, (LPALLOCATEMORE) ScBufAllocateMore
, &cmb) == S_OK );
}
// Move the index column to the front
propTmp = *(lprowCopy->lpProps);
*(lprowCopy->lpProps) = lprowCopy->lpProps[iIndexCol];
lprowCopy->lpProps[iIndexCol] = propTmp;
// Return the copied row and the count of new properties
*lplprowCopy = lprowCopy;
if (lpcTagsAdded)
{
*lpcTagsAdded = cTagsAdded;
}
ret:
return sc;
err:
// Reset the TAD columns
lptad->lpptaCols->cValues -= cTagsAdded;
ScFreeBuffer( lptad, lprowCopy);
goto ret;
}
/*============================================================================
- UpdateViews()
-
* Updates all the views on a particular table data. For each view, if
* lprowToRemove is non-NULL and present in the view, it is removed
* from the view. If lprowToAdd is non-NULL and satisfies the current
* restriction on the view, it is added to the view at the position
* dictated by the view's current sort order.
*
* If the row to remove is bookmarked, the bookmark is moved to the next
* row.
*
* OOM errors in adding a row to a view's row list are ignored; the view
* simply doesn't see the new row.
*
*
* Parameters:
* lptad in TAD containing views to update
* cRowsToRemove in Count of rows to remove from each view
* parglprowToRemove in Array of LPSRows to remove from each view
* cRowsToAdd in Count of rows to to each view
* parglprowToAddUnsorted in Unsorted array of LPSRows to add to each view
* parglprowToAddSorted in Sorted array of LPSRows to add to each view
*/
VOID
UpdateViews(
LPTAD lptad,
ULONG cRowsToRemove,
LPSRow * parglprowToRemove,
ULONG cRowsToAdd,
LPSRow * parglprowToAddUnsorted,
LPSRow * parglprowToAddSorted )
{
LPVUE lpvue;
// This is an internal call which assumes that lptad is locked.
for ( lpvue = (LPVUE) lptad->lpvueList;
lpvue != NULL;
lpvue = (LPVUE) lpvue->lpvueNext )
{
AssertSz( !IsBadWritePtr(lpvue, sizeof(VUE))
, TEXT("Bad lpvue in TAD vue List.") );
FixupView( lpvue
, cRowsToRemove, parglprowToRemove
, cRowsToAdd
, parglprowToAddUnsorted
, parglprowToAddSorted);
}
}
/*============================================================================
- FixupView()
-
* Updates one view on a particular table data. Each row in
* parglprowToRemove that is present in the view is removed
* from the view. Each row in parglprowToAdd that satisfies the current
* restriction on the view, is added to the view at the position
* dictated by the view's current sort order.
*
* If the row to remove is bookmarked, the bookmark is moved to the next
* row.
*
* OOM errors in adding a row to a view's row list are ignored; the view
* simply doesn't see the new row.
*
*
* Parameters:
* lpvue in View to fixup
* cRowsToRemove in Count of rows to remove from view
* parglprowToRemove in Array of LPSRows to remove from view
* cRowsToAdd in Count of rows to to view
* parglprowToAddUnsorted in Unsorted array of LPSRows to add to view
* parglprowToAddSorted in Sorted array of LPSRows to add to view
*/
VOID
FixupView(
LPVUE lpvue,
ULONG cRowsToRemove,
LPSRow * parglprowToRemove,
ULONG cRowsToAdd,
LPSRow * parglprowToAddUnsorted,
LPSRow * parglprowToAddSorted )
{
BOOL fBatchNotifs = TRUE;
ULONG cNotifs = 0;
ULONG ulcRows;
NOTIFICATION argnotifBatch[MAX_BATCHED_NOTIFS];
SizedSSortOrderSet( 1, sosIndex) = { 1, 0, 0 };
LPNOTIFICATION lpnotif;
PBK pbk;
SCODE sc;
Assert( !cRowsToRemove
|| !IsBadReadPtr( parglprowToRemove
, (UINT) (cRowsToRemove * sizeof(LPSRow))));
Assert( !cRowsToAdd
|| !IsBadReadPtr( parglprowToAddUnsorted
, (UINT) (cRowsToAdd * sizeof(LPSRow))));
Assert( !cRowsToAdd
|| !IsBadReadPtr( parglprowToAddSorted
, (UINT) (cRowsToAdd * sizeof(LPSRow))));
if (!cRowsToRemove && !cRowsToAdd)
{
// Nothing to do in this case
goto ret;
}
// This is an internal call which assumes that lptad is locked.
// Set up a an Index sort order so that when we are checking to see
// if a bookmark is deleted or changed we can search the SORTED row
// row set using a binary search.
sosIndex.aSort[0].ulPropTag = lpvue->lptadParent->ulPropTagIndexCol;
sosIndex.aSort[0].ulOrder = TABLE_SORT_ASCEND;
// Mark bookmarks as moving or changed
// This checks all bookmarks including BOOKMARK_CURRENT and BOOKMARK_END
// BOOKMARK_BEGINNING is not checked or touched
// Note that even though BOOKMARK_END is checked, it is not changed.
pbk = lpvue->rgbk + cBookmarksMax;
// Rememeber the number of rows in the view so we can fixup bkEnd
ulcRows = lpvue->bkEnd.uliRow;
while ( --pbk > lpvue->rgbk )
{
LPSRow * plprowBk;
LPSRow lprowBk;
ULONG uliRow;
ULONG fRowReplaced = FALSE;
// If it's not a valid bookmark, don't update it.
if ( !(pbk->dwfBKS & dwfBKSValid)
|| (pbk->dwfBKS & dwfBKSStale) )
{
continue;
}
// Moving bookmarks always point to the actual row
// Get a row index for moving bookmarks
if (pbk->dwfBKS & dwfBKSMoving)
{
plprowBk = PlprowByLprow( ulcRows
, lpvue->parglprows
, pbk->lprow);
AssertSz( plprowBk
, TEXT("FixupViews() - Moving Bookmark lost it's row\n"));
uliRow = (ULONG) (plprowBk - lpvue->parglprows);
}
else if (!ulcRows && !pbk->uliRow)
{
continue;
}
else if ((uliRow = pbk->uliRow) >= ulcRows)
{
// Bookmark is at the end of the table. Make sure it stays there
pbk->uliRow += cRowsToAdd;
continue;
}
Assert(uliRow < ulcRows);
lprowBk = lpvue->parglprows[uliRow];
// If a row is on the "Remove" list then it may end up
// moving or changed depending on whether it is also on the
// "Add" list.
if ( cRowsToRemove
&& (plprowBk = PlprowByLprow( cRowsToRemove
, parglprowToRemove
, lprowBk)))
{
// If a deleted row is on the "Add" list then it is marked as
// moving and it is pointed (->lprow) at the added row.
if ( cRowsToAdd
&& (plprowBk = PlprowCollateRow( cRowsToAdd
, parglprowToAddSorted
, (LPSSortOrderSet) &sosIndex
, FALSE
, lprowBk))
&& (plprowBk < (parglprowToAddSorted + cRowsToAdd))
&& !LPropCompareProp( lprowBk->lpProps
, (*plprowBk)->lpProps))
{
// Row is being replaced
// Check to see if the row satisfies the specified restriction
if ( FAILED(sc = ScSatisfiesRestriction( *plprowBk
, lpvue->lpres
, &fRowReplaced)) )
{
DebugTrace(TEXT("FixupView() - Error evaluating restriction (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
// If it doesn't, return now.
if ( fRowReplaced )
{
pbk->lprow = *plprowBk;
pbk->dwfBKS = dwfBKSMoving | dwfBKSValid;
}
}
// If a deleted row is not going to be listed then its bookmark
// is "Changed".
if (!fRowReplaced)
{
// Marked row was deleted.
pbk->uliRow = uliRow;
pbk->dwfBKS = dwfBKSChanged | dwfBKSValid;
}
}
// If the row is not on the deletion list then it is automatically
// marked as moving.
else
{
// Marked row may move
pbk->lprow = lprowBk;
pbk->dwfBKS = dwfBKSMoving | dwfBKSValid;
}
}
// Restore bkEnd
lpvue->bkEnd.uliRow = ulcRows;
// Remove rows from the view
for ( ; cRowsToRemove; parglprowToRemove++, cRowsToRemove--)
{
LPSRow lprowRemove;
LPSRow * plprowRemove;
LPSRow * plprowAdd;
BOOL fCanReplace = FALSE;
ULONG uliRowAddDefault;
// If the REMOVED row is not in the view then there is nothing to do
if ( !*parglprowToRemove
|| !(plprowRemove = PlprowByLprow( lpvue->bkEnd.uliRow
, lpvue->parglprows
, *parglprowToRemove)) )
{
continue;
}
// We will need this to fill in the notificaton later
lprowRemove = *plprowRemove;
// Go ahead and delete the current row from the VUE but remember
// where it came from (uliRowAddDefault) so that if there is a
// replacement and no sort order the replacement can be put back
// into the same place
uliRowAddDefault = (ULONG) (plprowRemove - lpvue->parglprows);
MoveMemory( plprowRemove
, plprowRemove + 1
, (size_t) (lpvue->bkEnd.uliRow - uliRowAddDefault - 1)
* sizeof(LPSRow));
lpvue->bkEnd.uliRow -= 1;
// See if the deleted row can be replaced by one
// that is being added. For this to be TRUE:
// There must be a Row with the same Index in the "Add" list
// The row in the "Add" list must satisfy the VUE's restriction
if ( (plprowAdd = PlprowCollateRow( cRowsToAdd
, parglprowToAddSorted
, (LPSSortOrderSet) &sosIndex
, FALSE
, lprowRemove))
&& (plprowAdd < (parglprowToAddSorted + cRowsToAdd))
&& !LPropCompareProp( (lprowRemove)->lpProps
, (*plprowAdd)->lpProps) )
{
// If the row to add satisifies the current restriction,
// add it to the view according to the sort order or
// the default location.
if ( FAILED(sc = ScMaybeAddRow( lpvue
, lpvue->lpres
, lpvue->lpsos
, *plprowAdd
, uliRowAddDefault
, &lpvue->bkEnd.uliRow
, &lpvue->ulcRowMac
, &lpvue->parglprows
, &plprowAdd)) )
{
DebugTrace(TEXT("TAD::FixupViews() - Error replacing row in VUE (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
}
// Make sure that we don't have more than MAX_BATCHED_NOTIFS
// and that there is no replacment row
if (!fBatchNotifs || (cNotifs >= MAX_BATCHED_NOTIFS))
{
fBatchNotifs = FALSE;
continue;
}
// Init a new notificaton
lpnotif = argnotifBatch + cNotifs++;
ZeroMemory(lpnotif, sizeof(NOTIFICATION));
lpnotif->ulEventType = fnevTableModified;
// If row was deleted and added back...
if ( (plprowAdd >= lpvue->parglprows)
&& (plprowAdd < (lpvue->parglprows + lpvue->bkEnd.uliRow)))
{
// Fill in a HACKED MODIFIED notificaion
lpnotif->info.tab.ulTableEvent = TABLE_ROW_MODIFIED;
// Use the notifs row structure to TEMPORARILY store
// a pointer to the replacement row.
lpnotif->info.tab.row.lpProps = (LPSPropValue) (*plprowAdd);
}
// ...else row was deleted and NOT added back.
else
{
// Fill in a DELETE notification
lpnotif->info.tab.ulTableEvent = TABLE_ROW_DELETED;
lpnotif->info.tab.propIndex = *(lprowRemove->lpProps);
lpnotif->info.tab.propPrior.ulPropTag = PR_NULL;
}
}
// Add new rows to the table. This is done in the UNSORTED order
// in case there is no VUE sort order
for ( ; cRowsToAdd; parglprowToAddUnsorted++, cRowsToAdd--)
{
LPSRow * plprowAdd;
// If the row has already been added then there is nothing to do.
if ( *parglprowToAddUnsorted
&& (plprowAdd = PlprowByLprow( lpvue->bkEnd.uliRow
, lpvue->parglprows
, *parglprowToAddUnsorted)) )
{
continue;
}
// If the row to add satisifies the current restriction,
// add it to the view according to the sort order or
// to the end of the table if no sort order is applied.
if ( FAILED(sc = ScMaybeAddRow( lpvue
, lpvue->lpres
, lpvue->lpsos
, *parglprowToAddUnsorted
, lpvue->bkEnd.uliRow
, &lpvue->bkEnd.uliRow
, &lpvue->ulcRowMac
, &lpvue->parglprows
, &plprowAdd)) )
{
DebugTrace(TEXT("TAD::FixupViews() - Error adding row to VUE (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
if (!plprowAdd)
{
// Row was not added so don't fill out a notification
continue;
}
// Make sure that we don't have more than MAX_BATCHED_NOTIFS
// and that there is no replacment row
if (!fBatchNotifs || (cNotifs >= MAX_BATCHED_NOTIFS))
{
fBatchNotifs = FALSE;
continue;
}
// Fill in a HACKED ADDED notificaion
lpnotif = argnotifBatch + cNotifs++;
ZeroMemory(lpnotif, sizeof(NOTIFICATION));
lpnotif->ulEventType = fnevTableModified;
lpnotif->info.tab.ulTableEvent = TABLE_ROW_ADDED;
// Use the notifs row structure to TEMPORARILY store
// a pointer to the replacement row.
lpnotif->info.tab.row.lpProps = (LPSPropValue) (*plprowAdd);
}
// If there too many notifications to batch then fill out a single
// TABLE_CHANGED notification...
if (!fBatchNotifs)
{
cNotifs = 1;
lpnotif = argnotifBatch;
ZeroMemory(lpnotif, sizeof(NOTIFICATION));
lpnotif->ulEventType = fnevTableModified;
lpnotif->info.tab.ulTableEvent = TABLE_CHANGED;
lpnotif->info.tab.propIndex.ulPropTag
= lpnotif->info.tab.propPrior.ulPropTag
= PR_NULL;
}
// ...else go through the batch of notifications and fixup
// the ROW_ADDED and ROW_MODIFIED entries.
else
{
LPSRow * plprowNotif;
// Raid: Horsefly/Exchange/36281
//
// The code above which fills in argnotifBatch doesn't necessarily
// fill in the notifications in an order that can be processed
// from first to last, which is a requirement for batched
// notifications. As a workaround, the maximum number of
// notifications in a batch was changed to 1 (MAX_BATCHED_NOTIFS
// in _itable.h) so that order is not a problem. Should that
// ever change to something other than 1, this bug will have to
// be revisited a well as a crash below where ScFreeBuffer()
// in the cleanup can end up clobbering a VUE's copy of the row
// data if ScCopyVueRow() fails. See comment about filling in
// TEMPORARY pointer to replacement row above.
//
AssertSz( cNotifs < 2, TEXT("Batch notifications of more than 1 not supported") );
for (lpnotif = argnotifBatch + cNotifs; lpnotif-- > argnotifBatch; )
{
Assert( (lpnotif->ulEventType == fnevTableModified)
&& ( (lpnotif->info.tab.ulTableEvent == TABLE_ROW_MODIFIED)
|| (lpnotif->info.tab.ulTableEvent == TABLE_ROW_DELETED)
|| (lpnotif->info.tab.ulTableEvent == TABLE_ROW_ADDED)));
if (lpnotif->info.tab.ulTableEvent == TABLE_ROW_DELETED)
{
// DELETE notifications don't need to be fixed up
continue;
}
plprowNotif
= PlprowByLprow( lpvue->bkEnd.uliRow
, lpvue->parglprows
, (LPSRow) (lpnotif->info.tab.row.lpProps));
Assert(plprowNotif);
lpnotif->info.tab.propIndex = *((*plprowNotif)->lpProps);
if (plprowNotif > lpvue->parglprows)
{
lpnotif->info.tab.propPrior = *((*(plprowNotif - 1))->lpProps);
}
else
{
lpnotif->info.tab.propPrior.ulPropTag = PR_NULL;
}
// Fill in row added/modified using the column set
// currently active on the view being notified
if ( FAILED(sc = ScCopyVueRow( lpvue
, lpvue->lpptaCols
, *plprowNotif
, &(lpnotif->info.tab.row))))
{
DebugTrace(TEXT("TAD::UpdateViews() - Error copying row to view notify (SCODE = 0x%08lX)\n"), sc );
// If the row can't be copied, then just skip this view
goto ret;
}
}
}
// If a rows were added, modified or deleted, send the notification
#ifdef NOTIFICATIONS
if ( cNotifs )
{
VUENOTIFKEY vuenotifkey;
ULONG ulNotifFlags = 0;
// Kick off notifications to all the notifications open on the view
vuenotifkey.ulcb = sizeof(MAPIUID);
vuenotifkey.mapiuid = lpvue->mapiuidNotif;
(void) HrNotify((LPNOTIFKEY) &vuenotifkey,
cNotifs,
argnotifBatch,
&ulNotifFlags);
}
#endif // NOTIFICATIONS
ret:
// Always fixup bkCurrent before leaving
if ( FBookMarkStale( lpvue, BOOKMARK_CURRENT) )
{
TrapSz( TEXT("FixupViews() - BOOKMARK_CURRENT became bad.\n"));
}
if (lpvue->bkCurrent.uliRow > lpvue->bkEnd.uliRow)
{
lpvue->bkCurrent.uliRow = lpvue->bkEnd.uliRow;
}
for (lpnotif = argnotifBatch; cNotifs; lpnotif++, --cNotifs)
{
// Free the notification's copy of any added/modified row
ScFreeBuffer(lpvue, lpnotif->info.tab.row.lpProps);
}
return;
}
/*============================================================================
- ScReplaceRows()
-
* Replaces the rows with indexes matching the indexes of the list
* rows with the corresponding row from the list. The old row is then
* added to the list of replaced (old) rows.
*
* If a listed row has no existing counterpart then it is added
* to TAD's. There is no row added to the replaced row list in this case.
*
* If a row is added it is appended to the end of the unsorted row table
* and collated (by IndexCol) into the Index sorted row table.
*
* Parameters:
* lptad in Table data object
* cRowsNew in Count of rows to modify/add
* parglprowNew in List of rows to modify/add
* pcRowsOld Out Pointer count of rows replaced
* pparglprowOld out Pointer to list of rows replaced
*/
SCODE
ScReplaceRows(
LPTAD lptad,
ULONG cRowsNew,
LPSRow * parglprowNew,
ULONG * pcRowsOld,
LPSRow * * pparglprowOld )
{
SCODE sc;
LPSRow * plprowNew;
LPSRow * plprowOld;
LPSRow * parglprowOld = NULL;
// Make sure the table doesn't grow too big. This is not an exact test
// but will be reasonable in almost all cases. May fail when
// NumberRowsAdded + NumberRowsDeleted > 64K
if (HIWORD(lptad->ulcRowsAdd + cRowsNew) != 0)
{
sc = MAPI_E_TABLE_TOO_BIG;
DebugTrace(TEXT("ScReplaceRows() - In memory table has > 32767 rows (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
// Make sure the unsorted and the Index sorted row lists are big
// enough to handle all of the new rows.
// This is done first so that we won't fail after we start adding rows.
if ((lptad->ulcRowsAdd + cRowsNew) >= lptad->ulcRowMacAdd)
{
ULONG ulcRowsToAdd;
ulcRowsToAdd = (cRowsNew > ROW_CHUNK_SIZE) ? cRowsNew
: ROW_CHUNK_SIZE;
if (FAILED(sc = ScCOReallocate( lptad
, (lptad->ulcRowMacAdd + ulcRowsToAdd)
* sizeof(LPSRow)
, (VOID **) (&lptad->parglprowAdd))))
{
DebugTrace(TEXT("ScAddRows() - Error growing unsorted row set (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
// Increment ulcRowMacAdd only AFTER successfull allocation
lptad->ulcRowMacAdd += ulcRowsToAdd;
}
if ((lptad->ulcRowsIndex + cRowsNew) >= lptad->ulcRowMacIndex)
{
ULONG ulcRowsToAdd;
ulcRowsToAdd = (cRowsNew > ROW_CHUNK_SIZE) ? cRowsNew
: ROW_CHUNK_SIZE;
if (FAILED(sc = ScCOReallocate( lptad
, (lptad->ulcRowMacIndex + ulcRowsToAdd)
* sizeof(LPSRow)
, (VOID **) (&lptad->parglprowIndex))))
{
DebugTrace(TEXT("ScAddRows() - Error growing Index sorted row set (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
// Increment ulcRowMacIndex only AFTER successfull allocation
lptad->ulcRowMacIndex += ulcRowsToAdd;
}
// Allocate the list of old rows now so we won't fail after we start
// adding rows.
if (FAILED(sc = ScAllocateBuffer( lptad
, cRowsNew * sizeof(LPSRow)
, &parglprowOld)))
{
DebugTrace(TEXT("ScAddRows() - Error creating old row list (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
MAPISetBufferName(parglprowOld, TEXT("ITable old row list (replace)"));
// This routine is not allowed to fail after this point.
plprowOld = parglprowOld;
for (plprowNew = parglprowNew; cRowsNew; plprowNew++, cRowsNew--)
{
LPSRow * plprow = NULL;
sc = ScFindRow(lptad, (*plprowNew)->lpProps, &plprow);
if (sc == S_OK)
{
// Put the old row into the old row set
*plprowOld = *plprow;
// Replace the row in the Index sorted set
*plprow = *plprowNew;
// Replace the row in the unsorted row set
if (plprow = PlprowByLprow( lptad->ulcRowsAdd
, lptad->parglprowAdd
, *plprowOld))
{
*plprow = *plprowNew;
}
// If the row was in the Index sorted set it should always be in
// the unsorted set
Assert(plprow);
// Point at the next available slot for old rows
plprowOld++;
}
// ...else didn't find the row.
else
{
// Insert the row into the Index sorted set
sc = ScAddRow( (LPUNKOBJ) lptad
, NULL // We already collated the row
, *plprowNew
, (plprow) ? (ULONG) (plprow - lptad->parglprowIndex) : 0
, &lptad->ulcRowsIndex
, &lptad->ulcRowMacIndex
, &lptad->parglprowIndex
, NULL);
AssertSz1( !FAILED(sc)
, TEXT("TAD::ScReplaceRows() - Error adding row to Index sorted set (SCODE = 0x%08lX)\n")
, sc);
// Add the row to the end of the unsorted set
Assert( !IsBadWritePtr( lptad->parglprowAdd + lptad->ulcRowsAdd
, sizeof(*plprowNew)));
lptad->parglprowAdd[lptad->ulcRowsAdd++] = *plprowNew;
}
}
sc = S_OK; // ignore NOT_FOUND error (or Asserted errors).
if (plprowOld > parglprowOld)
{
*pparglprowOld = parglprowOld;
*pcRowsOld = (ULONG) (plprowOld - parglprowOld);
}
else
{
ScFreeBuffer(lptad, parglprowOld);
*pparglprowOld = NULL;
*pcRowsOld = 0;
}
ret:
return sc;
//err:
// No need to free old rows here since call is not allowed to fail
// after old row list is allocated!
}
/*============================================================================
- VUE::Release()
-
*/
STDMETHODIMP_(ULONG)
VUE_Release( LPVUE lpvue )
{
LPTAD lptadParent;
ULONG ulcRef;
#if !defined(NO_VALIDATION)
if ( BAD_STANDARD_OBJ(lpvue,VUE_,Release,lpVtbl))
{
DebugTrace(TEXT("VUE::Release() - Bad parameter passed as VUE object\n") );
return !0;
}
#endif
lptadParent = lpvue->lptadParent;
LockObj(lptadParent);
// If there is an instance left then release it
ulcRef = lpvue->ulcRef;
if (ulcRef != 0)
ulcRef = --lpvue->ulcRef;
// The object can only be destroyed if there is no instance and no
// active Advise left.
//$ We can use lpvue->lpAdviselist if we can depend on HrUnsubscribe
//$ leaving lpvue->lpAdviseList NULL after the last HrUnsubscribe
if ( ulcRef == 0 && !lpvue->ulcAdvise )
{
CALLERRELEASE FAR * lpfReleaseCallback = lpvue->lpfReleaseCallback;
ULONG ulReleaseData = lpvue->ulReleaseData;
LPVUE * plpvue;
// Call the release callback. Leave our crit sect before
// calling back to prevent deadlock.
if (lpfReleaseCallback)
{
UnlockObj(lptadParent);
lpfReleaseCallback(ulReleaseData,
(LPTABLEDATA) lptadParent,
(LPMAPITABLE) lpvue);
LockObj(lptadParent);
}
// Search the linked list of VUEs in our parent TAD for this VUE.
for ( plpvue = &(lptadParent->lpvueList)
; *plpvue
; plpvue = &((*plpvue)->lpvueNext))
{
if (*plpvue == lpvue)
break;
}
// If this VUE was in the list then UNLINK it and FREE it
if (*plpvue)
{
// Unlink the VUE
*plpvue = lpvue->lpvueNext;
// Free resources used by the VUE
ScFreeBuffer(lpvue, lpvue->lpptaCols);
ScFreeBuffer(lpvue, lpvue->lpres);
ScFreeBuffer(lpvue, lpvue->lpsos);
COFree(lpvue, lpvue->parglprows);
#ifdef NOTIFICATIONS
DestroyAdviseList(&lpvue->lpAdviseList);
#endif // NOTIFICATIONS
UNKOBJ_Deinit((LPUNKOBJ) lpvue);
ScFreeBuffer(lpvue, lpvue);
// Unlock and Release the parent TAD
// This must be done after ScFreeBuffer sinse *pinst may go
// away
UnlockObj(lptadParent);
UlRelease(lptadParent);
}
else
{
DebugTrace(TEXT("VUE::Release() - Table VUE not linked to TAD"));
// Just unlock the parent tad. We will leak an unlinked vue.
UnlockObj(lptadParent);
}
}
else
{
#if DEBUG
if ( ulcRef == 0 && lpvue->ulcAdvise )
{
DebugTrace(TEXT("VUE::Release() - Table VUE still has active Advise"));
}
#endif // DEBUG
UnlockObj(lptadParent);
}
return ulcRef;
}
/*============================================================================
- VUE::Advise()
-
*/
STDMETHODIMP
VUE_Advise(
LPVUE lpvue,
ULONG ulEventMask,
LPMAPIADVISESINK lpAdviseSink,
ULONG FAR * lpulConnection)
{
#ifdef NOTIFICATIONS
SCODE sc;
VUENOTIFKEY vuenotifkey;
#endif // NOTIFICATIONS
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,Advise,lpVtbl);
Validate_IMAPITable_Advise(
lpvue,
ulEventMask,
lpAdviseSink,
lpulConnection);
#endif
#ifdef NOTIFICATIONS
LockObj(lpvue->lptadParent);
vuenotifkey.ulcb = sizeof(MAPIUID);
vuenotifkey.mapiuid = lpvue->mapiuidNotif;
if ( FAILED(sc = GetScode(HrSubscribe(&lpvue->lpAdviseList,
(LPNOTIFKEY) &vuenotifkey,
ulEventMask,
lpAdviseSink,
0,
lpulConnection))) )
{
DebugTrace(TEXT("VUE::Advise() - Error subscribing notification (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
//$ We don't need lpvue->ulcAdvise if we can depend on HrUnsubscribe
//$ leaving lpvue->lpAdviseList NULL after the last HrUnsubscribe
++lpvue->ulcAdvise;
ret:
UnlockObj(lpvue->lptadParent);
return HrSetLastErrorIds(lpvue, sc, 0);
#endif // NOTIFICATIONS
return(ResultFromScode(MAPI_E_NO_SUPPORT));
}
/*============================================================================
- VUE::Unadvise()
-
*/
STDMETHODIMP
VUE_Unadvise(
LPVUE lpvue,
ULONG ulConnection)
{
SCODE sc = S_OK;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,Unadvise,lpVtbl);
Validate_IMAPITable_Unadvise( lpvue, ulConnection );
#endif
#ifdef NOTIFICATIONS
LockObj(lpvue->lptadParent);
if ( FAILED(sc = GetScode(HrUnsubscribe(&lpvue->lpAdviseList,
ulConnection))) )
{
DebugTrace(TEXT("VUE::Unadvise() - Error unsubscribing notification (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
// Decrement our advise count.
//$ We don't need lpvue->ulcAdvise if we can depend on HrUnsubscribe
//$ leaving lpvue->lpAdviseList NULL after the last HrUnsubscribe
if (lpvue->ulcAdvise)
{
--lpvue->ulcAdvise;
}
ret:
UnlockObj(lpvue->lptadParent);
return ResultFromScode(sc);
#endif // NOTIFICATIONS
return(ResultFromScode(MAPI_E_NO_SUPPORT));
}
/*============================================================================
- VUE::GetStatus()
-
* Since TAD based IMAPITables don't do anything asynchronously, this
* function always reports TBLSTAT_COMPLETE.
*/
STDMETHODIMP
VUE_GetStatus(
LPVUE lpvue,
ULONG FAR * lpulTableStatus,
ULONG FAR * lpulTableType )
{
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,GetStatus,lpVtbl);
Validate_IMAPITable_GetStatus( lpvue, lpulTableStatus, lpulTableType );
#endif
*lpulTableStatus = TBLSTAT_COMPLETE;
*lpulTableType = lpvue->lptadParent->ulTableType;
return HrSetLastErrorIds(lpvue, S_OK, 0);
}
/*============================================================================
- VUE::SetColumns()
-
* Replaces the current column set with a copy of the specified column set
* and frees the old column set.
*/
STDMETHODIMP
VUE_SetColumns(
LPVUE lpvue,
LPSPropTagArray lpptaCols,
ULONG ulFlags )
{
LPSPropTagArray lpptaColsCopy;
SCODE sc;
#if !defined(NO_VALIDATION)
// VALIDATE_OBJ(lpvue,VUE_,SetColumns,lpVtbl);
// Validate_IMAPITable_SetColumns( lpvue, lpptaCols, ulFlags ); // Commented by YST
#endif
LockObj(lpvue->lptadParent);
// Copy the column set
if ( FAILED(sc = ScDupRgbEx( (LPUNKOBJ) lpvue
, CbNewSPropTagArray(lpptaCols->cValues)
, (LPBYTE) lpptaCols
, 0
, (LPBYTE FAR *) &lpptaColsCopy)) )
{
DebugTrace(TEXT("VUE::SetColumns() - Error duping column set (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
MAPISetBufferName(lpptaColsCopy, TEXT("ITable: dup column set"));
// Replace the current column set with the copy and
// free the old one.
ScFreeBuffer(lpvue, lpvue->lpptaCols);
lpvue->lpptaCols = lpptaColsCopy;
// [PaulHi] 4/5/99 @comment Shouldn't
// clients of the WAB request ANSI/UNICODE based on property tags in the
// column array, rather than doing mass conversions?
// Seems like the fix is to create two versions of column property arrays,
// an ANSI and Unicode version.
FixupColsWA(lpvue->lpptaCols, lpvue->bMAPIUnicodeTable);
ret:
UnlockObj(lpvue->lptadParent);
return HrSetLastErrorIds(lpvue, sc, 0);
}
/*============================================================================
- VUE::QueryColumns()
-
* Returns a copy of the current column set or the available column set.
*/
STDMETHODIMP
VUE_QueryColumns(
LPVUE lpvue,
ULONG ulFlags,
LPSPropTagArray FAR * lplpptaCols )
{
LPSPropTagArray lpptaCols;
SCODE sc;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,QueryColumns,lpVtbl);
Validate_IMAPITable_QueryColumns( lpvue, ulFlags, lplpptaCols );
#endif
LockObj(lpvue->lptadParent);
// Figure out which column set to return
lpptaCols = (ulFlags & TBL_ALL_COLUMNS) ?
lpvue->lptadParent->lpptaCols :
lpvue->lpptaCols;
// Return a copy of it to the caller
if ( FAILED(sc = ScDupRgbEx( (LPUNKOBJ) lpvue
, CbNewSPropTagArray(lpptaCols->cValues)
, (LPBYTE) lpptaCols
, 0
, (LPBYTE FAR *) lplpptaCols)) )
{
DebugTrace(TEXT("VUE::QueryColumns() - Error copying column set to return (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
MAPISetBufferName(*lplpptaCols, TEXT("ITable: QueryColumns column set"));
ret:
UnlockObj(lpvue->lptadParent);
return HrSetLastErrorIds(lpvue, sc, 0);
}
/*============================================================================
- VUE::GetRowCount()
-
* Returns the count of rows in the table.
*/
STDMETHODIMP
VUE_GetRowCount(
LPVUE lpvue,
ULONG ulFlags,
ULONG FAR * lpulcRows )
{
SCODE sc = S_OK;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,GetRowCount,lpVtbl);
Validate_IMAPITable_GetRowCount( lpvue, ulFlags, lpulcRows );
#endif
LockObj(lpvue->lptadParent);
*lpulcRows = lpvue->bkEnd.uliRow;
UnlockObj(lpvue->lptadParent);
return HrSetLastErrorIds(lpvue, sc, 0);
}
/*============================================================================
- VUE::SeekRow()
-
* Seeks to the specified row in the table.
*/
STDMETHODIMP
VUE_SeekRow(
LPVUE lpvue,
BOOKMARK bkOrigin,
LONG lcRowsToSeek,
LONG FAR * lplcRowsSought )
{
LONG lcRowsSought;
PBK pbk;
SCODE sc = S_OK;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,SeekRow,lpVtbl);
Validate_IMAPITable_SeekRow( lpvue, bkOrigin, lcRowsToSeek, lplcRowsSought );
if ( FBadBookmark(lpvue,bkOrigin) ||
(lplcRowsSought && IsBadWritePtr(lplcRowsSought,sizeof(LONG))) )
{
DebugTrace(TEXT("VUE::SeekRow() - Bad parameter(s) passed\n") );
return HrSetLastErrorIds(lpvue, MAPI_E_INVALID_PARAMETER, 0);
}
#endif
LockObj(lpvue->lptadParent);
// Validate the bookmark and adjust Moving and Changed bookmarks
if ( FBookMarkStale( lpvue, bkOrigin) )
{
sc = MAPI_E_INVALID_BOOKMARK;
DebugTrace(TEXT("VUE::SeekRow() - Invalid bookmark (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
// Do the seek
pbk = lpvue->rgbk + bkOrigin;
lcRowsSought = lcRowsToSeek < 0 ?
-min((LONG) pbk->uliRow, -lcRowsToSeek) :
min(lcRowsToSeek, (LONG)(lpvue->bkEnd.uliRow - pbk->uliRow));
lpvue->bkCurrent.uliRow = pbk->uliRow + lcRowsSought;
// If caller wants to know how far we sought, fill it in
if ( lplcRowsSought )
*lplcRowsSought = lcRowsSought;
// Warn if the bookmark sought from refered to a different
// row from the last time it was used
if ( pbk->dwfBKS & dwfBKSChanged )
{
pbk->dwfBKS &= ~dwfBKSChanged; // Warn only once
sc = MAPI_W_POSITION_CHANGED;
}
ret:
UnlockObj(lpvue->lptadParent);
return HrSetLastErrorIds(lpvue, sc, 0);
}
/*============================================================================
- VUE::SeekRowApprox()
-
* Seeks to the approximate fractional position in the table.
*/
STDMETHODIMP
VUE_SeekRowApprox(
LPVUE lpvue,
ULONG ulNumerator,
ULONG ulDenominator )
{
SCODE sc = S_OK;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,SeekRowApprox,lpVtbl);
Validate_IMAPITable_SeekRowApprox( lpvue, ulNumerator, ulDenominator );
#endif
LockObj(lpvue->lptadParent);
// Any fraction whose numerator is greater than or equal to its
// denominator should be fixed up to be equivalent to ulcRows/ulcRows.
// (i.e. Seek to the end of the table). Also, fixup denominator
// so it's never 0 (which would crash the approximate position
// calculation).
if ( ulNumerator >= ulDenominator )
{
ulDenominator = UlDenominator(lpvue->bkEnd.uliRow);
ulNumerator = ulDenominator;
}
// Pare the approximate position down to 16-bit accuracy
// (If someone wants to seek approximate to something that's accurate
// to more than 1/32768th, tough!)
while ( HIWORD(ulNumerator) != 0 )
{
ulNumerator >>= 1;
ulDenominator >>= 1;
}
// Assert that we have less than a word's worth of rows in the table.
// (If someone wants > 32767 entries in an *IN MEMORY* table, tough!)
AssertSz( HIWORD(lpvue->bkEnd.uliRow) == 0,
TEXT("Table has more than 32767 rows. Can't be supported in memory.") );
// Set the position
lpvue->bkCurrent.uliRow = lpvue->bkEnd.uliRow * ulNumerator / ulDenominator;
UnlockObj(lpvue->lptadParent);
return HrSetLastErrorIds(lpvue, sc, 0);
}
/*============================================================================
- VUE::QueryPosition()
-
* Query the current exact and approximate fractional position in
* the table.
*/
STDMETHODIMP
VUE_QueryPosition(
LPVUE lpvue,
ULONG FAR * lpulRow,
ULONG FAR * lpulNumerator,
ULONG FAR * lpulDenominator )
{
SCODE sc = S_OK;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,QueryPosition,lpVtbl);
Validate_IMAPITable_QueryPosition(
lpvue,
lpulRow,
lpulNumerator,
lpulDenominator);
#endif
LockObj(lpvue->lptadParent);
*lpulRow = lpvue->bkCurrent.uliRow;
*lpulNumerator = lpvue->bkCurrent.uliRow;
*lpulDenominator = UlDenominator(lpvue->bkEnd.uliRow);
UnlockObj(lpvue->lptadParent);
return HrSetLastErrorIds(lpvue, sc, 0);
}
/*============================================================================
- VUE::FindRow()
-
*/
STDMETHODIMP
VUE_FindRow(
LPVUE lpvue,
LPSRestriction lpres,
BOOKMARK bkOrigin,
ULONG ulFlags )
{
PBK pbk;
LPSRow * plprow;
ULONG fSatisfies;
SCODE sc;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,FindRow,lpVtbl);
Validate_IMAPITable_FindRow(
lpvue,
lpres,
bkOrigin,
ulFlags );
if ( FBadBookmark(lpvue,bkOrigin) )
{
DebugTrace(TEXT("VUE::FindRow() - Bad parameter(s) passed\n") );
return HrSetLastErrorIds(lpvue, MAPI_E_INVALID_PARAMETER, 0);
}
#endif
LockObj(lpvue->lptadParent);
// Validate the bookmark and adjust Moving and Changed bookmarks
if ( FBookMarkStale( lpvue, bkOrigin) )
{
sc = MAPI_E_INVALID_BOOKMARK;
DebugTrace(TEXT("VUE::FindRow() - Invalid bookmark (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
pbk = lpvue->rgbk + bkOrigin;
plprow = lpvue->parglprows + pbk->uliRow;
if ( ulFlags & DIR_BACKWARD )
{
while ( plprow-- > lpvue->parglprows )
{
if ( FAILED(sc = ScSatisfiesRestriction(*plprow,
lpres,
&fSatisfies)) )
{
DebugTrace(TEXT("VUE::FindRow() - Error evaluating restriction on row (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
if ( fSatisfies )
goto found_row;
}
}
else
{
while ( plprow < lpvue->parglprows + lpvue->bkEnd.uliRow )
{
if ( FAILED(sc = ScSatisfiesRestriction(*plprow,
lpres,
&fSatisfies )) )
{
DebugTrace(TEXT("VUE::FindRow() - Error evaluating restriction on row (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
if ( fSatisfies )
goto found_row;
++plprow;
}
}
sc = MAPI_E_NOT_FOUND;
goto ret;
found_row:
lpvue->bkCurrent.uliRow = (ULONG) (plprow - lpvue->parglprows);
// Warn if the bookmark sought from refered to a different
// row from the last time it was used
if ( pbk->dwfBKS & dwfBKSChanged )
{
pbk->dwfBKS &= ~dwfBKSChanged; // Warn only once
sc = MAPI_W_POSITION_CHANGED;
}
ret:
UnlockObj(lpvue->lptadParent);
return HrSetLastErrorIds(lpvue, sc, 0);
}
/*============================================================================
- HrVUERestrict()
-
* 4/22/97
* This is basically the VUE_Restrict function isolated so that it can be
* called from LDAPVUE_Restrict without any parameter validation
*
*
*/
HRESULT HrVUERestrict( LPVUE lpvue,
LPSRestriction lpres,
ULONG ulFlags )
{
LPSRestriction lpresCopy;
SCODE sc;
LockObj(lpvue->lptadParent);
// Make a copy of the restriction for our use
if ( FAILED(sc = ScDupRestriction((LPUNKOBJ) lpvue, lpres, &lpresCopy)) )
{
DebugTrace(TEXT("VUE::Restrict() - Error duping restriction (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
// Build a new list of rows from the TAD which satisfy the new restriction
if ( FAILED(sc = ScLoadRows(lpvue->lptadParent->ulcRowsAdd,
lpvue->lptadParent->parglprowAdd,
lpvue,
lpresCopy,
lpvue->lpsos)) )
{
DebugTrace(TEXT("VUE::Restrict() - Error building restricted row set (SCODE = 0x%08lX)\n"), sc );
ScFreeBuffer(lpvue, lpresCopy);
goto ret;
}
// Replace the old restriction with the new one
ScFreeBuffer(lpvue, lpvue->lpres);
lpvue->lpres = lpresCopy;
ret:
UnlockObj(lpvue->lptadParent);
return HrSetLastErrorIds(lpvue, sc, 0);
}
/*============================================================================
- VUE::Restrict()
-
* Reloads the view with rows satisfying the new restriction.
*/
STDMETHODIMP
VUE_Restrict(
LPVUE lpvue,
LPSRestriction lpres,
ULONG ulFlags )
{
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,Restrict,lpVtbl);
Validate_IMAPITable_Restrict(
lpvue,
lpres,
ulFlags );
#endif
return HrVUERestrict(lpvue, lpres, ulFlags);
}
/*============================================================================
- VUE::CreateBookmark()
-
*/
STDMETHODIMP
VUE_CreateBookmark(
LPVUE lpvue,
BOOKMARK FAR * lpbkPosition )
{
PBK pbk;
SCODE sc = S_OK;
IDS ids = 0;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,CreateBookmark,lpVtbl);
Validate_IMAPITable_CreateBookmark( lpvue, lpbkPosition);
#endif
LockObj(lpvue->lptadParent);
if ( lpvue->bkCurrent.uliRow == lpvue->bkEnd.uliRow )
{
// If we're at EOT, just return bkEnd
*lpbkPosition = (BOOKMARK) BOOKMARK_END;
}
else
{
// Othewise, look for a free bookmark and return it
pbk = lpvue->rgbk + cBookmarksMax;
while ( pbk-- > lpvue->rgbk )
{
if ( pbk->dwfBKS == dwfBKSFree )
{
pbk->dwfBKS = dwfBKSValid;
pbk->uliRow = lpvue->bkCurrent.uliRow;
*lpbkPosition = (BOOKMARK)(pbk - lpvue->rgbk);
goto ret;
}
}
DebugTrace(TEXT("VUE::CreateBookmark() - Out of bookmarks\n") );
sc = MAPI_E_UNABLE_TO_COMPLETE;
#ifdef OLD_STUFF
ids = IDS_OUT_OF_BOOKMARKS;
#endif // OLD_STUFF
}
ret:
UnlockObj(lpvue->lptadParent);
return HrSetLastErrorIds(lpvue, sc, ids);
}
/*============================================================================
- VUE::FreeBookmark()
-
*/
STDMETHODIMP
VUE_FreeBookmark(
LPVUE lpvue,
BOOKMARK bkOrigin )
{
SCODE sc = S_OK;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,FreeBookmark,lpVtbl);
Validate_IMAPITable_FreeBookmark( lpvue, bkOrigin );
if ( FBadBookmark(lpvue,bkOrigin) )
{
DebugTrace(TEXT("VUE::FreeBookmark() - Bad parameter(s) passed\n") );
return HrSetLastErrorIds(lpvue, MAPI_E_INVALID_PARAMETER, 0);
}
#endif
LockObj(lpvue->lptadParent);
// Free the bookmark (ignoring predefined ones)
if ( bkOrigin > cBookmarksReserved )
lpvue->rgbk[bkOrigin].dwfBKS = dwfBKSFree;
UnlockObj(lpvue->lptadParent);
return HrSetLastErrorIds(lpvue, sc, 0);
}
/*============================================================================
- VUE::SortTable()
-
* Reloads the view with rows in the new sort order. Note that the
* sort order may be NULL (since ITABLE.DLL allows creating tables
* views NULL sort orders).
*
* //$??? While being minimal in code size, this approach is somewhat
* //$??? slow having to reload the table. If it becomes a performance
* //$??? issue, a sort function can be implemented instead..
*/
STDMETHODIMP
VUE_SortTable(
LPVUE lpvue,
LPSSortOrderSet lpsos,
ULONG ulFlags )
{
LPSSortOrderSet lpsosCopy = NULL;
SCODE sc = S_OK;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,SortTable,lpVtbl);
Validate_IMAPITable_SortTable(
lpvue,
lpsos,
ulFlags );
#endif
if (lpsos && lpsos->cCategories)
{
DebugTrace(TEXT("VUE::SortTable() - VUE::SortTable doesn't support categories\n") );
#ifdef OLD_STUFF
return HrSetLastErrorIds(lpvue, MAPI_E_TOO_COMPLEX, IDS_CANT_CATEGORIZE);
#endif // OLD_STUFF
return HrSetLastErrorIds(lpvue, MAPI_E_TOO_COMPLEX, 0);
}
LockObj(lpvue->lptadParent);
// If the sort order is not empty then dup it...
// adding the index column as the least significant sort if it's not
// already there.
if ( lpsos && lpsos->cSorts )
{
LPSSortOrder lpsoIndex;
UINT iSortIndex;
ULONG ulTagIndex = lpvue->lptadParent->ulPropTagIndexCol;
// Look to see if the index column is alread in the sort.
for ( lpsoIndex = lpsos->aSort, iSortIndex = 0
; iSortIndex < lpsos->cSorts
; iSortIndex++, lpsoIndex++)
{
if ( (lpsoIndex->ulPropTag == ulTagIndex)
|| ( (PROP_ID(lpsoIndex->ulPropTag) == PROP_ID(ulTagIndex))
&& (PROP_TYPE(lpsoIndex->ulPropTag) == PT_UNSPECIFIED)))
{
break;
}
}
// Make a copy of the sort order set for our own use
if ( FAILED(sc = ScDupRgbEx( (LPUNKOBJ) lpvue
, CbSSortOrderSet(lpsos)
, (LPBYTE) lpsos
, (iSortIndex == lpsos->cSorts)
? sizeof(SSortOrder) : 0
, (LPBYTE FAR *) &lpsosCopy)) )
{
DebugTrace(TEXT("VUE::SortTable() - Error duping sort order set (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
MAPISetBufferName(lpsosCopy, TEXT("ITable: SortTable SOS copy"));
if (iSortIndex == lpsos->cSorts)
{
lpsosCopy->aSort[iSortIndex].ulPropTag = ulTagIndex;
lpsosCopy->aSort[iSortIndex].ulOrder = TABLE_SORT_ASCEND;
lpsosCopy->cSorts++;
}
}
// ...else the lpsos is empty so we are already sorted
else
{
// Put the old lpsos into lpsosCopy so that it will be freed.
lpsosCopy = lpvue->lpsos;
lpvue->lpsos = NULL;
goto ret;
}
// Only do the sort if the new sort is NOT a proper subset of the new
// sort.
//$ Well... Now that we added the SECRET last sort, this optimization
//$ is "almost" useless!
if ( !lpvue->lpsos
|| lpsosCopy->cSorts > lpvue->lpsos->cSorts
|| memcmp( lpvue->lpsos->aSort
, lpsosCopy->aSort
, (UINT) (lpsosCopy->cSorts * sizeof(SSortOrder))) )
{
// Sort the VUE rows into a new row set
// NOTE! We use the rows from the existing VUE set in order to
// take advantage of the fact the restriction is already
// done.
if ( FAILED(sc = ScLoadRows(lpvue->bkEnd.uliRow,
lpvue->parglprows,
lpvue,
NULL, // The restriction is already done
lpsosCopy)) )
{
DebugTrace(TEXT("VUE::SortTable() - Building sorted row set (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
}
// Swap the old lpsos with lpsosCopy
lpsos = lpvue->lpsos;
lpvue->lpsos = lpsosCopy;
lpsosCopy = lpsos;
ret:
// Free the left over SOS
ScFreeBuffer(lpvue, lpsosCopy);
UnlockObj(lpvue->lptadParent);
return HrSetLastErrorIds(lpvue, sc, 0);
}
/*============================================================================
- VUE::QuerySortOrder()
-
* Returns the current sort order which may be NULL since ITABLE.DLL
* allows creation of views with NULL sort orders.
*/
STDMETHODIMP
VUE_QuerySortOrder(
LPVUE lpvue,
LPSSortOrderSet FAR * lplpsos )
{
SCODE sc = S_OK;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,QuerySortOrder,lpVtbl);
Validate_IMAPITable_QuerySortOrder(
lpvue,
lplpsos );
#endif
LockObj(lpvue->lptadParent);
if ( !lpvue->lpsos )
{
UINT cb = CbNewSSortOrderSet(0);
// allocate a sort order set containing zero sort orders
if ( FAILED(sc = ScAllocateBuffer(lpvue, cb, (LPBYTE *) lplpsos)))
{
DebugTrace(TEXT("VUE::QuerySortOrder() - Error allocating SortOrderSet (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
MAPISetBufferName(*lplpsos, TEXT("ITable new sort order set"));
// zero the sort order set - which sets the number of sort columns to zero
ZeroMemory(*lplpsos, cb);
}
// Make a copy of our sort order set to return to the caller.
else if ( FAILED(sc = ScDupRgbEx( (LPUNKOBJ) lpvue
, CbSSortOrderSet(lpvue->lpsos)
, (LPBYTE) (lpvue->lpsos)
, 0
, (LPBYTE FAR *) lplpsos)) )
{
DebugTrace(TEXT("VUE::QuerySortOrder() - Error duping sort order set (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
MAPISetBufferName(*lplpsos, TEXT("ITable: QuerySortOrder SOS"));
ret:
UnlockObj(lpvue->lptadParent);
return HrSetLastErrorIds(lpvue, sc, 0);
}
/*============================================================================
- VUE::QueryRows()
-
*/
STDMETHODIMP
VUE_QueryRows(
LPVUE lpvue,
LONG lcRows,
ULONG ulFlags,
LPSRowSet FAR * lplprows )
{
LPSRow * plprowSrc;
LPSRow lprowDst;
LPSRowSet lprows = NULL;
SCODE sc;
#define ABS(n) ((n) < 0 ? -1*(n) : (n))
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,QueryRows,lpVtbl);
#ifndef _WIN64 // Need to investigate more, why this is always fail (YST)
Validate_IMAPITable_QueryRows(
lpvue,
lcRows,
ulFlags,
lplprows );
#endif // _WIN64
#endif
LockObj(lpvue->lptadParent);
// If querying backward, seek back as far as needed and read from there
plprowSrc = lpvue->parglprows + lpvue->bkCurrent.uliRow;
if ( lcRows < 0 )
{
lcRows = -min((LONG)lpvue->bkCurrent.uliRow, -lcRows);
plprowSrc += lcRows;
}
else
{
lcRows = min(lcRows, (LONG)(lpvue->bkEnd.uliRow - lpvue->bkCurrent.uliRow));
}
// Allocate the row set
if ( FAILED(sc = ScAllocateBuffer( lpvue,
CbNewSRowSet(ABS(lcRows)),
&lprows)) )
{
DebugTrace(TEXT("VUE::QueryRows() - Error allocating row set (SCODE = 0x%08lX)\n"), sc );
goto err;
}
MAPISetBufferName(lprows, TEXT("ITable query rows"));
// Copy the rows
// Start with a count of zero rows so we end up with the correct number
// on partial success
lprows->cRows = 0;
for ( lprowDst = lprows->aRow;
lprowDst < lprows->aRow + ABS(lcRows);
lprowDst++, plprowSrc++ )
{
if ( FAILED(sc = ScCopyVueRow(lpvue,
lpvue->lpptaCols,
*plprowSrc,
lprowDst)) )
{
DebugTrace(TEXT("VUE::QueryRows() - Error copying row (SCODE = 0x%08lX)\n"), sc );
goto err;
}
lprows->cRows++;
}
if ( (lcRows >= 0 && !(ulFlags & TBL_NOADVANCE))
|| (lcRows < 0 && (ulFlags & TBL_NOADVANCE)) )
{
lpvue->bkCurrent.uliRow += lcRows;
}
*lplprows = lprows;
ret:
UnlockObj(lpvue->lptadParent);
return HrSetLastErrorIds(lpvue, sc, 0);
err:
MAPIFreeRows(lpvue, lprows);
goto ret;
#undef ABS
}
/*============================================================================
- VUE::Abort()
-
*/
STDMETHODIMP
VUE_Abort( LPVUE lpvue )
{
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,Abort,lpVtbl);
Validate_IMAPITable_Abort( lpvue );
#endif
return HrSetLastErrorIds(lpvue, S_OK, 0);
}
STDMETHODIMP
VUE_ExpandRow(LPVUE lpvue, ULONG cbIKey, LPBYTE pbIKey,
ULONG ulRowCount, ULONG ulFlags, LPSRowSet FAR *lppRows,
ULONG FAR *lpulMoreRows)
{
SCODE sc = S_OK;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,ExpandRow,lpVtbl);
Validate_IMAPITable_ExpandRow(
lpvue,
cbIKey,
pbIKey,
ulRowCount,
ulFlags,
lppRows,
lpulMoreRows);
#endif
sc = MAPI_E_NO_SUPPORT;
return HrSetLastErrorIds(lpvue, sc, 0);
}
STDMETHODIMP
VUE_CollapseRow(LPVUE lpvue, ULONG cbIKey, LPBYTE pbIKey,
ULONG ulFlags, ULONG FAR *lpulRowCount)
{
SCODE sc = S_OK;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,CollapseRow,lpVtbl);
Validate_IMAPITable_CollapseRow(
lpvue,
cbIKey,
pbIKey,
ulFlags,
lpulRowCount);
#endif
sc = MAPI_E_NO_SUPPORT;
return HrSetLastErrorIds(lpvue, sc, 0);
}
STDMETHODIMP
VUE_WaitForCompletion(LPVUE lpvue, ULONG ulFlags, ULONG ulTimeout,
ULONG FAR *lpulTableStatus)
{
SCODE sc = S_OK;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,WaitForCompletion,lpVtbl);
Validate_IMAPITable_WaitForCompletion(
lpvue,
ulFlags,
ulTimeout,
lpulTableStatus);
#endif
if (lpulTableStatus)
*lpulTableStatus = TBLSTAT_COMPLETE;
return HrSetLastErrorIds(lpvue, sc, 0);
}
STDMETHODIMP
VUE_GetCollapseState(LPVUE lpvue, ULONG ulFlags, ULONG cbInstanceKey, LPBYTE pbInstanceKey,
ULONG FAR * lpcbCollapseState, LPBYTE FAR * lppbCollapseState)
{
SCODE sc = MAPI_E_NO_SUPPORT;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,GetCollapseState,lpVtbl);
Validate_IMAPITable_GetCollapseState(
lpvue,
ulFlags,
cbInstanceKey,
pbInstanceKey,
lpcbCollapseState,
lppbCollapseState);
#endif
return HrSetLastErrorIds(lpvue, sc, 0);
}
STDMETHODIMP
VUE_SetCollapseState(LPVUE lpvue, ULONG ulFlags, ULONG cbCollapseState,
LPBYTE pbCollapseState, BOOKMARK FAR * lpbkLocation)
{
SCODE sc = MAPI_E_NO_SUPPORT;
#if !defined(NO_VALIDATION)
VALIDATE_OBJ(lpvue,VUE_,SetCollapseState,lpVtbl);
Validate_IMAPITable_SetCollapseState(
lpvue,
ulFlags,
cbCollapseState,
pbCollapseState,
lpbkLocation);
#endif
return HrSetLastErrorIds(lpvue, sc, 0);
}
/*============================================================================
- ScDeleteAllRows()
-
* Deletes all rows from a TAD and its views.
*
* Parameters:
* lptad in TAD to delete all rows from
*/
SCODE
ScDeleteAllRows( LPTAD lptad)
{
LPSRow * plprow;
#ifdef NOTIFICATIONS
LPVUE lpvue;
#endif // NOTIFICATIONS
NOTIFICATION notif;
// Delete all rows from the unsorted set
for ( plprow = lptad->parglprowAdd + lptad->ulcRowsAdd
; --plprow >= lptad->parglprowAdd
;)
{
Assert( plprow && *plprow);
ScFreeBuffer( lptad, *plprow);
}
// Tell the TAD it has no rows left
lptad->ulcRowsAdd = lptad->ulcRowsIndex = 0;
// Set the constant portion of the notification
ZeroMemory(&notif, sizeof(NOTIFICATION));
notif.ulEventType = fnevTableModified;
notif.info.tab.ulTableEvent = TABLE_RELOAD;
notif.info.tab.propIndex.ulPropTag
= notif.info.tab.propPrior.ulPropTag
= PR_NULL;
#ifdef NOTIFICATIONS
// Tell each view that it has no rows left
for ( lpvue = (LPVUE) lptad->lpvueList;
lpvue != NULL;
lpvue = (LPVUE) lpvue->lpvueNext )
{
VUENOTIFKEY vuenotifkey;
ULONG ulNotifFlags = 0;
AssertSz( !IsBadWritePtr(lpvue, sizeof(VUE))
&& lpvue->lpVtbl == &vtblVUE
, TEXT("Bad lpvue in TAD vue List.") );
// Reset all of the bookmarks
// This automagically tells the view that it has no rows.
ZeroMemory( lpvue->rgbk, cBookmarksMax * sizeof(BK));
// Initialize the predefined bookmarks
lpvue->bkBeginning.dwfBKS = dwfBKSValid;
lpvue->bkCurrent.dwfBKS = dwfBKSValid;
lpvue->bkEnd.dwfBKS = dwfBKSValid;
vuenotifkey.ulcb = sizeof(MAPIUID);
vuenotifkey.mapiuid = lpvue->mapiuidNotif;
(void) HrNotify((LPNOTIFKEY) &vuenotifkey,
1,
&notif,
&ulNotifFlags);
}
#endif // NOTIFICATIONS
// Currently this call cannot fail!
return S_OK;
}
/*============================================================================
- ScLoadRows()
-
* Loads a view's row set with rows from the table data according
* to the specified restriction and sort order and resets all bookmarks.
*
* Parameters:
* lpvue in View whose row set is to be loaded
* lpres in Restriction on loaded row set
* lpsos in Sort order of loaded row set
*/
SCODE
ScLoadRows(
ULONG ulcRowsSrc,
LPSRow * rglprowsSrc,
LPVUE lpvue,
LPSRestriction lpres,
LPSSortOrderSet lpsos )
{
LPTAD lptad = (LPTAD) lpvue->lptadParent;
LPSRow * plprowSrc;
LPSRow * plprowDst;
PBK pbk;
ULONG ulcRows = 0;
ULONG ulcRowMac = 0;
LPSRow * parglprows = NULL;
SCODE sc = S_OK;
// Iterate through the table data adding to the view any rows
// which satisfy the specified restriction. Note, the forward
// iteration is necessary here so that the rows load in order
// when no sort order set is specified.
for ( plprowSrc = rglprowsSrc;
plprowSrc < rglprowsSrc + ulcRowsSrc;
plprowSrc++ )
{
// If the row satisfies the specified restriction, add it to the
// row set updating bookmarks as necessary.
if ( FAILED(sc = ScMaybeAddRow(lpvue,
lpres,
lpsos,
*plprowSrc,
ulcRows,
&ulcRows,
&ulcRowMac,
&parglprows,
&plprowDst)) )
{
DebugTrace(TEXT("VUE::ScLoadRows() - Error adding row to view (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
}
// Replace the row set
COFree(lpvue, lpvue->parglprows);
lpvue->parglprows = parglprows;
lpvue->ulcRowMac = ulcRowMac;
lpvue->bkEnd.uliRow = ulcRows;
// Lose all user-defined bookmarks, and reset BOOKMARK_CURRENT
// to BOOKMARK_BEGINNING (i.e. 0) (Raid 1331)
pbk = lpvue->rgbk + cBookmarksMax;
while ( pbk-- > lpvue->rgbk + cBookmarksReserved )
if ( pbk->dwfBKS & dwfBKSValid )
pbk->dwfBKS = dwfBKSValid | dwfBKSStale;
lpvue->bkCurrent.uliRow = 0;
ret:
return sc;
}
/*============================================================================
- ScMaybeAddRow()
-
* If the specified row satisfies the specified restriction, add it
* to the row set of the specified view according to the specified
* sort order returning a pointer to the location where the row was
* added.
*
*
* Parameters:
* lpvue in VUE with instance variable containing
* allocators.
* lpres in Restriction row must satisfy to be added
* lpsos in Sort order of row set.
* lprow in Row to maybe add.
* ulcRows in Count of rows in the row set.
* pparglprows in/out Pointer to buffer containing row set.
* pplprow out Pointer to location of added row in row set.
* (Set to NULL if the row isn't added.)
*/
SCODE
ScMaybeAddRow(
LPVUE lpvue,
LPSRestriction lpres,
LPSSortOrderSet lpsos,
LPSRow lprow,
ULONG uliRow,
ULONG * pulcRows,
ULONG * pulcRowMac,
LPSRow ** pparglprows,
LPSRow ** pplprow )
{
ULONG fSatisfies;
SCODE sc;
// Check to see if the row satisfies the specified restriction
if ( FAILED(sc = ScSatisfiesRestriction(lprow, lpres, &fSatisfies)) )
{
DebugTrace(TEXT("VUE::ScMaybeAddRow() - Error evaluating restriction (SCODE = 0x%08lX)\n"), sc );
return sc;
}
// If it doesn't, return now.
if ( !fSatisfies )
{
*pplprow = NULL;
return S_OK;
}
// The row satisfies the restriction, so add it to the row set
// according to the specified sort order
if ( FAILED(sc = ScAddRow((LPUNKOBJ) lpvue,
lpsos,
lprow,
uliRow,
pulcRows,
pulcRowMac,
pparglprows,
pplprow)) )
{
DebugTrace(TEXT("VUE::ScMaybeAddRow() - Error adding row (SCODE = 0x%08lX)\n"), sc );
return sc;
}
return S_OK;
}
/*============================================================================
- ScAddRow()
-
* Adds a row to a row set according to the specified sort order returning
* a pointer to the location in the row set where the row was added.
*
*
* Parameters:
* lpunkobj in UNKOBJ with instance variable containing
* allocators.
* lpsos in Sort order of row set.
* lprow in Row to maybe add.
* uliRow in Location to add row if lpsos is NULL
* pulcRows in/out Count of rows in the row set.
* pparglprows in/out Pointer to buffer containing row set.
* pplprow out Pointer to location of added row in row set.
*/
SCODE
ScAddRow(
LPUNKOBJ lpunkobj,
LPSSortOrderSet lpsos,
LPSRow lprow,
ULONG uliRow,
ULONG * pulcRows,
ULONG * pulcRowMac,
LPSRow ** pparglprows,
LPSRow ** pplprow )
{
LPSRow * plprow;
SCODE sc = S_OK;
Assert(lpsos || uliRow <= *pulcRows);
if (HIWORD(*pulcRows + 1) != 0)
{
sc = MAPI_E_TABLE_TOO_BIG;
DebugTrace(TEXT("ScAddRow() - In memory table has > 32767 rows (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
// Grow the row set
if (*pulcRows >= *pulcRowMac)
{
sc = ScCOReallocate( lpunkobj
, (*pulcRowMac + ROW_CHUNK_SIZE) * sizeof(LPSRow)
, (LPVOID*) pparglprows);
if (FAILED(sc))
{
DebugTrace(TEXT("ScAddRow() - Error growing row set (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
*pulcRowMac += ROW_CHUNK_SIZE;
}
// Collate the row
if ( lpsos )
{
plprow = PlprowCollateRow(*pulcRows, *pparglprows, lpsos, TRUE, lprow);
}
else
{
plprow = *pparglprows + uliRow;
}
// And insert it into the row set
MoveMemory(plprow+1,
plprow,
(size_t) (*pulcRows - (plprow - *pparglprows)) * sizeof(LPSRow));
*plprow = lprow;
++*pulcRows;
if ( pplprow )
*pplprow = plprow;
ret:
return sc;
}
/*============================================================================
* The following functions are generic utility functions to manipulate
* table-related data structures. They can easily be modified to be usable
* in the common subsystem, and should eventually be put there. The reason
* they are here now is to avoid unnecessarily bloating proputil.c until
* it can become a lib or DLL.
*/
/*============================================================================
- ScCopyVueRow()
-
* For use with IMAPITable::QueryRows(). Copies a row by filling in
* the specified SRow with the count of columns in the row and copying,
* in order, the specified columns for that row (filling in PT_ERROR
* for columns with no value in the row) into a prop value array
* allocated using MAPI linked memory.
*
*
* Parameters:
* lpunkobj in UNKOBJ with instance variable containing
* MAPI allocators.
* lpptaCols in Columns to copy.
* lprowSrc in Row to copy.
* lprowDst out Copied row.
*/
SCODE
ScCopyVueRow(
LPVUE lpvue,
LPSPropTagArray lpptaCols,
LPSRow lprowSrc,
LPSRow lprowDst )
{
ULONG ulcCols = lpptaCols->cValues;
ULONG * pulPropTag;
LPSPropValue lppropSrc;
LPSPropValue lppropDst;
CMB cmb;
SCODE sc;
ZeroMemory(&cmb, sizeof(CMB));
// Calculate space needed to copy the requested columns
pulPropTag = (ULONG *) (lpptaCols->aulPropTag + ulcCols);
while ( pulPropTag-- > lpptaCols->aulPropTag )
{
lppropSrc = lprowSrc->lpProps + lprowSrc->cValues;
while ( lppropSrc-- > lprowSrc->lpProps )
if ( lppropSrc->ulPropTag == *pulPropTag )
{
cmb.ulcb += UlcbPropToCopy(lppropSrc);
break;
}
}
// Initialize the pointer to NULL in case the allocation fails
lprowDst->lpProps = NULL;
// Allocate the prop value array for those columns
if ( FAILED(sc = ScAllocateBuffer( lpvue,
cmb.ulcb + ulcCols * sizeof(SPropValue),
&lprowDst->lpProps)) )
{
DebugTrace(TEXT("ScCopyRow() - Error allocating row copy (SCODE = 0x%08lX)\n"), sc );
return sc;
}
MAPISetBufferName(lprowDst->lpProps, TEXT("ITable: one row"));
lprowDst->cValues = ulcCols;
cmb.lpv = lprowDst->lpProps + ulcCols;
// Copy the columns
pulPropTag = (ULONG *) (lpptaCols->aulPropTag + ulcCols);
lppropDst = lprowDst->lpProps + ulcCols;
while ( --pulPropTag, lppropDst-- > lprowDst->lpProps )
{
// Find the column in the source row
lppropSrc = lprowSrc->lpProps + lprowSrc->cValues;
while ( lppropSrc-- > lprowSrc->lpProps )
if ( lppropSrc->ulPropTag == *pulPropTag )
{
// Copy it into the dest row
SideAssert( PropCopyMore(lppropDst,
lppropSrc,
(LPALLOCATEMORE) ScBufAllocateMore,
&cmb) == S_OK );
goto next_column;
}
// No corresponding column -->
// Copy a null property for this column
//
// Yeah, we want to do this, but we don't want to do this
// on PR_NULL properties!
//
if (*pulPropTag != PR_NULL)
{
lppropDst->ulPropTag = PROP_TAG(PT_ERROR,PROP_ID(*pulPropTag));
lppropDst->Value.err = MAPI_E_NOT_FOUND;
}
else
lppropDst->ulPropTag = PR_NULL;
next_column:
;
}
return S_OK;
}
/*============================================================================
- PlprowByLprow()
-
* Returns a pointer to where the specified row is in the given row
* set. Note! This simply compares pointers to rows for equality.
*
*
* Parameters:
* ulcRows in Count of rows in row set.
* rglprows in Row set.
* lprow in Row to collate.
*/
LPSRow *
PlprowByLprow( ULONG ulcRows,
LPSRow * rglprows,
LPSRow lprow )
{
LPSRow * plprow = NULL;
for (plprow = rglprows; ulcRows ; ulcRows--, plprow++ )
{
if (*plprow == lprow)
return plprow;
}
return NULL;
}
/*============================================================================
- PlprowCollateRow()
-
* Returns a pointer to where the specified row collates in the
* specified row set according to the specified sort order set.
* A NULL sort order set implies collating at the end of the row set.
*
*
* Parameters:
* ulcRows in Count of rows in row set.
* rglprows in Row set.
* lpsos in Sort order to collate on.
* fAfterExisting in True if the row is to go after a range of
* equal rows. False means before the range.
* lprow in Row to collate.
*/
LPSRow *
PlprowCollateRow(
ULONG ulcRows,
LPSRow * rglprows,
LPSSortOrderSet lpsos,
BOOL fAfterExisting,
LPSRow lprow )
{
LPSRow * plprowMic = rglprows;
LPSRow * plprowMac = rglprows + ulcRows;
LPSRow * plprow;
LPSSortOrder lpso;
LPSPropValue lpprop1;
LPSPropValue lpprop2;
LONG lResult;
ULONG i = 0;
// If no sort order, collate at the end
if ( !lpsos )
return rglprows + ulcRows;
// Otherwise, collate the row according to the specified sort order
// using a binary search through the rows
// Start by checking the last row. This is to speed up the case where
// the rows added are already in sort order.
plprow = plprowMac - 1;
while ( plprowMic < plprowMac )
{
lResult = 0;
lpso = lpsos->aSort;
for (i=0;!lResult && i<lpsos->cSorts;i++)
{
lpprop1 = LpSPropValueFindColumn(lprow, lpso[i].ulPropTag);
lpprop2 = LpSPropValueFindColumn(*plprow, lpso[i].ulPropTag);
if ( lpprop1 && lpprop2 )
{
// If both the row to be collated and the row being
// checked against have values for this sort column,
// compare the two to determine their relative positions
lResult = LPropCompareProp(lpprop1, lpprop2);
}
else
{
// Either one or both rows don't have values for this
// sort column, so the relative position is determined
// by which row (if any) does have a value.
lResult = (LONG) (lpprop2 - lpprop1);
}
// If sorting in reverse order, flip the sense of the comparison
if ( lpso[i].ulOrder == TABLE_SORT_DESCEND )
lResult = -lResult;
}
if ( (lResult > 0) || (!lResult && fAfterExisting) )
{
// Row collates after this row
plprowMic = plprow + 1;
}
else
{
// Row collates before this row
plprowMac = plprow;
}
plprow = (plprowMac - plprowMic) / 2 + plprowMic;
}
return plprowMic;
}
/*============================================================================
- UNKOBJ_ScDupRestriction()
-
* For use with IMAPITable::Restrict(). Makes a copy of the specified
* restriction using MAPI linked memory. NULL restrictions are
* copied trivially.
*
* //$BUG ScDupRestriction() calls ScDupRestrictionMore() which is
* //$BUG recursive.
*
*
* Parameters:
* lpunkobj in UNKOBJ with instance variable containing
* MAPI allocators.
* lpres in Restriction to copy.
* lplpresCopy out Pointer to copied restriction.
*/
SCODE
ScDupRestriction(
LPUNKOBJ lpunkobj,
LPSRestriction lpres,
LPSRestriction FAR * lplpresCopy )
{
LPSRestriction lpresCopy;
SCODE sc = S_OK;
// Duping a NULL restriction is easy....
if ( !lpres )
{
*lplpresCopy = NULL;
goto ret;
}
// Allocate space for a more complicated restriction
if ( FAILED(sc = ScAllocateBuffer( lpunkobj,
sizeof(SRestriction),
&lpresCopy)) )
{
DebugTrace(TEXT("UNKOBJ::ScDupRestriction() - Error allocating restriction copy (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
MAPISetBufferName(lpresCopy, TEXT("ITable: copy of restriction"));
// And copy it.
if ( FAILED(sc = ScDupRestrictionMore( lpunkobj,
lpres,
lpresCopy,
lpresCopy )) )
{
DebugTrace(TEXT("UNKOBJ_ScDupRestriction() - Error duping complex restriction (SCODE = 0x%08lX)\n"), sc );
ScFreeBuffer(lpunkobj, lpresCopy);
goto ret;
}
*lplpresCopy = lpresCopy;
ret:
return sc;
}
/*============================================================================
- ScDupRestrictionMore()
-
* For use with IMAPITable::Restrict(). Makes a copy of the specified
* restriction using MAPI linked memory.
*
* //$BUG ScDupRestrictionMore() is recursive.
*
*
* Parameters:
* lpunkobj in UNKOBJ with instance variable containing
* MAPI allocators.
* lpresSrc in Restriction to copy.
* lpvLink in Pointer to buffer to which copied restriction
* should be linked.
* lplpresDst out Pointer to copied restriction.
*/
SCODE
ScDupRestrictionMore(
LPUNKOBJ lpunkobj,
LPSRestriction lpresSrc,
LPVOID lpvLink,
LPSRestriction lpresDst )
{
SCODE sc = S_OK;
switch ( lpresDst->rt = lpresSrc->rt )
{
// 'AND' restrictions and 'OR' restrictions have
// similar structures, so they can share code
// to copy them.
//
// 'SUB' is about the same as well, only where 'OR' and 'AND'
// have a count, it has a subobject. The copy works fine if
// you use a count of 1 for the copy, since the "cRes" member
// of 'AND' and 'OR' is the same size as the "ulSubObject"
// member of 'SUBRESTRICTION'.
case RES_AND:
case RES_OR:
case RES_SUBRESTRICTION:
{
LONG liRes;
lpresDst->res.resAnd.cRes = liRes = lpresSrc->res.resAnd.cRes;
if (lpresDst->rt == RES_SUBRESTRICTION)
liRes = 1;
if ( FAILED(sc = ScAllocateMore(
lpunkobj,
liRes * sizeof(SRestriction),
lpvLink,
&lpresDst->res.resAnd.lpRes)) )
{
DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error allocating 'AND' or 'OR' restriction list (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
while ( --liRes >= 0 )
{
if ( FAILED(sc = ScDupRestrictionMore(
lpunkobj,
lpresSrc->res.resAnd.lpRes + liRes,
lpvLink,
lpresDst->res.resAnd.lpRes + liRes)) )
{
DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error duping 'AND' or 'OR' restriction (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
}
goto ret;
}
case RES_NOT:
case RES_COMMENT:
{
ULONG cValues;
// Assert that we can use common code to DUP restriction.
Assert( offsetof(SCommentRestriction, lpRes)
== offsetof(SNotRestriction, lpRes));
if (FAILED(sc = ScAllocateMore(
lpunkobj,
sizeof(SRestriction),
lpvLink,
&lpresDst->res.resComment.lpRes)))
{
DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error allocating 'COMMENT' restriction (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
if ( FAILED(sc = ScDupRestrictionMore(
lpunkobj,
lpresSrc->res.resComment.lpRes,
lpvLink,
lpresDst->res.resComment.lpRes)) )
{
DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error duping 'COMMENT' restriction (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
// Dup the Prop Value array for COMMENT restrictions
if (lpresDst->rt == RES_COMMENT)
{
lpresDst->res.resComment.cValues =
lpresSrc->res.resComment.cValues;
if ( FAILED(sc = ScAllocateMore(
lpunkobj,
sizeof(SPropValue)*
lpresSrc->res.resComment.cValues,
lpvLink,
&lpresDst->res.resComment.lpProp)) )
{
DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error allocating 'COMMENT' property (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
for(cValues=0;cValues<lpresSrc->res.resComment.cValues;cValues++)
{
if ( FAILED(sc = PropCopyMore(
lpresDst->res.resComment.lpProp + cValues,
lpresSrc->res.resComment.lpProp + cValues,
lpunkobj->pinst->lpfAllocateMore,
lpvLink )) )
{
DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error duping 'COMMENT' restriction prop val (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
}
}
goto ret;
}
// 'CONTENT' and 'PROPERTY' restrictions have
// similar structures, so they can share code
// to copy them
case RES_CONTENT:
case RES_PROPERTY:
{
lpresDst->res.resContent.ulFuzzyLevel = lpresSrc->res.resContent.ulFuzzyLevel;
lpresDst->res.resContent.ulPropTag = lpresSrc->res.resContent.ulPropTag;
if ( FAILED(sc = ScAllocateMore(
lpunkobj,
sizeof(SPropValue),
lpvLink,
&lpresDst->res.resContent.lpProp)) )
{
DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error allocating 'CONTENT' or 'PROPERTY' property (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
if ( FAILED(sc = PropCopyMore(
lpresDst->res.resContent.lpProp,
lpresSrc->res.resContent.lpProp,
lpunkobj->pinst->lpfAllocateMore,
lpvLink )) )
{
DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error duping 'CONTENT' or 'PROPERTY' restriction prop val (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
goto ret;
}
// Each of these restrictions contain no pointers in their
// structures, so they can be copied all the same way
// by copying the SRestriction union itself
case RES_COMPAREPROPS:
case RES_BITMASK:
case RES_SIZE:
case RES_EXIST:
{
*lpresDst = *lpresSrc;
goto ret;
}
default:
{
TrapSz( TEXT("ITABLE:ScDupRestrictionMore - Bad restriction type"));
}
}
ret:
return sc;
}
/*============================================================================
- ScDupRgbEx()
-
* General utility for copying a hunk of bytes using MAPI allocated
* memory. Useful in IMAPITable::SetColumns/QueryColumns to copy
* column sets and in IMAPITable::SortTable/QuerySortOrder to copy
* sort orders.
*
*
* Parameters:
* lpunkobj in UNKOBJ with instance variable containing
* MAPI allocators.
* ulcb in Count of bytes to copy.
* lpb in Buffer to copy from.
* lplpbCopy out Pointer to allocated buffer copied to.
*/
SCODE
ScDupRgbEx(
LPUNKOBJ lpunkobj,
ULONG ulcb,
LPBYTE lpb,
ULONG ulcbExtra,
LPBYTE FAR * lplpbCopy )
{
SCODE sc = S_OK;
if ( FAILED(sc = ScAllocateBuffer(lpunkobj, ulcb + ulcbExtra, lplpbCopy)) )
{
goto ret;
}
CopyMemory(*lplpbCopy, lpb, (size_t) ulcb);
ret:
return sc;
}
/*============================================================================
- ScSatisfiesRestriction()
-
* Determines if the specified row satisfies the specified restriction
*
* //$BUG This function is recursive....
*
*
* Paremeters:
* lprow in Row to check.
* lpres in Restriction to check against.
* pfSatisfies out Pointer to bool which is the result of the
* test; TRUE if the row satisfies the
* restriction, FALSE otherwise.
*/
SCODE
ScSatisfiesRestriction(
LPSRow lprow,
LPSRestriction lpres,
ULONG * pfSatisfies )
{
SCODE sc = S_OK;
// Empty restrictions are trivial....
if ( !lpres )
{
*pfSatisfies = TRUE;
goto ret;
}
*pfSatisfies = FALSE;
switch ( lpres->rt )
{
case RES_AND:
{
ULONG uliRes;
for ( uliRes = 0;
uliRes < lpres->res.resAnd.cRes;
uliRes++ )
{
if ( FAILED(sc = ScSatisfiesRestriction(
lprow,
lpres->res.resAnd.lpRes + uliRes,
pfSatisfies)) )
{
DebugTrace(TEXT("ScSatisfiesRestriction() - Error evaluating 'AND' restriction (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
if ( !*pfSatisfies )
break;
}
goto ret;
}
case RES_OR:
{
ULONG uliRes;
for ( uliRes = 0;
uliRes < lpres->res.resOr.cRes;
uliRes++ )
{
if ( FAILED(sc = ScSatisfiesRestriction(
lprow,
lpres->res.resOr.lpRes + uliRes,
pfSatisfies)) )
{
DebugTrace(TEXT("ScSatisfiesRestriction() - Error evaluating 'OR' restriction (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
if ( *pfSatisfies )
break;
}
goto ret;
}
case RES_COMMENT:
case RES_NOT:
{
// Assert that we can use common code to eval restriction.
Assert( offsetof(SCommentRestriction, lpRes)
== offsetof(SNotRestriction, lpRes));
if ( FAILED(sc = ScSatisfiesRestriction(
lprow,
lpres->res.resNot.lpRes,
pfSatisfies)) )
{
DebugTrace(TEXT("ScSatisfiesRestriction() - Error evaulating 'NOT'or 'COMMENT' restriction (SCODE = 0x%08lX)\n"), sc );
goto ret;
}
if (lpres->rt == RES_NOT)
{
*pfSatisfies = !*pfSatisfies;
}
goto ret;
}
case RES_CONTENT:
{
LPSPropValue lpprop;
lpprop = LpSPropValueFindColumn(lprow, lpres->res.resContent.ulPropTag);
*pfSatisfies = lpprop ?
FPropContainsProp(
lpprop,
lpres->res.resContent.lpProp,
lpres->res.resContent.ulFuzzyLevel) :
FALSE;
goto ret;
}
case RES_PROPERTY:
{
LPSPropValue lpprop;
*pfSatisfies = FALSE;
// Special case for PR_ANR
if(lpres->res.resProperty.ulPropTag == PR_ANR_A || lpres->res.resProperty.ulPropTag == PR_ANR_W)
{
BOOL bUnicode = (PROP_TYPE(lpres->res.resProperty.ulPropTag) == PT_UNICODE);
// First check the display name
lpprop = LpSPropValueFindColumn(lprow, bUnicode ? PR_DISPLAY_NAME_W : PR_DISPLAY_NAME_A);
if(lpprop)
{
LPTSTR lpT = bUnicode ? lpprop->Value.lpszW : ConvertAtoW(lpprop->Value.lpszA);
LPTSTR lpS = bUnicode ? lpres->res.resProperty.lpProp->Value.lpszW :
ConvertAtoW(lpres->res.resProperty.lpProp->Value.lpszA);
// Do a fuzzy search on this property's string value
*pfSatisfies = SubstringSearch( lpT, lpS );
if(!bUnicode)
{
LocalFreeAndNull(&lpT);
LocalFreeAndNull(&lpS);
}
}
if(!*pfSatisfies)
{
//Display name not found or not matched .. check e-mail address
lpprop = LpSPropValueFindColumn(lprow, bUnicode ? PR_EMAIL_ADDRESS_W : PR_EMAIL_ADDRESS_A);
if(lpprop)
{
LPTSTR lpT = bUnicode ? lpprop->Value.lpszW : ConvertAtoW(lpprop->Value.lpszA);
LPTSTR lpS = bUnicode ? lpres->res.resProperty.lpProp->Value.lpszW :
ConvertAtoW(lpres->res.resProperty.lpProp->Value.lpszA);
// Do a fuzzy search on this property's string value
*pfSatisfies = SubstringSearch( lpT, lpS );
if(!bUnicode)
{
LocalFreeAndNull(&lpT);
LocalFreeAndNull(&lpS);
}
}
}
}
else
{
lpprop = LpSPropValueFindColumn(lprow, lpres->res.resProperty.ulPropTag);
if(lpprop)
{
*pfSatisfies = FPropCompareProp(
lpprop,
lpres->res.resProperty.relop,
lpres->res.resProperty.lpProp);
}
}
goto ret;
}
case RES_COMPAREPROPS:
{
LPSPropValue lpprop1;
LPSPropValue lpprop2;
lpprop1 = LpSPropValueFindColumn(lprow, lpres->res.resCompareProps.ulPropTag1);
lpprop2 = LpSPropValueFindColumn(lprow, lpres->res.resCompareProps.ulPropTag2);
*pfSatisfies = (lpprop1 && lpprop2) ?
FPropCompareProp(
lpprop1,
lpres->res.resCompareProps.relop,
lpprop2) :
FALSE;
goto ret;
}
case RES_BITMASK:
{
LPSPropValue lpprop;
lpprop = LpSPropValueFindColumn(lprow, lpres->res.resBitMask.ulPropTag);
*pfSatisfies = lpprop ?
((ULONG) !!(lpprop->Value.l &
lpres->res.resBitMask.ulMask) ==
lpres->res.resBitMask.relBMR) :
FALSE;
goto ret;
}
case RES_SIZE:
{
LPSPropValue lpprop;
LONG ldcb;
*pfSatisfies = FALSE;
if ( (lpprop = LpSPropValueFindColumn(
lprow,
lpres->res.resSize.ulPropTag)) != NULL )
{
ldcb = (LONG) lpres->res.resSize.cb - (LONG) UlPropSize(lpprop);
switch (lpres->res.resSize.relop)
{
case RELOP_LT:
*pfSatisfies = (0 < ldcb);
break;
case RELOP_LE:
*pfSatisfies = (0 <= ldcb);
break;
case RELOP_GT:
*pfSatisfies = (0 > ldcb);
break;
case RELOP_GE:
*pfSatisfies = (0 >= ldcb);
break;
case RELOP_EQ:
*pfSatisfies = (0 == ldcb);
break;
case RELOP_NE:
*pfSatisfies = (0 != ldcb);
break;
}
}
goto ret;
}
case RES_EXIST:
{
*pfSatisfies = !!LpSPropValueFindColumn(
lprow,
lpres->res.resExist.ulPropTag);
goto ret;
}
case RES_SUBRESTRICTION:
{
sc = MAPI_E_TOO_COMPLEX;
goto ret;
}
default:
{
TrapSz( TEXT("ITABLE:ScSatisfiesRestriction - Bad restriction type"));
}
}
ret:
return sc;
}
/*============================================================================
- LpSPropValueFindColumn()
-
* Utility function to find a column in a row given its proptag.
* NOTE! This function compares the entire prop tag. PROP_TYPEs MUST
* match!
* Returns:
* a pointer to the column found or, if the row doesn't contain the
* specified column, returns NULL.
*/
LPSPropValue __fastcall
LpSPropValueFindColumn(
LPSRow lprow,
ULONG ulPropTagColumn )
{
ULONG i = 0;
for (i=0;i<lprow->cValues;i++)
{
if((lprow->lpProps)[i].ulPropTag == ulPropTagColumn)
return (&(lprow->lpProps[i]));
}
/* if(ulPropTagColumn == PR_ANR)
{
// This is a special case table restriction
if( lpprop->ulPropTag == PR_DISPLAY_NAME ||
lpprop->ulPropTag == PR_EMAIL_ADDRESS )
return lpprop;
}
else
*/
return NULL;
}
/*============================================================================
- ScBufAllocateMore()
-
* MAPIAllocateMore-compatible function to use with proputil's
* PropCopyMore when copying into an already allocated buffer.
* It avoids having PropCopyMore calling MAPIAllocateMore (which
* continually allocates memory from the system) resulting
* in faster copying at the expense of running through the properties
* to copy once first to determine the amount of additional memory
* needed to copy them (see UlcbPropToCopy() below).
*/
STDMETHODIMP_(SCODE)
ScBufAllocateMore(
ULONG ulcb,
LPCMB lpcmb,
LPVOID FAR * lplpv )
{
ulcb = LcbAlignLcb(ulcb);
if ( ulcb > lpcmb->ulcb )
{
TrapSz( TEXT("ScBufAllocateMore() - Buffer wasn't big enough for allocations\n") );
return MAPI_E_NOT_ENOUGH_MEMORY;
}
*((UNALIGNED LPVOID FAR*) lplpv) = lpcmb->lpv;
(LPBYTE) lpcmb->lpv += ulcb;
lpcmb->ulcb -= ulcb;
return S_OK;
}
/*============================================================================
- UlcbPropToCopy()
-
* Not to be confused with UlPropSize in proputil!
*
* UlcbPropToCopy() returns the size, in bytes, needed to store the value
* portion of a prop value not including the size of the SPropValue
* structure itself plus any necessary alignment padding.
* E.g. A PT_I2 property would always have a size equal to
* sizeof(SPropValue); a PT_BINARY property would have a size
* equal to sizeof(SPropValue) + ALIGN(Value.bin.cb).
*/
ULONG
UlcbPropToCopy( LPSPropValue lpprop )
{
ULONG ulcb = 0;
LPVOID lpv;
UNALIGNED LPWSTR FAR * lplpwstr = NULL;
switch ( PROP_TYPE(lpprop->ulPropTag) )
{
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:
return 0;
case PT_CLSID:
return LcbAlignLcb(sizeof(CLSID));
case PT_BINARY:
return LcbAlignLcb(lpprop->Value.bin.cb);
case PT_STRING8:
return LcbAlignLcb((lstrlenA(lpprop->Value.lpszA)+1) *
sizeof(CHAR));
#ifndef WIN16
case PT_UNICODE:
// ((UNALIGNED LPWSTR *) lpv1) = &(lpprop->Value.lpszW);
ulcb = (ULONG) lstrlenW((LPWSTR) lpprop->Value.lpszW);
return LcbAlignLcb((ulcb + 1) * sizeof(WCHAR));
#endif // !WIN16
case PT_MV_I2:
ulcb = sizeof(short int);
break;
case PT_MV_LONG:
ulcb = sizeof(LONG);
break;
case PT_MV_R4:
ulcb = sizeof(float);
break;
case PT_MV_APPTIME:
case PT_MV_DOUBLE:
ulcb = sizeof(double);
break;
case PT_MV_CURRENCY:
ulcb = sizeof(CURRENCY);
break;
case PT_MV_SYSTIME:
ulcb = sizeof(FILETIME);
break;
case PT_MV_I8:
ulcb = sizeof(LARGE_INTEGER);
break;
case PT_MV_BINARY:
{
ulcb = lpprop->Value.MVbin.cValues * sizeof(SBinary);
lpv = lpprop->Value.MVbin.lpbin + lpprop->Value.MVbin.cValues;
while ( ((SBinary FAR *)lpv)-- > lpprop->Value.MVbin.lpbin )
ulcb += LcbAlignLcb(((SBinary FAR *)lpv)->cb);
return LcbAlignLcb(ulcb);
}
case PT_MV_STRING8:
{
ulcb = sizeof(LPSTR) * lpprop->Value.MVszA.cValues;
lpv = lpprop->Value.MVszA.lppszA + lpprop->Value.MVszA.cValues;
while ( ((LPSTR FAR *)lpv)-- > lpprop->Value.MVszA.lppszA )
ulcb += (lstrlenA(*(LPSTR FAR *)lpv)+1) * sizeof(CHAR);
return LcbAlignLcb(ulcb);
}
#ifndef WIN16
case PT_MV_UNICODE:
{
ulcb = sizeof(LPWSTR) * lpprop->Value.MVszW.cValues;
// lpv1 = lpprop->Value.MVszW.lppszW;
lplpwstr = lpprop->Value.MVszW.lppszW;
lplpwstr += lpprop->Value.MVszW.cValues;
while (lplpwstr-- > ((UNALIGNED LPWSTR * ) lpprop->Value.MVszW.lppszW) )
ulcb += (lstrlenW(*lplpwstr)+1) * sizeof(WCHAR);
return LcbAlignLcb(ulcb);
}
#endif // !WIN16
}
// For multi-valued arrays of constant-size objects...
return lpprop->Value.MVi.cValues * LcbAlignLcb(ulcb);
}
/*============================================================================
- FRowContainsProp()
-
* Determines whether or not the given row contains and matches the given
* property value array.
*
* Returns TRUE if the row contains and matches all the propery values
*
* Parameters:
* lprow in Row to examine
* cValues in number of property values
* lpsv in property value array to be compared
*/
BOOL
FRowContainsProp(
LPSRow lprow,
ULONG cValues,
LPSPropValue lpsv)
{
ULONG uliProp;
LPSPropValue lpsvT;
Assert(lprow);
Assert(lpsv);
for (uliProp=0; uliProp < cValues; uliProp++)
{
// find the column with the same property tag
lpsvT=LpSPropValueFindColumn(lprow,lpsv[uliProp].ulPropTag);
if (lpsvT==NULL)
return FALSE;
// do the properties match?
if (LPropCompareProp(lpsvT,&lpsv[uliProp])!=0)
return FALSE;
}
return TRUE;
}
/*============================================================================
- FBookmarkStale()
-
* If a bookmark is Moving this function determines its uliRow and
* returns FALSE.
*
* If a bookmark has a uliRow that is too big then it is adjusted to
* point to the end of the table and marked as Changed. FALSE is returned.
*
* If a bookmark is marked as Stale or can't be adjusted then it
* is remarked as Stale and TRUE is returned
*
* Parameters:
* lpvue in View to check bookmark against
* bk in BOOKMARK to check
*/
BOOL
FBookMarkStale( LPVUE lpvue,
BOOKMARK bk)
{
PBK pbk;
LPSRow * plprowBk;
// bk too big should already have been caught!
Assert( bk < cBookmarksMax);
pbk = lpvue->rgbk + bk;
if (pbk->dwfBKS & dwfBKSStale)
{
return TRUE;
}
if ( !(pbk->dwfBKS & dwfBKSMoving)
&& (pbk->uliRow > lpvue->bkEnd.uliRow))
{
pbk->uliRow = lpvue->bkEnd.uliRow;
}
else if (plprowBk = PlprowByLprow( lpvue->bkEnd.uliRow
, lpvue->parglprows
, pbk->lprow))
{
pbk->uliRow = (ULONG) (plprowBk - lpvue->parglprows);
pbk->dwfBKS &= ~dwfBKSMoving;
}
else if (pbk->dwfBKS & dwfBKSMoving)
{
TrapSz( TEXT("Moving bookmark lost its row.\n"));
pbk->dwfBKS = dwfBKSValid | dwfBKSStale;
return TRUE;
}
return FALSE;
}
#ifdef WIN16 // Imported INLINE function.
/*============================================================================
- FFindColumn()
-
* Checks a prop tag array to see if a given prop tag exists.
*
* NOTE! The prop tag must match completely (even type).
*
*
* Parameters:
* lpptaCols in Prop tag array to check
* ulPropTag in Prop tag to check for.
*
* Returns:
* TRUE if ulPropTag is in lpptaCols
* FALSE if ulPropTag is not in lpptaCols
*/
BOOL
FFindColumn( LPSPropTagArray lpptaCols,
ULONG ulPropTag )
{
ULONG * pulPropTag;
pulPropTag = lpptaCols->aulPropTag + lpptaCols->cValues;
while ( --pulPropTag >= lpptaCols->aulPropTag )
if ( *pulPropTag == ulPropTag )
return TRUE;
return FALSE;
}
/*============================================================================
- ScFindRow()
-
* Finds the first row in the table data whose index column property
* value is equal to that of the specified property and returns the
* location of that row in the table data, or, if no such row exists,
* the end of the table data.
*
* Parameters:
* lptad in TAD in which to find row
* lpprop in Index property to match
* puliRow out Pointer to location of found row
*
* Error returns:
* MAPI_E_INVALID_PARAMETER If proptag of property isn't the TAD's
* index column's proptag.
* MAPI_E_NOT_FOUND If no matching row is found (*pplprow
* is set to lptad->parglprows +
* lptad->cRows in this case).
*/
SCODE
ScFindRow(
LPTAD lptad,
LPSPropValue lpprop,
LPSRow * * pplprow)
{
SCODE sc = S_OK;
SRow row;
SizedSSortOrderSet(1, sosIndex) = { 1, 0, 0 };
row.ulAdrEntryPad = 0;
row.cValues = 1;
row.lpProps = lpprop;
if (lpprop->ulPropTag != lptad->ulPropTagIndexCol)
{
sc = MAPI_E_INVALID_PARAMETER;
goto ret;
}
Assert(!IsBadWritePtr(pplprow, sizeof(*pplprow)));
// Build a sort order set for the Index Column
sosIndex.aSort[0].ulPropTag = lptad->ulPropTagIndexCol;
sosIndex.aSort[0].ulOrder = TABLE_SORT_ASCEND;
*pplprow = PlprowCollateRow(lptad->ulcRowsIndex,
lptad->parglprowIndex,
(LPSSortOrderSet) &sosIndex,
FALSE,
&row);
// Find the row in the Index Sorted Row Set
if ( !lptad->ulcRowsIndex
|| (*pplprow >= (lptad->parglprowIndex + lptad->ulcRowsIndex))
|| LPropCompareProp( lpprop, (**pplprow)->lpProps))
{
sc = MAPI_E_NOT_FOUND;
}
ret:
return sc;
}
#endif // WIN16
/*
-
- FixupCols
*
*/
void FixupColsWA(LPSPropTagArray lpptaCols, BOOL bUnicodeTable)
{
if(!bUnicodeTable) //<note> assumes UNICODE is defined
{
// We need to mark the table columns as not having UNICODE props
ULONG i = 0;
for(i = 0;i<lpptaCols->cValues;i++)
{
switch(PROP_TYPE(lpptaCols->aulPropTag[i]))
{
case PT_UNICODE:
lpptaCols->aulPropTag[i] = CHANGE_PROP_TYPE(lpptaCols->aulPropTag[i], PT_STRING8);
break;
case PT_MV_UNICODE:
lpptaCols->aulPropTag[i] = CHANGE_PROP_TYPE(lpptaCols->aulPropTag[i], PT_MV_STRING8);
break;
}
}
}
}