|
|
//---------------------------------------------------------------------------
// Cursor.cpp : Cursor implementation
//
// Copyright (c) 1996 Microsoft Corporation, All Rights Reserved
// Developed by Sheridan Software Systems, Inc.
//---------------------------------------------------------------------------
#include "stdafx.h"
#include "Notifier.h"
#include "RSColumn.h"
#include "RSSource.h"
#include "CursMain.h"
#include "ColUpdat.h"
#include "CursPos.h"
#include "enumcnpt.h"
#include "CursBase.h"
#include "Cursor.h"
#include "CursMeta.h"
#include "EntryID.h"
#include "Stream.h"
#include "fastguid.h"
#include "resource.h"
#include "NConnPt.h"
#include "NConnPtC.h"
#include "FromVar.h"
#include "timeconv.h"
SZTHISFILE
//=--------------------------------------------------------------------------=
// CVDCursor - Constructor
//
CVDCursor::CVDCursor() { m_hAccessor = 0; m_hVarHelper = 0; m_ulVarBindings = 0; m_rghVarAccessors = NULL; m_rghAdjustAccessors = NULL; m_pdwAdjustFlags = NULL; m_ppColumns = NULL;
m_pCursorPosition = NULL; m_pConnPtContainer = NULL;
#ifdef _DEBUG
g_cVDCursorCreated++; #endif
}
//=--------------------------------------------------------------------------=
// ~CVDCursor - Destructor
//
CVDCursor::~CVDCursor() { DestroyAccessors(); DestroyColumns();
if (m_pCursorPosition->GetSameRowClone()) m_pCursorPosition->ReleaseSameRowClone();
LeaveFamily(); // leave m_pCursorPosition's notification family
if (m_pConnPtContainer) m_pConnPtContainer->Destroy();
if (m_pCursorPosition) ((CVDNotifier*)m_pCursorPosition)->Release(); // release associated cursor position object
#ifdef _DEBUG
g_cVDCursorDestroyed++; #endif
}
//=--------------------------------------------------------------------------=
// GetRowsetColumn - Get rowset column from ordinal
//=--------------------------------------------------------------------------=
// This function retrieves the rowset column with the specified rowset ordinal
//
// Parameters:
// ulOrdinal - [in] rowset ordinal
//
// Output:
// CVDRowsetColumn pointer
//
// Notes:
//
CVDRowsetColumn * CVDCursor::GetRowsetColumn(ULONG ulOrdinal) { CVDRowsetColumn * pRowsetColumn = NULL;
ULONG ulColumns = GetCursorMain()->GetColumnsCount(); CVDRowsetColumn * pColumn = GetCursorMain()->InternalGetColumns();
for (ULONG ulCol = 0; ulCol < ulColumns && !pRowsetColumn; ulCol++) { if (pColumn->GetOrdinal() == ulOrdinal) pRowsetColumn = pColumn;
pColumn++; }
return pRowsetColumn; }
//=--------------------------------------------------------------------------=
// GetRowsetColumn - Get rowset column from cursor column identifier
//=--------------------------------------------------------------------------=
// This function retrieves the rowset column associated with the specified
// cursor column identifier
//
// Parameters:
// cursorColumnID - [in] a reference to cursor column identifier
//
// Output:
// CVDRowsetColumn pointer
//
// Notes:
//
CVDRowsetColumn * CVDCursor::GetRowsetColumn(CURSOR_DBCOLUMNID& cursorColumnID) { CVDRowsetColumn * pRowsetColumn = NULL;
ULONG ulColumns = GetCursorMain()->GetColumnsCount(); CVDRowsetColumn * pColumn = GetCursorMain()->InternalGetColumns();
for (ULONG ulCol = 0; ulCol < ulColumns && !pRowsetColumn; ulCol++) { if (IsEqualCursorColumnID(cursorColumnID, pColumn->GetCursorColumnID())) pRowsetColumn = pColumn;
pColumn++; }
return pRowsetColumn; }
//=--------------------------------------------------------------------------=
// GetOrdinal - Get ordinal from cursor column identifier
//=--------------------------------------------------------------------------=
// This function converts a cursor column identifier its ordinal eqivalent
//
// Parameters:
// cursorColumnID - [in] a reference to cursor column identifier
// pulOrdinal - [out] a pointer to memory in which to return ordinal
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL bad cursor column identifier
//
// Notes:
//
HRESULT CVDCursor::GetOrdinal(CURSOR_DBCOLUMNID& cursorColumnID, ULONG * pulOrdinal) { HRESULT hr = E_FAIL;
ULONG ulColumns = GetCursorMain()->GetColumnsCount(); CVDRowsetColumn * pColumn = GetCursorMain()->InternalGetColumns();
for (ULONG ulCol = 0; ulCol < ulColumns && FAILED(hr); ulCol++) { if (IsEqualCursorColumnID(cursorColumnID, pColumn->GetCursorColumnID())) { *pulOrdinal = pColumn->GetOrdinal(); hr = S_OK; }
pColumn++; }
return hr; }
//=--------------------------------------------------------------------------=
// StatusToCursorInfo - Get cursor info from rowset status field
//=--------------------------------------------------------------------------=
// This function converts a rowset status to its cursor information field
// eqivalent
//
// Parameters:
// dwStatus - [in] rowset status
//
// Output:
// DWORD - cursor information
//
// Notes:
// There are more rowset statuses than cursor information values
//
DWORD CVDCursor::StatusToCursorInfo(DBSTATUS dwStatus) { DWORD dwCursorInfo = CURSOR_DB_UNKNOWN;
switch (dwStatus) { case DBSTATUS_S_OK: dwCursorInfo = CURSOR_DB_NOINFO; break;
case DBSTATUS_E_CANTCONVERTVALUE: dwCursorInfo = CURSOR_DB_CANTCOERCE; break;
case DBSTATUS_S_ISNULL: dwCursorInfo = CURSOR_DB_NULL; break;
case DBSTATUS_S_TRUNCATED: dwCursorInfo = CURSOR_DB_TRUNCATED; break; }
return dwCursorInfo; }
//=--------------------------------------------------------------------------=
// CursorInfoToStatus - Get rowset status from cursor info field
//=--------------------------------------------------------------------------=
// This function converts a cursor information field to its rowset status
// eqivalent
//
// Parameters:
// dwInfo - [in] rowset status
//
// Output:
// DWORD - cursor information
//
// Notes:
// This function only converts successful cursor information fields for
// the purpose of setting data
//
DBSTATUS CVDCursor::CursorInfoToStatus(DWORD dwCursorInfo) { DBSTATUS dwStatus;
switch (dwCursorInfo) { case CURSOR_DB_NULL: dwStatus = DBSTATUS_S_ISNULL; break;
case CURSOR_DB_EMPTY: dwStatus = DBSTATUS_S_ISNULL; break;
case CURSOR_DB_TRUNCATED: dwStatus = DBSTATUS_S_TRUNCATED; break;
case CURSOR_DB_NOINFO: dwStatus = DBSTATUS_S_OK; break; }
return dwStatus; }
//=--------------------------------------------------------------------------=
// ValidateCursorBindParams - Validate cursor column binding parameters
//=--------------------------------------------------------------------------=
// This function makes sure the specified column binding parameters are
// acceptable and then returns a pointer to the corresponding rowset column
//
// Parameters:
// pCursorColumnID - [in] a pointer to column identifier of the
// column to bind
// pCursorBindParams - [in] a pointer to binding structure
// ppRowsetColumn - [out] a pointer to memory in which to return
// a pointer to the rowset column to bind
//
// Output:
// HRESULT - S_OK if successful
// CURSOR_DB_E_BADBINDINFO bad binding information
// CURSOR_DB_E_BADCOLUMNID columnID is not available
//
// Notes:
//
HRESULT CVDCursor::ValidateCursorBindParams(CURSOR_DBCOLUMNID * pCursorColumnID, CURSOR_DBBINDPARAMS * pCursorBindParams, CVDRowsetColumn ** ppRowsetColumn) { ASSERT_POINTER(pCursorColumnID, CURSOR_DBCOLUMNID) ASSERT_POINTER(pCursorBindParams, CURSOR_DBBINDPARAMS) ASSERT_POINTER(ppRowsetColumn, CVDRowsetColumn*)
// make sure we have all necessary pointers
if (!pCursorColumnID || !pCursorBindParams || !ppRowsetColumn) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorUpdateARow, m_pResourceDLL); return E_INVALIDARG; }
// init out parameter
*ppRowsetColumn = NULL;
// make sure column identifier is available
BOOL fColumnIDAvailable = FALSE;
DWORD dwCursorType; ULONG ulColumns = GetCursorMain()->GetColumnsCount(); CVDRowsetColumn * pColumns = GetCursorMain()->InternalGetColumns(); CVDRowsetColumn * pColumn = pColumns;
// iterate through rowset columns looking for match
for (ULONG ulCol = 0; ulCol < ulColumns && !fColumnIDAvailable; ulCol++) { if (IsEqualCursorColumnID(*pCursorColumnID, pColumn->GetCursorColumnID())) { dwCursorType = pColumn->GetCursorType(); *ppRowsetColumn = pColumn; fColumnIDAvailable = TRUE; }
pColumn++; }
// get out if not found
if (!fColumnIDAvailable) { VDSetErrorInfo(IDS_ERR_BADCOLUMNID, IID_ICursorUpdateARow, m_pResourceDLL); return CURSOR_DB_E_BADCOLUMNID; }
// make sure caller supplied a maximum length if a default binding was specified
// for the cursor types CURSOR_DBTYPE_CHARS, CURSOR_DBTYPE_WCHARS or CURSOR_DBTYPE_BYTES
if (pCursorBindParams->cbMaxLen == CURSOR_DB_NOMAXLENGTH && pCursorBindParams->dwBinding == CURSOR_DBBINDING_DEFAULT) { if (pCursorBindParams->dwDataType == CURSOR_DBTYPE_CHARS || pCursorBindParams->dwDataType == CURSOR_DBTYPE_WCHARS || pCursorBindParams->dwDataType == CURSOR_DBTYPE_BYTES) { VDSetErrorInfo(IDS_ERR_BADCURSORBINDINFO, IID_ICursorUpdateARow, m_pResourceDLL); return CURSOR_DB_E_BADBINDINFO; } }
// check binding bit mask for possible values
if (pCursorBindParams->dwBinding != CURSOR_DBBINDING_DEFAULT && pCursorBindParams->dwBinding != CURSOR_DBBINDING_VARIANT && pCursorBindParams->dwBinding != CURSOR_DBBINDING_ENTRYID && pCursorBindParams->dwBinding != (CURSOR_DBBINDING_VARIANT | CURSOR_DBBINDING_ENTRYID)) { VDSetErrorInfo(IDS_ERR_BADCURSORBINDINFO, IID_ICursorUpdateARow, m_pResourceDLL); return CURSOR_DB_E_BADBINDINFO; }
// check for valid cursor type
if (!IsValidCursorType(pCursorBindParams->dwDataType)) { VDSetErrorInfo(IDS_ERR_BADCURSORBINDINFO, IID_ICursorUpdateARow, m_pResourceDLL); return CURSOR_DB_E_BADBINDINFO; }
// if a variant binding was specified make sure the cursor type is not CURSOR_DBTYPE_CHARS,
// CURSOR_DBTYPE_WCHARS or CURSOR_DBTYPE_BYTES
if (pCursorBindParams->dwBinding & CURSOR_DBBINDING_VARIANT) { if (pCursorBindParams->dwDataType == CURSOR_DBTYPE_CHARS || pCursorBindParams->dwDataType == CURSOR_DBTYPE_WCHARS || pCursorBindParams->dwDataType == CURSOR_DBTYPE_BYTES) { VDSetErrorInfo(IDS_ERR_BADCURSORBINDINFO, IID_ICursorUpdateARow, m_pResourceDLL); return CURSOR_DB_E_BADBINDINFO; } }
// if its not a variant binding make sure the cursor type is not CURSOR_DBTYPE_ANYVARIANT
if (!(pCursorBindParams->dwBinding & CURSOR_DBBINDING_VARIANT) && pCursorBindParams->dwDataType == CURSOR_DBTYPE_ANYVARIANT) { VDSetErrorInfo(IDS_ERR_BADCURSORBINDINFO, IID_ICursorUpdateARow, m_pResourceDLL); return CURSOR_DB_E_BADBINDINFO; }
return S_OK; }
//=--------------------------------------------------------------------------=
// ValidateEntryID - Validate entry identifier
//=--------------------------------------------------------------------------=
// This function makes sure the specified enrty identifier is acceptable,
// if it is, then return rowset column and hRow associated with it
//
// Parameters:
// cbEntryID - [in] the size of the entryID
// pEntryID - [in] a pointer to the entryID
// ppColumn - [out] a pointer to memory in which to return rowset column pointer
// phRow - [out] a pointer to memory in which to return row handle
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
// CURSOR_DB_E_BADENTRYID bad entry identifier
//
HRESULT CVDCursor::ValidateEntryID(ULONG cbEntryID, BYTE * pEntryID, CVDRowsetColumn ** ppColumn, HROW * phRow) { ASSERT_POINTER(pEntryID, BYTE) ASSERT_POINTER(ppColumn, CVDRowsetColumn*) ASSERT_POINTER(phRow, HROW)
IRowsetLocate * pRowsetLocate = GetRowsetLocate();
// make sure we have a valid rowset locate pointer
if (!pRowsetLocate || !IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_IEntryID, m_pResourceDLL); return E_FAIL; }
// make sure we have all necessary pointers
if (!pEntryID || !ppColumn || !phRow) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_IEntryID, m_pResourceDLL); return E_INVALIDARG; }
// init out parameters
*ppColumn = NULL; *phRow = NULL;
// check length of enrtyID
if (cbEntryID != sizeof(ULONG) + sizeof(ULONG) + GetCursorMain()->GetMaxBookmarkLen()) { VDSetErrorInfo(IDS_ERR_BADENTRYID, IID_IEntryID, m_pResourceDLL); return CURSOR_DB_E_BADENTRYID; }
// extract column ordinal
ULONG ulOrdinal = *(ULONG*)pEntryID;
// make sure column ordinal is okay
BOOL fColumnOrdinalOkay = FALSE;
ULONG ulColumns = GetCursorMain()->GetColumnsCount(); CVDRowsetColumn * pColumn = GetCursorMain()->InternalGetColumns();
// iterate through rowset columns looking for match
for (ULONG ulCol = 0; ulCol < ulColumns && !fColumnOrdinalOkay; ulCol++) { if (ulOrdinal == pColumn->GetOrdinal()) fColumnOrdinalOkay = TRUE; else pColumn++; }
// if not found, get out
if (!fColumnOrdinalOkay) { VDSetErrorInfo(IDS_ERR_BADENTRYID, IID_IEntryID, m_pResourceDLL); return CURSOR_DB_E_BADENTRYID; }
// set column pointer
*ppColumn = pColumn;
// extract row bookmark
ULONG cbBookmark = *(ULONG*)(pEntryID + sizeof(ULONG)); BYTE * pBookmark = (BYTE*)pEntryID + sizeof(ULONG) + sizeof(ULONG);
// attempt to retrieve hRow from bookmark
HRESULT hr = pRowsetLocate->GetRowsByBookmark(0, 1, &cbBookmark, (const BYTE**)&pBookmark, phRow, NULL);
if (FAILED(hr)) VDSetErrorInfo(IDS_ERR_BADENTRYID, IID_IEntryID, m_pResourceDLL);
return hr; }
//=--------------------------------------------------------------------------=
// QueryEntryIDInterface - Get specified interface for entry identifier
//=--------------------------------------------------------------------------=
// This function attempts to get the requested interface from the specified
// column row
//
// Parameters:
// pColumn - [in] rowset column pointer
// hRow - [in] the row handle
// dwFlags - [in] interface specific flags
// riid - [in] interface identifier requested
// ppUnknown - [out] a pointer to memory in which to return interface pointer
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL error occured
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory
// E_NOINTERFACE interface not available
//
HRESULT CVDCursor::QueryEntryIDInterface(CVDRowsetColumn * pColumn, HROW hRow, DWORD dwFlags, REFIID riid, IUnknown ** ppUnknown) { ASSERT_POINTER(pColumn, CVDRowsetColumn) ASSERT_POINTER(ppUnknown, IUnknown*)
IRowset * pRowset = GetRowset(); IAccessor * pAccessor = GetAccessor();
// make sure we have valid rowset and accessor pointers
if (!pRowset || !pAccessor || !IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_IEntryID, m_pResourceDLL); return E_FAIL; }
// make sure we have all necessay pointers
if (!pColumn || !ppUnknown) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_IEntryID, m_pResourceDLL); return E_INVALIDARG; }
// init out parameters
*ppUnknown = NULL;
DBOBJECT object; DBBINDING binding;
// clear out binding
memset(&binding, 0, sizeof(DBBINDING));
// create interface binding
binding.iOrdinal = pColumn->GetOrdinal(); binding.pObject = &object; binding.pObject->dwFlags = dwFlags; binding.pObject->iid = riid; binding.dwPart = DBPART_VALUE; binding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; binding.cbMaxLen = sizeof(IUnknown*); binding.wType = DBTYPE_IUNKNOWN;
HACCESSOR hAccessor;
// create interface accessor
HRESULT hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &binding, 0, &hAccessor, NULL);
if (FAILED(hr)) return E_NOINTERFACE;
IUnknown * pUnknown = NULL;
// try to get interface
hr = pRowset->GetData(hRow, hAccessor, &pUnknown);
// release interface accessor
pAccessor->ReleaseAccessor(hAccessor, NULL);
if (FAILED(hr)) return E_NOINTERFACE;
// return pointer
*ppUnknown = pUnknown;
return hr; }
#ifndef VD_DONT_IMPLEMENT_ISTREAM
//=--------------------------------------------------------------------------=
// CreateEntryIDStream - Create stream for entry identifier
//=--------------------------------------------------------------------------=
// This function retrieves supplied column row's data and create a
// stream containing this data
//
// Parameters:
// pColumn - [in] rowset column pointer
// hRow - [in] the row handle
// ppStream - [out] a pointer to memory in which to return stream pointer
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL error occured
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory
//
HRESULT CVDCursor::CreateEntryIDStream(CVDRowsetColumn * pColumn, HROW hRow, IStream ** ppStream) { ASSERT_POINTER(pColumn, CVDRowsetColumn) ASSERT_POINTER(ppStream, IStream*)
IRowset * pRowset = GetRowset(); IAccessor * pAccessor = GetAccessor();
// make sure we have valid rowset and accessor pointers
if (!pRowset || !pAccessor || !IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_IEntryID, m_pResourceDLL); return E_FAIL; }
// make sure we have all necessay pointers
if (!pColumn || !ppStream) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_IEntryID, m_pResourceDLL); return E_INVALIDARG; }
// init out parameters
*ppStream = NULL;
DBBINDING binding;
// clear out binding
memset(&binding, 0, sizeof(DBBINDING));
// create length binding
binding.iOrdinal = pColumn->GetOrdinal(); binding.obLength = 0; binding.dwPart = DBPART_LENGTH; binding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; binding.wType = DBTYPE_BYTES;
HACCESSOR hAccessor;
// create length accessor
HRESULT hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &binding, 0, &hAccessor, NULL);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_CREATEACCESSORFAILED, IID_IEntryID, pAccessor, IID_IAccessor, m_pResourceDLL);
if (FAILED(hr)) return hr;
ULONG cbData;
// get size of data
hr = pRowset->GetData(hRow, hAccessor, &cbData);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_IEntryID, pRowset, IID_IRowset, m_pResourceDLL);
// release length accessor
pAccessor->ReleaseAccessor(hAccessor, NULL);
if (FAILED(hr)) return hr;
// create value binding
binding.iOrdinal = pColumn->GetOrdinal(); binding.obValue = 0; binding.dwPart = DBPART_VALUE; binding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; binding.cbMaxLen = cbData; binding.wType = DBTYPE_BYTES;
// create value accessor
hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &binding, 0, &hAccessor, NULL);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_CREATEACCESSORFAILED, IID_IEntryID, pAccessor, IID_IAccessor, m_pResourceDLL);
if (FAILED(hr)) return hr;
// create data buffer
HGLOBAL hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD, cbData);
if (!hData) { // release value accessor
pAccessor->ReleaseAccessor(hAccessor, NULL);
VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_IEntryID, m_pResourceDLL); return E_OUTOFMEMORY; }
// get pointer to data buffer
BYTE * pData = (BYTE*)GlobalLock(hData);
// get data value
hr = pRowset->GetData(hRow, hAccessor, pData);
// release pointer to data buffer
GlobalUnlock(hData);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_IEntryID, pRowset, IID_IRowset, m_pResourceDLL);
// release value accessor
pAccessor->ReleaseAccessor(hAccessor, NULL);
if (FAILED(hr)) { GlobalFree(hData); return hr; }
// create stream containing data
hr = CreateStreamOnHGlobal(hData, TRUE, ppStream);
if (FAILED(hr)) GlobalFree(hData);
return hr; }
#endif //VD_DONT_IMPLEMENT_ISTREAM
//=--------------------------------------------------------------------------=
// MakeAdjustments - Make adjustments to fixed length buffer accessor bindings
//=--------------------------------------------------------------------------=
// This function makes adjustments to the fixed length buffer accessor
// bindings, after a call to CreateAccessor fails, to try and make the
// binding more suitable
//
// Parameters:
// ulBindings - [in] number of fixed length buffer bindings
// pBindings - [in] a pointer to fixed length buffer bindings
// pulIndex - [in] a pointer to an array of indices, which
// specify which cursor binding each fixed
// length buffer binding applies
// ulTotalBindings - [in] number of cursor bindings
// prghAdjustAccessors - [out] a pointer to memory in which to return
// a pointer to adjusted fixed length buffer
// accessors
// ppdwAdjustFlags - [out] a pointer to memory in which to return
// a pointer to adjusted fixed length buffer
// accessors flags
// fBefore - [in] a flag which indicated whether this call
// has been made before or after the call
// to CreateAccessor
//
// Output:
// S_OK - if adjustments were made
// E_FAIL - failed to make any adjustments
// E_OUTOFMEMORY - not enough memory
// E_INVALIDARG - bad parameter
//
// Notes:
// Specifically, this function can make the following adjustments...
// (1) Change variant binding byte field -> byte binding (fails in GetData)
// (2) Change variant binding date field -> wide string binding (fails in CreateAccessor)
// (3) Change variant binding memo field -> string binding (fails in CreateAccessor)
//
HRESULT CVDCursor::MakeAdjustments(ULONG ulBindings, DBBINDING * pBindings, ULONG * pulIndex, ULONG ulTotalBindings, HACCESSOR ** prghAdjustAccessors, DWORD ** ppdwAdjustFlags, BOOL fBefore) { ASSERT_POINTER(pBindings, DBBINDING) ASSERT_POINTER(pulIndex, ULONG) ASSERT_POINTER(prghAdjustAccessors, HACCESSOR*) ASSERT_POINTER(ppdwAdjustFlags, DWORD*)
IAccessor * pAccessor = GetAccessor();
// make sure we have a valid accessor pointer
if (!pAccessor || !IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL); return E_FAIL; }
// make sure we have all necessary pointers
if (!pBindings || !pulIndex || !prghAdjustAccessors || !ppdwAdjustFlags) return E_INVALIDARG;
BOOL fWeAllocatedMemory = FALSE;
// try to get storage for adjusted accessors and flags
HACCESSOR * rghAdjustAccessors = *prghAdjustAccessors; DWORD * pdwAdjustFlags = *ppdwAdjustFlags;
// if not supplied, then create storage
if (!rghAdjustAccessors || !pdwAdjustFlags) { rghAdjustAccessors = new HACCESSOR[ulTotalBindings]; pdwAdjustFlags = new DWORD[ulTotalBindings];
// make sure we got the requested memory
if (!rghAdjustAccessors || !pdwAdjustFlags) { delete [] rghAdjustAccessors; delete [] pdwAdjustFlags; VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL); return E_OUTOFMEMORY; }
// clear adjusted accessors and flags
memset(rghAdjustAccessors, 0, ulTotalBindings * sizeof(HACCESSOR)); memset(pdwAdjustFlags, 0, ulTotalBindings * sizeof(DWORD));
fWeAllocatedMemory = TRUE; }
// initialize variables
DBBINDING * pBinding = pBindings; CVDRowsetColumn * pColumn; DBTYPE wType; ULONG cbMaxLength; DBBINDING binding; HRESULT hr; HACCESSOR hAccessor; HRESULT hrAdjust = E_FAIL;
// iterate through fixed length buffer bindings
for (ULONG ulBind = 0; ulBind < ulBindings; ulBind++) { // first check for a variant binding, where value is to be returned
if (pBinding->wType == DBTYPE_VARIANT && (pBinding->dwPart & DBPART_VALUE)) { // get rowset column associated with this binding
pColumn = GetRowsetColumn(pBinding->iOrdinal);
if (pColumn) { // get attributes of this column
wType = pColumn->GetType(); cbMaxLength = pColumn->GetMaxLength();
// check for a byte field
if (fBefore && wType == DBTYPE_UI1) { // make adjustments to fixed length buffer binding
pBinding->wType = DBTYPE_UI1;
// store associated flag
pdwAdjustFlags[pulIndex[ulBind]] = VD_ADJUST_VARIANT_TO_BYTE;
// we succeeded
hrAdjust = S_OK; }
// check for a date field
if (!fBefore && wType == DBTYPE_DBTIMESTAMP) { // clear binding
memset(&binding, 0, sizeof(DBBINDING));
// create adjusted accessor binding
binding.iOrdinal = pBinding->iOrdinal; binding.dwPart = DBPART_VALUE; binding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; binding.cbMaxLen = 0x7FFFFFFF; binding.wType = DBTYPE_WSTR;
// try to create adjusted accessor
hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &binding, 0, &hAccessor, NULL);
if (SUCCEEDED(hr)) { // make adjustments to fixed length buffer binding
pBinding->obLength = pBinding->obValue; pBinding->dwPart &= ~DBPART_VALUE; pBinding->dwPart |= DBPART_LENGTH; pBinding->wType = DBTYPE_WSTR;
// store adjusted accessor and associated flag
rghAdjustAccessors[pulIndex[ulBind]] = hAccessor; pdwAdjustFlags[pulIndex[ulBind]] = VD_ADJUST_VARIANT_TO_WSTR;
// we succeeded
hrAdjust = S_OK; } }
// check for a memo field
if (!fBefore && wType == DBTYPE_STR && cbMaxLength >= 0x40000000) { // clear binding
memset(&binding, 0, sizeof(DBBINDING));
// create adjusted accessor binding
binding.iOrdinal = pBinding->iOrdinal; binding.dwPart = DBPART_VALUE; binding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; binding.cbMaxLen = 0x7FFFFFFF; binding.wType = DBTYPE_STR;
// try to create adjusted accessor
hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &binding, 0, &hAccessor, NULL);
if (SUCCEEDED(hr)) { // make adjustments to fixed length buffer binding
pBinding->obLength = pBinding->obValue; pBinding->dwPart &= ~DBPART_VALUE; pBinding->dwPart |= DBPART_LENGTH; pBinding->wType = DBTYPE_STR;
// store adjusted accessor and associated flag
rghAdjustAccessors[pulIndex[ulBind]] = hAccessor; pdwAdjustFlags[pulIndex[ulBind]] = VD_ADJUST_VARIANT_TO_STR;
// we succeeded
hrAdjust = S_OK; } } } }
pBinding++; }
if (SUCCEEDED(hrAdjust)) { // if we made any adjustments, return accessors and flags
*prghAdjustAccessors = rghAdjustAccessors; *ppdwAdjustFlags = pdwAdjustFlags; } else if (fWeAllocatedMemory) { // destroy allocated memory
delete [] rghAdjustAccessors; delete [] pdwAdjustFlags; }
return hrAdjust; }
//=--------------------------------------------------------------------------=
// ReCreateAccessors - Re-create accessors
//=--------------------------------------------------------------------------=
// This function attempts to recreate accessors based on old and new bindings
//
// Parameters:
// ulNewCursorBindings - [in] the number of new cursor column bindings
// pNewCursorBindings - [in] an array of new cursor column bindings
// dwFlags - [in] a flag that specifies whether to replace the
// existing column bindings or add to them
//
// Output:
// HRESULT - S_OK if successful
// E_OUTOFMEMORY not enough memory to create object
//
// Notes:
//
HRESULT CVDCursor::ReCreateAccessors(ULONG ulNewCursorBindings, CURSOR_DBCOLUMNBINDING * pNewCursorBindings, DWORD dwFlags) { IAccessor * pAccessor = GetAccessor();
// make sure we have a valid accessor pointer
if (!pAccessor || !IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL); return E_FAIL; }
ULONG ulOldCursorBindings = 0; CURSOR_DBCOLUMNBINDING * pOldCursorBindings = NULL;
// if we're adding bindings include old bindings
if (dwFlags == CURSOR_DBCOLUMNBINDOPTS_ADD) { ulOldCursorBindings = m_ulCursorBindings; pOldCursorBindings = m_pCursorBindings; }
// get total binding count (sum of old and new)
ULONG ulTotalBindings = ulOldCursorBindings + ulNewCursorBindings;
ULONG * pulIndex = NULL; DBBINDING * pBindings = NULL; DBBINDING * pHelperBindings = NULL; DBBINDING * pVarBindings = NULL;
if (ulTotalBindings) { // create storage for new rowset bindings
pulIndex = new ULONG[ulTotalBindings]; pBindings = new DBBINDING[ulTotalBindings]; pHelperBindings = new DBBINDING[ulTotalBindings]; pVarBindings = new DBBINDING[ulTotalBindings];
// make sure we got all requested memory
if (!pulIndex || !pBindings || !pHelperBindings || !pVarBindings) { delete [] pulIndex; delete [] pBindings; delete [] pHelperBindings; delete [] pVarBindings; VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL); return E_OUTOFMEMORY; }
// clear rowset bindings
memset(pulIndex, 0, ulTotalBindings * sizeof(ULONG)); memset(pBindings, 0, ulTotalBindings * sizeof(DBBINDING)); memset(pHelperBindings, 0, ulTotalBindings * sizeof(DBBINDING)); memset(pVarBindings, 0, ulTotalBindings * sizeof(DBBINDING)); }
// set adjustments to null
HACCESSOR * rghAdjustAccessors = NULL; DWORD * pdwAdjustFlags = NULL;
HRESULT hr; WORD wType; ULONG ulBindings = 0; ULONG ulHelperBindings = 0; ULONG ulVarBindings = 0; ULONG obVarDataInfo = 0; DBBINDING * pBinding = pBindings; DBBINDING * pHelperBinding = pHelperBindings; DBBINDING * pVarBinding = pVarBindings; CURSOR_DBCOLUMNBINDING * pCursorBinding = pOldCursorBindings; CVDRowsetColumn * pColumn; BOOL fEntryIDBinding;
// iterate through cursor bindings and set rowset bindings
for (ULONG ulCol = 0; ulCol < ulTotalBindings; ulCol++) { // if necessary, switch to new bindings
if (ulCol == ulOldCursorBindings) pCursorBinding = pNewCursorBindings;
// get rowset column for this binding
pColumn = GetRowsetColumn(pCursorBinding->columnID);
// get desired rowset datatype
wType = CVDRowsetColumn::CursorTypeToType((CURSOR_DBVARENUM)pCursorBinding->dwDataType);
// set entryID binding flag
fEntryIDBinding = (pCursorBinding->dwBinding & CURSOR_DBBINDING_ENTRYID);
// check for datatypes which require variable length buffer
if (DoesCursorTypeNeedVarData(pCursorBinding->dwDataType)) { // create fixed length buffer binding
pBinding->iOrdinal = pColumn->GetOrdinal(); pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED; pBinding->wType = wType;
// determine offset to the length part
if (pCursorBinding->obVarDataLen != CURSOR_DB_NOVALUE) { pBinding->obLength = pCursorBinding->obVarDataLen; pBinding->dwPart |= DBPART_LENGTH; }
// determine offset to the status part
if (pCursorBinding->obInfo != CURSOR_DB_NOVALUE) { pBinding->obStatus = pCursorBinding->obInfo; pBinding->dwPart |= DBPART_STATUS; }
// BLOBs always require the length part
if (pCursorBinding->dwDataType == CURSOR_DBTYPE_BLOB && pCursorBinding->obData != CURSOR_DB_NOVALUE && !fEntryIDBinding) { pBinding->obLength = pCursorBinding->obData; pBinding->dwPart |= DBPART_LENGTH; }
// bookmark columns require native type
if (!pColumn->GetDataColumn()) pBinding->wType = pColumn->GetType();
// create variable length helper buffer binding
if (!pColumn->GetFixed() && !fEntryIDBinding) { // if column contains variable length data, then create binding
pHelperBinding->iOrdinal = pColumn->GetOrdinal(); pHelperBinding->obLength = obVarDataInfo; pHelperBinding->obStatus = obVarDataInfo + sizeof(ULONG); pHelperBinding->dwPart = DBPART_LENGTH | DBPART_STATUS; pHelperBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED; pHelperBinding->wType = wType; }
// always increase offset in helper buffer
obVarDataInfo += sizeof(ULONG) + sizeof(DBSTATUS);
// create variable length buffer binding
pVarBinding->iOrdinal = pColumn->GetOrdinal(); pVarBinding->dwPart = DBPART_VALUE; pVarBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED; pVarBinding->cbMaxLen = pCursorBinding->cbMaxLen; pVarBinding->wType = wType;
// adjust for no maximum length
if (pVarBinding->cbMaxLen == CURSOR_DB_NOMAXLENGTH) pVarBinding->cbMaxLen = 0x7FFFFFFF; } else // datatype requires only fixed length buffer
{ // create fixed length buffer binding
pBinding->iOrdinal = pColumn->GetOrdinal(); pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED; pBinding->cbMaxLen = pCursorBinding->cbMaxLen; pBinding->wType = wType;
// determine offset to the value part
if (pCursorBinding->obData != CURSOR_DB_NOVALUE && !fEntryIDBinding) { pBinding->obValue = pCursorBinding->obData; pBinding->dwPart |= DBPART_VALUE; }
// determine offset to the length part
if (pCursorBinding->obVarDataLen != CURSOR_DB_NOVALUE) { pBinding->obLength = pCursorBinding->obVarDataLen; pBinding->dwPart |= DBPART_LENGTH; }
// determine offset to the status part
if (pCursorBinding->obInfo != CURSOR_DB_NOVALUE) { pBinding->obStatus = pCursorBinding->obInfo; pBinding->dwPart |= DBPART_STATUS; }
// BYTES always require the length part
if (pCursorBinding->dwDataType == CURSOR_DBTYPE_BYTES && pCursorBinding->obData != CURSOR_DB_NOVALUE && !fEntryIDBinding) { pBinding->obLength = pCursorBinding->obData; pBinding->obValue += sizeof(ULONG); pBinding->dwPart |= DBPART_LENGTH; }
// check for variant binding, in which case ask for variant
if (pCursorBinding->dwBinding & CURSOR_DBBINDING_VARIANT) pBinding->wType = DBTYPE_VARIANT; }
// if any parts needed, increment fixed buffer binding
if (pBinding->dwPart) { pulIndex[ulBindings] = ulCol; ulBindings++; pBinding++; }
// if any parts needed, increment variable buffer helper binding
if (pHelperBinding->dwPart) { ulHelperBindings++; pHelperBinding++; }
// if any parts needed, increment variable buffer binding count
if (pVarBinding->dwPart) { // entryID bindings do not need value part
if (fEntryIDBinding) pVarBinding->dwPart &= ~DBPART_VALUE;
ulVarBindings++; }
// however, always increment variable buffer binding
pVarBinding++;
// get next cursor binding
pCursorBinding++; }
hr = S_OK;
// try to create fixed length buffer accessor
HACCESSOR hAccessor = 0;
if (ulBindings) { // make adjustments that can cause failure in GetData
MakeAdjustments(ulBindings, pBindings, pulIndex, ulTotalBindings, &rghAdjustAccessors, &pdwAdjustFlags, TRUE);
hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, ulBindings, pBindings, 0, &hAccessor, NULL); }
if (FAILED(hr)) { // make other known adjustments that can cause CreateAccessor to fail
hr = MakeAdjustments(ulBindings, pBindings, pulIndex, ulTotalBindings, &rghAdjustAccessors, &pdwAdjustFlags, FALSE);
if (SUCCEEDED(hr)) hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, ulBindings, pBindings, 0, &hAccessor, NULL); }
delete [] pulIndex; delete [] pBindings;
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_CREATEACCESSORFAILED, IID_ICursor, pAccessor, IID_IAccessor, m_pResourceDLL);
if (FAILED(hr)) { delete [] pHelperBindings; delete [] pVarBindings; ReleaseAccessorArray(rghAdjustAccessors); delete [] rghAdjustAccessors; delete [] pdwAdjustFlags; return hr; }
// try to create variable length buffer accessors helper
HACCESSOR hVarHelper = 0;
if (ulHelperBindings) hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, ulHelperBindings, pHelperBindings, 0, &hVarHelper, NULL);
delete [] pHelperBindings;
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_CREATEACCESSORFAILED, IID_ICursor, pAccessor, IID_IAccessor, m_pResourceDLL);
if (FAILED(hr)) { pAccessor->ReleaseAccessor(hAccessor, NULL); delete [] pVarBindings; ReleaseAccessorArray(rghAdjustAccessors); delete [] rghAdjustAccessors; delete [] pdwAdjustFlags; return hr; }
// try to create variable length buffer accessors
HACCESSOR * rghVarAccessors = NULL;
if (ulTotalBindings) { rghVarAccessors = new HACCESSOR[ulTotalBindings];
if (!rghVarAccessors) hr = E_OUTOFMEMORY; else { pVarBinding = pVarBindings; memset(rghVarAccessors, 0, ulTotalBindings * sizeof(HACCESSOR));
// iterate through rowset bindings and create accessor for one which have a part
for (ULONG ulBind = 0; ulBind < ulTotalBindings && SUCCEEDED(hr); ulBind++) { if (pVarBinding->dwPart) hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, pVarBinding, 0, &rghVarAccessors[ulBind], NULL);
pVarBinding++; } } }
delete [] pVarBindings;
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_CREATEACCESSORFAILED, IID_ICursor, pAccessor, IID_IAccessor, m_pResourceDLL);
if (FAILED(hr)) { if (rghVarAccessors) { // iterate through rowset bindings and destroy any created accessors
for (ULONG ulBind = 0; ulBind < ulTotalBindings; ulBind++) { if (rghVarAccessors[ulBind]) pAccessor->ReleaseAccessor(rghVarAccessors[ulBind], NULL); }
delete [] rghVarAccessors; }
pAccessor->ReleaseAccessor(hAccessor, NULL); pAccessor->ReleaseAccessor(hVarHelper, NULL); ReleaseAccessorArray(rghAdjustAccessors); delete [] rghAdjustAccessors; delete [] pdwAdjustFlags; return hr; }
// destroy old accessors
DestroyAccessors();
// store new accessors
m_hAccessor = hAccessor; m_hVarHelper = hVarHelper; m_ulVarBindings = ulVarBindings; m_rghVarAccessors = rghVarAccessors; m_rghAdjustAccessors = rghAdjustAccessors; m_pdwAdjustFlags = pdwAdjustFlags;
return hr; }
//=--------------------------------------------------------------------------=
// ReleaseAccessorArray - Release all accessors in specified array
//
void CVDCursor::ReleaseAccessorArray(HACCESSOR * rghAccessors) { IAccessor * pAccessor = GetAccessor();
if (pAccessor && rghAccessors) { for (ULONG ulBind = 0; ulBind < m_ulCursorBindings; ulBind++) { if (rghAccessors[ulBind]) { pAccessor->ReleaseAccessor(rghAccessors[ulBind], NULL); rghAccessors[ulBind] = NULL; } } } }
//=--------------------------------------------------------------------------=
// DestroyAccessors - Destroy all rowset accessors
//
void CVDCursor::DestroyAccessors() { IAccessor * pAccessor = GetAccessor();
if (pAccessor && m_hAccessor) { pAccessor->ReleaseAccessor(m_hAccessor, NULL); m_hAccessor = 0; }
if (pAccessor && m_hVarHelper) { pAccessor->ReleaseAccessor(m_hVarHelper, NULL); m_hVarHelper = 0; }
m_ulVarBindings = 0;
ReleaseAccessorArray(m_rghVarAccessors); delete [] m_rghVarAccessors; m_rghVarAccessors = NULL;
ReleaseAccessorArray(m_rghAdjustAccessors); delete [] m_rghAdjustAccessors; m_rghAdjustAccessors = NULL;
delete [] m_pdwAdjustFlags; m_pdwAdjustFlags = NULL; }
//=--------------------------------------------------------------------------=
// ReCreateColumns - Re-create rowset columns associated with current bindings
//
HRESULT CVDCursor::ReCreateColumns() { DestroyColumns();
if (m_ulCursorBindings) { m_ppColumns = new CVDRowsetColumn*[m_ulCursorBindings];
if (!m_ppColumns) { VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL); return E_OUTOFMEMORY; }
CURSOR_DBCOLUMNBINDING * pCursorBinding = m_pCursorBindings;
for (ULONG ulBind = 0; ulBind < m_ulCursorBindings; ulBind++) { m_ppColumns[ulBind] = GetRowsetColumn(pCursorBinding->columnID); pCursorBinding++; } }
return S_OK; }
//=--------------------------------------------------------------------------=
// DestroyColumns - Destroy rowset column pointers
//
void CVDCursor::DestroyColumns() { delete [] m_ppColumns; m_ppColumns = NULL; }
//=--------------------------------------------------------------------------=
// InsertNewRow - Insert a new row and set in cursor position object
//
HRESULT CVDCursor::InsertNewRow() { IRowset * pRowset = GetRowset(); IAccessor * pAccessor = GetAccessor(); IRowsetChange * pRowsetChange = GetRowsetChange();
// make sure we have valid rowset, accessor and change pointers
if (!pRowset || !pAccessor || !pRowsetChange || !IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorUpdateARow, m_pResourceDLL); return E_FAIL; }
HACCESSOR hAccessor;
// create null accessor
HRESULT hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 0, NULL, 0, &hAccessor, NULL);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_CREATEACCESSORFAILED, IID_ICursorUpdateARow, pAccessor, IID_IAccessor, m_pResourceDLL);
if (FAILED(hr)) return hr;
HROW hRow;
// insert an empty row using null accessor (set/clear internal insert row flag)
GetCursorMain()->SetInternalInsertRow(TRUE); hr = pRowsetChange->InsertRow(0, hAccessor, NULL, &hRow); GetCursorMain()->SetInternalInsertRow(FALSE);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_INSERTROWFAILED, IID_ICursorUpdateARow, pRowsetChange, IID_IRowsetChange, m_pResourceDLL);
// release null accessor
pAccessor->ReleaseAccessor(hAccessor, NULL);
if (FAILED(hr)) return hr;
// set hRow in cursor position object
hr = m_pCursorPosition->SetAddHRow(hRow);
// release our reference on hRow
pRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL);
return hr; }
//=--------------------------------------------------------------------------=
// GetOriginalColumn - Get original column data using same-row clone
//
HRESULT CVDCursor::GetOriginalColumn(CVDRowsetColumn * pColumn, CURSOR_DBBINDPARAMS * pBindParams) { ASSERT_POINTER(pColumn, CVDRowsetColumn) ASSERT_POINTER(pBindParams, CURSOR_DBBINDPARAMS)
// make sure we have all necessary pointers
if (!pColumn || !pBindParams || !pBindParams->pData) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorUpdateARow, m_pResourceDLL); return E_INVALIDARG; }
// get hRow of the row currently being edited
HROW hRow = m_pCursorPosition->GetEditRow();
// see if we already have a same-row clone
ICursor * pSameRowClone = m_pCursorPosition->GetSameRowClone();
if (!pSameRowClone) { // if not, create new same-row clone
HRESULT hr = Clone(CURSOR_DBCLONEOPTS_SAMEROW, IID_ICursor, (IUnknown**)&pSameRowClone);
if (FAILED(hr)) return hr;
// set same-row clone in cursor position object
m_pCursorPosition->SetSameRowClone(pSameRowClone); }
CURSOR_DBCOLUMNBINDING columnBinding;
// set common column binding members
columnBinding.columnID = pColumn->GetCursorColumnID(); columnBinding.obData = CURSOR_DB_NOVALUE; columnBinding.cbMaxLen = pBindParams->cbMaxLen; columnBinding.obVarDataLen = CURSOR_DB_NOVALUE; columnBinding.obInfo = CURSOR_DB_NOVALUE; columnBinding.dwBinding = CURSOR_DBBINDING_DEFAULT; columnBinding.dwDataType = pBindParams->dwDataType;
// adjust column binding for variable length datatypes
if (DoesCursorTypeNeedVarData(pBindParams->dwDataType)) { switch (pBindParams->dwDataType) { case CURSOR_DBTYPE_BLOB: columnBinding.dwDataType = CURSOR_DBTYPE_BYTES; break;
case CURSOR_DBTYPE_LPSTR: columnBinding.dwDataType = CURSOR_DBTYPE_CHARS; break;
case CURSOR_DBTYPE_LPWSTR: columnBinding.dwDataType = CURSOR_DBTYPE_WCHARS; break; } }
CURSOR_DBFETCHROWS fetchRows;
// set common fetch rows members
fetchRows.cRowsRequested = 1; fetchRows.dwFlags = CURSOR_DBROWFETCH_DEFAULT; fetchRows.pVarData = NULL; fetchRows.cbVarData = 0;
// retrieve length and/or information field if requested
if (pBindParams->cbVarDataLen != CURSOR_DB_NOVALUE || pBindParams->dwInfo != CURSOR_DB_NOVALUE) { // set column binding offsets
if (pBindParams->cbVarDataLen != CURSOR_DB_NOVALUE) columnBinding.obVarDataLen = offsetof(CURSOR_DBBINDPARAMS, cbVarDataLen);
if (pBindParams->dwInfo != CURSOR_DB_NOVALUE) columnBinding.obInfo = offsetof(CURSOR_DBBINDPARAMS, dwInfo);
// set bindings on same-row clone
HRESULT hr = pSameRowClone->SetBindings(1, &columnBinding, 0, CURSOR_DBCOLUMNBINDOPTS_REPLACE);
if (FAILED(hr)) return hr;
// set fetch rows buffer
fetchRows.pData = pBindParams;
// retrieve length and/or information field from same-row clone
hr = ((CVDCursor*)pSameRowClone)->FillConsumersBuffer(S_OK, &fetchRows, 1, &hRow);
if (FAILED(hr)) return hr; }
// set column binding offsets and bind-type
columnBinding.obData = 0; columnBinding.obVarDataLen = CURSOR_DB_NOVALUE; columnBinding.obInfo = CURSOR_DB_NOVALUE; columnBinding.dwBinding = pBindParams->dwBinding;
// adjust offsets for variable length datatypes
if (DoesCursorTypeNeedVarData(pBindParams->dwDataType)) { columnBinding.dwBinding = CURSOR_DBBINDING_DEFAULT;
if (pBindParams->dwBinding & CURSOR_DBBINDING_VARIANT) { if (pBindParams->dwDataType == CURSOR_DBTYPE_BLOB) columnBinding.obVarDataLen = columnBinding.obData;
columnBinding.obData += sizeof(CURSOR_DBVARIANT); } else { switch (pBindParams->dwDataType) { case CURSOR_DBTYPE_BLOB: columnBinding.obVarDataLen = columnBinding.obData; columnBinding.obData += sizeof(ULONG) + sizeof(LPBYTE); break;
case CURSOR_DBTYPE_LPSTR: columnBinding.obData += sizeof(LPSTR); break;
case CURSOR_DBTYPE_LPWSTR: columnBinding.obData += sizeof(LPWSTR); break; } } }
// set bindings on same-row clone
HRESULT hr = pSameRowClone->SetBindings(1, &columnBinding, pBindParams->cbMaxLen, CURSOR_DBCOLUMNBINDOPTS_REPLACE);
if (FAILED(hr)) return hr;
// set fetch rows buffer
fetchRows.pData = pBindParams->pData;
// retrieve data value from same-row clone
hr = ((CVDCursor*)pSameRowClone)->FillConsumersBuffer(S_OK, &fetchRows, 1, &hRow);
if (FAILED(hr)) return hr;
// place data pointers in buffer for variable length datatypes
if (DoesCursorTypeNeedVarData(pBindParams->dwDataType)) { BYTE * pData = (BYTE*)pBindParams->pData;
// first check for variant binding
if (pBindParams->dwBinding & CURSOR_DBBINDING_VARIANT) { CURSOR_BLOB cursorBlob; CURSOR_DBVARIANT * pVariant = (CURSOR_DBVARIANT*)pBindParams->pData;
switch (pBindParams->dwDataType) { case CURSOR_DBTYPE_BLOB: cursorBlob.cbSize = *(ULONG*)pVariant; cursorBlob.pBlobData = pData + sizeof(CURSOR_DBVARIANT); VariantInit((VARIANT*)pVariant); pVariant->vt = CURSOR_DBTYPE_BLOB; pVariant->blob = cursorBlob; break;
case CURSOR_DBTYPE_LPSTR: VariantInit((VARIANT*)pVariant); pVariant->vt = CURSOR_DBTYPE_LPSTR; pVariant->pszVal = (LPSTR)(pData + sizeof(CURSOR_DBVARIANT)); break;
case CURSOR_DBTYPE_LPWSTR: VariantInit((VARIANT*)pVariant); pVariant->vt = CURSOR_DBTYPE_LPSTR; pVariant->pwszVal = (LPWSTR)(pData + sizeof(CURSOR_DBVARIANT)); break; } } else // otherwise, default binding
{ switch (pBindParams->dwDataType) { case CURSOR_DBTYPE_BLOB: *(LPBYTE*)(pData + sizeof(ULONG)) = pData + sizeof(ULONG) + sizeof(LPBYTE); break;
case CURSOR_DBTYPE_LPSTR: *(LPSTR*)pData = (LPSTR)(pData + sizeof(LPSTR)); break;
case CURSOR_DBTYPE_LPWSTR: *(LPWSTR*)pData = (LPWSTR)(pData + sizeof(LPWSTR)); break; } } }
return S_OK; }
//=--------------------------------------------------------------------------=
// GetModifiedColumn - Get modified column data from column update object
//
HRESULT CVDCursor::GetModifiedColumn(CVDColumnUpdate * pColumnUpdate, CURSOR_DBBINDPARAMS * pBindParams) { ASSERT_POINTER(pColumnUpdate, CVDColumnUpdate) ASSERT_POINTER(pBindParams, CURSOR_DBBINDPARAMS)
// make sure we have all necessary pointers
if (!pColumnUpdate || !pBindParams || !pBindParams->pData) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorUpdateARow, m_pResourceDLL); return E_INVALIDARG; }
// get source variant
CURSOR_DBVARIANT varSrc = pColumnUpdate->GetVariant();
// check for any variant binding
if (pBindParams->dwDataType == CURSOR_DBTYPE_ANYVARIANT) pBindParams->dwDataType = varSrc.vt;
// determine which type the destination variant should be
VARTYPE vtDest = (VARTYPE)pBindParams->dwDataType;
switch (vtDest) { case CURSOR_DBTYPE_BYTES: vtDest = CURSOR_DBTYPE_BLOB; break;
case CURSOR_DBTYPE_CHARS: case CURSOR_DBTYPE_WCHARS: case CURSOR_DBTYPE_LPSTR: case CURSOR_DBTYPE_LPWSTR: vtDest = VT_BSTR; break; }
HRESULT hr = S_OK; CURSOR_DBVARIANT varDest; BOOL fVariantCreated = FALSE;
// init destination variant
VariantInit((VARIANT*)&varDest);
// get destination variant
if (varSrc.vt != vtDest) { // if the types do not match, then create a variant of the desired type
hr = VariantChangeType((VARIANT*)&varDest, (VARIANT*)&varSrc, 0, vtDest); fVariantCreated = TRUE; } else varDest = varSrc;
if (FAILED(hr)) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorUpdateARow, m_pResourceDLL); return E_INVALIDARG; }
// get pointer to data
BYTE * pData = (BYTE*)pBindParams->pData;
// return coerced data
if (pBindParams->dwBinding & CURSOR_DBBINDING_VARIANT) { // get pointer to variant data
CURSOR_DBVARIANT * pVariant = (CURSOR_DBVARIANT*)pData;
// return variant
*pVariant = varDest;
// adjust variant for variable length datatypes
if (pBindParams->dwDataType == CURSOR_DBTYPE_BLOB) { pVariant->blob.pBlobData = pData + sizeof(CURSOR_DBVARIANT); memcpy(pData + sizeof(CURSOR_DBVARIANT), varDest.blob.pBlobData, varDest.blob.cbSize); } else if (pBindParams->dwDataType == CURSOR_DBTYPE_LPSTR) { ULONG cbLength = GET_MBCSLEN_FROMWIDE(varDest.bstrVal); MAKE_MBCSPTR_FROMWIDE(psz, varDest.bstrVal); pVariant->pszVal = (LPSTR)(pData + sizeof(CURSOR_DBVARIANT)); memcpy(pData + sizeof(CURSOR_DBVARIANT), psz, min(pBindParams->cbMaxLen, cbLength)); } else if (pBindParams->dwDataType == CURSOR_DBTYPE_LPWSTR) { ULONG cbLength = (lstrlenW(varDest.bstrVal) + 1) * sizeof(WCHAR); pVariant->pwszVal = (LPWSTR)(pData + sizeof(CURSOR_DBVARIANT)); memcpy(pData + sizeof(CURSOR_DBVARIANT), varDest.bstrVal, min(pBindParams->cbMaxLen, cbLength)); } else if (pBindParams->dwDataType == VT_BSTR) { pVariant->bstrVal = SysAllocString(pVariant->bstrVal); } } else // otherwise, default binding
{ // first check for variable length datatypes
if (pBindParams->dwDataType == CURSOR_DBTYPE_BYTES) { *(ULONG*)pData = varDest.blob.cbSize; memcpy(pData + sizeof(ULONG), varDest.blob.pBlobData, varDest.blob.cbSize); } else if (pBindParams->dwDataType == CURSOR_DBTYPE_CHARS) { ULONG cbLength = GET_MBCSLEN_FROMWIDE(varDest.bstrVal); MAKE_MBCSPTR_FROMWIDE(psz, varDest.bstrVal); memcpy(pData, psz, min(pBindParams->cbMaxLen, cbLength)); } else if (pBindParams->dwDataType == CURSOR_DBTYPE_WCHARS) { ULONG cbLength = (lstrlenW(varDest.bstrVal) + 1) * sizeof(WCHAR); memcpy(pData, varDest.bstrVal, min(pBindParams->cbMaxLen, cbLength)); } else if (pBindParams->dwDataType == CURSOR_DBTYPE_BLOB) { *(ULONG*)pData = varDest.blob.cbSize; *(LPBYTE*)(pData + sizeof(ULONG)) = pData + sizeof(ULONG) + sizeof(LPBYTE); memcpy(pData + sizeof(ULONG) + sizeof(LPBYTE), varDest.blob.pBlobData, varDest.blob.cbSize); } else if (pBindParams->dwDataType == CURSOR_DBTYPE_LPSTR) { ULONG cbLength = GET_MBCSLEN_FROMWIDE(varDest.bstrVal); MAKE_MBCSPTR_FROMWIDE(psz, varDest.bstrVal); *(LPSTR*)pData = (LPSTR)(pData + sizeof(LPSTR)); memcpy(pData + sizeof(LPSTR), psz, min(pBindParams->cbMaxLen, cbLength)); } else if (pBindParams->dwDataType == CURSOR_DBTYPE_LPWSTR) { ULONG cbLength = (lstrlenW(varDest.bstrVal) + 1) * sizeof(WCHAR); *(LPWSTR*)pData = (LPWSTR)(pData + sizeof(LPWSTR)); memcpy(pData + sizeof(LPWSTR), varDest.bstrVal, min(pBindParams->cbMaxLen, cbLength)); } else // fixed length datatypes
{ ULONG cbLength = CVDCursorBase::GetCursorTypeLength(pBindParams->dwDataType, 0); memcpy(pData, &varDest.cyVal, cbLength); } }
// if created, destroy variant
if (fVariantCreated) VariantClear((VARIANT*)&varDest);
return S_OK; }
//=--------------------------------------------------------------------------=
// Create - Create cursor object
//=--------------------------------------------------------------------------=
// This function creates and initializes a new cursor object
//
// Parameters:
// pCursorPosition - [in] backwards pointer to CVDCursorPosition object
// ppCursor - [out] a pointer in which to return pointer to cursor object
// pResourceDLL - [in] a pointer which keeps track of resource DLL
//
// Output:
// HRESULT - S_OK if successful
// E_OUTOFMEMORY not enough memory to create object
//
// Notes:
//
HRESULT CVDCursor::Create(CVDCursorPosition * pCursorPosition, CVDCursor ** ppCursor, CVDResourceDLL * pResourceDLL) { ASSERT_POINTER(pCursorPosition, CVDCursorPosition) ASSERT_POINTER(ppCursor, CVDCursor*) ASSERT_POINTER(pResourceDLL, CVDResourceDLL)
if (!pCursorPosition || !ppCursor) return E_INVALIDARG;
*ppCursor = NULL;
CVDCursor * pCursor = new CVDCursor();
if (!pCursor) return E_OUTOFMEMORY;
// create connection point container
HRESULT hr = CVDNotifyDBEventsConnPtCont::Create(pCursor, &pCursor->m_pConnPtContainer);
if (FAILED(hr)) { delete pCursor; return hr; }
((CVDNotifier*)pCursorPosition)->AddRef(); // add reference to associated cursor position object
pCursor->m_pCursorPosition = pCursorPosition; pCursor->m_pResourceDLL = pResourceDLL;
// add to pCursorPosition's notification family
pCursor->JoinFamily(pCursorPosition);
*ppCursor = pCursor;
return S_OK; }
//=--------------------------------------------------------------------------=
// IUnknown methods implemented
//=--------------------------------------------------------------------------=
//=--------------------------------------------------------------------------=
// IUnknown QueryInterface
//
HRESULT CVDCursor::QueryInterface(REFIID riid, void **ppvObjOut) { ASSERT_POINTER(ppvObjOut, IUnknown*)
if (!ppvObjOut) return E_INVALIDARG;
*ppvObjOut = NULL;
switch (riid.Data1) { QI_INTERFACE_SUPPORTED_IF(this, ICursorUpdateARow, GetRowsetChange()); QI_INTERFACE_SUPPORTED_IF(this, ICursorFind, GetRowsetFind()); QI_INTERFACE_SUPPORTED(this, IEntryID); QI_INTERFACE_SUPPORTED(m_pConnPtContainer, IConnectionPointContainer); }
if (NULL == *ppvObjOut) return CVDCursorBase::QueryInterface(riid, ppvObjOut);
CVDNotifier::AddRef();
return S_OK; }
//=--------------------------------------------------------------------------=
// IUnknown AddRef (this override is needed to instantiate class)
//
ULONG CVDCursor::AddRef(void) { return CVDNotifier::AddRef(); }
//=--------------------------------------------------------------------------=
// IUnknown Release (this override is needed to instantiate class)
//
ULONG CVDCursor::Release(void) { return CVDNotifier::Release(); }
//=--------------------------------------------------------------------------=
// ICursor methods implemented
//=--------------------------------------------------------------------------=
//=--------------------------------------------------------------------------=
// ICursor GetColumnsCursor
//=--------------------------------------------------------------------------=
// Creates a cursor containing information about the current cursor
//
// Parameters:
// riid - [in] the interface ID to which to return a pointer
// ppvColumnsCursor - [out] a pointer to memory in which to return the
// interface pointer
// pcRows - [out] a pointer to memory in which to return the
// number of rows in the metadata cursor
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL can't create cursor
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory
// E_NOINTERFACE interface not available
//
// Notes:
//
HRESULT CVDCursor::GetColumnsCursor(REFIID riid, IUnknown **ppvColumnsCursor, ULONG *pcRows) { ASSERT_POINTER(ppvColumnsCursor, IUnknown*) ASSERT_NULL_OR_POINTER(pcRows, ULONG)
if (!IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL); return E_FAIL; }
if (!ppvColumnsCursor) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursor, m_pResourceDLL); return E_INVALIDARG; }
// init out parameters
*ppvColumnsCursor = NULL;
if (pcRows) *pcRows = 0;
// make sure caller asked for an available interface
if (riid != IID_IUnknown && riid != IID_ICursor && riid != IID_ICursorMove && riid != IID_ICursorScroll) { VDSetErrorInfo(IDS_ERR_NOINTERFACE, IID_ICursor, m_pResourceDLL); return E_NOINTERFACE; }
// create metadata cursor
CVDMetadataCursor * pMetadataCursor;
ULONG ulColumns = GetCursorMain()->GetColumnsCount(); CVDRowsetColumn * pColumns = GetCursorMain()->InternalGetColumns();
ULONG ulMetaColumns = GetCursorMain()->GetMetaColumnsCount(); CVDRowsetColumn * pMetaColumns = GetCursorMain()->InternalGetMetaColumns(); if (!GetCursorMain()->IsColumnsRowsetSupported()) ulMetaColumns -= VD_COLUMNSROWSET_MAX_OPT_COLUMNS;
HRESULT hr = CVDMetadataCursor::Create(ulColumns, pColumns, ulMetaColumns, pMetaColumns, &pMetadataCursor, m_pResourceDLL);
if (FAILED(hr)) // the only reason for failing here is an out of memory condition
{ VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL); return hr; }
*ppvColumnsCursor = (ICursor*)pMetadataCursor;
if (pcRows) *pcRows = ulColumns;
return S_OK; }
//=--------------------------------------------------------------------------=
// ICursor SetBindings
//=--------------------------------------------------------------------------=
// Replaces the existing column bindings or adds new column bindings to the
// existing ones
//
// Parameters:
// cCol - [in] the number of columns to bind
// rgBoundColumns - [in] an array of column bindings, one for each
// column for which data is to be returned
// cbRowLength - [in] the number of bytes of inline memory in a
// single row of data
// dwFlags - [in] a flag that specifies whether to replace the
// existing column bindings or add to them
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory
// CURSOR_DB_E_BADBINDINFO bad binding information
// CURSOR_DB_E_COLUMNUNAVAILABLE columnID is not available
// CURSOR_DB_E_ROWTOOSHORT cbRowLength was less than the minumum (and not zero)
//
// Notes:
//
HRESULT CVDCursor::SetBindings(ULONG cCol, CURSOR_DBCOLUMNBINDING rgBoundColumns[], ULONG cbRowLength, DWORD dwFlags) { ASSERT_NULL_OR_POINTER(rgBoundColumns, CURSOR_DBCOLUMNBINDING)
if (!IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL); return E_FAIL; }
if (!cCol && dwFlags == CURSOR_DBCOLUMNBINDOPTS_ADD) return S_OK;
if (cCol && !rgBoundColumns) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursor, m_pResourceDLL); return E_INVALIDARG; }
if (dwFlags != CURSOR_DBCOLUMNBINDOPTS_REPLACE && dwFlags != CURSOR_DBCOLUMNBINDOPTS_ADD) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursor, m_pResourceDLL); return E_INVALIDARG; }
// make sure the bindings are okay
ULONG ulColumns = GetCursorMain()->GetColumnsCount(); CVDRowsetColumn * pColumns = GetCursorMain()->InternalGetColumns();
ULONG cbNewRowLength; ULONG cbNewVarRowLength;
HRESULT hr = ValidateCursorBindings(ulColumns, pColumns, cCol, rgBoundColumns, cbRowLength, dwFlags, &cbNewRowLength, &cbNewVarRowLength);
if (SUCCEEDED(hr)) { // if so, then try to create new accessors
hr = ReCreateAccessors(cCol, rgBoundColumns, dwFlags);
if (SUCCEEDED(hr)) { // if all is okay, then set bindings in cursor
hr = CVDCursorBase::SetBindings(cCol, rgBoundColumns, cbRowLength, dwFlags);
if (SUCCEEDED(hr)) { // store new row lengths computed during validation
m_cbRowLength = cbNewRowLength; m_cbVarRowLength = cbNewVarRowLength;
// recreate column pointers
hr = ReCreateColumns(); } } }
return hr; }
//=--------------------------------------------------------------------------=
// FilterNewRow - Filter addrow from fetch
//=--------------------------------------------------------------------------=
// This function determines if the last row fetch was an addrow, in which case
// it releases and removes this row from the block of fetched hRows
//
// Parameters:
// pcRowsObtained - [in/out] a pointer to the number of hRows
// rghRows - [in/out] an array of nRows fetched
// hr - [in] result of fetch
//
// Output:
// HRESULT - E_FAIL rowset is invalid
// E_INVALIDARG bad parameter
// DB_E_BADSTARTPOSITION no rows fetched
// DB_S_ENDOFROWSET reached end of rowset
//
// Notes:
// This function was added to assist in filtering out of add-rows which
// appear as part of the underlying rowset, however should not appear as
// part of the implemeted cursor.
//
HRESULT CVDCursor::FilterNewRow(ULONG * pcRowsObtained, HROW * rghRows, HRESULT hr) { ASSERT_POINTER(pcRowsObtained, ULONG) ASSERT_NULL_OR_POINTER(rghRows, HROW)
IRowset * pRowset = GetRowset();
// make sure we have a valid rowset pointer
if (!pRowset || !IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL); return E_FAIL; }
// make sure we have necessary pointers
if (!pcRowsObtained || *pcRowsObtained && !rghRows) return E_INVALIDARG;
if (*pcRowsObtained == 0) return hr;
// detemine if last row fetched is an addrow
if (GetCursorMain()->IsSameRowAsNew(rghRows[*pcRowsObtained - 1])) { // if so, release hRow
pRowset->ReleaseRows(1, &rghRows[*pcRowsObtained - 1], NULL, NULL, NULL);
// decrement fetch count
*pcRowsObtained -= 1;
// return appropriate result
return *pcRowsObtained == 0 ? DB_E_BADSTARTPOSITION : DB_S_ENDOFROWSET; }
return hr; }
//=--------------------------------------------------------------------------=
// ICursor GetNextRows
//=--------------------------------------------------------------------------=
// Fetches the specified number of rows starting with the row after the
// current one
//
// Parameters:
// udlRowsToSkip - [in] the number of rows to skip before fetching
// pFetchParams - [in, out] a pointer to fetch rows structure
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL rowset is invalid
// CURSOR_DB_S_ENDOFCURSOR reached end of the cursor
//
// Notes:
//
HRESULT CVDCursor::GetNextRows(LARGE_INTEGER udlRowsToSkip, CURSOR_DBFETCHROWS *pFetchParams) { ASSERT_NULL_OR_POINTER(pFetchParams, CURSOR_DBFETCHROWS)
if (!IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL); return E_FAIL; }
// return if caller doesn't supply fetch rows structure
if (!pFetchParams) return S_OK;
// vaildate fetch params (implemented on CVDCursorBase
HRESULT hr = ValidateFetchParams(pFetchParams, IID_ICursor);
// return if fetch params are invalid
if (FAILED(hr)) return hr;
// return if caller didn't ask for any rows
if (!pFetchParams->cRowsRequested) return S_OK;
HRESULT hrFetch; IRowset * pRowset = GetRowset();
// notify other interested parties
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_CHANGED; CURSOR_DBNOTIFYREASON rgReasons[1]; rgReasons[0].dwReason = CURSOR_DBREASON_MOVE; rgReasons[0].arg1 = m_pCursorPosition->m_bmCurrent.GetBookmarkVariant();
VariantInit((VARIANT*)&rgReasons[0].arg2); rgReasons[0].arg2.vt = VT_I8; rgReasons[0].arg2.cyVal.Lo = udlRowsToSkip.LowPart; rgReasons[0].arg2.cyVal.Hi = udlRowsToSkip.HighPart;
hrFetch = m_pCursorPosition->NotifyBefore(dwEventWhat, 1, rgReasons);
// make sure action was not cancelled
if (hrFetch != S_OK) { VDSetErrorInfo(IDS_ERR_ACTIONCANCELLED, IID_ICursor, m_pResourceDLL); return E_FAIL; }
// make sure that an update is not already in progress
if (m_pCursorPosition->GetEditMode() != CURSOR_DBEDITMODE_NONE) { m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons); VDSetErrorInfo(IDS_ERR_UPDATEINPROGRESS, IID_ICursor, m_pResourceDLL); return CURSOR_DB_E_UPDATEINPROGRESS; }
ULONG cRowsObtained = 0; HROW * rghRows = NULL;
BYTE bSpecialBM; ULONG cbBookmark; BYTE * pBookmark; switch (m_pCursorPosition->m_bmCurrent.GetStatus()) { case VDBOOKMARKSTATUS_BEGINNING: cbBookmark = sizeof(BYTE); bSpecialBM = DBBMK_FIRST; pBookmark = &bSpecialBM; break;
case VDBOOKMARKSTATUS_END: m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons); return CURSOR_DB_S_ENDOFCURSOR;
case VDBOOKMARKSTATUS_CURRENT: cbBookmark = m_pCursorPosition->m_bmCurrent.GetBookmarkLen(); pBookmark = m_pCursorPosition->m_bmCurrent.GetBookmark(); udlRowsToSkip.LowPart++; break;
default: m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons); ASSERT_(FALSE); VDSetErrorInfo(IDS_ERR_INVALIDBMSTATUS, IID_ICursor, m_pResourceDLL); return E_FAIL; }
hrFetch = GetRowsetLocate()->GetRowsAt(0, 0, cbBookmark, pBookmark, udlRowsToSkip.LowPart, pFetchParams->cRowsRequested, &cRowsObtained, &rghRows);
hrFetch = FilterNewRow(&cRowsObtained, rghRows, hrFetch);
if (S_OK != hrFetch) hrFetch = VDMapRowsetHRtoCursorHR(hrFetch, IDS_ERR_GETROWSATFAILED, IID_ICursor, GetRowsetLocate(), IID_IRowsetLocate, m_pResourceDLL);
if FAILED(hrFetch) { if (cRowsObtained) { // release hRows and associated memory
pRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL, NULL); g_pMalloc->Free(rghRows); } m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons); return hrFetch; }
if (cRowsObtained) { HRESULT hrMove = S_OK;
// if got all rows requested then set current position to last row retrieved
if (SUCCEEDED(hrFetch) && cRowsObtained == pFetchParams->cRowsRequested) hrMove = m_pCursorPosition->SetRowPosition(rghRows[cRowsObtained - 1]);
// only do this if succeeded
if (SUCCEEDED(hrMove)) { // fill consumers buffer
hrFetch = FillConsumersBuffer(hrFetch, pFetchParams, cRowsObtained, rghRows);
// if got all rows requested then set current position to last row retrieved (internally)
if (SUCCEEDED(hrFetch) && cRowsObtained == pFetchParams->cRowsRequested) m_pCursorPosition->SetCurrentHRow(rghRows[cRowsObtained - 1]); }
// release hRows and associated memory
pRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL, NULL); g_pMalloc->Free(rghRows);
// report failure
if (FAILED(hrMove)) { cRowsObtained = 0; hrFetch = E_FAIL; } }
if (SUCCEEDED(hrFetch) && cRowsObtained < pFetchParams->cRowsRequested) m_pCursorPosition->SetCurrentRowStatus(VDBOOKMARKSTATUS_END);
if SUCCEEDED(hrFetch) { rgReasons[0].arg1 = m_pCursorPosition->m_bmCurrent.GetBookmarkVariant(); m_pCursorPosition->NotifyAfter(dwEventWhat, 1, rgReasons); } else m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons);
return hrFetch; }
//=--------------------------------------------------------------------------=
// UseAdjustments - Use adjustments to fix-up returned data
//=--------------------------------------------------------------------------=
// This uses adjustments to fix-up returned data, see MakeAdjustments function
//
// Parameters:
// hRow - [in] row handle
// pData - [in] a pointer to data
//
// Output:
// S_OK - if successful
// E_INVALIDARG - bad parameter
// E_OUTOFMEMORY - not enough memory
//
// Notes:
//
HRESULT CVDCursor::UseAdjustments(HROW hRow, BYTE * pData) { ASSERT_POINTER(m_rghAdjustAccessors, HACCESSOR) ASSERT_POINTER(m_pdwAdjustFlags, DWORD) ASSERT_POINTER(pData, BYTE)
IRowset * pRowset = GetRowset();
// make sure we have a valid rowset pointer
if (!pRowset || !IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL); return E_FAIL; }
// make sure we have all neccessary pointers
if (!m_rghAdjustAccessors || !m_pdwAdjustFlags || !pData) return E_INVALIDARG;
// iterate through cursor bindings, checking for adjustments
for (ULONG ulBind = 0; ulBind < m_ulCursorBindings; ulBind++) { // check for variant binding byte field -> byte binding
if (m_pdwAdjustFlags[ulBind] == VD_ADJUST_VARIANT_TO_BYTE) { // get variant pointer
VARIANT * pVariant = (VARIANT*)(pData + m_pCursorBindings[ulBind].obData);
// extract byte
BYTE value = *(BYTE*)pVariant;
// init byte variant
VariantInit(pVariant);
// fix-up returned data
pVariant->vt = VT_UI1; pVariant->bVal = value; }
// check for variant binding date field -> wide string binding
if (m_pdwAdjustFlags[ulBind] == VD_ADJUST_VARIANT_TO_WSTR) { // get variant pointer
VARIANT * pVariant = (VARIANT*)(pData + m_pCursorBindings[ulBind].obData);
// extract length of string
ULONG ulLength = *(ULONG*)pVariant;
// place length field in proper place, if originally requested
if (m_pCursorBindings[ulBind].obVarDataLen != CURSOR_DB_NOVALUE) *(ULONG*)(pData + m_pCursorBindings[ulBind].obVarDataLen) = ulLength;
// init string variant
VariantInit(pVariant);
// create storage for string
BSTR bstr = SysAllocStringByteLen(NULL, ulLength);
if (!bstr) { VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL); return E_OUTOFMEMORY; }
// clear wide string
memset(bstr, 0, ulLength);
HRESULT hr = S_OK;
// get memo string value
if (ulLength) { hr = pRowset->GetData(hRow, m_rghAdjustAccessors[ulBind], bstr);
// ignore these return values
if (hr == DB_S_ERRORSOCCURRED || hr == DB_E_ERRORSOCCURRED) hr = S_OK; }
if (SUCCEEDED(hr)) { // fix-up returned data
pVariant->vt = VT_BSTR; pVariant->bstrVal = bstr; } else SysFreeString(bstr); }
// check for variant binding memo field -> string binding
if (m_pdwAdjustFlags[ulBind] == VD_ADJUST_VARIANT_TO_STR) { // get variant pointer
VARIANT * pVariant = (VARIANT*)(pData + m_pCursorBindings[ulBind].obData);
// extract length of string
ULONG ulLength = *(ULONG*)pVariant;
// place length field in proper place, if originally requested
if (m_pCursorBindings[ulBind].obVarDataLen != CURSOR_DB_NOVALUE) *(ULONG*)(pData + m_pCursorBindings[ulBind].obVarDataLen) = ulLength;
// init string variant
VariantInit(pVariant);
// create temporary string buffer
CHAR * pszBuffer = new CHAR[ulLength + 1];
if (!pszBuffer) { VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL); return E_OUTOFMEMORY; }
// clear string buffer
memset(pszBuffer, 0, ulLength + 1);
HRESULT hr = S_OK;
// get memo string value
if (ulLength) { hr = pRowset->GetData(hRow, m_rghAdjustAccessors[ulBind], pszBuffer);
// ignore these return values
if (hr == DB_S_ERRORSOCCURRED || hr == DB_E_ERRORSOCCURRED) hr = S_OK; }
if (SUCCEEDED(hr)) { // fix-up returned data
pVariant->vt = VT_BSTR; pVariant->bstrVal = BSTRFROMANSI(pszBuffer); }
delete [] pszBuffer; } }
return S_OK; }
//=--------------------------------------------------------------------------=
// FillConsumersBuffer
//=--------------------------------------------------------------------------=
// Fills the ICursor consumer's buffer with data from the obtained rows.
// Called from our implementations of GetNextRows, Move, Find, Scroll etc.
//
// Notes:
// End of string characters are inserted into variable length buffer to resolve an
// apparent difference between ICursor and IRowset. ICursor places an empty string
// in variable length buffer for NULL data, but this does not seem to be the behavior
// with IRowset, because it does not touch the variable length buffer in this case.
//
// Likewise, all variants are initialized before they are fetched to resolve another
// apparent difference between ICursor and IRowset. ICursor returns a NULL variant
// in cases where the underlying data is NULL, however IRowset leaves the variant
// untouched similar to the above.
//
HRESULT CVDCursor::FillConsumersBuffer(HRESULT hrFetch, CURSOR_DBFETCHROWS *pFetchParams, ULONG cRowsObtained, HROW * rghRows) { HRESULT hr; ULONG ulRow; ULONG ulBind; BYTE * pVarLength = NULL; BYTE * pVarHelperData = NULL; CURSOR_DBCOLUMNBINDING * pCursorBinding; BOOL fEntryIDBinding;
IRowset * pRowset = GetRowset();
// if caller requested callee allocated memory, then compute sizes and allocate memory
if (pFetchParams->dwFlags & CURSOR_DBROWFETCH_CALLEEALLOCATES) { // allocate inline memory
pFetchParams->pData = g_pMalloc->Alloc(cRowsObtained * m_cbRowLength);
if (!pFetchParams->pData) { VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL); return E_OUTOFMEMORY; }
// if needed, allocate out-of-line memory
if (m_ulVarBindings) { // create variable length data table
ULONG cbVarHelperData = cRowsObtained * m_ulVarBindings * (sizeof(ULONG) + sizeof(DBSTATUS));
pVarHelperData = new BYTE[cbVarHelperData];
if (!pVarHelperData) { g_pMalloc->Free(pFetchParams->pData); pFetchParams->pData = NULL; VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL); return E_OUTOFMEMORY; }
// clear table
memset(pVarHelperData, 0, cbVarHelperData);
ULONG cbVarData = 0; pVarLength = pVarHelperData;
// determine necessary size of variable length buffer
for (ulRow = 0; ulRow < cRowsObtained; ulRow++) { hr = S_OK;
// if necessary, get variable length and status information
if (m_hVarHelper) { hr = pRowset->GetData(rghRows[ulRow], m_hVarHelper, pVarLength);
// ignore these errors
if (hr == DB_S_ERRORSOCCURRED || hr == DB_E_ERRORSOCCURRED) hr = S_OK;
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_ICursor, pRowset, IID_IRowset, m_pResourceDLL); }
if (FAILED(hr)) { g_pMalloc->Free(pFetchParams->pData); pFetchParams->pData = NULL; delete [] pVarHelperData; return hr; }
pCursorBinding = m_pCursorBindings;
// calculate sizes of data returned in out-of-line memory
for (ulBind = 0; ulBind < m_ulCursorBindings; ulBind++) { // set entryID binding flag
fEntryIDBinding = (pCursorBinding->dwBinding & CURSOR_DBBINDING_ENTRYID);
if (m_rghVarAccessors[ulBind] || fEntryIDBinding && pCursorBinding->dwDataType == CURSOR_DBTYPE_BLOB) { // insert length entries for fixed datatypes
if (m_ppColumns[ulBind]->GetFixed()) { *(ULONG*)pVarLength = m_ppColumns[ulBind]->GetMaxStrLen(); *(DBSTATUS*)(pVarLength + sizeof(ULONG)) = DBSTATUS_S_OK; }
// insert length entries for entryID bindings
if (fEntryIDBinding) { *(ULONG*)pVarLength = sizeof(ULONG) + sizeof(ULONG) + GetCursorMain()->GetMaxBookmarkLen(); *(DBSTATUS*)(pVarLength + sizeof(ULONG)) = DBSTATUS_S_OK; }
// allow for null-terminator
if (pCursorBinding->dwDataType == CURSOR_DBTYPE_LPSTR || pCursorBinding->dwDataType == CURSOR_DBTYPE_LPWSTR) *((ULONG*)pVarLength) += 1;
// allow for wide characters
if (pCursorBinding->dwDataType == CURSOR_DBTYPE_LPWSTR) *((ULONG*)pVarLength) *= sizeof(WCHAR);
cbVarData += *(ULONG*)pVarLength; pVarLength += sizeof(ULONG) + sizeof(DBSTATUS); }
pCursorBinding++; } }
// now, allocate out-of-line memory
pFetchParams->pVarData = g_pMalloc->Alloc(cbVarData);
if (!pFetchParams->pData) { g_pMalloc->Free(pFetchParams->pData); pFetchParams->pData = NULL; delete [] pVarHelperData; VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL); return E_OUTOFMEMORY; } } else pFetchParams->pVarData = NULL; }
// fetch data
CURSOR_BLOB cursorBlob; BYTE * pData = (BYTE*)pFetchParams->pData; BYTE * pVarData = (BYTE*)pFetchParams->pVarData; pVarLength = pVarHelperData;
// iterate through the returned hRows
for (ulRow = 0; ulRow < cRowsObtained; ulRow++) { hr = S_OK;
pCursorBinding = m_pCursorBindings;
// iterate through bindings and initialize variants
for (ulBind = 0; ulBind < m_ulCursorBindings; ulBind++) { if (pCursorBinding->dwBinding & CURSOR_DBBINDING_VARIANT) { if (pCursorBinding->obData != CURSOR_DB_NOVALUE) VariantInit((VARIANT*)(pData + pCursorBinding->obData)); }
pCursorBinding++; }
// if necessary get fixed length data
if (m_hAccessor) { hr = pRowset->GetData(rghRows[ulRow], m_hAccessor, pData);
// ignore these return values
if (hr == DB_S_ERRORSOCCURRED || hr == DB_E_ERRORSOCCURRED) hr = S_OK;
// check to see if need to use adjustments
if (m_rghAdjustAccessors && SUCCEEDED(hr)) hr = UseAdjustments(rghRows[ulRow], pData);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_ICursor, pRowset, IID_IRowset, m_pResourceDLL); }
if (FAILED(hr)) { hrFetch = hr; pFetchParams->cRowsReturned = 0; goto DoneFetchingData; }
pCursorBinding = m_pCursorBindings;
// if necessary get fixed length entryIDs
for (ulBind = 0; ulBind < m_ulCursorBindings; ulBind++) { // set entryID binding flag
fEntryIDBinding = (pCursorBinding->dwBinding & CURSOR_DBBINDING_ENTRYID);
if (fEntryIDBinding && pCursorBinding->dwDataType == CURSOR_DBTYPE_BYTES) { // return entryID length
*(ULONG*)(pData + pCursorBinding->obData) = sizeof(ULONG) + sizeof(ULONG) + GetCursorMain()->GetMaxBookmarkLen();
// return column ordinal
*(ULONG*)(pData + pCursorBinding->obData + sizeof(ULONG)) = m_ppColumns[ulBind]->GetOrdinal();
// return row bookmark
hr = pRowset->GetData(rghRows[ulRow], GetCursorMain()->GetBookmarkAccessor(), pData + pCursorBinding->obData + sizeof(ULONG) + sizeof(ULONG));
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_ICursor, pRowset, IID_IRowset, m_pResourceDLL);
if (FAILED(hr)) { hrFetch = hr; pFetchParams->cRowsReturned = 0; goto DoneFetchingData; } }
pCursorBinding++; }
pCursorBinding = m_pCursorBindings;
// if necessary get variable length data
if (m_rghVarAccessors) { for (ulBind = 0; ulBind < m_ulCursorBindings; ulBind++) { // set entryID binding flag
fEntryIDBinding = (pCursorBinding->dwBinding & CURSOR_DBBINDING_ENTRYID);
if (m_rghVarAccessors[ulBind] || fEntryIDBinding && pCursorBinding->dwDataType == CURSOR_DBTYPE_BLOB) { // place end of string characters in variable length buffer
if (pCursorBinding->dwDataType == CURSOR_DBTYPE_LPSTR) { pVarData[0] = 0; } else if (pCursorBinding->dwDataType == CURSOR_DBTYPE_LPWSTR) { pVarData[0] = 0; pVarData[1] = 0; }
// get data if we have accessor
if (m_rghVarAccessors[ulBind]) { // get variable length data
hr = pRowset->GetData(rghRows[ulRow], m_rghVarAccessors[ulBind], pVarData);
// ignore these return values
if (hr == DB_S_ERRORSOCCURRED || hr == DB_E_ERRORSOCCURRED) hr = S_OK;
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_ICursor, pRowset, IID_IRowset, m_pResourceDLL); } else // otherwise, get variable length entryIDs
{ // return entryID length
*(ULONG*)(pData + pCursorBinding->obData) = sizeof(ULONG) + sizeof(ULONG) + GetCursorMain()->GetMaxBookmarkLen();
// return column ordinal
*(ULONG*)(pVarData) = m_ppColumns[ulBind]->GetOrdinal();
// return row bookmark
hr = pRowset->GetData(rghRows[ulRow], GetCursorMain()->GetBookmarkAccessor(), pVarData + sizeof(ULONG));
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_ICursor, pRowset, IID_IRowset, m_pResourceDLL); }
if (FAILED(hr)) { hrFetch = hr; pFetchParams->cRowsReturned = 0; goto DoneFetchingData; }
// make adjustments in fixed length buffer for default bindings
if (!(pCursorBinding->dwBinding & CURSOR_DBBINDING_VARIANT)) { switch (pCursorBinding->dwDataType) { case CURSOR_DBTYPE_BLOB: *(LPBYTE*)(pData + pCursorBinding->obData + sizeof(ULONG)) = (LPBYTE)pVarData; break;
case CURSOR_DBTYPE_LPSTR: *(LPSTR*)(pData + pCursorBinding->obData) = (LPSTR)pVarData; break;
case CURSOR_DBTYPE_LPWSTR: *(LPWSTR*)(pData + pCursorBinding->obData) = (LPWSTR)pVarData; break; } } else // make adjustments in fixed length buffer for variant bindings
{ CURSOR_DBVARIANT * pVariant = (CURSOR_DBVARIANT*)(pData + pCursorBinding->obData);
switch (pCursorBinding->dwDataType) { case CURSOR_DBTYPE_BLOB: cursorBlob.cbSize = *(ULONG*)pVariant; cursorBlob.pBlobData = (LPBYTE)pVarData; VariantInit((VARIANT*)pVariant); pVariant->vt = CURSOR_DBTYPE_BLOB; pVariant->blob = cursorBlob; break;
case CURSOR_DBTYPE_LPSTR: VariantInit((VARIANT*)pVariant); pVariant->vt = CURSOR_DBTYPE_LPSTR; pVariant->pszVal = (LPSTR)pVarData; break;
case CURSOR_DBTYPE_LPWSTR: VariantInit((VARIANT*)pVariant); pVariant->vt = CURSOR_DBTYPE_LPSTR; pVariant->pwszVal = (LPWSTR)pVarData; break; } }
if (pVarLength) { pVarData += *(ULONG*)pVarLength; pVarLength += sizeof(ULONG) + sizeof(DBSTATUS); } else { if (pCursorBinding->dwDataType == CURSOR_DBTYPE_BLOB) pVarData += *(ULONG *) (pData + pCursorBinding->obData); else pVarData += pCursorBinding->cbMaxLen; } } else { if (pCursorBinding->dwDataType == CURSOR_DBTYPE_FILETIME) { VDConvertToFileTime((DBTIMESTAMP*)(pData + pCursorBinding->obData), (FILETIME*)(pData + pCursorBinding->obData)); } }
pCursorBinding++; } }
pCursorBinding = m_pCursorBindings;
// make adjustments for status fields
for (ulBind = 0; ulBind < m_ulCursorBindings; ulBind++) { if (pCursorBinding->obInfo != CURSOR_DB_NOVALUE) { *(DWORD*)(pData + pCursorBinding->obInfo) = StatusToCursorInfo(*(DBSTATUS*)(pData + pCursorBinding->obInfo)); }
pCursorBinding++; }
// increment returned row count
pFetchParams->cRowsReturned++; pData += m_cbRowLength; }
DoneFetchingData: delete [] pVarHelperData;
// cleanup memory allocations if we did not retrieve any rows
if (pFetchParams->dwFlags & CURSOR_DBROWFETCH_CALLEEALLOCATES && !pFetchParams->cRowsReturned) { if (pFetchParams->pData) { g_pMalloc->Free(pFetchParams->pData); pFetchParams->pData = NULL; }
if (pFetchParams->pVarData) { g_pMalloc->Free(pFetchParams->pVarData); pFetchParams->pVarData = NULL; } }
return hrFetch; }
//=--------------------------------------------------------------------------=
// ICursor Requery
//=--------------------------------------------------------------------------=
// Repopulates the cursor based on its original definition
//
// Parameters:
// none
//
// Output:
// HRESULT - S_OK if successful
//
// Notes:
//
HRESULT CVDCursor::Requery(void) {
if (!IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL); return E_FAIL; }
IRowset * pRowset = GetRowset(); IRowsetResynch * pRowsetResynch = NULL;
HRESULT hr = pRowset->QueryInterface(IID_IRowsetResynch, (void**)&pRowsetResynch);
if (SUCCEEDED(hr)) { hr = pRowsetResynch->ResynchRows(0, NULL, NULL, NULL, NULL); pRowsetResynch->Release(); if (FAILED(hr)) return VDMapRowsetHRtoCursorHR(hr, IDS_ERR_RESYNCHFAILED, IID_ICursor, pRowset, IID_IRowsetResynch, m_pResourceDLL); }
hr = pRowset->RestartPosition(0);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_RESTARTPOSFAILED, IID_ICursor, pRowset, IID_IRowset, m_pResourceDLL);
m_pCursorPosition->PositionToFirstRow();
return hr; }
//=--------------------------------------------------------------------------=
// FetchAtBookmark
//=--------------------------------------------------------------------------=
// Called from ICursorMove::Move, ICursorScroll::Scroll and ICursorFind::Find
//
// Parameters:
// cbBookmark [in] The length of the bookmark
// pBookmark [in] A pointer to the bookmarks data
// dlOffset [in] Offset from the bookmark position
// pFetchParams [in] A pointer to the CURSOR_DBFETCHROWS structure (optional)
//
// Output:
// HRESULT - S_OK if successful
//
// Notes:
//
//
HRESULT CVDCursor::FetchAtBookmark(ULONG cbBookmark, void *pBookmark, LARGE_INTEGER dlOffset, CURSOR_DBFETCHROWS *pFetchParams) {
HRESULT hr = S_OK;
// vaildate fetch params (implemented on CVDCursorBase
if (pFetchParams) hr = ValidateFetchParams(pFetchParams, IID_ICursorMove);
// return if fetch params are invalid
if (FAILED(hr)) return hr;
IRowset * pRowset = GetRowset();
// make sure that an update is not already in progress
if (m_pCursorPosition->GetEditMode() != CURSOR_DBEDITMODE_NONE) { VDSetErrorInfo(IDS_ERR_UPDATEINPROGRESS, IID_ICursor, m_pResourceDLL); return CURSOR_DB_E_UPDATEINPROGRESS; }
ULONG cRowsObtained = 0; HROW * rghRows = NULL;
HRESULT hrFetch = S_OK; BYTE bSpecialBM; BOOL fFetchData = TRUE; WORD wSpecialBMStatus = 0;
if (CURSOR_DB_BMK_SIZE == cbBookmark) { if (memcmp(&CURSOR_DBBMK_BEGINNING, pBookmark, CURSOR_DB_BMK_SIZE) == 0) { if ((long)dlOffset.LowPart < 0) { m_pCursorPosition->SetCurrentRowStatus(VDBOOKMARKSTATUS_BEGINNING); return CURSOR_DB_S_ENDOFCURSOR; }
bSpecialBM = DBBMK_FIRST; pBookmark = &bSpecialBM;
// make sure we properly handle situation when caller move before the first,
// and does not fetch any rows
if ((!pFetchParams || !pFetchParams->cRowsRequested) && (long)dlOffset.LowPart < 1) { fFetchData = FALSE; wSpecialBMStatus = VDBOOKMARKSTATUS_BEGINNING; } else dlOffset.LowPart--; } else if (memcmp(&CURSOR_DBBMK_END, pBookmark, CURSOR_DB_BMK_SIZE) == 0) { if ((long)dlOffset.LowPart > 0) { m_pCursorPosition->SetCurrentRowStatus(VDBOOKMARKSTATUS_END); return CURSOR_DB_S_ENDOFCURSOR; }
bSpecialBM = DBBMK_LAST; pBookmark = &bSpecialBM;
// make sure we properly handle situation when caller move after the last
if ((!pFetchParams || !pFetchParams->cRowsRequested) && (long)dlOffset.LowPart > -1) { fFetchData = FALSE; wSpecialBMStatus = VDBOOKMARKSTATUS_END; } else dlOffset.LowPart++; } else if (memcmp(&CURSOR_DBBMK_CURRENT, pBookmark, CURSOR_DB_BMK_SIZE) == 0) { switch (m_pCursorPosition->m_bmCurrent.GetStatus()) { case VDBOOKMARKSTATUS_BEGINNING: cbBookmark = sizeof(BYTE); bSpecialBM = DBBMK_FIRST; pBookmark = &bSpecialBM; dlOffset.LowPart--; break;
case VDBOOKMARKSTATUS_END: cbBookmark = sizeof(BYTE); bSpecialBM = DBBMK_LAST; pBookmark = &bSpecialBM; dlOffset.LowPart++; break;
case VDBOOKMARKSTATUS_CURRENT: cbBookmark = m_pCursorPosition->m_bmCurrent.GetBookmarkLen(); pBookmark = m_pCursorPosition->m_bmCurrent.GetBookmark(); break;
default: ASSERT_(FALSE); VDSetErrorInfo(IDS_ERR_INVALIDBMSTATUS, IID_ICursor, m_pResourceDLL); return E_FAIL; } } } ULONG cRowsToFetch = 1;
// if caller requested rows, fetch that count
if (pFetchParams && pFetchParams->cRowsRequested > 0) cRowsToFetch = pFetchParams->cRowsRequested;
if (fFetchData) { // fetch hRows
hrFetch = GetRowsetLocate()->GetRowsAt(0, 0, cbBookmark, (const BYTE *)pBookmark, dlOffset.LowPart, cRowsToFetch, &cRowsObtained, &rghRows);
if (hrFetch == E_UNEXPECTED) { // set rowset released flag, since original rowset is zombie'd
m_pCursorPosition->GetRowsetSource()->SetRowsetReleasedFlag(); }
hrFetch = FilterNewRow(&cRowsObtained, rghRows, hrFetch); // check for before the first or after the last
if (hrFetch == DB_E_BADSTARTPOSITION) { if ((long)dlOffset.LowPart < 0) wSpecialBMStatus = VDBOOKMARKSTATUS_BEGINNING; else wSpecialBMStatus = VDBOOKMARKSTATUS_END;
hrFetch = DB_S_ENDOFROWSET; fFetchData = FALSE; } }
hrFetch = VDMapRowsetHRtoCursorHR(hrFetch, IDS_ERR_GETROWSATFAILED, IID_ICursorMove, GetRowsetLocate(), IID_IRowsetLocate, m_pResourceDLL);
if (FAILED(hrFetch)) { if (cRowsObtained) { // release hRows and associated memory
pRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL, NULL); g_pMalloc->Free(rghRows); } return hrFetch; }
if (cRowsObtained) { HRESULT hrMove = S_OK;
// if got all rows requested then set current position to last row retrieved
if (SUCCEEDED(hrFetch) && cRowsObtained == cRowsToFetch) hrMove = m_pCursorPosition->SetRowPosition(rghRows[cRowsObtained - 1]);
// only do this if succeeded
if (SUCCEEDED(hrMove)) { // fill consumers buffer
if (pFetchParams && pFetchParams->cRowsRequested > 0) hrFetch = FillConsumersBuffer(hrFetch, pFetchParams, cRowsObtained, rghRows);
// if got all rows requested then set current position to last row retrieved (internally)
if (SUCCEEDED(hrFetch) && cRowsObtained == cRowsToFetch) m_pCursorPosition->SetCurrentHRow(rghRows[cRowsObtained - 1]); }
// release hRows and associated memory
pRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL, NULL); g_pMalloc->Free(rghRows);
// report failure
if (FAILED(hrMove)) { cRowsObtained = 0; hrFetch = E_FAIL; } } else if (wSpecialBMStatus) { m_pCursorPosition->SetCurrentRowStatus(wSpecialBMStatus); hrFetch = CURSOR_DB_S_ENDOFCURSOR; }
if (SUCCEEDED(hrFetch) && cRowsObtained < cRowsToFetch && !wSpecialBMStatus) m_pCursorPosition->SetCurrentRowStatus(VDBOOKMARKSTATUS_END);
return hrFetch; }
//=--------------------------------------------------------------------------=
// ICursorMove methods implemented
//=--------------------------------------------------------------------------=
//=--------------------------------------------------------------------------=
// ICursorMove Move
//=--------------------------------------------------------------------------=
// Moves the current row to a new row within the cursor and optionally fetches
// rows from that new position
//
// Parameters:
// cbBookmark - [in] length in bytes of the bookmark
// pBookmark - [in] a pointer to a bookmark which serves as the
// origin for the calculation that determines the
// target row
// dlOffset - [in] a signed count of the rows from the origin
// bookmark to the target row
// pFetchParams - [in, out] a pointer to fetch rows structure
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
//
// Notes:
//
HRESULT CVDCursor::Move(ULONG cbBookmark, void *pBookmark, LARGE_INTEGER dlOffset, CURSOR_DBFETCHROWS *pFetchParams) { ASSERT_POINTER(pBookmark, BYTE) ASSERT_NULL_OR_POINTER(pFetchParams, CURSOR_DBFETCHROWS)
if (!IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorMove, m_pResourceDLL); return E_FAIL; }
if (!cbBookmark || !pBookmark) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorMove, m_pResourceDLL); return E_INVALIDARG; }
HRESULT hr = S_OK; BOOL fNotifyOthers = TRUE;
// get current bookmark
ULONG cbCurrent = m_pCursorPosition->m_bmCurrent.GetBookmarkLen(); BYTE * pCurrent = m_pCursorPosition->m_bmCurrent.GetBookmark();
// check to see if caller is moving to the current row using the standard bookmark
if (CURSOR_DB_BMK_SIZE == cbBookmark && memcmp(&CURSOR_DBBMK_CURRENT, pBookmark, CURSOR_DB_BMK_SIZE) == 0 && dlOffset.HighPart == 0 && dlOffset.LowPart == 0) { // if caller is not fetching any rows, then get out
if (!pFetchParams || pFetchParams->cRowsRequested == 0) return S_OK;
// if caller is only fetching one row, then don't generate notifications
if (pFetchParams && pFetchParams->cRowsRequested == 1) fNotifyOthers = FALSE; }
CURSOR_DBNOTIFYREASON rgReasons[1]; rgReasons[0].dwReason = CURSOR_DBREASON_MOVE; rgReasons[0].arg1 = m_pCursorPosition->m_bmCurrent.GetBookmarkVariant();
VariantInit((VARIANT*)&rgReasons[0].arg2);
// notify other interested parties
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_CHANGED;
if (fNotifyOthers) hr = m_pCursorPosition->NotifyBefore(dwEventWhat, 1, rgReasons);
// make sure action was not cancelled
if (hr != S_OK) { VDSetErrorInfo(IDS_ERR_ACTIONCANCELLED, IID_ICursorMove, m_pResourceDLL); return E_FAIL; }
hr = FetchAtBookmark(cbBookmark, pBookmark, dlOffset, pFetchParams);
if (SUCCEEDED(hr)) { rgReasons[0].arg1 = m_pCursorPosition->m_bmCurrent.GetBookmarkVariant(); if (fNotifyOthers) m_pCursorPosition->NotifyAfter(dwEventWhat, 1, rgReasons); } else { if (fNotifyOthers) m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons); }
return hr; }
//=--------------------------------------------------------------------------=
// ICursorMove GetBookmark
//=--------------------------------------------------------------------------=
// Returns the bookmark of the current row
//
// Parameters:
// pBookmarkType - [in] a pointer to the type of bookmark desired
// cbMaxSize - [in] length in bytes of the client buffer to put the
// returned bookmark into
// pcbBookmark - [out] a pointer to memory in which to return the actual
// length of the returned bookmark
// pBookmark - [out] a pointer to client buffer to put the returned
// bookmark into
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
//
// Notes:
//
HRESULT CVDCursor::GetBookmark(CURSOR_DBCOLUMNID *pBookmarkType, ULONG cbMaxSize, ULONG *pcbBookmark, void *pBookmark) {
ASSERT_POINTER(pBookmarkType, CURSOR_DBCOLUMNID); ASSERT_POINTER(pcbBookmark, ULONG); ASSERT_POINTER(pBookmark, BYTE); ASSERT_(cbMaxSize > 0)
if (!IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorMove, m_pResourceDLL); return E_FAIL; }
if (!pcbBookmark || !pBookmark) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorMove, m_pResourceDLL); return E_INVALIDARG; }
// verify bookmark type
if (memcmp(&CURSOR_COLUMN_BMKTEMPORARY, pBookmarkType, sizeof(CURSOR_DBCOLUMNID)) != 0 && memcmp(&CURSOR_COLUMN_BMKTEMPORARYREL, pBookmarkType, sizeof(CURSOR_DBCOLUMNID)) != 0) { VDSetErrorInfo(IDS_ERR_BADCOLUMNID, IID_ICursorMove, m_pResourceDLL); return DB_E_BADCOLUMNID; }
HRESULT hr = S_OK;
if (0 == cbMaxSize) { VDSetErrorInfo(IDS_ERR_BUFFERTOOSMALL, IID_ICursorMove, m_pResourceDLL); hr = CURSOR_DB_E_BUFFERTOOSMALL; } else { switch (m_pCursorPosition->m_bmCurrent.GetStatus()) { case VDBOOKMARKSTATUS_BEGINNING: case VDBOOKMARKSTATUS_END: case VDBOOKMARKSTATUS_CURRENT: if (m_pCursorPosition->m_bmCurrent.GetBookmarkLen() > cbMaxSize) { VDSetErrorInfo(IDS_ERR_BUFFERTOOSMALL, IID_ICursorMove, m_pResourceDLL); hr = CURSOR_DB_E_BUFFERTOOSMALL; break; } *pcbBookmark = m_pCursorPosition->m_bmCurrent.GetBookmarkLen(); memcpy(pBookmark, m_pCursorPosition->m_bmCurrent.GetBookmark(), *pcbBookmark); break;
case VDBOOKMARKSTATUS_INVALID: *pcbBookmark = CURSOR_DB_BMK_SIZE; *(BYTE*)pBookmark = CURSOR_DBBMK_INVALID; break;
default: ASSERT_(FALSE); VDSetErrorInfo(IDS_ERR_INVALIDBMSTATUS, IID_ICursorMove, m_pResourceDLL); hr = E_FAIL; break; } }
return hr; }
//=--------------------------------------------------------------------------=
// ICursorMove Clone
//=--------------------------------------------------------------------------=
// Returns a clone of the cursor
//
// Parameters:
// dwFlags - [in] a flag that specifies the clone options
// riid - [in] the interface desired for the returned clone
// ppvClonedCursor - [out] a pointer to memory in which to return newly
// created clone pointer
//
// Output:
// HRESULT - S_OK if successful
//
// Notes:
//
HRESULT CVDCursor::Clone(DWORD dwFlags, REFIID riid, IUnknown **ppvClonedCursor) {
if (!IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorMove, m_pResourceDLL); return E_FAIL; }
CVDCursorPosition * pCursorPosition;
HRESULT hr;
if (CURSOR_DBCLONEOPTS_SAMEROW == dwFlags) { pCursorPosition = m_pCursorPosition; } else { // create new cursor position object
hr = CVDCursorPosition::Create(NULL, m_pCursorPosition->GetCursorMain(), &pCursorPosition, m_pResourceDLL); if (FAILED(hr)) return hr; }
CVDCursor * pCursor = 0;
hr = CVDCursor::Create(pCursorPosition, &pCursor, m_pResourceDLL);
if (CURSOR_DBCLONEOPTS_SAMEROW != dwFlags) { // release our reference
pCursorPosition->Release(); }
*ppvClonedCursor = (ICursorScroll*)pCursor;
return hr; }
//=--------------------------------------------------------------------------=
// ICursorScroll methods implemented
//=--------------------------------------------------------------------------=
//=--------------------------------------------------------------------------=
// ICursorScroll Scroll
//=--------------------------------------------------------------------------=
// Moves the current row to a new row within the cursor, specified as a
// fraction, and optionally fetches rows from that new position
//
// Parameters:
// ulNumerator - [in] the numerator of the fraction that states the
// position to scroll to in the cursor
// ulDenominator - [in] the denominator of that same fraction
// pFetchParams - [in, out] a pointer to fetch rows structure
//
// Output:
// HRESULT - S_OK if successful
// CURSOR_DB_E_BADFRACTION - bad fraction
//
// Notes:
//
HRESULT CVDCursor::Scroll(ULONG ulNumerator, ULONG ulDenominator, CURSOR_DBFETCHROWS *pFetchParams) {
if (!IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorScroll, m_pResourceDLL); return E_FAIL; }
IRowsetScroll * pRowsetScroll = GetRowsetScroll();
if (!pRowsetScroll) return E_NOTIMPL;
CURSOR_DBNOTIFYREASON rgReasons[1];
rgReasons[0].dwReason = CURSOR_DBREASON_MOVEPERCENT; VariantInit((VARIANT*)&rgReasons[0].arg1); rgReasons[0].arg1.vt = VT_UI4; rgReasons[0].arg1.lVal = ulNumerator;
VariantInit((VARIANT*)&rgReasons[0].arg2); rgReasons[0].arg2.vt = VT_UI4; rgReasons[0].arg2.lVal = ulDenominator;
// notify other interested parties
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_CHANGED;
HRESULT hr = m_pCursorPosition->NotifyBefore(dwEventWhat, 1, rgReasons);
// make sure action was not cancelled
if (hr != S_OK) { VDSetErrorInfo(IDS_ERR_ACTIONCANCELLED, IID_ICursorScroll, m_pResourceDLL); return E_FAIL; }
if (0 == ulNumerator) // go to first row
{ LARGE_INTEGER dlOffset; dlOffset.HighPart = 0; dlOffset.LowPart = 1; hr = FetchAtBookmark(CURSOR_DB_BMK_SIZE, (void*)&CURSOR_DBBMK_BEGINNING, dlOffset, pFetchParams); } else if (ulDenominator == ulNumerator) // go to last row
{ LARGE_INTEGER dlOffset; dlOffset.HighPart = -1; dlOffset.LowPart = 0xFFFFFFFF; hr = FetchAtBookmark(CURSOR_DB_BMK_SIZE, (void*)&CURSOR_DBBMK_END, dlOffset, pFetchParams); } else { HROW * pRow = NULL; ULONG cRowsObtained = 0;
hr = pRowsetScroll->GetRowsAtRatio(0, 0, ulNumerator, ulDenominator, 1, &cRowsObtained, &pRow);
if FAILED(hr) hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_SCROLLFAILED, IID_ICursorScroll, pRowsetScroll, IID_IRowsetScroll, m_pResourceDLL);
if (SUCCEEDED(hr) && cRowsObtained) {
// allocate buffer for bookmark plus length indicator
BYTE * pBuff = new BYTE[GetCursorMain()->GetMaxBookmarkLen() + sizeof(ULONG)]; if (!pBuff) hr = E_OUTOFMEMORY; else { // get the bookmark data
hr = GetRowset()->GetData(*pRow, GetCursorMain()->GetBookmarkAccessor(), pBuff); if SUCCEEDED(hr) { ULONG * pulLen = (ULONG*)pBuff; BYTE * pbmdata = pBuff + sizeof(ULONG); LARGE_INTEGER dlOffset; dlOffset.HighPart = 0; dlOffset.LowPart = 0; hr = FetchAtBookmark(*pulLen, pbmdata, dlOffset, pFetchParams); } else { hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_ICursorScroll, pRowsetScroll, IID_IRowset, m_pResourceDLL); }
delete [] pBuff; }
} if (pRow) { if (cRowsObtained) GetRowset()->ReleaseRows(1, pRow, NULL, NULL, NULL); g_pMalloc->Free(pRow); } }
if SUCCEEDED(hr) { rgReasons[0].arg1 = m_pCursorPosition->m_bmCurrent.GetBookmarkVariant(); VariantClear((VARIANT*)&rgReasons[0].arg2); m_pCursorPosition->NotifyAfter(dwEventWhat, 1, rgReasons); } else m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons);
return hr; }
//=--------------------------------------------------------------------------=
// ICursorScroll GetApproximatePosition
//=--------------------------------------------------------------------------=
// Returns the approximate location of a bookmark within the cursor, specified
// as a fraction
//
// Parameters:
// cbBookmark - [in] length in bytes of the bookmark
// pBookmark - [in] a pointer to the bookmark
// pulNumerator - [out] a pointer to memory in which to return the
// numerator of the faction that defines the
// approximate position of the bookmark
// pulDenominator - [out] a pointer to memory in which to return the
// denominator of that same faction
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
//
// Notes:
//
HRESULT CVDCursor::GetApproximatePosition(ULONG cbBookmark, void *pBookmark, ULONG *pulNumerator, ULONG *pulDenominator) {
if (!IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorScroll, m_pResourceDLL); return E_FAIL; }
ASSERT_(cbBookmark); ASSERT_POINTER(pBookmark, BYTE); ASSERT_POINTER(pulNumerator, ULONG); ASSERT_POINTER(pulDenominator, ULONG);
if (!cbBookmark || !pBookmark || !pulNumerator || !pulDenominator) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorScroll, m_pResourceDLL); return E_INVALIDARG; }
if (CURSOR_DB_BMK_SIZE == cbBookmark) { if (memcmp(&CURSOR_DBBMK_BEGINNING, pBookmark, CURSOR_DB_BMK_SIZE) == 0) { *pulNumerator = 0; *pulDenominator = 1; return S_OK; } if (memcmp(&CURSOR_DBBMK_END, pBookmark, CURSOR_DB_BMK_SIZE) == 0) { *pulNumerator = 1; *pulDenominator = 1; return S_OK; } if (memcmp(&CURSOR_DBBMK_CURRENT, pBookmark, CURSOR_DB_BMK_SIZE) == 0) { cbBookmark = m_pCursorPosition->m_bmCurrent.GetBookmarkLen(); pBookmark = m_pCursorPosition->m_bmCurrent.GetBookmark(); } }
IRowsetScroll * pRowsetScroll = GetRowsetScroll();
if (!pRowsetScroll) return E_NOTIMPL;
HRESULT hr = pRowsetScroll->GetApproximatePosition(0, cbBookmark, (const BYTE *)pBookmark, pulNumerator, pulDenominator);
if SUCCEEDED(hr) { // since ICursor returns a zero based approximate position and IRowset is 1 based
// we need to adjust the return value
if (0 < *pulNumerator) (*pulNumerator)--; } else hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETAPPROXPOSFAILED, IID_ICursorScroll, pRowsetScroll, IID_IRowsetScroll, m_pResourceDLL);
return hr;
}
//=--------------------------------------------------------------------------=
// ICursorScroll GetApproximateCount
//=--------------------------------------------------------------------------=
// Returns the approximate number of rows in the cursor
//
// Parameters:
// pudlApproxCount - [out] a pointer to a buffer containing the
// returned approximate count of the rows
// in the cursor
// pdwFullyPopuldated - [out] a pointer to a buffer containing returned
// flags indicating whether the cursor is fully
// populated
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
//
// Notes:
//
HRESULT CVDCursor::GetApproximateCount(LARGE_INTEGER *pudlApproxCount, DWORD *pdwFullyPopulated) {
if (!IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorScroll, m_pResourceDLL); return E_FAIL; }
ASSERT_POINTER(pudlApproxCount, LARGE_INTEGER); ASSERT_NULL_OR_POINTER(pdwFullyPopulated, DWORD);
if (!pudlApproxCount) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorScroll, m_pResourceDLL); return E_INVALIDARG; }
IRowsetScroll * pRowsetScroll = GetRowsetScroll();
if (!pRowsetScroll) return E_NOTIMPL;
HRESULT hr;
if (pdwFullyPopulated) { *pdwFullyPopulated = CURSOR_DBCURSORPOPULATED_FULLY;
IDBAsynchStatus * pDBAsynchStatus = NULL; hr = pRowsetScroll->QueryInterface(IID_IDBAsynchStatus, (void**)&pDBAsynchStatus); if (SUCCEEDED(hr) && pDBAsynchStatus) { ULONG ulProgress; ULONG ulProgressMax; ULONG ulStatusCode; hr = pDBAsynchStatus->GetStatus(DB_NULL_HCHAPTER, DBASYNCHOP_OPEN, &ulProgress, &ulProgressMax, &ulStatusCode, NULL); if (SUCCEEDED(hr)) { if (ulProgress < ulProgressMax) *pdwFullyPopulated = CURSOR_DBCURSORPOPULATED_PARTIALLY; } pDBAsynchStatus->Release(); } }
pudlApproxCount->HighPart = 0;
hr = pRowsetScroll->GetApproximatePosition(0, 0, NULL, NULL, &pudlApproxCount->LowPart);
if FAILED(hr) hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETAPPROXPOSFAILED, IID_ICursorScroll, pRowsetScroll, IID_IRowsetScroll, m_pResourceDLL); else pudlApproxCount->LowPart -= m_pCursorPosition->GetCursorMain()->AddedRows();
return hr; }
//=--------------------------------------------------------------------------=
// ICursorUpdateARow methods
//=--------------------------------------------------------------------------=
// ICursorUpdateARow BeginUpdate
//=--------------------------------------------------------------------------=
// Begins an operation that updates the current or adds a new row
//
// Parameters:
// dwFlags - [in] specifies the operation to begin
//
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL a provider-specific error occured
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory
// CURSOR_DB_E_UPDATEINPROGRESS an update is already in progress
//
// Notes:
//
HRESULT CVDCursor::BeginUpdate(DWORD dwFlags) { IRowset * pRowset = GetRowset(); IAccessor * pAccessor = GetAccessor(); IRowsetChange * pRowsetChange = GetRowsetChange();
// make sure we have valid rowset, accessor and change pointers
if (!pRowset || !pAccessor || !pRowsetChange || !IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorUpdateARow, m_pResourceDLL); return E_FAIL; }
// check dwFlags for acceptable values
if (dwFlags != CURSOR_DBROWACTION_UPDATE && dwFlags != CURSOR_DBROWACTION_ADD) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorUpdateARow, m_pResourceDLL); return E_INVALIDARG; }
// make sure that an update is not already in progress
if (m_pCursorPosition->GetEditMode() != CURSOR_DBEDITMODE_NONE) { VDSetErrorInfo(IDS_ERR_UPDATEINPROGRESS, IID_ICursorUpdateARow, m_pResourceDLL); return CURSOR_DB_E_UPDATEINPROGRESS; }
// setup notification structures
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED | CURSOR_DBEVENT_SET_OF_ROWS_CHANGED;
CURSOR_DBNOTIFYREASON rgReasons[1]; VariantInit((VARIANT*)&rgReasons[0].arg1); VariantInit((VARIANT*)&rgReasons[0].arg2);
switch (dwFlags) { case CURSOR_DBROWACTION_UPDATE: rgReasons[0].dwReason = CURSOR_DBREASON_EDIT; break;
case CURSOR_DBROWACTION_ADD: rgReasons[0].dwReason = CURSOR_DBREASON_ADDNEW; break; }
// notify other interested parties of action
HRESULT hr = m_pCursorPosition->NotifyBefore(dwEventWhat, 1, rgReasons);
// make sure action was not cancelled
if (hr != S_OK) { VDSetErrorInfo(IDS_ERR_ACTIONCANCELLED, IID_ICursorUpdateARow, m_pResourceDLL); return E_FAIL; }
// insert new hRow if we're going into add mode
if (dwFlags == CURSOR_DBROWACTION_ADD) { hr = InsertNewRow();
if (FAILED(hr)) { // notify other interested parties of failure
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons); return hr; } }
// reset column updates
hr = m_pCursorPosition->ResetColumnUpdates();
if (FAILED(hr)) { // notify other interested parties of failure
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons); return hr; }
// place cursor in correct mode
switch (dwFlags) { case CURSOR_DBROWACTION_UPDATE: m_pCursorPosition->SetEditMode(CURSOR_DBEDITMODE_UPDATE); break;
case CURSOR_DBROWACTION_ADD: m_pCursorPosition->SetEditMode(CURSOR_DBEDITMODE_ADD); break; }
// notify other interested parties of success
m_pCursorPosition->NotifyAfter(dwEventWhat, 1, rgReasons);
return S_OK; }
//=--------------------------------------------------------------------------=
// ICursorUpdateARow SetColumn
//=--------------------------------------------------------------------------=
// Sets the current value of the specified column
//
// Parameters:
// pcid - [in] a pointer to the columnID for which data is
// to be set
// pBindParams - [in] a pointer to a column binding structure containing
// information about the data and a pointer to the data
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory
// E_FAIL a provider-specific error occured
// CURSOR_DB_E_STATEERROR not in update or add mode
// CURSOR_DB_E_BADCOLUMNID pcid was not a valid column identifier
// CURSOR_DB_E_BADBINDINFO bad binding information
//
// Notes:
//
HRESULT CVDCursor::SetColumn(CURSOR_DBCOLUMNID *pcid, CURSOR_DBBINDPARAMS *pBindParams) { ASSERT_POINTER(pcid, CURSOR_DBCOLUMNID) ASSERT_POINTER(pBindParams, CURSOR_DBBINDPARAMS)
// make sure we have valid rowset
if (!IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorUpdateARow, m_pResourceDLL); return E_FAIL; }
// make sure we have all necessary pointers
if (!pcid || !pBindParams || !pBindParams->pData) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorUpdateARow, m_pResourceDLL); return E_INVALIDARG; }
// make sure we are in update or add mode
if (m_pCursorPosition->GetEditMode() == CURSOR_DBEDITMODE_NONE) { VDSetErrorInfo(IDS_ERR_STATEERROR, IID_ICursorUpdateARow, m_pResourceDLL); return CURSOR_DB_E_STATEERROR; }
CVDRowsetColumn * pColumn;
// validate cursor binding parameters and get rowset column
HRESULT hr = ValidateCursorBindParams(pcid, pBindParams, &pColumn);
if (FAILED(hr)) return hr;
CVDColumnUpdate * pColumnUpdate;
// create new column update object
hr = CVDColumnUpdate::Create(pColumn, pBindParams, &pColumnUpdate, m_pResourceDLL);
if (FAILED(hr)) return hr;
// setup notification structures
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED;
CURSOR_DBNOTIFYREASON rgReasons[1]; VariantInit((VARIANT*)&rgReasons[0].arg1); VariantInit((VARIANT*)&rgReasons[0].arg2);
rgReasons[0].dwReason = CURSOR_DBREASON_SETCOLUMN; rgReasons[0].arg1.vt = VT_I4; rgReasons[0].arg1.lVal = pColumn->GetNumber(); rgReasons[0].arg2 = pColumnUpdate->GetVariant();
// notify other interested parties of action
hr = m_pCursorPosition->NotifyBefore(dwEventWhat, 1, rgReasons);
// make sure action was not cancelled
if (hr != S_OK) { // release column update object
pColumnUpdate->Release();
VDSetErrorInfo(IDS_ERR_ACTIONCANCELLED, IID_ICursorUpdateARow, m_pResourceDLL); return E_FAIL; }
// update column in cursor position
m_pCursorPosition->SetColumnUpdate(pColumn->GetNumber(), pColumnUpdate);
// notify other interested parties of success
m_pCursorPosition->NotifyAfter(dwEventWhat, 1, rgReasons);
return S_OK; }
//=--------------------------------------------------------------------------=
// ICursorUpdateARow GetColumn
//=--------------------------------------------------------------------------=
// Gets the current value of the specified column
//
// Parameters:
// pcid - [in] a pointer to the columnID for which data is
// to be returned
// pBindParams - [out] a pointer to a column binding structure in which
// to return data
// pdwFlags - [out] a pointer to memory in which to return the
// changed state of the returned data
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory
// E_FAIL a provider-specific error occured
// CURSOR_DB_E_STATEERROR not in update or add mode
// CURSOR_DB_E_BADCOLUMNID pcid was not a valid column identifier
// CURSOR_DB_E_BADBINDINFO bad binding information
//
// Notes:
//
HRESULT CVDCursor::GetColumn(CURSOR_DBCOLUMNID *pcid, CURSOR_DBBINDPARAMS *pBindParams, DWORD *pdwFlags) { ASSERT_POINTER(pcid, CURSOR_DBCOLUMNID) ASSERT_POINTER(pBindParams, CURSOR_DBBINDPARAMS) ASSERT_NULL_OR_POINTER(pdwFlags, DWORD)
// make sure rowset is valid
if (!IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorUpdateARow, m_pResourceDLL); return E_FAIL; }
// make sure we have all necessary pointers
if (!pcid || !pBindParams || !pBindParams->pData) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorUpdateARow, m_pResourceDLL); return E_INVALIDARG; }
// make sure we are in update or add mode
if (m_pCursorPosition->GetEditMode() == CURSOR_DBEDITMODE_NONE) { VDSetErrorInfo(IDS_ERR_STATEERROR, IID_ICursorUpdateARow, m_pResourceDLL); return CURSOR_DB_E_STATEERROR; }
CVDRowsetColumn * pColumn;
// validate cursor binding parameters and get rowset column
HRESULT hr = ValidateCursorBindParams(pcid, pBindParams, &pColumn);
if (FAILED(hr)) return hr;
// get column update pointer for this column
CVDColumnUpdate * pColumnUpdate = m_pCursorPosition->GetColumnUpdate(pColumn->GetNumber());
// if not changed, get original value
if (!pColumnUpdate) { hr = GetOriginalColumn(pColumn, pBindParams);
if (pdwFlags) *pdwFlags = CURSOR_DBCOLUMNDATA_UNCHANGED; } else // otherwise, get modified value
{ hr = GetModifiedColumn(pColumnUpdate, pBindParams);
if (pdwFlags) *pdwFlags = CURSOR_DBCOLUMNDATA_CHANGED; }
return hr; }
//=--------------------------------------------------------------------------=
// ICursorUpdateARow GetEditMode
//=--------------------------------------------------------------------------=
// Gets the current edit mode: add, update or none
//
// Parameters:
// pdwState - [out] a pointer to memory in which to return the
// current edit mode
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL a provider-specific error occured
// E_INVALIDARG bad parameter
//
// Notes:
//
HRESULT CVDCursor::GetEditMode(DWORD *pdwState) { ASSERT_POINTER(pdwState, DWORD)
// make sure we have a valid rowset
if (!IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorUpdateARow, m_pResourceDLL); return E_FAIL; }
// make sure we have a pointer
if (!pdwState) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorUpdateARow, m_pResourceDLL); return E_INVALIDARG; }
// return edit mode
*pdwState = m_pCursorPosition->GetEditMode();
return S_OK; }
//=--------------------------------------------------------------------------=
// ICursorUpdateARow Update
//=--------------------------------------------------------------------------=
// Sends the contents of the edit buffer to the database and optionally
// returns the bookmark for the updated or added row
//
// Parameters:
// pBookmarkType - [in] a pointer to a columnID that specifies the type
// of bookmark desired
// pcbBookmark - [out] a pointer to memory in which to return the actual
// length of the returned bookmark
// ppBookmark - [out] a pointer to memory in which to return a pointer
// to a bookmark
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL a provider-specific error occured
// E_OUTOFMEMORY not enough memory
// CURSOR_DB_E_STATEERROR not in update or add mode
//
// Notes:
// Kagera does not allow variant bindings on dbtimestamp fields, so this code
// updates dbtimestamp fields using string pointers if possible.
//
HRESULT CVDCursor::Update(CURSOR_DBCOLUMNID *pBookmarkType, ULONG *pcbBookmark, void **ppBookmark) { ASSERT_NULL_OR_POINTER(pBookmarkType, CURSOR_DBCOLUMNID) ASSERT_NULL_OR_POINTER(pcbBookmark, ULONG) ASSERT_NULL_OR_POINTER(ppBookmark, void*)
IAccessor * pAccessor = GetAccessor(); IRowsetChange * pRowsetChange = GetRowsetChange(); IRowsetUpdate * pRowsetUpdate = GetRowsetUpdate(); BOOL fUndo = FALSE; BOOL fInsert = FALSE;
// make sure we have valid accessor and change pointers
if (!pAccessor || !pRowsetChange || !IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorUpdateARow, m_pResourceDLL); return E_FAIL; }
// get current edit mode
const DWORD dwEditMode = m_pCursorPosition->GetEditMode();
// make sure we are in update or add mode
if (dwEditMode == CURSOR_DBEDITMODE_NONE) { VDSetErrorInfo(IDS_ERR_STATEERROR, IID_ICursorUpdateARow, m_pResourceDLL); return CURSOR_DB_E_STATEERROR; }
// get hRow of the row currently being edited
HROW hRow = m_pCursorPosition->GetEditRow();
// get column count
const ULONG ulColumns = GetCursorMain()->GetColumnsCount();
// create update buffer accessor bindings
DBBINDING * pBindings = new DBBINDING[ulColumns];
if (!pBindings) { VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursorUpdateARow, m_pResourceDLL); return E_OUTOFMEMORY; }
// clear out bindings
memset(pBindings, 0, ulColumns * sizeof(DBBINDING));
// setup notification structures
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED | CURSOR_DBEVENT_NONCURRENT_ROW_DATA_CHANGED | CURSOR_DBEVENT_SET_OF_ROWS_CHANGED;
CURSOR_DBNOTIFYREASON rgReasons[1]; VariantInit((VARIANT*)&rgReasons[0].arg1); VariantInit((VARIANT*)&rgReasons[0].arg2);
switch (dwEditMode) { case CURSOR_DBEDITMODE_UPDATE: rgReasons[0].dwReason = CURSOR_DBREASON_MODIFIED; rgReasons[0].arg1 = m_pCursorPosition->m_bmCurrent.GetBookmarkVariant(); break;
case CURSOR_DBEDITMODE_ADD: rgReasons[0].dwReason = CURSOR_DBREASON_INSERTED; rgReasons[0].arg1 = m_pCursorPosition->m_bmAddRow.GetBookmarkVariant(); break; } // notify other interested parties of action
HRESULT hr = m_pCursorPosition->NotifyBefore(dwEventWhat, 1, rgReasons);
// make sure action was not cancelled
if (hr != S_OK) { // destroy bindings
delete [] pBindings;
VDSetErrorInfo(IDS_ERR_ACTIONCANCELLED, IID_ICursorUpdateARow, m_pResourceDLL); return E_FAIL; }
// variables
ULONG cBindings = 0; DBBINDING * pBinding = pBindings; CVDColumnUpdate * pColumnUpdate; ULONG obUpdate = 0;
// iterate through columns and setup binding structures
for (ULONG ulCol = 0; ulCol < ulColumns; ulCol++) { // get column update pointer
pColumnUpdate = m_pCursorPosition->GetColumnUpdate(ulCol);
if (pColumnUpdate) { // create column update buffer binding
pBinding->iOrdinal = pColumnUpdate->GetColumn()->GetOrdinal(); pBinding->obValue = obUpdate + sizeof(DBSTATUS) + sizeof(ULONG); pBinding->obLength = obUpdate + sizeof(DBSTATUS); pBinding->obStatus = obUpdate; pBinding->dwPart = DBPART_VALUE; pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED; pBinding->wType = DBTYPE_VARIANT;
// determine if length part is included
if (pColumnUpdate->GetVarDataLen() != CURSOR_DB_NOVALUE) pBinding->dwPart |= DBPART_LENGTH;
pBinding->dwPart |= DBPART_STATUS;
// check for variant binding on dbtimestamp field, and supplied variant is a bstr
if (pColumnUpdate->GetColumn()->GetType() == DBTYPE_DBTIMESTAMP && pColumnUpdate->GetVariantType() == VT_BSTR) { pBinding->dwPart &= ~DBPART_LENGTH; pBinding->wType = DBTYPE_BYREF | DBTYPE_WSTR; }
// increment update offset
obUpdate += sizeof(DBSTATUS) + sizeof(ULONG) + sizeof(VARIANT);
// increment binding
cBindings++; pBinding++; } }
// if we have any bindings, then update
if (cBindings) { HACCESSOR hAccessor;
// create update accessor
hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, cBindings, pBindings, 0, &hAccessor, NULL);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_CREATEACCESSORFAILED, IID_ICursorUpdateARow, pAccessor, IID_IAccessor, m_pResourceDLL);
if (FAILED(hr)) { // destroy bindings
delete [] pBindings;
// notify other interested parties of failure
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons); return hr; }
// create update buffer
BYTE * pBuffer = new BYTE[cBindings * (sizeof(DBSTATUS) + sizeof(ULONG) + sizeof(VARIANT))]; BYTE * pBufferOld = new BYTE[cBindings * (sizeof(DBSTATUS) + sizeof(ULONG) + sizeof(VARIANT))];
if (!pBuffer || !pBufferOld) { // destroy bindings
delete [] pBindings;
// destroy buffers
delete [] pBuffer; delete [] pBufferOld;
// release update accessor
pAccessor->ReleaseAccessor(hAccessor, NULL);
// notify other interested parties of failure
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons); return E_OUTOFMEMORY; }
// variables
obUpdate = 0; pBinding = pBindings; CURSOR_DBVARIANT variant;
// iterate through columns and setup buffer
for (ULONG ulCol = 0; ulCol < ulColumns; ulCol++) { // get column update pointer
pColumnUpdate = m_pCursorPosition->GetColumnUpdate(ulCol);
if (pColumnUpdate) { // obtain current status
DBSTATUS status = CursorInfoToStatus(pColumnUpdate->GetInfo());
// get value
variant = pColumnUpdate->GetVariant();
// check for empty value since some controls treat this as NULL and make sure that we
// treat the empty value as null
if (status == DBSTATUS_S_OK && variant.vt == VT_BSTR && wcslen(variant.bstrVal) == 0) { *(DBSTATUS*)(pBuffer + obUpdate) = DBSTATUS_S_ISNULL; } else { *(DBSTATUS*)(pBuffer + obUpdate) = status; }
*(DBSTATUS*)(pBufferOld + obUpdate) = DBSTATUS_S_ISNULL;
// if necessary, set length part in buffer
if (pBinding->dwPart & DBPART_LENGTH) { *(ULONG*)(pBuffer + obUpdate + sizeof(DBSTATUS)) = pColumnUpdate->GetVarDataLen(); *(ULONG*)(pBufferOld + obUpdate + sizeof(DBSTATUS)) = 0; }
// always set value part in buffer
if (pBinding->wType == (DBTYPE_BYREF | DBTYPE_WSTR)) { *(BSTR*)(pBuffer + obUpdate + sizeof(DBSTATUS) + sizeof(ULONG)) = variant.bstrVal; *(BSTR*)(pBufferOld + obUpdate + sizeof(DBSTATUS) + sizeof(ULONG)) = NULL; } else { memcpy(pBuffer + obUpdate + sizeof(DBSTATUS) + sizeof(ULONG), &variant, sizeof(VARIANT)); VariantInit((VARIANT *) (pBufferOld + obUpdate + sizeof(DBSTATUS) + sizeof(ULONG))); }
// increment update offset
obUpdate += sizeof(DBSTATUS) + sizeof(ULONG) + sizeof(VARIANT);
// increment binding
pBinding++; } }
DBPENDINGSTATUS status;
if (dwEditMode == CURSOR_DBEDITMODE_ADD) fInsert = TRUE; else if (pRowsetUpdate) { pRowsetUpdate->GetRowStatus(NULL, 1, &hRow, &status); if (status == DBPENDINGSTATUS_UNCHANGED) fUndo = TRUE; }
if (!fUndo && !fInsert) { hr = GetRowset()->GetData(hRow, hAccessor, pBufferOld); if (status != DBPENDINGSTATUS_NEW) fUndo = TRUE; }
// modify columns (set/clear internal set data flag)
GetCursorMain()->SetInternalSetData(TRUE); hr = pRowsetChange->SetData(hRow, hAccessor, pBuffer); if (hr == DB_S_ERRORSOCCURRED) { // since partial changes occurred, restore data back
// to original values.
if (fUndo) pRowsetUpdate->Undo(NULL, 1, &hRow, NULL, NULL, NULL); else if (status != DBPENDINGSTATUS_NEW) pRowsetChange->SetData(hRow, hAccessor, pBufferOld);
hr = DB_E_ERRORSOCCURRED; }
GetCursorMain()->SetInternalSetData(FALSE);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_SETDATAFAILED, IID_ICursorUpdateARow, pRowsetChange, IID_IRowsetChange, m_pResourceDLL);
// release update accessor
pAccessor->ReleaseAccessor(hAccessor, NULL);
obUpdate = 0; pBinding = pBindings; // iterate through columns and reset buffer
for (ulCol = 0; ulCol < ulColumns; ulCol++) { if (m_pCursorPosition->GetColumnUpdate(ulCol)) { VARIANT var;
if (pBinding->wType == (DBTYPE_BYREF | DBTYPE_WSTR)) { var.vt = VT_BSTR; var.bstrVal = *(BSTR*)(pBufferOld + obUpdate + sizeof(DBSTATUS) + sizeof(ULONG)); } else { memcpy(&var, pBufferOld + obUpdate + sizeof(DBSTATUS) + sizeof(ULONG), sizeof(VARIANT)); }
VariantClear(&var);
// increment update offset
obUpdate += sizeof(DBSTATUS) + sizeof(ULONG) + sizeof(VARIANT);
// increment binding
pBinding++; } }
// destroy update buffer
delete [] pBuffer; delete [] pBufferOld; }
// destroy bindings
delete [] pBindings;
if (FAILED(hr)) { // notify other interested parties of failure
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons); } else { // return bookmark if requested to do so
if (pBookmarkType && pcbBookmark && ppBookmark) { switch (dwEditMode) { case CURSOR_DBEDITMODE_UPDATE: *pcbBookmark = m_pCursorPosition->m_bmCurrent.GetBookmarkLen(); memcpy(*ppBookmark, m_pCursorPosition->m_bmCurrent.GetBookmark(), *pcbBookmark); break;
case CURSOR_DBEDITMODE_ADD: *pcbBookmark = m_pCursorPosition->m_bmAddRow.GetBookmarkLen(); memcpy(*ppBookmark, m_pCursorPosition->m_bmAddRow.GetBookmark(), *pcbBookmark); break; } }
// if acquired, release same-row clone
if (m_pCursorPosition->GetSameRowClone()) m_pCursorPosition->ReleaseSameRowClone();
// also, release add row if we have one
if (m_pCursorPosition->m_bmAddRow.GetHRow()) m_pCursorPosition->ReleaseAddRow();
// reset edit mode
m_pCursorPosition->SetEditMode(CURSOR_DBEDITMODE_NONE);
// reset column updates
m_pCursorPosition->ResetColumnUpdates();
// notify other interested parties of success
m_pCursorPosition->NotifyAfter(dwEventWhat, 1, rgReasons); }
return hr; }
//=--------------------------------------------------------------------------=
// ICursorUpdateARow Cancel
//=--------------------------------------------------------------------------=
// Cancels the update or add operation
//
// Parameters:
// none
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL a provider-specific error occured
// CURSOR_DB_E_STATEERROR not in update or add mode
//
// Notes:
//
HRESULT CVDCursor::Cancel(void) { // make sure we have a valid rowset
if (!IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorUpdateARow, m_pResourceDLL); return E_FAIL; }
// get current edit mode
const DWORD dwEditMode = m_pCursorPosition->GetEditMode();
// make sure we are in update or add mode
if (dwEditMode == CURSOR_DBEDITMODE_NONE) { VDSetErrorInfo(IDS_ERR_STATEERROR, IID_ICursorUpdateARow, m_pResourceDLL); return CURSOR_DB_E_STATEERROR; }
// try to get update pointer
IRowsetUpdate * pRowsetUpdate = GetRowsetUpdate();
// get hRow of the row currently being edited
HROW hRow = m_pCursorPosition->GetEditRow();
// setup notification structures
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED;
CURSOR_DBNOTIFYREASON rgReasons[1]; VariantInit((VARIANT*)&rgReasons[0].arg1); VariantInit((VARIANT*)&rgReasons[0].arg2);
rgReasons[0].dwReason = CURSOR_DBREASON_CANCELUPDATE;
// notify other interested parties of action
HRESULT hr = m_pCursorPosition->NotifyBefore(dwEventWhat, 1, rgReasons);
// make sure action was not cancelled
if (hr != S_OK) { VDSetErrorInfo(IDS_ERR_ACTIONCANCELLED, IID_ICursorUpdateARow, m_pResourceDLL); return E_FAIL; }
// if we are comming out of add mode, undo inserted row
if (pRowsetUpdate && dwEditMode == CURSOR_DBEDITMODE_ADD) { hr = pRowsetUpdate->Undo(0, 1, &hRow, NULL, NULL, NULL);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_UNDOFAILED, IID_ICursorUpdateARow, pRowsetUpdate, IID_IRowsetUpdate, m_pResourceDLL);
if (FAILED(hr)) { // notify other interested parties of failure
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons); return hr; } }
// if acquired, release same-row clone
if (m_pCursorPosition->GetSameRowClone()) m_pCursorPosition->ReleaseSameRowClone();
// also, release add row if we have one
if (m_pCursorPosition->m_bmAddRow.GetHRow()) m_pCursorPosition->ReleaseAddRow();
// reset edit mode
m_pCursorPosition->SetEditMode(CURSOR_DBEDITMODE_NONE);
// reset column updates
m_pCursorPosition->ResetColumnUpdates();
// notify other interested parties of success
m_pCursorPosition->NotifyAfter(dwEventWhat, 1, rgReasons);
return S_OK; }
//=--------------------------------------------------------------------------=
// ICursorUpdateARow Delete
//=--------------------------------------------------------------------------=
// Deletes the current row
//
// Parameters:
// none
//
// Output:
// HRESULT - S_OK if successful
// E_FAIL a provider-specific error occured
// CURSOR_DB_E_UPDATEINPROGRESS an update is already in progress
//
// Notes:
//
HRESULT CVDCursor::Delete(void) { IRowsetChange * pRowsetChange = GetRowsetChange();
// make sure we have a valid change pointer
if (!pRowsetChange || !IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursorUpdateARow, m_pResourceDLL); return E_FAIL; }
// make sure that an update is not already in progress
if (m_pCursorPosition->GetEditMode() != CURSOR_DBEDITMODE_NONE) { VDSetErrorInfo(IDS_ERR_UPDATEINPROGRESS, IID_ICursorUpdateARow, m_pResourceDLL); return CURSOR_DB_E_UPDATEINPROGRESS; }
// get current hRow
HROW hRow = m_pCursorPosition->m_bmCurrent.GetHRow();
// setup notification structures
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED | CURSOR_DBEVENT_SET_OF_ROWS_CHANGED;
CURSOR_DBNOTIFYREASON rgReasons[1]; VariantInit((VARIANT*)&rgReasons[0].arg1); VariantInit((VARIANT*)&rgReasons[0].arg2); rgReasons[0].dwReason = CURSOR_DBREASON_DELETED; rgReasons[0].arg1 = m_pCursorPosition->m_bmCurrent.GetBookmarkVariant();
// notify other interested parties of action
HRESULT hr = m_pCursorPosition->NotifyBefore(dwEventWhat, 1, rgReasons);
// make sure action was not cancelled
if (hr != S_OK) { VDSetErrorInfo(IDS_ERR_ACTIONCANCELLED, IID_ICursorUpdateARow, m_pResourceDLL); return E_FAIL; }
// try to delete current row (set/clear internal delete rows flag)
GetCursorMain()->SetInternalDeleteRows(TRUE); hr = pRowsetChange->DeleteRows(0, 1, &hRow, NULL); GetCursorMain()->SetInternalDeleteRows(FALSE);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_DELETEROWSFAILED, IID_ICursorUpdateARow, pRowsetChange, IID_IRowsetChange, m_pResourceDLL);
if (FAILED(hr)) { // notify other interested parties of failure
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons); } else { // notify other interested parties of success
m_pCursorPosition->NotifyAfter(dwEventWhat, 1, rgReasons); }
return hr; }
//=--------------------------------------------------------------------------=
// ICursorFind methods
//=--------------------------------------------------------------------------=
// ICursorFind FindByValues
//
HRESULT CVDCursor::FindByValues(ULONG cbBookmark, LPVOID pBookmark, DWORD dwFindFlags, ULONG cValues, CURSOR_DBCOLUMNID rgColumns[], CURSOR_DBVARIANT rgValues[], DWORD rgdwSeekFlags[], CURSOR_DBFETCHROWS FAR *pFetchParams) { //////////////////////////////////////////////////////////////////////////
// this implementation limits the number of columns that can be searched
// to one, since current OLEDB spec only allows a single column accessor
// to be passed to IRowsetFind::FindNextRow (06/11/97)
//
if (cValues > 1) return E_FAIL; //
//////////////////////////////////////////////////////////////////////////
IAccessor * pAccessor = GetAccessor(); IRowsetFind * pRowsetFind = GetRowsetFind();
// make sure we have valid accessor and find pointers
if (!pAccessor || !pRowsetFind || !IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_IEntryID, m_pResourceDLL); return E_FAIL; }
// check for values
if (!cValues) return S_OK;
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_CHANGED;
CURSOR_DBNOTIFYREASON rgReasons[1]; rgReasons[0].dwReason = CURSOR_DBREASON_FIND; VariantInit((VARIANT*)&rgReasons[0].arg1); rgReasons[0].arg1.vt = VT_UI4; rgReasons[0].arg1.lVal = dwFindFlags;
VariantInit((VARIANT*)&rgReasons[0].arg2);
// notify other interested parties
HRESULT hr = m_pCursorPosition->NotifyBefore(dwEventWhat, 1, rgReasons);
// make sure action was not cancelled
if (hr != S_OK) { VDSetErrorInfo(IDS_ERR_ACTIONCANCELLED, IID_ICursorFind, m_pResourceDLL); return E_FAIL; }
ULONG ul; HROW * pRow = NULL; ULONG cRowsObtained = 0; HACCESSOR hAccessor = NULL;
// allocate necessary memory
ULONG * pColumns = new ULONG[cValues]; DBTYPE * pDBTypes = new DBTYPE[cValues]; DBCOMPAREOP * pDBCompareOp = new DBCOMPAREOP[cValues]; BYTE ** ppValues = new BYTE*[cValues]; BOOL * fMemAllocated = new BOOL[cValues];
if (fMemAllocated) { // always init fMemAllocated flags to false
memset(fMemAllocated, 0, sizeof(BOOL) * cValues); }
// make sure we received all requested memory
if (!pColumns || !pDBTypes || !ppValues || !pDBCompareOp || !fMemAllocated) { VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_IRowsetFind, m_pResourceDLL); hr = E_OUTOFMEMORY; goto cleanup; }
// iterate through columns
for (ul = 0; ul < cValues; ul++) { // get column ordinal position
hr = GetOrdinal(rgColumns[ul], &pColumns[ul]); if (FAILED(hr)) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_IRowsetFind, m_pResourceDLL); hr = E_INVALIDARG; goto cleanup; }
// get find values from CURSOR_DBVARIANT
hr = GetDataFromDBVariant(&rgValues[ul], &pDBTypes[ul], &(ppValues[ul]), &fMemAllocated[ul]);
if (FAILED(hr)) { VDSetErrorInfo(IDS_ERR_CANTCOERCE, IID_IRowsetFind, m_pResourceDLL); goto cleanup; }
// setup seek flags
switch (rgdwSeekFlags[ul]) { case CURSOR_DBSEEK_LT: pDBCompareOp[ul] = DBCOMPAREOPS_LT; break;
case CURSOR_DBSEEK_LE: pDBCompareOp[ul] = DBCOMPAREOPS_LE; break;
case CURSOR_DBSEEK_EQ: pDBCompareOp[ul] = DBCOMPAREOPS_EQ; break;
case CURSOR_DBSEEK_GE: pDBCompareOp[ul] = DBCOMPAREOPS_GE; break;
case CURSOR_DBSEEK_GT: pDBCompareOp[ul] = DBCOMPAREOPS_GT; break;
case CURSOR_DBSEEK_PARTIALEQ: pDBCompareOp[ul] = DBCOMPAREOPS_BEGINSWITH; break;
default: VDSetErrorInfo(IDS_ERR_INVALIDSEEKFLAGS, IID_IRowsetFind, m_pResourceDLL); hr = E_FAIL; goto cleanup; } }
LONG cRows; BOOL fSkipCurrent;
// determine direction of seek
if (CURSOR_DBFINDFLAGS_FINDPRIOR == dwFindFlags) { cRows = -1; fSkipCurrent = TRUE; } else { cRows = 1; fSkipCurrent = TRUE; }
BYTE bSpecialBM;
// check for standard bookmarks
if (CURSOR_DB_BMK_SIZE == cbBookmark) { if (memcmp(&CURSOR_DBBMK_BEGINNING, pBookmark, CURSOR_DB_BMK_SIZE) == 0) { cbBookmark = sizeof(BYTE); bSpecialBM = DBBMK_FIRST; pBookmark = &bSpecialBM; fSkipCurrent = FALSE; } else if (memcmp(&CURSOR_DBBMK_END, pBookmark, CURSOR_DB_BMK_SIZE) == 0) { cbBookmark = sizeof(BYTE); bSpecialBM = DBBMK_LAST; pBookmark = &bSpecialBM; fSkipCurrent = FALSE; } else if (memcmp(&CURSOR_DBBMK_CURRENT, pBookmark, CURSOR_DB_BMK_SIZE) == 0) { cbBookmark = m_pCursorPosition->m_bmCurrent.GetBookmarkLen(); pBookmark = m_pCursorPosition->m_bmCurrent.GetBookmark(); } }
DBBINDING binding;
// clear out binding
memset(&binding, 0, sizeof(DBBINDING));
// create value binding
binding.iOrdinal = pColumns[0]; binding.obValue = 0; binding.dwPart = DBPART_VALUE; binding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; binding.cbMaxLen = 0x7FFFFFFF; binding.wType = pDBTypes[0];
// create accessor describing the value to be matched
hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &binding, 0, &hAccessor, NULL);
if (FAILED(hr)) goto cleanup;
// try to find hRow satisfying our condition
hr = pRowsetFind->FindNextRow(DB_NULL_HCHAPTER, hAccessor, ppValues[0], pDBCompareOp[0], cbBookmark, (BYTE*)pBookmark, fSkipCurrent, cRows, &cRowsObtained, &pRow);
// check to see if we rached end of rowset
if (hr == DB_S_ENDOFROWSET && !cRowsObtained) hr = E_FAIL;
if (FAILED(hr)) hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_FINDFAILED, IID_ICursorFind, pRowsetFind, IID_IRowsetFind, m_pResourceDLL);
// check to see if we got the hRow
if (SUCCEEDED(hr) && cRowsObtained) { // allocate buffer for bookmark plus length indicator
BYTE * pBuff = new BYTE[GetCursorMain()->GetMaxBookmarkLen() + sizeof(ULONG)];
if (!pBuff) hr = E_OUTOFMEMORY; else { // get the bookmark data
hr = GetRowset()->GetData(*pRow, GetCursorMain()->GetBookmarkAccessor(), pBuff); if (SUCCEEDED(hr)) { ULONG * pulLen = (ULONG*)pBuff; BYTE * pbmdata = pBuff + sizeof(ULONG); LARGE_INTEGER dlOffset; dlOffset.HighPart = 0; dlOffset.LowPart = 0; hr = FetchAtBookmark(*pulLen, pbmdata, dlOffset, pFetchParams); } else hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_ICursorFind, pRowsetFind, IID_IRowset, m_pResourceDLL);
delete [] pBuff; }
} if (pRow) { // release hRow
if (cRowsObtained) GetRowset()->ReleaseRows(1, pRow, NULL, NULL, NULL); g_pMalloc->Free(pRow); }
cleanup:
rgReasons[0].arg2.vt = VT_BOOL; V_BOOL(&rgReasons[0].arg2) = SUCCEEDED(hr) ? TRUE : FALSE;
if (SUCCEEDED(hr)) { // notify other interested parties of success
m_pCursorPosition->NotifyAfter(dwEventWhat, 1, rgReasons); } else { // notify other interested parties of failure
m_pCursorPosition->NotifyFail(dwEventWhat, 1, rgReasons); }
// free values
if (ppValues && fMemAllocated) { for (ul = 0; ul < cValues; ul++) { if (fMemAllocated[ul] && ppValues[ul]) g_pMalloc->Free(ppValues[ul]); } }
// free memory
delete [] pColumns; delete [] pDBTypes; delete [] ppValues; delete [] pDBCompareOp; delete [] fMemAllocated;
// release accessor
if (hAccessor) pAccessor->ReleaseAccessor(hAccessor, NULL);
return hr; }
//=--------------------------------------------------------------------------=
// IEnrtyID methods
//=--------------------------------------------------------------------------=
// IEntryID GetInterface
//=--------------------------------------------------------------------------=
// Gets the requested interface pointer to the given entryID
//
// Parameters:
// cbEntryID - [in] the size of the entryID
// pEntryID - [in] a pointer to the entryID
// dwFlags - [in] interface specific flags
// riid - [in] the interface id for the interface desired
// ppvObj - [out] a pointer to memory in which to return interface pointer
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory
// E_FAIL a provider-specific error occured
// E_NOINTERFACE no such interface supported
// CURSOR_DB_E_BADENTRYID bad entry identifier
//
// Notes:
//
HRESULT CVDCursor::GetInterface(ULONG cbEntryID, void *pEntryID, DWORD dwFlags, REFIID riid, IUnknown **ppvObj) { ASSERT_POINTER(pEntryID, BYTE) ASSERT_POINTER(ppvObj, IUnknown*)
IRowset * pRowset = GetRowset();
// make sure we have a valid rowset pointer
if (!pRowset || !IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_IEntryID, m_pResourceDLL); return E_FAIL; }
// make sure we have all necessary pointers
if (!pEntryID || !ppvObj) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_IEntryID, m_pResourceDLL); return E_INVALIDARG; }
// init out parameter
*ppvObj = NULL;
HROW hRow; CVDRowsetColumn * pColumn;
// validate supplied entryID, and get rowset column and hRow
HRESULT hr = ValidateEntryID(cbEntryID, (BYTE*)pEntryID, &pColumn, &hRow);
if (FAILED(hr)) return hr;
IUnknown * pUnknown = NULL;
// first, try to get requested interface from entry identifier
hr = QueryEntryIDInterface(pColumn, hRow, dwFlags, riid, &pUnknown);
// if we succeeded or caller is not asking for IStream then leave
if (SUCCEEDED(hr) || riid != IID_IStream) { // release reference on hRow
pRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL); *ppvObj = pUnknown; return hr; }
#ifndef VD_DONT_IMPLEMENT_ISTREAM
IStream * pStream;
// create stream from entry identifier
hr = CreateEntryIDStream(pColumn, hRow, &pStream);
if (FAILED(hr)) { // release reference on hRow
pRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL); return hr; }
CVDEntryIDData * pEntryIDData;
// create entryID data object
hr = CVDEntryIDData::Create(m_pCursorPosition, pColumn, hRow, pStream, &pEntryIDData, m_pResourceDLL);
// release reference on hRow
pRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL);
// release reference on stream
pStream->Release();
if (FAILED(hr)) return hr;
CVDStream * pVDStream;
// create viaduct stream object
hr = CVDStream::Create(pEntryIDData, pStream, &pVDStream, m_pResourceDLL);
// release reference on entryID data object
pEntryIDData->Release();
if (FAILED(hr)) return hr;
// return stream
*ppvObj = pVDStream;
return S_OK;
#else //VD_DONT_IMPLEMENT_ISTREAM
// release reference on hRow
pRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL);
VDSetErrorInfo(IDS_ERR_NOINTERFACE, IID_IEntryID, m_pResourceDLL); return E_NOINTERFACE;
#endif //VD_DONT_IMPLEMENT_ISTREAM
}
/////////////////////////////////////////////////////////////////////////
// CVDNotifier functions
/////////////////////////////////////////////////////////////////////////
//+-------------------------------------------------------------------------
// Member: Notify Fail (public)
//
// Synopsis: send NotifyFail notification
//
// Arguments: dwEventWhat [in] what event is causing the notification
// cReasons [in] how many reasons
// rgReasons [in] list of reasons for the event
//
// Returns: S_OK it worked
HRESULT CVDCursor::NotifyFail(DWORD dwEventWhat, ULONG cReasons, CURSOR_DBNOTIFYREASON rgReasons[]) { CVDNotifyDBEventsConnPt * pNotifyDBEventsConnPt = m_pConnPtContainer->GetNotifyDBEventsConnPt();
UINT uiConnectionsActive = pNotifyDBEventsConnPt->GetConnectionsActive();
INotifyDBEvents ** ppNotifyDBEvents = pNotifyDBEventsConnPt->GetNotifyDBEventsTable();
for (UINT uiConn = 0; uiConn < uiConnectionsActive; uiConn++) ppNotifyDBEvents[uiConnectionsActive - uiConn - 1]->FailedToDo(dwEventWhat, cReasons, rgReasons);
return S_OK; }
/////////////////////////////////////////////////////////////////////////
// CCursorNotifier helper functions
/////////////////////////////////////////////////////////////////////////
//+-------------------------------------------------------------------------
// Member: Notify OK To Do (public)
//
// Synopsis: Send OKToDo notification. If a client objects (by
// returning a non-zero HR, send FailedToDo to notified
// clients to cancel the event.
//
// Arguments: dwEventWhat [in] what event is causing the notification
// cReasons [in] how many reasons
// rgReasons [in] list of reasons for the event
//
// Returns: S_OK all clients agree it's OK to do the event
// other some client disagrees
HRESULT CVDCursor::NotifyOKToDo(DWORD dwEventWhat, ULONG cReasons, CURSOR_DBNOTIFYREASON rgReasons[]) { HRESULT hr = S_OK;
CVDNotifyDBEventsConnPt * pNotifyDBEventsConnPt = m_pConnPtContainer->GetNotifyDBEventsConnPt();
UINT uiConnectionsActive = pNotifyDBEventsConnPt->GetConnectionsActive();
INotifyDBEvents ** ppNotifyDBEvents = pNotifyDBEventsConnPt->GetNotifyDBEventsTable();
for (UINT uiConn = 0; uiConn < uiConnectionsActive; uiConn++) { hr = ppNotifyDBEvents[uiConnectionsActive - uiConn - 1]->OKToDo(dwEventWhat, cReasons, rgReasons); if (S_OK != hr) { for (UINT ui = 0; ui <= uiConn; ui++) ppNotifyDBEvents[uiConnectionsActive - ui - 1]->Cancelled(dwEventWhat, cReasons, rgReasons); break; } }
return hr; }
//+-------------------------------------------------------------------------
// Member: Notify Sync Before (public)
//
// Synopsis: Send SyncBefore notification
//
// Arguments: dwEventWhat [in] what event is causing the notification
// cReasons [in] how many reasons
// rgReasons [in] list of reasons for the event
//
// Returns: S_OK all clients received notification
// other some client returned an error
HRESULT CVDCursor::NotifySyncBefore(DWORD dwEventWhat, ULONG cReasons, CURSOR_DBNOTIFYREASON rgReasons[]) { HRESULT hr = S_OK;
CVDNotifyDBEventsConnPt * pNotifyDBEventsConnPt = m_pConnPtContainer->GetNotifyDBEventsConnPt();
UINT uiConnectionsActive = pNotifyDBEventsConnPt->GetConnectionsActive();
INotifyDBEvents ** ppNotifyDBEvents = pNotifyDBEventsConnPt->GetNotifyDBEventsTable();
for (UINT uiConn = 0; uiConn < uiConnectionsActive; uiConn++) { hr = ppNotifyDBEvents[uiConnectionsActive - uiConn - 1]->SyncBefore(dwEventWhat, cReasons, rgReasons); if (S_OK != hr) break; }
return hr; }
//+-------------------------------------------------------------------------
// Member: Notify About To Do (public)
//
// Synopsis: Send AboutToDo notification
//
// Arguments: dwEventWhat [in] what event is causing the notification
// cReasons [in] how many reasons
// rgReasons [in] list of reasons for the event
//
// Returns: S_OK all clients notified
// other some client returned an error
HRESULT CVDCursor::NotifyAboutToDo(DWORD dwEventWhat, ULONG cReasons, CURSOR_DBNOTIFYREASON rgReasons[]) { HRESULT hr = S_OK;
CVDNotifyDBEventsConnPt * pNotifyDBEventsConnPt = m_pConnPtContainer->GetNotifyDBEventsConnPt();
UINT uiConnectionsActive = pNotifyDBEventsConnPt->GetConnectionsActive();
INotifyDBEvents ** ppNotifyDBEvents = pNotifyDBEventsConnPt->GetNotifyDBEventsTable();
for (UINT uiConn = 0; uiConn < uiConnectionsActive; uiConn++) { hr = ppNotifyDBEvents[uiConnectionsActive - uiConn - 1]->AboutToDo(dwEventWhat, cReasons, rgReasons); if (S_OK != hr) break; }
return hr; }
//+-------------------------------------------------------------------------
// Member: Notify Sync After (public)
//
// Synopsis: Send SyncAfter notification.
//
// Arguments: dwEventWhat [in] what event is causing the notification
// cReasons [in] how many reasons
// rgReasons [in] list of reasons for the event
//
// Returns: S_OK all clients notified
HRESULT CVDCursor::NotifySyncAfter(DWORD dwEventWhat, ULONG cReasons, CURSOR_DBNOTIFYREASON rgReasons[]) { CVDNotifyDBEventsConnPt * pNotifyDBEventsConnPt = m_pConnPtContainer->GetNotifyDBEventsConnPt();
UINT uiConnectionsActive = pNotifyDBEventsConnPt->GetConnectionsActive();
INotifyDBEvents ** ppNotifyDBEvents = pNotifyDBEventsConnPt->GetNotifyDBEventsTable();
for (UINT uiConn = 0; uiConn < uiConnectionsActive; uiConn++) ppNotifyDBEvents[uiConnectionsActive - uiConn - 1]->SyncAfter(dwEventWhat, cReasons, rgReasons);
return S_OK; }
//+-------------------------------------------------------------------------
// Member: Notify Did Event (public)
//
// Synopsis: Send DidEvent notification
//
// Arguments: dwEventWhat [in] what event is causing the notification
// cReasons [in] how many reasons
// rgReasons [in] list of reasons for the event
//
// Returns: S_OK all clients notified
HRESULT CVDCursor::NotifyDidEvent(DWORD dwEventWhat, ULONG cReasons, CURSOR_DBNOTIFYREASON rgReasons[]) { CVDNotifyDBEventsConnPt * pNotifyDBEventsConnPt = m_pConnPtContainer->GetNotifyDBEventsConnPt();
UINT uiConnectionsActive = pNotifyDBEventsConnPt->GetConnectionsActive();
INotifyDBEvents ** ppNotifyDBEvents = pNotifyDBEventsConnPt->GetNotifyDBEventsTable();
for (UINT uiConn = 0; uiConn < uiConnectionsActive; uiConn++) ppNotifyDBEvents[uiConnectionsActive - uiConn - 1]->DidEvent(dwEventWhat, cReasons, rgReasons);
return S_OK; }
//+-------------------------------------------------------------------------
// Member: Notify Cancel (public)
//
// Synopsis: Send Cancelled notification
//
// Arguments: dwEventWhat [in] what event is causing the notification
// cReasons [in] how many reasons
// rgReasons [in] list of reasons for the event
//
// Returns: S_OK all clients notified
HRESULT CVDCursor::NotifyCancel(DWORD dwEventWhat, ULONG cReasons, CURSOR_DBNOTIFYREASON rgReasons[]) { CVDNotifyDBEventsConnPt * pNotifyDBEventsConnPt = m_pConnPtContainer->GetNotifyDBEventsConnPt();
UINT uiConnectionsActive = pNotifyDBEventsConnPt->GetConnectionsActive();
INotifyDBEvents ** ppNotifyDBEvents = pNotifyDBEventsConnPt->GetNotifyDBEventsTable();
for (UINT uiConn = 0; uiConn < uiConnectionsActive; uiConn++) ppNotifyDBEvents[uiConnectionsActive - uiConn - 1]->Cancelled(dwEventWhat, cReasons, rgReasons);
return S_OK; }
|