//--------------------------------------------------------------------------- // CursorMain.cpp : CursorMain implementation // // Copyright (c) 1996 Microsoft Corporation, All Rights Reserved // Developed by Sheridan Software Systems, Inc. //--------------------------------------------------------------------------- #include "stdafx.h" #include "MSR2C.h" #include "Notifier.h" #include "RSColumn.h" #include "RSSource.h" #include "CursMain.h" #include "ColUpdat.h" #include "CursPos.h" #include "CursBase.h" #include "enumcnpt.h" #include "Cursor.h" #include "Bookmark.h" #include "fastguid.h" SZTHISFILE #include "ARRAY_P.inl" // static data DWORD CVDCursorMain::s_dwMetaRefCount = 0; ULONG CVDCursorMain::s_ulMetaColumns = 0; CVDRowsetColumn * CVDCursorMain::s_rgMetaColumns = NULL; //=--------------------------------------------------------------------------= // CVDCursorMain - Constructor // CVDCursorMain::CVDCursorMain(LCID lcid) : m_resourceDLL(lcid) { m_fWeAddedMetaRef = FALSE; m_fPassivated = FALSE; m_fColumnsRowsetSupported = FALSE; m_fInternalInsertRow = FALSE; m_fInternalDeleteRows = FALSE; m_fInternalSetData = FALSE; m_fLiteralBookmarks = FALSE; m_fOrderedBookmarks = FALSE; m_fBookmarkSkipped = FALSE; m_fConnected = FALSE; m_dwAdviseCookie = 0; m_ulColumns = 0; m_rgColumns = NULL; m_cbMaxBookmark = 0; VDUpdateObjectCount(1); // update object count to prevent dll from being unloaded #ifdef _DEBUG g_cVDCursorMainCreated++; #endif } //=--------------------------------------------------------------------------= // ~CVDCursorMain - Destructor // CVDCursorMain::~CVDCursorMain() { Passivate(); VDUpdateObjectCount(-1); // update object count to allow dll to be unloaded #ifdef _DEBUG g_cVDCursorMainDestroyed++; #endif } //=--------------------------------------------------------------------------= // Pasivate when external ref count gets to zero // void CVDCursorMain::Passivate() { if (m_fPassivated) return; m_fPassivated = TRUE; if (IsRowsetValid()) { if (m_hAccessorBM) GetAccessor()->ReleaseAccessor(m_hAccessorBM, NULL); if (m_fConnected) DisconnectIRowsetNotify(); } DestroyColumns(); DestroyMetaColumns(); } //=--------------------------------------------------------------------------= // Create - Create cursor provider from row position or rowset //=--------------------------------------------------------------------------= // This function creates and initializes a new cursor main object // // Parameters: // pRowPosition - [in] original IRowPosition provider (may be NULL) // pRowset - [in] original IRowset provider // ppCursor - [out] resulting ICursor provider // lcid - [in] locale identifier // // Output: // HRESULT - S_OK if successful // E_INVALIDARG bad parameter // E_OUTOFMEMORY not enough memory // VD_E_CANNOTCONNECTIROWSETNOTIFY unable to connect IRowsetNotify // // Notes: // HRESULT CVDCursorMain::Create(IRowPosition* pRowPosition, IRowset * pRowset, ICursor ** ppCursor, LCID lcid) { ASSERT_POINTER(pRowset, IRowset) ASSERT_POINTER(ppCursor, ICursor*) if (!pRowset || !ppCursor) return E_INVALIDARG; // create new cursor main object CVDCursorMain * pCursorMain = new CVDCursorMain(lcid); if (!pCursorMain) return E_OUTOFMEMORY; // initialize rowset source HRESULT hr = pCursorMain->Initialize(pRowset); if (FAILED(hr)) { pCursorMain->Release(); return hr; } // create array of column objects hr = pCursorMain->CreateColumns(); if (FAILED(hr)) { pCursorMain->Release(); return hr; } // create array of meta-column objects hr = pCursorMain->CreateMetaColumns(); if (FAILED(hr)) { pCursorMain->Release(); return hr; } // create bookmark accessor DBBINDING rgBindings[1]; DBBINDSTATUS rgStatus[1]; memset(rgBindings, 0, sizeof(DBBINDING)); rgBindings[0].iOrdinal = 0; rgBindings[0].obValue = 4; rgBindings[0].obLength = 0; rgBindings[0].dwPart = DBPART_VALUE | DBPART_LENGTH; rgBindings[0].dwMemOwner = DBMEMOWNER_CLIENTOWNED; rgBindings[0].cbMaxLen = pCursorMain->GetMaxBookmarkLen(); rgBindings[0].wType = DBTYPE_BYTES; hr = pCursorMain->GetAccessor()->CreateAccessor(DBACCESSOR_ROWDATA, 1, rgBindings, 0, &pCursorMain->m_hAccessorBM, rgStatus); if (FAILED(hr)) { pCursorMain->Release(); return VD_E_CANNOTCREATEBOOKMARKACCESSOR; } // create new cursor position object CVDCursorPosition * pCursorPosition; hr = CVDCursorPosition::Create(pRowPosition, pCursorMain, &pCursorPosition, &pCursorMain->m_resourceDLL); if (FAILED(hr)) { pCursorMain->Release(); return hr; } // create new cursor object CVDCursor * pCursor; hr = CVDCursor::Create(pCursorPosition, &pCursor, &pCursorMain->m_resourceDLL); if (FAILED(hr)) { ((CVDNotifier*)pCursorPosition)->Release(); pCursorMain->Release(); return hr; } // connect IRowsetNotify hr = pCursorMain->ConnectIRowsetNotify(); if (SUCCEEDED(hr)) pCursorMain->m_fConnected = TRUE; // check rowset properties BOOL fCanHoldRows = TRUE; IRowsetInfo * pRowsetInfo = pCursorMain->GetRowsetInfo(); if (pRowsetInfo) { DBPROPID propids[] = { DBPROP_LITERALBOOKMARKS, DBPROP_ORDEREDBOOKMARKS, DBPROP_BOOKMARKSKIPPED, DBPROP_CANHOLDROWS }; const DBPROPIDSET propsetids[] = { propids, 4, {0,0,0,0} }; memcpy((void*)&propsetids[0].guidPropertySet, &DBPROPSET_ROWSET, sizeof(DBPROPSET_ROWSET)); ULONG cPropertySets = 0; DBPROPSET * propset = NULL; hr = pRowsetInfo->GetProperties(1, propsetids, &cPropertySets, &propset); if (SUCCEEDED(hr) && propset && propset->rgProperties) { if (DBPROPSTATUS_OK == propset->rgProperties[0].dwStatus) pCursorMain->m_fLiteralBookmarks = V_BOOL(&propset->rgProperties[0].vValue); if (DBPROPSTATUS_OK == propset->rgProperties[1].dwStatus) pCursorMain->m_fOrderedBookmarks = V_BOOL(&propset->rgProperties[1].vValue); if (DBPROPSTATUS_OK == propset->rgProperties[2].dwStatus) pCursorMain->m_fBookmarkSkipped = V_BOOL(&propset->rgProperties[2].vValue); if (DBPROPSTATUS_OK == propset->rgProperties[3].dwStatus) fCanHoldRows = V_BOOL(&propset->rgProperties[3].vValue); } if (propset) { if (propset->rgProperties) g_pMalloc->Free(propset->rgProperties); g_pMalloc->Free(propset); } } // release our references pCursorMain->Release(); ((CVDNotifier*)pCursorPosition)->Release(); // check for required property if (!fCanHoldRows) { pCursor->Release(); return VD_E_REQUIREDPROPERTYNOTSUPPORTED; } // we're done *ppCursor = pCursor; return S_OK; } //=--------------------------------------------------------------------------= // Create - Create cursor provider from rowset //=--------------------------------------------------------------------------= // This function creates and initializes a new cursor main object // // Parameters: // pRowset - [in] original IRowset provider // ppCursor - [out] resulting ICursor provider // lcid - [in] locale identifier // // Output: // HRESULT - S_OK if successful // E_INVALIDARG bad parameter // E_OUTOFMEMORY not enough memory // VD_E_CANNOTCONNECTIROWSETNOTIFY unable to connect IRowsetNotify // // Notes: // HRESULT CVDCursorMain::Create(IRowset * pRowset, ICursor ** ppCursor, LCID lcid) { ASSERT_POINTER(pRowset, IRowset) ASSERT_POINTER(ppCursor, ICursor*) if (!pRowset || !ppCursor) return E_INVALIDARG; // create cursor as done before row position return Create(NULL, pRowset, ppCursor, lcid); } //=--------------------------------------------------------------------------= // Create - Create cursor provider from row position //=--------------------------------------------------------------------------= // This function creates and initializes a new cursor main object // // Parameters: // pRowPosition - [in] original IRowPosition provider // ppCursor - [out] resulting ICursor provider // lcid - [in] locale identifier // // Output: // HRESULT - S_OK if successful // E_INVALIDARG bad parameter // E_OUTOFMEMORY not enough memory // VD_E_CANNOTCONNECTIROWSETNOTIFY unable to connect IRowPositionNotify // VD_E_CANNOTGETROWSETINTERFACE unable to get IRowset // // Notes: // HRESULT CVDCursorMain::Create(IRowPosition * pRowPosition, ICursor ** ppCursor, LCID lcid) { ASSERT_POINTER(pRowPosition, IRowPosition) ASSERT_POINTER(ppCursor, ICursor*) if (!pRowPosition || !ppCursor) return E_INVALIDARG; IRowset * pRowset; // get IRowset from IRowPosition HRESULT hr = pRowPosition->GetRowset(IID_IRowset, (IUnknown**)&pRowset); if (FAILED(hr)) return VD_E_CANNOTGETROWSETINTERFACE; // create cursor with new row position parameter hr = Create(pRowPosition, pRowset, ppCursor, lcid); pRowset->Release(); // we're done return hr; } typedef struct tagVDMETADATA_METADATA { const CURSOR_DBCOLUMNID * pCursorColumnID; ULONG cbMaxLength; CHAR * pszName; DWORD dwCursorType; } VDMETADATA_METADATA; #define MAX_METADATA_COLUMNS 21 static const VDMETADATA_METADATA g_MetaDataMetaData[MAX_METADATA_COLUMNS] = { // Bookmark column { &CURSOR_COLUMN_BMKTEMPORARY, sizeof(ULONG), NULL, CURSOR_DBTYPE_BLOB }, // data columns { &CURSOR_COLUMN_COLUMNID, sizeof(CURSOR_DBCOLUMNID), "COLUMN_COLUMNID", CURSOR_DBTYPE_COLUMNID }, { &CURSOR_COLUMN_DATACOLUMN, sizeof(VARIANT_BOOL), "COLUMN_DATACOLUMN", CURSOR_DBTYPE_BOOL }, { &CURSOR_COLUMN_ENTRYIDMAXLENGTH, sizeof(ULONG), "COLUMN_ENTRYIDMAXLENGTH",CURSOR_DBTYPE_I4 }, { &CURSOR_COLUMN_FIXED, sizeof(VARIANT_BOOL), "COLUMN_FIXED", CURSOR_DBTYPE_BOOL }, { &CURSOR_COLUMN_MAXLENGTH, sizeof(ULONG), "COLUMN_MAXLENGTH", CURSOR_DBTYPE_I4 }, { &CURSOR_COLUMN_NAME, 256, "COLUMN_NAME", VT_LPWSTR }, { &CURSOR_COLUMN_NULLABLE, sizeof(VARIANT_BOOL), "COLUMN_NULLABLE", CURSOR_DBTYPE_BOOL }, { &CURSOR_COLUMN_NUMBER, sizeof(ULONG), "COLUMN_NUMBER", CURSOR_DBTYPE_I4 }, { &CURSOR_COLUMN_SCALE, sizeof(ULONG), "COLUMN_SCALE", CURSOR_DBTYPE_I4 }, { &CURSOR_COLUMN_TYPE, sizeof(ULONG), "COLUMN_TYPE", CURSOR_DBTYPE_I4 }, { &CURSOR_COLUMN_UPDATABLE, sizeof(ULONG), "COLUMN_UPDATABLE", CURSOR_DBTYPE_I4 }, { &CURSOR_COLUMN_BINDTYPE, sizeof(ULONG), "COLUMN_BINDTYPE", CURSOR_DBTYPE_I4 }, // optional metadata columns - supported with IColumnsRowset only) { &CURSOR_COLUMN_AUTOINCREMENT, sizeof(VARIANT_BOOL), "COLUMN_AUTOINCREMENT", CURSOR_DBTYPE_BOOL }, { &CURSOR_COLUMN_BASECOLUMNNAME, 256, "COLUMN_BASECOLUMNNAME",VT_LPWSTR }, { &CURSOR_COLUMN_BASENAME, 256, "COLUMN_BASENAME", VT_LPWSTR }, { &CURSOR_COLUMN_COLLATINGORDER, sizeof(LCID), "COLUMN_COLLATINGORDER",CURSOR_DBTYPE_I4 }, { &CURSOR_COLUMN_DEFAULTVALUE, 256, "COLUMN_DEFAULTVALUE", VT_LPWSTR }, { &CURSOR_COLUMN_HASDEFAULT, sizeof(VARIANT_BOOL), "COLUMN_HASDEFAULT", CURSOR_DBTYPE_BOOL }, { &CURSOR_COLUMN_CASESENSITIVE, sizeof(VARIANT_BOOL), "COLUMN_CASESENSITIVE", CURSOR_DBTYPE_BOOL }, { &CURSOR_COLUMN_UNIQUE, sizeof(VARIANT_BOOL), "COLUMN_UNIQUE", CURSOR_DBTYPE_BOOL }, }; //=--------------------------------------------------------------------------= // CreateMetaColumns - Create array of meta-column objects // HRESULT CVDCursorMain::CreateMetaColumns() { HRESULT hr = S_OK; EnterCriticalSection(&g_CriticalSection); if (!s_dwMetaRefCount) { // allocate a static aray of metadata metadata columns s_rgMetaColumns = new CVDRowsetColumn[MAX_METADATA_COLUMNS]; if (!s_rgMetaColumns) { hr = E_OUTOFMEMORY; goto cleanup; } s_ulMetaColumns = MAX_METADATA_COLUMNS; // number of columns for IColumnsInfo // initialize the array elments from the static g_MetaDataMetaData table for (int i = 0; i < MAX_METADATA_COLUMNS; i++) { s_rgMetaColumns[i].Initialize(g_MetaDataMetaData[i].pCursorColumnID, (BOOL)i, // false for 1st column (bookmark) TRUE for all other columns g_MetaDataMetaData[i].cbMaxLength, g_MetaDataMetaData[i].pszName, g_MetaDataMetaData[i].dwCursorType, i ); // ordinal number } } s_dwMetaRefCount++; m_fWeAddedMetaRef = TRUE; cleanup: LeaveCriticalSection(&g_CriticalSection); return hr; } //=--------------------------------------------------------------------------= // DestroyMetaColumns - Destroy array of meta-columns objects // void CVDCursorMain::DestroyMetaColumns() { EnterCriticalSection(&g_CriticalSection); if (m_fWeAddedMetaRef) { s_dwMetaRefCount--; if (!s_dwMetaRefCount) { delete [] s_rgMetaColumns; s_ulMetaColumns = 0; s_rgMetaColumns = NULL; } } LeaveCriticalSection(&g_CriticalSection); } //=--------------------------------------------------------------------------= // CreateColumns - Create array of column objects // HRESULT CVDCursorMain::CreateColumns() { IColumnsInfo * pColumnsInfo; // try to get IRowset's simple metadata interface HRESULT hr = m_pRowset->QueryInterface(IID_IColumnsInfo, (void**)&pColumnsInfo); if (FAILED(hr)) return VD_E_CANNOTGETMANDATORYINTERFACE; ULONG cColumns = 0; DBCOLUMNINFO * pInfo = NULL; WCHAR * pStringsBuffer = NULL; // now get column information hr = pColumnsInfo->GetColumnInfo(&cColumns, &pInfo, &pStringsBuffer); if (FAILED(hr)) { pColumnsInfo->Release(); return VD_E_CANNOTGETCOLUMNINFO; } // store column count // note cColumns includes the bookmark column (0) m_ulColumns = cColumns; // add one for CURSOR_COLUMN_BMK_CURSOR m_ulColumns++; // if rowset supports DBPROP_BOOKMARKSKIPPED add two for // CURSOR_COLUMN_BMK_TEMPORARYREL and CURSOR_COLUMN_BMK_CURSORREL if (m_fBookmarkSkipped) m_ulColumns += 2; // create array of rowset column objects m_rgColumns = new CVDRowsetColumn[m_ulColumns]; if (!m_rgColumns) { if (pInfo) g_pMalloc->Free(pInfo); if (pStringsBuffer) g_pMalloc->Free(pStringsBuffer); pColumnsInfo->Release(); return E_OUTOFMEMORY; } ULONG ulCursorOrdinal = 0; // get maximum length of bookmarks m_cbMaxBookmark = pInfo[0].ulColumnSize; // initialize data column(s) for (ULONG ulCol = 1; ulCol < cColumns; ulCol++) { m_rgColumns[ulCursorOrdinal].Initialize(ulCol, ulCursorOrdinal, &pInfo[ulCol], m_cbMaxBookmark); ulCursorOrdinal++; } // initialize bookmark columns pInfo[0].pwszName = NULL; // ICursor requires bookmark columns have a NULL name m_rgColumns[ulCursorOrdinal].Initialize(0, ulCursorOrdinal, &pInfo[0], m_cbMaxBookmark, (CURSOR_DBCOLUMNID*)&CURSOR_COLUMN_BMKTEMPORARY); ulCursorOrdinal++; m_rgColumns[ulCursorOrdinal].Initialize(0, ulCursorOrdinal, &pInfo[0], m_cbMaxBookmark, (CURSOR_DBCOLUMNID*)&CURSOR_COLUMN_BMKCURSOR); ulCursorOrdinal++; if (m_fBookmarkSkipped) { m_rgColumns[ulCursorOrdinal].Initialize(0, ulCursorOrdinal, &pInfo[0], m_cbMaxBookmark, (CURSOR_DBCOLUMNID*)&CURSOR_COLUMN_BMKTEMPORARYREL); ulCursorOrdinal++; m_rgColumns[ulCursorOrdinal].Initialize(0, ulCursorOrdinal, &pInfo[0], m_cbMaxBookmark, (CURSOR_DBCOLUMNID*)&CURSOR_COLUMN_BMKCURSORREL); ulCursorOrdinal++; } // free resources if (pInfo) g_pMalloc->Free(pInfo); if (pStringsBuffer) g_pMalloc->Free(pStringsBuffer); pColumnsInfo->Release(); InitOptionalMetadata(cColumns); return S_OK; } //=--------------------------------------------------------------------------= // InitOptionalMetadata - gets additional metadata from IColumnsRowset (if available) // void CVDCursorMain::InitOptionalMetadata(ULONG cColumns) { // we should return if there is only a bookmark column if (cColumns < 2) return; IColumnsRowset * pColumnsRowset = NULL; // try to get IColumnsRowset interface HRESULT hr = m_pRowset->QueryInterface(IID_IColumnsRowset, (void**)&pColumnsRowset); if (FAILED(hr)) return; IRowset * pRowset = NULL; IColumnsInfo * pColumnsInfo = NULL; IAccessor * pAccessor = NULL; ULONG cOptColumnsAvailable = 0; DBID * rgOptColumnsAvailable = NULL; DBID * pOptColumnsAvailable = NULL; // work ptr ULONG cOptColumns = 0; DBID * rgOptColumns = NULL; DBID * pOptColumns = NULL; //work ptr ULONG ulBuffLen = 0; BYTE * pBuff = NULL; HACCESSOR hAccessor; BOOL fAccessorCreated = FALSE; HROW * rgRows = NULL; ULONG cRowsObtained = 0; // we are only interested in a few of the optional columns ULONG rgColumnPropids[VD_COLUMNSROWSET_MAX_OPT_COLUMNS]; ULONG rgOrdinals[VD_COLUMNSROWSET_MAX_OPT_COLUMNS]; DBBINDING rgBindings[VD_COLUMNSROWSET_MAX_OPT_COLUMNS]; DBBINDING * pBinding = NULL; // work ptr BOOL fMatched; GUID guidCID = DBCIDGUID; ULONG cColumnsMatched = 0; ULONG i, j; // get array of available columns hr = pColumnsRowset->GetAvailableColumns(&cOptColumnsAvailable, &rgOptColumnsAvailable); if (FAILED(hr) || 0 == cOptColumnsAvailable) goto cleanup; ASSERT_(rgOptColumnsAvailable); // allocate enough DBIDs for the lesser of the total available columns or the total number of // columns we're interested in rgOptColumns = (DBID *)g_pMalloc->Alloc(min(cOptColumnsAvailable, VD_COLUMNSROWSET_MAX_OPT_COLUMNS) * sizeof(DBID)); if (!rgOptColumns) goto cleanup; // initalize work pointers pOptColumnsAvailable = rgOptColumnsAvailable; pOptColumns = rgOptColumns; pBinding = rgBindings; memset(pBinding, 0, sizeof(DBBINDING) * VD_COLUMNSROWSET_MAX_OPT_COLUMNS); // search available columns for the ones we are interested in copying them into rgOptColumns for (i = 0; i < cOptColumnsAvailable && cColumnsMatched < VD_COLUMNSROWSET_MAX_OPT_COLUMNS; i++) { fMatched = FALSE; // initialize to false if (DBKIND_GUID_PROPID == pOptColumnsAvailable->eKind && DO_GUIDS_MATCH(pOptColumnsAvailable->uGuid.guid, guidCID)) { switch (pOptColumnsAvailable->uName.ulPropid) { case 12: //DBCOLUMN_COLLATINGSEQUENCE = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)12}; pBinding->obValue = ulBuffLen; ulBuffLen += sizeof(ULONG); pBinding->obStatus = ulBuffLen; ulBuffLen += sizeof(DBSTATUS); pBinding->dwPart = DBPART_VALUE | DBPART_STATUS; pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED; pBinding->wType = DBTYPE_I4; fMatched = TRUE; break; //string properties case 10: //DBCOLUMN_BASECOLUMNNAME = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)10}; case 11: //DBCOLUMN_BASETABLENAME = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)11}; case 14: //DBCOLUMN_DEFAULTVALUE = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)14}; pBinding->obValue = ulBuffLen; ulBuffLen += 512; pBinding->obLength = ulBuffLen; ulBuffLen += sizeof(ULONG); pBinding->obStatus = ulBuffLen; ulBuffLen += sizeof(DBSTATUS); pBinding->dwPart = DBPART_VALUE | DBPART_LENGTH |DBPART_STATUS; pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED; pBinding->cbMaxLen = 512; pBinding->wType = DBTYPE_WSTR; fMatched = TRUE; break; // bool properties case 16: //DBCOLUMN_HASDEFAULT = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)16}; case 17: //DBCOLUMN_ISAUTOINCREMENT = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)17}; case 18: //DBCOLUMN_ISCASESENSITIVE = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)18}; case 21: //DBCOLUMN_ISUNIQUE = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)21}; pBinding->obValue = ulBuffLen; ulBuffLen += sizeof(VARIANT_BOOL); pBinding->obStatus = ulBuffLen; ulBuffLen += sizeof(DBSTATUS); pBinding->dwPart = DBPART_VALUE | DBPART_STATUS; pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED; pBinding->wType = DBTYPE_BOOL; fMatched = TRUE; break; } } if (fMatched) { rgColumnPropids[cColumnsMatched] = pOptColumnsAvailable->uName.ulPropid; *pOptColumns = *pOptColumnsAvailable; pBinding++; pOptColumns++; cColumnsMatched++; } pOptColumnsAvailable++; } if (!cColumnsMatched) goto cleanup; // get column's rowset hr = pColumnsRowset->GetColumnsRowset(NULL, cColumnsMatched, rgOptColumns, IID_IRowset, 0, NULL, (IUnknown**)&pRowset); if FAILED(hr) { ASSERT_(FALSE); goto cleanup; } // get IColumnsInfo interface on column's rowset hr = pRowset->QueryInterface(IID_IColumnsInfo, (void**)&pColumnsInfo); if (FAILED(hr)) { ASSERT_(FALSE); goto cleanup; } // get ordinals for our optional columns hr = pColumnsInfo->MapColumnIDs(cColumnsMatched, rgOptColumns, rgOrdinals); if (S_OK != hr) { ASSERT_(FALSE); goto cleanup; } // update binding structures with ordinals for (i = 0; i < cColumnsMatched; i++) rgBindings[i].iOrdinal = rgOrdinals[i]; // get IAccessor interface on column's rowset hr = pRowset->QueryInterface(IID_IAccessor, (void**)&pAccessor); if (FAILED(hr)) { ASSERT_(FALSE); goto cleanup; } // create accessor based on rgBindings array hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, cColumnsMatched, rgBindings, ulBuffLen, &hAccessor, NULL); if (S_OK != hr) { ASSERT_(FALSE); goto cleanup; } // set flag that accessor was successfully created (used during cleanup) fAccessorCreated = TRUE; // allocate a buffer to hold the metadata pBuff = (BYTE *)g_pMalloc->Alloc(ulBuffLen); if (!pBuff) { ASSERT_(FALSE); goto cleanup; } // get all rows (each row represents a column in the original rowset) // except the first row which represents the bookmark column hr = pRowset->GetNextRows(0, // reserved 1, // skip the bookmark row cColumns - 1, // get 1 less than cColumns to account for bookmark row &cRowsObtained, // return count of rows obtanied &rgRows); if (FAILED(hr) || !cRowsObtained) { ASSERT_(FALSE); goto cleanup; } BYTE * pValue; // loop through all rows obtained for (i = 0; i < cRowsObtained; i++) { // call GetData to get the metadata for this row (which represents a column in the orig rowset) hr = pRowset->GetData(rgRows[i], hAccessor, pBuff); if SUCCEEDED(hr) { // now update the CVDRowsetColumn object (that this row represents) // with the values returned from GetData for (j = 0; j < cColumnsMatched; j++) { if (DBBINDSTATUS_OK != *(DBSTATUS*)(pBuff + rgBindings[j].obStatus)) continue; // set pValue to point into buffer at correct offset pValue = pBuff + rgBindings[j].obValue; switch (rgColumnPropids[j]) { case 12: //DBCOLUMN_COLLATINGSEQUENCE = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)12}; m_rgColumns[i].SetCollatingOrder(*(LCID*)pValue); break; //string properties case 10: //DBCOLUMN_BASECOLUMNNAME = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)10}; m_rgColumns[i].SetBaseColumnName((WCHAR*)pValue, *(ULONG*)(pBuff + rgBindings[j].obLength)); break; case 11: //DBCOLUMN_BASETABLENAME = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)11}; m_rgColumns[i].SetBaseName((WCHAR*)pValue, *(ULONG*)(pBuff + rgBindings[j].obLength)); break; case 14: //DBCOLUMN_DEFAULTVALUE = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)14}; m_rgColumns[i].SetDefaultValue((WCHAR*)pValue, *(ULONG*)(pBuff + rgBindings[j].obLength)); break; // bool properties case 16: //DBCOLUMN_HASDEFAULT = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)16}; m_rgColumns[i].SetHasDefault(*(VARIANT_BOOL*)pValue); break; case 17: //DBCOLUMN_ISAUTOINCREMENT = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)17}; m_rgColumns[i].SetAutoIncrement(*(VARIANT_BOOL*)pValue); break; case 18: //DBCOLUMN_ISCASESENSITIVE = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)18}; m_rgColumns[i].SetCaseSensitive(*(VARIANT_BOOL*)pValue); break; case 21: //DBCOLUMN_ISUNIQUE = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)21}; m_rgColumns[i].SetUnique(*(VARIANT_BOOL*)pValue); break; default: ASSERT_(FALSE); break; } } } else ASSERT_(FALSE); } m_fColumnsRowsetSupported = TRUE; cleanup: if (pBuff) g_pMalloc->Free(pBuff); if (pAccessor) { if (fAccessorCreated) pAccessor->ReleaseAccessor(hAccessor, NULL); pAccessor->Release(); } if (pRowset) { if (cRowsObtained) { pRowset->ReleaseRows(cRowsObtained, rgRows, NULL, NULL, NULL); ASSERT_(rgRows); g_pMalloc->Free(rgRows); } pRowset->Release(); } if (pColumnsInfo) pColumnsInfo->Release(); if (rgOptColumnsAvailable) g_pMalloc->Free(rgOptColumnsAvailable); if (rgOptColumns) g_pMalloc->Free(rgOptColumns); if (pColumnsRowset) pColumnsRowset->Release(); } //=--------------------------------------------------------------------------= // DestroyColumns - Destroy array of column objects // void CVDCursorMain::DestroyColumns() { delete [] m_rgColumns; m_ulColumns = 0; m_rgColumns = NULL; } //=--------------------------------------------------------------------------= // ConnectIRowsetNotify - Connect IRowsetNotify interface // HRESULT CVDCursorMain::ConnectIRowsetNotify() { IConnectionPointContainer * pConnectionPointContainer; HRESULT hr = GetRowset()->QueryInterface(IID_IConnectionPointContainer, (void**)&pConnectionPointContainer); if (FAILED(hr)) return VD_E_CANNOTCONNECTIROWSETNOTIFY; IConnectionPoint * pConnectionPoint; hr = pConnectionPointContainer->FindConnectionPoint(IID_IRowsetNotify, &pConnectionPoint); if (FAILED(hr)) { pConnectionPointContainer->Release(); return VD_E_CANNOTCONNECTIROWSETNOTIFY; } hr = pConnectionPoint->Advise(&m_RowsetNotify, &m_dwAdviseCookie); pConnectionPointContainer->Release(); pConnectionPoint->Release(); return hr; } //=--------------------------------------------------------------------------= // DisconnectIRowsetNotify - Disconnect IRowsetNotify interface // void CVDCursorMain::DisconnectIRowsetNotify() { IConnectionPointContainer * pConnectionPointContainer; HRESULT hr = GetRowset()->QueryInterface(IID_IConnectionPointContainer, (void**)&pConnectionPointContainer); if (FAILED(hr)) return; IConnectionPoint * pConnectionPoint; hr = pConnectionPointContainer->FindConnectionPoint(IID_IRowsetNotify, &pConnectionPoint); if (FAILED(hr)) { pConnectionPointContainer->Release(); return; } hr = pConnectionPoint->Unadvise(m_dwAdviseCookie); if (SUCCEEDED(hr)) m_dwAdviseCookie = 0; // clear connection point identifier pConnectionPointContainer->Release(); pConnectionPoint->Release(); } //=--------------------------------------------------------------------------= // IUnknown QueryInterface // HRESULT CVDCursorMain::QueryInterface(REFIID riid, void **ppvObjOut) { ASSERT_POINTER(ppvObjOut, IUnknown*) if (!ppvObjOut) return E_INVALIDARG; *ppvObjOut = NULL; if (DO_GUIDS_MATCH(riid, IID_IUnknown)) { *ppvObjOut = this; AddRef(); return S_OK; } return E_NOINTERFACE; } //=--------------------------------------------------------------------------= // IUnknown AddRef (needed to resolve ambiguity) // ULONG CVDCursorMain::AddRef(void) { return CVDNotifier::AddRef(); } //=--------------------------------------------------------------------------= // IUnknown Release (needed to resolve ambiguity) // ULONG CVDCursorMain::Release(void) { if (1 == m_dwRefCount) Passivate(); // unhook everything including notification sink if (1 > --m_dwRefCount) { if (0 == m_RowsetNotify.GetRefCount()) delete this; return 0; } return m_dwRefCount; } //=--------------------------------------------------------------------------= // IsSameRowAsNew - Determine if specified hRow is an addrow // BOOL CVDCursorMain::IsSameRowAsNew(HROW hrow) { for (int k = 0; k < m_Children.GetSize(); k++) { if (((CVDCursorPosition*)(CVDNotifier*)m_Children[k])->IsSameRowAsNew(hrow) == S_OK) return TRUE; } return FALSE; } //=--------------------------------------------------------------------------= // AddedRows - Get the number of add-rows in cursor // ULONG CVDCursorMain::AddedRows() { ULONG cAdded = 0; for (int k = 0; k < m_Children.GetSize(); k++) { if (((CVDCursorPosition*)(CVDNotifier*)m_Children[k])->GetEditMode() == CURSOR_DBEDITMODE_ADD) cAdded++; } return cAdded; } //=--------------------------------------------------------------------------= // IRowsetNotify Methods //=--------------------------------------------------------------------------= //=--------------------------------------------------------------------------= // IRowsetNotify OnFieldChange //=--------------------------------------------------------------------------= // Forward to all CVDCursorPosition objects in our family // HRESULT CVDCursorMain::OnFieldChange(IRowset *pRowset, HROW hRow, ULONG cColumns, ULONG rgColumns[], DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny) { HRESULT hr = S_OK; // return if notification caused by internal rowset call if (m_fInternalSetData && eReason == DBREASON_COLUMN_SET) return hr; for (int k = 0; k < m_Children.GetSize(); k++) { hr = ((CVDCursorPosition*)(CVDNotifier*)m_Children[k])->OnFieldChange(pRowset, hRow, cColumns, rgColumns, eReason, ePhase, fCantDeny); if (hr) break; } return hr; } //=--------------------------------------------------------------------------= // IRowsetNotify OnRowChange //=--------------------------------------------------------------------------= // Forward to all CVDCursorPosition objects in our family // HRESULT CVDCursorMain::OnRowChange(IRowset *pRowset, ULONG cRows, const HROW rghRows[], DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny) { HRESULT hr = S_OK; // return if notification caused by internal rowset call (either insert or delete) if (m_fInternalInsertRow && eReason == DBREASON_ROW_INSERT || m_fInternalDeleteRows && eReason == DBREASON_ROW_DELETE) return hr; for (int k = 0; k < m_Children.GetSize(); k++) { hr = ((CVDCursorPosition*)(CVDNotifier*)m_Children[k])->OnRowChange(pRowset, cRows, rghRows, eReason, ePhase, fCantDeny); if (hr) break; } return hr; } //=--------------------------------------------------------------------------= // IRowsetNotify OnRowsetChange //=--------------------------------------------------------------------------= // Forward to all CVDCursorPosition objects in our family // HRESULT CVDCursorMain::OnRowsetChange(IRowset *pRowset, DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny) { HRESULT hr = S_OK; for (int k = 0; k < m_Children.GetSize(); k++) { hr = ((CVDCursorPosition*)(CVDNotifier*)m_Children[k])->OnRowsetChange(pRowset, eReason, ePhase, fCantDeny); if (hr) break; } return hr; } //=--------------------------------------------------------------------------= // CVDCursorMain::CVDRowsetNotify::m_pMainUnknown //=--------------------------------------------------------------------------= // this method is used when we're sitting in the private unknown object, // and we need to get at the pointer for the main unknown. basically, it's // a little better to do this pointer arithmetic than have to store a pointer // to the parent, etc. // inline CVDCursorMain *CVDCursorMain::CVDRowsetNotify::m_pMainUnknown ( void ) { return (CVDCursorMain *)((LPBYTE)this - offsetof(CVDCursorMain, m_RowsetNotify)); } //=--------------------------------------------------------------------------= // CVDCursorMain::CVDRowsetNotify::QueryInterface //=--------------------------------------------------------------------------= // this is the non-delegating internal QI routine. // // Parameters: // REFIID - [in] interface they want // void ** - [out] where they want to put the resulting object ptr. // // Output: // HRESULT - S_OK, E_NOINTERFACE // // Notes: // STDMETHODIMP CVDCursorMain::CVDRowsetNotify::QueryInterface ( REFIID riid, void **ppvObjOut ) { if (!ppvObjOut) return E_INVALIDARG; *ppvObjOut = NULL; if (DO_GUIDS_MATCH(riid, IID_IUnknown)) *ppvObjOut = (IUnknown *)this; else if (DO_GUIDS_MATCH(riid, IID_IRowsetNotify)) *ppvObjOut = (IUnknown *)this; if (*ppvObjOut) { m_cRef++; return S_OK; } return E_NOINTERFACE; } //=--------------------------------------------------------------------------= // CVDCursorMain::CVDRowsetNotify::AddRef //=--------------------------------------------------------------------------= // adds a tick to the current reference count. // // Output: // ULONG - the new reference count // // Notes: // ULONG CVDCursorMain::CVDRowsetNotify::AddRef ( void ) { return ++m_cRef; } //=--------------------------------------------------------------------------= // CVDCursorMain::CVDRowsetNotify::Release //=--------------------------------------------------------------------------= // removes a tick from the count, and delets the object if necessary // // Output: // ULONG - remaining refs // // Notes: // ULONG CVDCursorMain::CVDRowsetNotify::Release ( void ) { ULONG cRef = --m_cRef; if (!m_cRef && !m_pMainUnknown()->m_dwRefCount) delete m_pMainUnknown(); return cRef; } //=--------------------------------------------------------------------------= // IRowsetNotify Methods //=--------------------------------------------------------------------------= //=--------------------------------------------------------------------------= // IRowsetNotify OnFieldChange //=--------------------------------------------------------------------------= // Forward to all CVDCursorPosition objects in our family // HRESULT CVDCursorMain::CVDRowsetNotify::OnFieldChange(IRowset *pRowset, HROW hRow, ULONG cColumns, ULONG rgColumns[], DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny) { return m_pMainUnknown()->OnFieldChange(pRowset, hRow, cColumns, rgColumns, eReason, ePhase, fCantDeny); } //=--------------------------------------------------------------------------= // IRowsetNotify OnRowChange //=--------------------------------------------------------------------------= // Forward to all CVDCursorPosition objects in our family // HRESULT CVDCursorMain::CVDRowsetNotify::OnRowChange(IRowset *pRowset, ULONG cRows, const HROW rghRows[], DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny) { return m_pMainUnknown()->OnRowChange(pRowset, cRows, rghRows, eReason, ePhase, fCantDeny); } //=--------------------------------------------------------------------------= // IRowsetNotify OnRowsetChange //=--------------------------------------------------------------------------= // Forward to all CVDCursorPosition objects in our family // HRESULT CVDCursorMain::CVDRowsetNotify::OnRowsetChange(IRowset *pRowset, DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny) { return m_pMainUnknown()->OnRowsetChange(pRowset, eReason, ePhase, fCantDeny); }