Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1288 lines
38 KiB

  1. //---------------------------------------------------------------------------
  2. // CursorMain.cpp : CursorMain implementation
  3. //
  4. // Copyright (c) 1996 Microsoft Corporation, All Rights Reserved
  5. // Developed by Sheridan Software Systems, Inc.
  6. //---------------------------------------------------------------------------
  7. #include "stdafx.h"
  8. #include "MSR2C.h"
  9. #include "Notifier.h"
  10. #include "RSColumn.h"
  11. #include "RSSource.h"
  12. #include "CursMain.h"
  13. #include "ColUpdat.h"
  14. #include "CursPos.h"
  15. #include "CursBase.h"
  16. #include "enumcnpt.h"
  17. #include "Cursor.h"
  18. #include "Bookmark.h"
  19. #include "fastguid.h"
  20. SZTHISFILE
  21. #include "ARRAY_P.inl"
  22. // static data
  23. DWORD CVDCursorMain::s_dwMetaRefCount = 0;
  24. ULONG CVDCursorMain::s_ulMetaColumns = 0;
  25. CVDRowsetColumn * CVDCursorMain::s_rgMetaColumns = NULL;
  26. //=--------------------------------------------------------------------------=
  27. // CVDCursorMain - Constructor
  28. //
  29. CVDCursorMain::CVDCursorMain(LCID lcid) : m_resourceDLL(lcid)
  30. {
  31. m_fWeAddedMetaRef = FALSE;
  32. m_fPassivated = FALSE;
  33. m_fColumnsRowsetSupported = FALSE;
  34. m_fInternalInsertRow = FALSE;
  35. m_fInternalDeleteRows = FALSE;
  36. m_fInternalSetData = FALSE;
  37. m_fLiteralBookmarks = FALSE;
  38. m_fOrderedBookmarks = FALSE;
  39. m_fBookmarkSkipped = FALSE;
  40. m_fConnected = FALSE;
  41. m_dwAdviseCookie = 0;
  42. m_ulColumns = 0;
  43. m_rgColumns = NULL;
  44. m_cbMaxBookmark = 0;
  45. VDUpdateObjectCount(1); // update object count to prevent dll from being unloaded
  46. #ifdef _DEBUG
  47. g_cVDCursorMainCreated++;
  48. #endif
  49. }
  50. //=--------------------------------------------------------------------------=
  51. // ~CVDCursorMain - Destructor
  52. //
  53. CVDCursorMain::~CVDCursorMain()
  54. {
  55. Passivate();
  56. VDUpdateObjectCount(-1); // update object count to allow dll to be unloaded
  57. #ifdef _DEBUG
  58. g_cVDCursorMainDestroyed++;
  59. #endif
  60. }
  61. //=--------------------------------------------------------------------------=
  62. // Pasivate when external ref count gets to zero
  63. //
  64. void CVDCursorMain::Passivate()
  65. {
  66. if (m_fPassivated)
  67. return;
  68. m_fPassivated = TRUE;
  69. if (IsRowsetValid())
  70. {
  71. if (m_hAccessorBM)
  72. GetAccessor()->ReleaseAccessor(m_hAccessorBM, NULL);
  73. if (m_fConnected)
  74. DisconnectIRowsetNotify();
  75. }
  76. DestroyColumns();
  77. DestroyMetaColumns();
  78. }
  79. //=--------------------------------------------------------------------------=
  80. // Create - Create cursor provider from row position or rowset
  81. //=--------------------------------------------------------------------------=
  82. // This function creates and initializes a new cursor main object
  83. //
  84. // Parameters:
  85. // pRowPosition - [in] original IRowPosition provider (may be NULL)
  86. // pRowset - [in] original IRowset provider
  87. // ppCursor - [out] resulting ICursor provider
  88. // lcid - [in] locale identifier
  89. //
  90. // Output:
  91. // HRESULT - S_OK if successful
  92. // E_INVALIDARG bad parameter
  93. // E_OUTOFMEMORY not enough memory
  94. // VD_E_CANNOTCONNECTIROWSETNOTIFY unable to connect IRowsetNotify
  95. //
  96. // Notes:
  97. //
  98. HRESULT CVDCursorMain::Create(IRowPosition* pRowPosition, IRowset * pRowset, ICursor ** ppCursor, LCID lcid)
  99. {
  100. ASSERT_POINTER(pRowset, IRowset)
  101. ASSERT_POINTER(ppCursor, ICursor*)
  102. if (!pRowset || !ppCursor)
  103. return E_INVALIDARG;
  104. // create new cursor main object
  105. CVDCursorMain * pCursorMain = new CVDCursorMain(lcid);
  106. if (!pCursorMain)
  107. return E_OUTOFMEMORY;
  108. // initialize rowset source
  109. HRESULT hr = pCursorMain->Initialize(pRowset);
  110. if (FAILED(hr))
  111. {
  112. pCursorMain->Release();
  113. return hr;
  114. }
  115. // create array of column objects
  116. hr = pCursorMain->CreateColumns();
  117. if (FAILED(hr))
  118. {
  119. pCursorMain->Release();
  120. return hr;
  121. }
  122. // create array of meta-column objects
  123. hr = pCursorMain->CreateMetaColumns();
  124. if (FAILED(hr))
  125. {
  126. pCursorMain->Release();
  127. return hr;
  128. }
  129. // create bookmark accessor
  130. DBBINDING rgBindings[1];
  131. DBBINDSTATUS rgStatus[1];
  132. memset(rgBindings, 0, sizeof(DBBINDING));
  133. rgBindings[0].iOrdinal = 0;
  134. rgBindings[0].obValue = 4;
  135. rgBindings[0].obLength = 0;
  136. rgBindings[0].dwPart = DBPART_VALUE | DBPART_LENGTH;
  137. rgBindings[0].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
  138. rgBindings[0].cbMaxLen = pCursorMain->GetMaxBookmarkLen();
  139. rgBindings[0].wType = DBTYPE_BYTES;
  140. hr = pCursorMain->GetAccessor()->CreateAccessor(DBACCESSOR_ROWDATA,
  141. 1,
  142. rgBindings,
  143. 0,
  144. &pCursorMain->m_hAccessorBM,
  145. rgStatus);
  146. if (FAILED(hr))
  147. {
  148. pCursorMain->Release();
  149. return VD_E_CANNOTCREATEBOOKMARKACCESSOR;
  150. }
  151. // create new cursor position object
  152. CVDCursorPosition * pCursorPosition;
  153. hr = CVDCursorPosition::Create(pRowPosition, pCursorMain, &pCursorPosition, &pCursorMain->m_resourceDLL);
  154. if (FAILED(hr))
  155. {
  156. pCursorMain->Release();
  157. return hr;
  158. }
  159. // create new cursor object
  160. CVDCursor * pCursor;
  161. hr = CVDCursor::Create(pCursorPosition, &pCursor, &pCursorMain->m_resourceDLL);
  162. if (FAILED(hr))
  163. {
  164. ((CVDNotifier*)pCursorPosition)->Release();
  165. pCursorMain->Release();
  166. return hr;
  167. }
  168. // connect IRowsetNotify
  169. hr = pCursorMain->ConnectIRowsetNotify();
  170. if (SUCCEEDED(hr))
  171. pCursorMain->m_fConnected = TRUE;
  172. // check rowset properties
  173. BOOL fCanHoldRows = TRUE;
  174. IRowsetInfo * pRowsetInfo = pCursorMain->GetRowsetInfo();
  175. if (pRowsetInfo)
  176. {
  177. DBPROPID propids[] = { DBPROP_LITERALBOOKMARKS,
  178. DBPROP_ORDEREDBOOKMARKS,
  179. DBPROP_BOOKMARKSKIPPED,
  180. DBPROP_CANHOLDROWS };
  181. const DBPROPIDSET propsetids[] = { propids, 4, {0,0,0,0} };
  182. memcpy((void*)&propsetids[0].guidPropertySet, &DBPROPSET_ROWSET, sizeof(DBPROPSET_ROWSET));
  183. ULONG cPropertySets = 0;
  184. DBPROPSET * propset = NULL;
  185. hr = pRowsetInfo->GetProperties(1, propsetids, &cPropertySets, &propset);
  186. if (SUCCEEDED(hr) && propset && propset->rgProperties)
  187. {
  188. if (DBPROPSTATUS_OK == propset->rgProperties[0].dwStatus)
  189. pCursorMain->m_fLiteralBookmarks = V_BOOL(&propset->rgProperties[0].vValue);
  190. if (DBPROPSTATUS_OK == propset->rgProperties[1].dwStatus)
  191. pCursorMain->m_fOrderedBookmarks = V_BOOL(&propset->rgProperties[1].vValue);
  192. if (DBPROPSTATUS_OK == propset->rgProperties[2].dwStatus)
  193. pCursorMain->m_fBookmarkSkipped = V_BOOL(&propset->rgProperties[2].vValue);
  194. if (DBPROPSTATUS_OK == propset->rgProperties[3].dwStatus)
  195. fCanHoldRows = V_BOOL(&propset->rgProperties[3].vValue);
  196. }
  197. if (propset)
  198. {
  199. if (propset->rgProperties)
  200. g_pMalloc->Free(propset->rgProperties);
  201. g_pMalloc->Free(propset);
  202. }
  203. }
  204. // release our references
  205. pCursorMain->Release();
  206. ((CVDNotifier*)pCursorPosition)->Release();
  207. // check for required property
  208. if (!fCanHoldRows)
  209. {
  210. pCursor->Release();
  211. return VD_E_REQUIREDPROPERTYNOTSUPPORTED;
  212. }
  213. // we're done
  214. *ppCursor = pCursor;
  215. return S_OK;
  216. }
  217. //=--------------------------------------------------------------------------=
  218. // Create - Create cursor provider from rowset
  219. //=--------------------------------------------------------------------------=
  220. // This function creates and initializes a new cursor main object
  221. //
  222. // Parameters:
  223. // pRowset - [in] original IRowset provider
  224. // ppCursor - [out] resulting ICursor provider
  225. // lcid - [in] locale identifier
  226. //
  227. // Output:
  228. // HRESULT - S_OK if successful
  229. // E_INVALIDARG bad parameter
  230. // E_OUTOFMEMORY not enough memory
  231. // VD_E_CANNOTCONNECTIROWSETNOTIFY unable to connect IRowsetNotify
  232. //
  233. // Notes:
  234. //
  235. HRESULT CVDCursorMain::Create(IRowset * pRowset, ICursor ** ppCursor, LCID lcid)
  236. {
  237. ASSERT_POINTER(pRowset, IRowset)
  238. ASSERT_POINTER(ppCursor, ICursor*)
  239. if (!pRowset || !ppCursor)
  240. return E_INVALIDARG;
  241. // create cursor as done before row position
  242. return Create(NULL, pRowset, ppCursor, lcid);
  243. }
  244. //=--------------------------------------------------------------------------=
  245. // Create - Create cursor provider from row position
  246. //=--------------------------------------------------------------------------=
  247. // This function creates and initializes a new cursor main object
  248. //
  249. // Parameters:
  250. // pRowPosition - [in] original IRowPosition provider
  251. // ppCursor - [out] resulting ICursor provider
  252. // lcid - [in] locale identifier
  253. //
  254. // Output:
  255. // HRESULT - S_OK if successful
  256. // E_INVALIDARG bad parameter
  257. // E_OUTOFMEMORY not enough memory
  258. // VD_E_CANNOTCONNECTIROWSETNOTIFY unable to connect IRowPositionNotify
  259. // VD_E_CANNOTGETROWSETINTERFACE unable to get IRowset
  260. //
  261. // Notes:
  262. //
  263. HRESULT CVDCursorMain::Create(IRowPosition * pRowPosition, ICursor ** ppCursor, LCID lcid)
  264. {
  265. ASSERT_POINTER(pRowPosition, IRowPosition)
  266. ASSERT_POINTER(ppCursor, ICursor*)
  267. if (!pRowPosition || !ppCursor)
  268. return E_INVALIDARG;
  269. IRowset * pRowset;
  270. // get IRowset from IRowPosition
  271. HRESULT hr = pRowPosition->GetRowset(IID_IRowset, (IUnknown**)&pRowset);
  272. if (FAILED(hr))
  273. return VD_E_CANNOTGETROWSETINTERFACE;
  274. // create cursor with new row position parameter
  275. hr = Create(pRowPosition, pRowset, ppCursor, lcid);
  276. pRowset->Release();
  277. // we're done
  278. return hr;
  279. }
  280. typedef struct tagVDMETADATA_METADATA
  281. {
  282. const CURSOR_DBCOLUMNID * pCursorColumnID;
  283. ULONG cbMaxLength;
  284. CHAR * pszName;
  285. DWORD dwCursorType;
  286. } VDMETADATA_METADATA;
  287. #define MAX_METADATA_COLUMNS 21
  288. static const VDMETADATA_METADATA g_MetaDataMetaData[MAX_METADATA_COLUMNS] =
  289. {
  290. // Bookmark column
  291. { &CURSOR_COLUMN_BMKTEMPORARY, sizeof(ULONG), NULL, CURSOR_DBTYPE_BLOB },
  292. // data columns
  293. { &CURSOR_COLUMN_COLUMNID, sizeof(CURSOR_DBCOLUMNID), "COLUMN_COLUMNID", CURSOR_DBTYPE_COLUMNID },
  294. { &CURSOR_COLUMN_DATACOLUMN, sizeof(VARIANT_BOOL), "COLUMN_DATACOLUMN", CURSOR_DBTYPE_BOOL },
  295. { &CURSOR_COLUMN_ENTRYIDMAXLENGTH, sizeof(ULONG), "COLUMN_ENTRYIDMAXLENGTH",CURSOR_DBTYPE_I4 },
  296. { &CURSOR_COLUMN_FIXED, sizeof(VARIANT_BOOL), "COLUMN_FIXED", CURSOR_DBTYPE_BOOL },
  297. { &CURSOR_COLUMN_MAXLENGTH, sizeof(ULONG), "COLUMN_MAXLENGTH", CURSOR_DBTYPE_I4 },
  298. { &CURSOR_COLUMN_NAME, 256, "COLUMN_NAME", VT_LPWSTR },
  299. { &CURSOR_COLUMN_NULLABLE, sizeof(VARIANT_BOOL), "COLUMN_NULLABLE", CURSOR_DBTYPE_BOOL },
  300. { &CURSOR_COLUMN_NUMBER, sizeof(ULONG), "COLUMN_NUMBER", CURSOR_DBTYPE_I4 },
  301. { &CURSOR_COLUMN_SCALE, sizeof(ULONG), "COLUMN_SCALE", CURSOR_DBTYPE_I4 },
  302. { &CURSOR_COLUMN_TYPE, sizeof(ULONG), "COLUMN_TYPE", CURSOR_DBTYPE_I4 },
  303. { &CURSOR_COLUMN_UPDATABLE, sizeof(ULONG), "COLUMN_UPDATABLE", CURSOR_DBTYPE_I4 },
  304. { &CURSOR_COLUMN_BINDTYPE, sizeof(ULONG), "COLUMN_BINDTYPE", CURSOR_DBTYPE_I4 },
  305. // optional metadata columns - supported with IColumnsRowset only)
  306. { &CURSOR_COLUMN_AUTOINCREMENT, sizeof(VARIANT_BOOL), "COLUMN_AUTOINCREMENT", CURSOR_DBTYPE_BOOL },
  307. { &CURSOR_COLUMN_BASECOLUMNNAME, 256, "COLUMN_BASECOLUMNNAME",VT_LPWSTR },
  308. { &CURSOR_COLUMN_BASENAME, 256, "COLUMN_BASENAME", VT_LPWSTR },
  309. { &CURSOR_COLUMN_COLLATINGORDER, sizeof(LCID), "COLUMN_COLLATINGORDER",CURSOR_DBTYPE_I4 },
  310. { &CURSOR_COLUMN_DEFAULTVALUE, 256, "COLUMN_DEFAULTVALUE", VT_LPWSTR },
  311. { &CURSOR_COLUMN_HASDEFAULT, sizeof(VARIANT_BOOL), "COLUMN_HASDEFAULT", CURSOR_DBTYPE_BOOL },
  312. { &CURSOR_COLUMN_CASESENSITIVE, sizeof(VARIANT_BOOL), "COLUMN_CASESENSITIVE", CURSOR_DBTYPE_BOOL },
  313. { &CURSOR_COLUMN_UNIQUE, sizeof(VARIANT_BOOL), "COLUMN_UNIQUE", CURSOR_DBTYPE_BOOL },
  314. };
  315. //=--------------------------------------------------------------------------=
  316. // CreateMetaColumns - Create array of meta-column objects
  317. //
  318. HRESULT CVDCursorMain::CreateMetaColumns()
  319. {
  320. HRESULT hr = S_OK;
  321. EnterCriticalSection(&g_CriticalSection);
  322. if (!s_dwMetaRefCount)
  323. {
  324. // allocate a static aray of metadata metadata columns
  325. s_rgMetaColumns = new CVDRowsetColumn[MAX_METADATA_COLUMNS];
  326. if (!s_rgMetaColumns)
  327. {
  328. hr = E_OUTOFMEMORY;
  329. goto cleanup;
  330. }
  331. s_ulMetaColumns = MAX_METADATA_COLUMNS; // number of columns for IColumnsInfo
  332. // initialize the array elments from the static g_MetaDataMetaData table
  333. for (int i = 0; i < MAX_METADATA_COLUMNS; i++)
  334. {
  335. s_rgMetaColumns[i].Initialize(g_MetaDataMetaData[i].pCursorColumnID,
  336. (BOOL)i, // false for 1st column (bookmark) TRUE for all other columns
  337. g_MetaDataMetaData[i].cbMaxLength,
  338. g_MetaDataMetaData[i].pszName,
  339. g_MetaDataMetaData[i].dwCursorType,
  340. i ); // ordinal number
  341. }
  342. }
  343. s_dwMetaRefCount++;
  344. m_fWeAddedMetaRef = TRUE;
  345. cleanup:
  346. LeaveCriticalSection(&g_CriticalSection);
  347. return hr;
  348. }
  349. //=--------------------------------------------------------------------------=
  350. // DestroyMetaColumns - Destroy array of meta-columns objects
  351. //
  352. void CVDCursorMain::DestroyMetaColumns()
  353. {
  354. EnterCriticalSection(&g_CriticalSection);
  355. if (m_fWeAddedMetaRef)
  356. {
  357. s_dwMetaRefCount--;
  358. if (!s_dwMetaRefCount)
  359. {
  360. delete [] s_rgMetaColumns;
  361. s_ulMetaColumns = 0;
  362. s_rgMetaColumns = NULL;
  363. }
  364. }
  365. LeaveCriticalSection(&g_CriticalSection);
  366. }
  367. //=--------------------------------------------------------------------------=
  368. // CreateColumns - Create array of column objects
  369. //
  370. HRESULT CVDCursorMain::CreateColumns()
  371. {
  372. IColumnsInfo * pColumnsInfo;
  373. // try to get IRowset's simple metadata interface
  374. HRESULT hr = m_pRowset->QueryInterface(IID_IColumnsInfo, (void**)&pColumnsInfo);
  375. if (FAILED(hr))
  376. return VD_E_CANNOTGETMANDATORYINTERFACE;
  377. ULONG cColumns = 0;
  378. DBCOLUMNINFO * pInfo = NULL;
  379. WCHAR * pStringsBuffer = NULL;
  380. // now get column information
  381. hr = pColumnsInfo->GetColumnInfo(&cColumns, &pInfo, &pStringsBuffer);
  382. if (FAILED(hr))
  383. {
  384. pColumnsInfo->Release();
  385. return VD_E_CANNOTGETCOLUMNINFO;
  386. }
  387. // store column count
  388. // note cColumns includes the bookmark column (0)
  389. m_ulColumns = cColumns;
  390. // add one for CURSOR_COLUMN_BMK_CURSOR
  391. m_ulColumns++;
  392. // if rowset supports DBPROP_BOOKMARKSKIPPED add two for
  393. // CURSOR_COLUMN_BMK_TEMPORARYREL and CURSOR_COLUMN_BMK_CURSORREL
  394. if (m_fBookmarkSkipped)
  395. m_ulColumns += 2;
  396. // create array of rowset column objects
  397. m_rgColumns = new CVDRowsetColumn[m_ulColumns];
  398. if (!m_rgColumns)
  399. {
  400. if (pInfo)
  401. g_pMalloc->Free(pInfo);
  402. if (pStringsBuffer)
  403. g_pMalloc->Free(pStringsBuffer);
  404. pColumnsInfo->Release();
  405. return E_OUTOFMEMORY;
  406. }
  407. ULONG ulCursorOrdinal = 0;
  408. // get maximum length of bookmarks
  409. m_cbMaxBookmark = pInfo[0].ulColumnSize;
  410. // initialize data column(s)
  411. for (ULONG ulCol = 1; ulCol < cColumns; ulCol++)
  412. {
  413. m_rgColumns[ulCursorOrdinal].Initialize(ulCol, ulCursorOrdinal, &pInfo[ulCol], m_cbMaxBookmark);
  414. ulCursorOrdinal++;
  415. }
  416. // initialize bookmark columns
  417. pInfo[0].pwszName = NULL; // ICursor requires bookmark columns have a NULL name
  418. m_rgColumns[ulCursorOrdinal].Initialize(0, ulCursorOrdinal, &pInfo[0], m_cbMaxBookmark,
  419. (CURSOR_DBCOLUMNID*)&CURSOR_COLUMN_BMKTEMPORARY);
  420. ulCursorOrdinal++;
  421. m_rgColumns[ulCursorOrdinal].Initialize(0, ulCursorOrdinal, &pInfo[0], m_cbMaxBookmark,
  422. (CURSOR_DBCOLUMNID*)&CURSOR_COLUMN_BMKCURSOR);
  423. ulCursorOrdinal++;
  424. if (m_fBookmarkSkipped)
  425. {
  426. m_rgColumns[ulCursorOrdinal].Initialize(0, ulCursorOrdinal, &pInfo[0], m_cbMaxBookmark,
  427. (CURSOR_DBCOLUMNID*)&CURSOR_COLUMN_BMKTEMPORARYREL);
  428. ulCursorOrdinal++;
  429. m_rgColumns[ulCursorOrdinal].Initialize(0, ulCursorOrdinal, &pInfo[0], m_cbMaxBookmark,
  430. (CURSOR_DBCOLUMNID*)&CURSOR_COLUMN_BMKCURSORREL);
  431. ulCursorOrdinal++;
  432. }
  433. // free resources
  434. if (pInfo)
  435. g_pMalloc->Free(pInfo);
  436. if (pStringsBuffer)
  437. g_pMalloc->Free(pStringsBuffer);
  438. pColumnsInfo->Release();
  439. InitOptionalMetadata(cColumns);
  440. return S_OK;
  441. }
  442. //=--------------------------------------------------------------------------=
  443. // InitOptionalMetadata - gets additional metadata from IColumnsRowset (if available)
  444. //
  445. void CVDCursorMain::InitOptionalMetadata(ULONG cColumns)
  446. {
  447. // we should return if there is only a bookmark column
  448. if (cColumns < 2)
  449. return;
  450. IColumnsRowset * pColumnsRowset = NULL;
  451. // try to get IColumnsRowset interface
  452. HRESULT hr = m_pRowset->QueryInterface(IID_IColumnsRowset, (void**)&pColumnsRowset);
  453. if (FAILED(hr))
  454. return;
  455. IRowset * pRowset = NULL;
  456. IColumnsInfo * pColumnsInfo = NULL;
  457. IAccessor * pAccessor = NULL;
  458. ULONG cOptColumnsAvailable = 0;
  459. DBID * rgOptColumnsAvailable = NULL;
  460. DBID * pOptColumnsAvailable = NULL; // work ptr
  461. ULONG cOptColumns = 0;
  462. DBID * rgOptColumns = NULL;
  463. DBID * pOptColumns = NULL; //work ptr
  464. ULONG ulBuffLen = 0;
  465. BYTE * pBuff = NULL;
  466. HACCESSOR hAccessor;
  467. BOOL fAccessorCreated = FALSE;
  468. HROW * rgRows = NULL;
  469. ULONG cRowsObtained = 0;
  470. // we are only interested in a few of the optional columns
  471. ULONG rgColumnPropids[VD_COLUMNSROWSET_MAX_OPT_COLUMNS];
  472. ULONG rgOrdinals[VD_COLUMNSROWSET_MAX_OPT_COLUMNS];
  473. DBBINDING rgBindings[VD_COLUMNSROWSET_MAX_OPT_COLUMNS];
  474. DBBINDING * pBinding = NULL; // work ptr
  475. BOOL fMatched;
  476. GUID guidCID = DBCIDGUID;
  477. ULONG cColumnsMatched = 0;
  478. ULONG i, j;
  479. // get array of available columns
  480. hr = pColumnsRowset->GetAvailableColumns(&cOptColumnsAvailable, &rgOptColumnsAvailable);
  481. if (FAILED(hr) || 0 == cOptColumnsAvailable)
  482. goto cleanup;
  483. ASSERT_(rgOptColumnsAvailable);
  484. // allocate enough DBIDs for the lesser of the total available columns or the total number of
  485. // columns we're interested in
  486. rgOptColumns = (DBID *)g_pMalloc->Alloc(min(cOptColumnsAvailable, VD_COLUMNSROWSET_MAX_OPT_COLUMNS)
  487. * sizeof(DBID));
  488. if (!rgOptColumns)
  489. goto cleanup;
  490. // initalize work pointers
  491. pOptColumnsAvailable = rgOptColumnsAvailable;
  492. pOptColumns = rgOptColumns;
  493. pBinding = rgBindings;
  494. memset(pBinding, 0, sizeof(DBBINDING) * VD_COLUMNSROWSET_MAX_OPT_COLUMNS);
  495. // search available columns for the ones we are interested in copying them into rgOptColumns
  496. for (i = 0; i < cOptColumnsAvailable && cColumnsMatched < VD_COLUMNSROWSET_MAX_OPT_COLUMNS; i++)
  497. {
  498. fMatched = FALSE; // initialize to false
  499. if (DBKIND_GUID_PROPID == pOptColumnsAvailable->eKind &&
  500. DO_GUIDS_MATCH(pOptColumnsAvailable->uGuid.guid, guidCID))
  501. {
  502. switch (pOptColumnsAvailable->uName.ulPropid)
  503. {
  504. case 12: //DBCOLUMN_COLLATINGSEQUENCE = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)12};
  505. pBinding->obValue = ulBuffLen;
  506. ulBuffLen += sizeof(ULONG);
  507. pBinding->obStatus = ulBuffLen;
  508. ulBuffLen += sizeof(DBSTATUS);
  509. pBinding->dwPart = DBPART_VALUE | DBPART_STATUS;
  510. pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED;
  511. pBinding->wType = DBTYPE_I4;
  512. fMatched = TRUE;
  513. break;
  514. //string properties
  515. case 10: //DBCOLUMN_BASECOLUMNNAME = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)10};
  516. case 11: //DBCOLUMN_BASETABLENAME = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)11};
  517. case 14: //DBCOLUMN_DEFAULTVALUE = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)14};
  518. pBinding->obValue = ulBuffLen;
  519. ulBuffLen += 512;
  520. pBinding->obLength = ulBuffLen;
  521. ulBuffLen += sizeof(ULONG);
  522. pBinding->obStatus = ulBuffLen;
  523. ulBuffLen += sizeof(DBSTATUS);
  524. pBinding->dwPart = DBPART_VALUE | DBPART_LENGTH |DBPART_STATUS;
  525. pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED;
  526. pBinding->cbMaxLen = 512;
  527. pBinding->wType = DBTYPE_WSTR;
  528. fMatched = TRUE;
  529. break;
  530. // bool properties
  531. case 16: //DBCOLUMN_HASDEFAULT = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)16};
  532. case 17: //DBCOLUMN_ISAUTOINCREMENT = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)17};
  533. case 18: //DBCOLUMN_ISCASESENSITIVE = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)18};
  534. case 21: //DBCOLUMN_ISUNIQUE = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)21};
  535. pBinding->obValue = ulBuffLen;
  536. ulBuffLen += sizeof(VARIANT_BOOL);
  537. pBinding->obStatus = ulBuffLen;
  538. ulBuffLen += sizeof(DBSTATUS);
  539. pBinding->dwPart = DBPART_VALUE | DBPART_STATUS;
  540. pBinding->dwMemOwner = DBMEMOWNER_CLIENTOWNED;
  541. pBinding->wType = DBTYPE_BOOL;
  542. fMatched = TRUE;
  543. break;
  544. }
  545. }
  546. if (fMatched)
  547. {
  548. rgColumnPropids[cColumnsMatched] = pOptColumnsAvailable->uName.ulPropid;
  549. *pOptColumns = *pOptColumnsAvailable;
  550. pBinding++;
  551. pOptColumns++;
  552. cColumnsMatched++;
  553. }
  554. pOptColumnsAvailable++;
  555. }
  556. if (!cColumnsMatched)
  557. goto cleanup;
  558. // get column's rowset
  559. hr = pColumnsRowset->GetColumnsRowset(NULL,
  560. cColumnsMatched,
  561. rgOptColumns,
  562. IID_IRowset,
  563. 0,
  564. NULL,
  565. (IUnknown**)&pRowset);
  566. if FAILED(hr)
  567. {
  568. ASSERT_(FALSE);
  569. goto cleanup;
  570. }
  571. // get IColumnsInfo interface on column's rowset
  572. hr = pRowset->QueryInterface(IID_IColumnsInfo, (void**)&pColumnsInfo);
  573. if (FAILED(hr))
  574. {
  575. ASSERT_(FALSE);
  576. goto cleanup;
  577. }
  578. // get ordinals for our optional columns
  579. hr = pColumnsInfo->MapColumnIDs(cColumnsMatched, rgOptColumns, rgOrdinals);
  580. if (S_OK != hr)
  581. {
  582. ASSERT_(FALSE);
  583. goto cleanup;
  584. }
  585. // update binding structures with ordinals
  586. for (i = 0; i < cColumnsMatched; i++)
  587. rgBindings[i].iOrdinal = rgOrdinals[i];
  588. // get IAccessor interface on column's rowset
  589. hr = pRowset->QueryInterface(IID_IAccessor, (void**)&pAccessor);
  590. if (FAILED(hr))
  591. {
  592. ASSERT_(FALSE);
  593. goto cleanup;
  594. }
  595. // create accessor based on rgBindings array
  596. hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA,
  597. cColumnsMatched,
  598. rgBindings,
  599. ulBuffLen,
  600. &hAccessor,
  601. NULL);
  602. if (S_OK != hr)
  603. {
  604. ASSERT_(FALSE);
  605. goto cleanup;
  606. }
  607. // set flag that accessor was successfully created (used during cleanup)
  608. fAccessorCreated = TRUE;
  609. // allocate a buffer to hold the metadata
  610. pBuff = (BYTE *)g_pMalloc->Alloc(ulBuffLen);
  611. if (!pBuff)
  612. {
  613. ASSERT_(FALSE);
  614. goto cleanup;
  615. }
  616. // get all rows (each row represents a column in the original rowset)
  617. // except the first row which represents the bookmark column
  618. hr = pRowset->GetNextRows(0, // reserved
  619. 1, // skip the bookmark row
  620. cColumns - 1, // get 1 less than cColumns to account for bookmark row
  621. &cRowsObtained, // return count of rows obtanied
  622. &rgRows);
  623. if (FAILED(hr) || !cRowsObtained)
  624. {
  625. ASSERT_(FALSE);
  626. goto cleanup;
  627. }
  628. BYTE * pValue;
  629. // loop through all rows obtained
  630. for (i = 0; i < cRowsObtained; i++)
  631. {
  632. // call GetData to get the metadata for this row (which represents a column in the orig rowset)
  633. hr = pRowset->GetData(rgRows[i], hAccessor, pBuff);
  634. if SUCCEEDED(hr)
  635. {
  636. // now update the CVDRowsetColumn object (that this row represents)
  637. // with the values returned from GetData
  638. for (j = 0; j < cColumnsMatched; j++)
  639. {
  640. if (DBBINDSTATUS_OK != *(DBSTATUS*)(pBuff + rgBindings[j].obStatus))
  641. continue;
  642. // set pValue to point into buffer at correct offset
  643. pValue = pBuff + rgBindings[j].obValue;
  644. switch (rgColumnPropids[j])
  645. {
  646. case 12: //DBCOLUMN_COLLATINGSEQUENCE = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)12};
  647. m_rgColumns[i].SetCollatingOrder(*(LCID*)pValue);
  648. break;
  649. //string properties
  650. case 10: //DBCOLUMN_BASECOLUMNNAME = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)10};
  651. m_rgColumns[i].SetBaseColumnName((WCHAR*)pValue, *(ULONG*)(pBuff + rgBindings[j].obLength));
  652. break;
  653. case 11: //DBCOLUMN_BASETABLENAME = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)11};
  654. m_rgColumns[i].SetBaseName((WCHAR*)pValue, *(ULONG*)(pBuff + rgBindings[j].obLength));
  655. break;
  656. case 14: //DBCOLUMN_DEFAULTVALUE = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)14};
  657. m_rgColumns[i].SetDefaultValue((WCHAR*)pValue, *(ULONG*)(pBuff + rgBindings[j].obLength));
  658. break;
  659. // bool properties
  660. case 16: //DBCOLUMN_HASDEFAULT = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)16};
  661. m_rgColumns[i].SetHasDefault(*(VARIANT_BOOL*)pValue);
  662. break;
  663. case 17: //DBCOLUMN_ISAUTOINCREMENT = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)17};
  664. m_rgColumns[i].SetAutoIncrement(*(VARIANT_BOOL*)pValue);
  665. break;
  666. case 18: //DBCOLUMN_ISCASESENSITIVE = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)18};
  667. m_rgColumns[i].SetCaseSensitive(*(VARIANT_BOOL*)pValue);
  668. break;
  669. case 21: //DBCOLUMN_ISUNIQUE = {DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)21};
  670. m_rgColumns[i].SetUnique(*(VARIANT_BOOL*)pValue);
  671. break;
  672. default:
  673. ASSERT_(FALSE);
  674. break;
  675. }
  676. }
  677. }
  678. else
  679. ASSERT_(FALSE);
  680. }
  681. m_fColumnsRowsetSupported = TRUE;
  682. cleanup:
  683. if (pBuff)
  684. g_pMalloc->Free(pBuff);
  685. if (pAccessor)
  686. {
  687. if (fAccessorCreated)
  688. pAccessor->ReleaseAccessor(hAccessor, NULL);
  689. pAccessor->Release();
  690. }
  691. if (pRowset)
  692. {
  693. if (cRowsObtained)
  694. {
  695. pRowset->ReleaseRows(cRowsObtained, rgRows, NULL, NULL, NULL);
  696. ASSERT_(rgRows);
  697. g_pMalloc->Free(rgRows);
  698. }
  699. pRowset->Release();
  700. }
  701. if (pColumnsInfo)
  702. pColumnsInfo->Release();
  703. if (rgOptColumnsAvailable)
  704. g_pMalloc->Free(rgOptColumnsAvailable);
  705. if (rgOptColumns)
  706. g_pMalloc->Free(rgOptColumns);
  707. if (pColumnsRowset)
  708. pColumnsRowset->Release();
  709. }
  710. //=--------------------------------------------------------------------------=
  711. // DestroyColumns - Destroy array of column objects
  712. //
  713. void CVDCursorMain::DestroyColumns()
  714. {
  715. delete [] m_rgColumns;
  716. m_ulColumns = 0;
  717. m_rgColumns = NULL;
  718. }
  719. //=--------------------------------------------------------------------------=
  720. // ConnectIRowsetNotify - Connect IRowsetNotify interface
  721. //
  722. HRESULT CVDCursorMain::ConnectIRowsetNotify()
  723. {
  724. IConnectionPointContainer * pConnectionPointContainer;
  725. HRESULT hr = GetRowset()->QueryInterface(IID_IConnectionPointContainer, (void**)&pConnectionPointContainer);
  726. if (FAILED(hr))
  727. return VD_E_CANNOTCONNECTIROWSETNOTIFY;
  728. IConnectionPoint * pConnectionPoint;
  729. hr = pConnectionPointContainer->FindConnectionPoint(IID_IRowsetNotify, &pConnectionPoint);
  730. if (FAILED(hr))
  731. {
  732. pConnectionPointContainer->Release();
  733. return VD_E_CANNOTCONNECTIROWSETNOTIFY;
  734. }
  735. hr = pConnectionPoint->Advise(&m_RowsetNotify, &m_dwAdviseCookie);
  736. pConnectionPointContainer->Release();
  737. pConnectionPoint->Release();
  738. return hr;
  739. }
  740. //=--------------------------------------------------------------------------=
  741. // DisconnectIRowsetNotify - Disconnect IRowsetNotify interface
  742. //
  743. void CVDCursorMain::DisconnectIRowsetNotify()
  744. {
  745. IConnectionPointContainer * pConnectionPointContainer;
  746. HRESULT hr = GetRowset()->QueryInterface(IID_IConnectionPointContainer, (void**)&pConnectionPointContainer);
  747. if (FAILED(hr))
  748. return;
  749. IConnectionPoint * pConnectionPoint;
  750. hr = pConnectionPointContainer->FindConnectionPoint(IID_IRowsetNotify, &pConnectionPoint);
  751. if (FAILED(hr))
  752. {
  753. pConnectionPointContainer->Release();
  754. return;
  755. }
  756. hr = pConnectionPoint->Unadvise(m_dwAdviseCookie);
  757. if (SUCCEEDED(hr))
  758. m_dwAdviseCookie = 0; // clear connection point identifier
  759. pConnectionPointContainer->Release();
  760. pConnectionPoint->Release();
  761. }
  762. //=--------------------------------------------------------------------------=
  763. // IUnknown QueryInterface
  764. //
  765. HRESULT CVDCursorMain::QueryInterface(REFIID riid, void **ppvObjOut)
  766. {
  767. ASSERT_POINTER(ppvObjOut, IUnknown*)
  768. if (!ppvObjOut)
  769. return E_INVALIDARG;
  770. *ppvObjOut = NULL;
  771. if (DO_GUIDS_MATCH(riid, IID_IUnknown))
  772. {
  773. *ppvObjOut = this;
  774. AddRef();
  775. return S_OK;
  776. }
  777. return E_NOINTERFACE;
  778. }
  779. //=--------------------------------------------------------------------------=
  780. // IUnknown AddRef (needed to resolve ambiguity)
  781. //
  782. ULONG CVDCursorMain::AddRef(void)
  783. {
  784. return CVDNotifier::AddRef();
  785. }
  786. //=--------------------------------------------------------------------------=
  787. // IUnknown Release (needed to resolve ambiguity)
  788. //
  789. ULONG CVDCursorMain::Release(void)
  790. {
  791. if (1 == m_dwRefCount)
  792. Passivate(); // unhook everything including notification sink
  793. if (1 > --m_dwRefCount)
  794. {
  795. if (0 == m_RowsetNotify.GetRefCount())
  796. delete this;
  797. return 0;
  798. }
  799. return m_dwRefCount;
  800. }
  801. //=--------------------------------------------------------------------------=
  802. // IsSameRowAsNew - Determine if specified hRow is an addrow
  803. //
  804. BOOL CVDCursorMain::IsSameRowAsNew(HROW hrow)
  805. {
  806. for (int k = 0; k < m_Children.GetSize(); k++)
  807. {
  808. if (((CVDCursorPosition*)(CVDNotifier*)m_Children[k])->IsSameRowAsNew(hrow) == S_OK)
  809. return TRUE;
  810. }
  811. return FALSE;
  812. }
  813. //=--------------------------------------------------------------------------=
  814. // AddedRows - Get the number of add-rows in cursor
  815. //
  816. ULONG CVDCursorMain::AddedRows()
  817. {
  818. ULONG cAdded = 0;
  819. for (int k = 0; k < m_Children.GetSize(); k++)
  820. {
  821. if (((CVDCursorPosition*)(CVDNotifier*)m_Children[k])->GetEditMode() == CURSOR_DBEDITMODE_ADD)
  822. cAdded++;
  823. }
  824. return cAdded;
  825. }
  826. //=--------------------------------------------------------------------------=
  827. // IRowsetNotify Methods
  828. //=--------------------------------------------------------------------------=
  829. //=--------------------------------------------------------------------------=
  830. // IRowsetNotify OnFieldChange
  831. //=--------------------------------------------------------------------------=
  832. // Forward to all CVDCursorPosition objects in our family
  833. //
  834. HRESULT CVDCursorMain::OnFieldChange(IRowset *pRowset,
  835. HROW hRow,
  836. ULONG cColumns,
  837. ULONG rgColumns[],
  838. DBREASON eReason,
  839. DBEVENTPHASE ePhase,
  840. BOOL fCantDeny)
  841. {
  842. HRESULT hr = S_OK;
  843. // return if notification caused by internal rowset call
  844. if (m_fInternalSetData && eReason == DBREASON_COLUMN_SET)
  845. return hr;
  846. for (int k = 0; k < m_Children.GetSize(); k++)
  847. {
  848. hr = ((CVDCursorPosition*)(CVDNotifier*)m_Children[k])->OnFieldChange(pRowset,
  849. hRow,
  850. cColumns,
  851. rgColumns,
  852. eReason,
  853. ePhase,
  854. fCantDeny);
  855. if (hr)
  856. break;
  857. }
  858. return hr;
  859. }
  860. //=--------------------------------------------------------------------------=
  861. // IRowsetNotify OnRowChange
  862. //=--------------------------------------------------------------------------=
  863. // Forward to all CVDCursorPosition objects in our family
  864. //
  865. HRESULT CVDCursorMain::OnRowChange(IRowset *pRowset,
  866. ULONG cRows,
  867. const HROW rghRows[],
  868. DBREASON eReason,
  869. DBEVENTPHASE ePhase,
  870. BOOL fCantDeny)
  871. {
  872. HRESULT hr = S_OK;
  873. // return if notification caused by internal rowset call (either insert or delete)
  874. if (m_fInternalInsertRow && eReason == DBREASON_ROW_INSERT || m_fInternalDeleteRows && eReason == DBREASON_ROW_DELETE)
  875. return hr;
  876. for (int k = 0; k < m_Children.GetSize(); k++)
  877. {
  878. hr = ((CVDCursorPosition*)(CVDNotifier*)m_Children[k])->OnRowChange(pRowset,
  879. cRows,
  880. rghRows,
  881. eReason,
  882. ePhase,
  883. fCantDeny);
  884. if (hr)
  885. break;
  886. }
  887. return hr;
  888. }
  889. //=--------------------------------------------------------------------------=
  890. // IRowsetNotify OnRowsetChange
  891. //=--------------------------------------------------------------------------=
  892. // Forward to all CVDCursorPosition objects in our family
  893. //
  894. HRESULT CVDCursorMain::OnRowsetChange(IRowset *pRowset,
  895. DBREASON eReason,
  896. DBEVENTPHASE ePhase,
  897. BOOL fCantDeny)
  898. {
  899. HRESULT hr = S_OK;
  900. for (int k = 0; k < m_Children.GetSize(); k++)
  901. {
  902. hr = ((CVDCursorPosition*)(CVDNotifier*)m_Children[k])->OnRowsetChange(pRowset,
  903. eReason,
  904. ePhase,
  905. fCantDeny);
  906. if (hr)
  907. break;
  908. }
  909. return hr;
  910. }
  911. //=--------------------------------------------------------------------------=
  912. // CVDCursorMain::CVDRowsetNotify::m_pMainUnknown
  913. //=--------------------------------------------------------------------------=
  914. // this method is used when we're sitting in the private unknown object,
  915. // and we need to get at the pointer for the main unknown. basically, it's
  916. // a little better to do this pointer arithmetic than have to store a pointer
  917. // to the parent, etc.
  918. //
  919. inline CVDCursorMain *CVDCursorMain::CVDRowsetNotify::m_pMainUnknown
  920. (
  921. void
  922. )
  923. {
  924. return (CVDCursorMain *)((LPBYTE)this - offsetof(CVDCursorMain, m_RowsetNotify));
  925. }
  926. //=--------------------------------------------------------------------------=
  927. // CVDCursorMain::CVDRowsetNotify::QueryInterface
  928. //=--------------------------------------------------------------------------=
  929. // this is the non-delegating internal QI routine.
  930. //
  931. // Parameters:
  932. // REFIID - [in] interface they want
  933. // void ** - [out] where they want to put the resulting object ptr.
  934. //
  935. // Output:
  936. // HRESULT - S_OK, E_NOINTERFACE
  937. //
  938. // Notes:
  939. //
  940. STDMETHODIMP CVDCursorMain::CVDRowsetNotify::QueryInterface
  941. (
  942. REFIID riid,
  943. void **ppvObjOut
  944. )
  945. {
  946. if (!ppvObjOut)
  947. return E_INVALIDARG;
  948. *ppvObjOut = NULL;
  949. if (DO_GUIDS_MATCH(riid, IID_IUnknown))
  950. *ppvObjOut = (IUnknown *)this;
  951. else
  952. if (DO_GUIDS_MATCH(riid, IID_IRowsetNotify))
  953. *ppvObjOut = (IUnknown *)this;
  954. if (*ppvObjOut)
  955. {
  956. m_cRef++;
  957. return S_OK;
  958. }
  959. return E_NOINTERFACE;
  960. }
  961. //=--------------------------------------------------------------------------=
  962. // CVDCursorMain::CVDRowsetNotify::AddRef
  963. //=--------------------------------------------------------------------------=
  964. // adds a tick to the current reference count.
  965. //
  966. // Output:
  967. // ULONG - the new reference count
  968. //
  969. // Notes:
  970. //
  971. ULONG CVDCursorMain::CVDRowsetNotify::AddRef
  972. (
  973. void
  974. )
  975. {
  976. return ++m_cRef;
  977. }
  978. //=--------------------------------------------------------------------------=
  979. // CVDCursorMain::CVDRowsetNotify::Release
  980. //=--------------------------------------------------------------------------=
  981. // removes a tick from the count, and delets the object if necessary
  982. //
  983. // Output:
  984. // ULONG - remaining refs
  985. //
  986. // Notes:
  987. //
  988. ULONG CVDCursorMain::CVDRowsetNotify::Release
  989. (
  990. void
  991. )
  992. {
  993. ULONG cRef = --m_cRef;
  994. if (!m_cRef && !m_pMainUnknown()->m_dwRefCount)
  995. delete m_pMainUnknown();
  996. return cRef;
  997. }
  998. //=--------------------------------------------------------------------------=
  999. // IRowsetNotify Methods
  1000. //=--------------------------------------------------------------------------=
  1001. //=--------------------------------------------------------------------------=
  1002. // IRowsetNotify OnFieldChange
  1003. //=--------------------------------------------------------------------------=
  1004. // Forward to all CVDCursorPosition objects in our family
  1005. //
  1006. HRESULT CVDCursorMain::CVDRowsetNotify::OnFieldChange(IRowset *pRowset,
  1007. HROW hRow,
  1008. ULONG cColumns,
  1009. ULONG rgColumns[],
  1010. DBREASON eReason,
  1011. DBEVENTPHASE ePhase,
  1012. BOOL fCantDeny)
  1013. {
  1014. return m_pMainUnknown()->OnFieldChange(pRowset,
  1015. hRow,
  1016. cColumns,
  1017. rgColumns,
  1018. eReason,
  1019. ePhase,
  1020. fCantDeny);
  1021. }
  1022. //=--------------------------------------------------------------------------=
  1023. // IRowsetNotify OnRowChange
  1024. //=--------------------------------------------------------------------------=
  1025. // Forward to all CVDCursorPosition objects in our family
  1026. //
  1027. HRESULT CVDCursorMain::CVDRowsetNotify::OnRowChange(IRowset *pRowset,
  1028. ULONG cRows,
  1029. const HROW rghRows[],
  1030. DBREASON eReason,
  1031. DBEVENTPHASE ePhase,
  1032. BOOL fCantDeny)
  1033. {
  1034. return m_pMainUnknown()->OnRowChange(pRowset,
  1035. cRows,
  1036. rghRows,
  1037. eReason,
  1038. ePhase,
  1039. fCantDeny);
  1040. }
  1041. //=--------------------------------------------------------------------------=
  1042. // IRowsetNotify OnRowsetChange
  1043. //=--------------------------------------------------------------------------=
  1044. // Forward to all CVDCursorPosition objects in our family
  1045. //
  1046. HRESULT CVDCursorMain::CVDRowsetNotify::OnRowsetChange(IRowset *pRowset,
  1047. DBREASON eReason,
  1048. DBEVENTPHASE ePhase,
  1049. BOOL fCantDeny)
  1050. {
  1051. return m_pMainUnknown()->OnRowsetChange(pRowset,
  1052. eReason,
  1053. ePhase,
  1054. fCantDeny);
  1055. }