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.

1399 lines
34 KiB

  1. //+--------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1996 - 1999
  5. //
  6. // File: view.cpp
  7. //
  8. // Contents: Cert Server Database interface implementation
  9. //
  10. //---------------------------------------------------------------------------
  11. #include <pch.cpp>
  12. #pragma hdrstop
  13. #include "csprop.h"
  14. #include "column.h"
  15. #include "enum.h"
  16. #include "db.h"
  17. #include "row.h"
  18. #include "view.h"
  19. #define __dwFILE__ __dwFILE_CERTDB_VIEW_CPP__
  20. #if DBG_CERTSRV
  21. #define THREAD_TIMEOUT INFINITE
  22. #else
  23. #define THREAD_TIMEOUT INFINITE // used to be 10000ms=10 seconds
  24. #endif
  25. #if DBG
  26. LONG g_cCertDBResultRow;
  27. LONG g_cCertDBResultRowTotal;
  28. #endif
  29. #ifdef DBG_CERTSRV_DEBUG_PRINT
  30. DWORD s_ssDB = DBG_SS_CERTDBI;
  31. #endif
  32. #if DBG_CERTSRV
  33. VOID
  34. dbDumpFileTime(
  35. IN DWORD dwSubSystemId,
  36. IN CHAR const *pszPrefix,
  37. IN FILETIME const *pft);
  38. #endif
  39. typedef struct
  40. {
  41. CERTSESSION *pcs;
  42. ICertDB *pdb;
  43. DWORD ccvr;
  44. CERTVIEWRESTRICTION const *acvr;
  45. DWORD ccolOut;
  46. DWORD const *acolOut;
  47. } THREAD_PARAM_OPEN;
  48. typedef struct
  49. {
  50. ICertDBComputedColumn *pIComputedColumn;
  51. ULONG celt;
  52. CERTDBRESULTROW *rgelt;
  53. ULONG *pceltFetched;
  54. } THREAD_PARAM_NEXT;
  55. CEnumCERTDBRESULTROW::CEnumCERTDBRESULTROW(
  56. IN BOOL fThreading) :
  57. m_fThreading(fThreading)
  58. {
  59. DBGCODE(InterlockedIncrement(&g_cCertDBResultRow));
  60. DBGCODE(InterlockedIncrement(&g_cCertDBResultRowTotal));
  61. m_pdb = NULL;
  62. m_pcs = NULL;
  63. m_aRestriction = NULL;
  64. m_acolOut = NULL;
  65. m_cRef = 1;
  66. m_ieltMax = 0;
  67. m_hWorkThread = NULL;
  68. m_hViewEvent = NULL;
  69. m_hrThread = S_OK;
  70. m_hReturnEvent = NULL;
  71. m_enumViewCall = ENUMTHREAD_END;
  72. m_pThreadParam = NULL;
  73. //#if DBG_CERTSRV
  74. m_dwCallerThreadId = 0;
  75. //#endif
  76. }
  77. CEnumCERTDBRESULTROW::~CEnumCERTDBRESULTROW()
  78. {
  79. DBGCODE(InterlockedDecrement(&g_cCertDBResultRow));
  80. _Cleanup();
  81. }
  82. // The following is the worker thread procedure to handle calls.
  83. // All view calls will be made in this thread.
  84. DWORD WINAPI
  85. CEnumCERTDBRESULTROW::_ViewWorkThreadFunctionHelper(
  86. LPVOID lpParam)
  87. {
  88. HRESULT hr = S_OK;
  89. DBGPRINT((s_ssDB, "worker thread (tid=%d) is created.\n", GetCurrentThreadId()));
  90. if (NULL == lpParam)
  91. {
  92. hr = E_POINTER;
  93. _PrintError(hr, "null pointer, kill worker thread unexpectedly");
  94. ExitThread(hr);
  95. }
  96. // call real one
  97. return (((CEnumCERTDBRESULTROW*)lpParam)->_ViewWorkThreadFunction());
  98. }
  99. DWORD
  100. CEnumCERTDBRESULTROW::_ViewWorkThreadFunction(VOID)
  101. {
  102. HRESULT hr = S_OK;
  103. while (TRUE)
  104. {
  105. if (WAIT_OBJECT_0 == WaitForSingleObject(m_hViewEvent, INFINITE))
  106. {
  107. switch (m_enumViewCall)
  108. {
  109. case ENUMTHREAD_OPEN:
  110. // call open
  111. m_hrThread = _ThreadOpen(m_dwCallerThreadId);
  112. if (!SetEvent(m_hReturnEvent))
  113. {
  114. hr = myHLastError();
  115. _PrintError(hr, "SetEvent");
  116. }
  117. break;
  118. case ENUMTHREAD_NEXT:
  119. // call next
  120. m_hrThread = _ThreadNext(m_dwCallerThreadId);
  121. if (!SetEvent(m_hReturnEvent))
  122. {
  123. hr = myHLastError();
  124. _PrintError(hr, "SetEvent");
  125. }
  126. break;
  127. case ENUMTHREAD_CLEANUP:
  128. // call cleanup
  129. _ThreadCleanup(m_dwCallerThreadId);
  130. m_hrThread = S_OK;
  131. if (!SetEvent(m_hReturnEvent))
  132. {
  133. hr = myHLastError();
  134. _PrintError(hr, "SetEvent");
  135. }
  136. break;
  137. case ENUMTHREAD_END:
  138. DBGPRINT((s_ssDB, "End worker thread (tid=%d)\n", GetCurrentThreadId()));
  139. ExitThread(hr);
  140. break;
  141. default:
  142. // unexpected
  143. DBGPRINT((DBG_SS_CERTDB, "Unexpected event from (tid=%d)\n", m_dwCallerThreadId));
  144. m_hrThread = E_UNEXPECTED;
  145. if (!SetEvent(m_hReturnEvent))
  146. {
  147. hr = myHLastError();
  148. _PrintError(hr, "SetEvent");
  149. }
  150. CSASSERT(FALSE);
  151. break;
  152. }
  153. }
  154. }
  155. return(hr);
  156. }
  157. VOID
  158. CEnumCERTDBRESULTROW::_Cleanup()
  159. {
  160. HRESULT hr;
  161. if (!m_fThreading)
  162. {
  163. CSASSERT(NULL == m_hWorkThread);
  164. _ThreadCleanup(0); // no work thread, call directly
  165. }
  166. else
  167. {
  168. if (NULL != m_hWorkThread &&
  169. NULL != m_hViewEvent &&
  170. NULL != m_hReturnEvent)
  171. {
  172. // ask work thread to do clean up
  173. m_enumViewCall = ENUMTHREAD_CLEANUP;
  174. //#if DBG_CERTSRV
  175. m_dwCallerThreadId = GetCurrentThreadId();
  176. DBGPRINT((
  177. s_ssDB,
  178. "CEnumCERTDBRESULTROW::_Cleanup(tid=%d) (this=0x%x)\n",
  179. m_dwCallerThreadId,
  180. this));
  181. //#endif
  182. //set cleanup event
  183. if (!SetEvent(m_hViewEvent))
  184. {
  185. hr = myHLastError();
  186. _PrintError(hr, "SetEvent");
  187. }
  188. else
  189. {
  190. hr = _HandleThreadError();
  191. _PrintIfError(hr, "_HandleThreadError");
  192. }
  193. // ask the thread end
  194. m_enumViewCall = ENUMTHREAD_END;
  195. if (!SetEvent(m_hViewEvent))
  196. {
  197. hr = myHLastError();
  198. _PrintError(hr, "SetEvent(thread still alive");
  199. }
  200. else
  201. {
  202. if (WAIT_OBJECT_0 != WaitForSingleObject(m_hWorkThread, THREAD_TIMEOUT))
  203. {
  204. hr = myHLastError();
  205. _PrintError(hr, "Thread is not killed");
  206. }
  207. }
  208. if (GetExitCodeThread(m_hWorkThread, (DWORD *) &hr))
  209. {
  210. _PrintIfError(hr, "Work thread error");
  211. }
  212. m_pThreadParam = NULL; //may not be necessary, but safe
  213. }
  214. if (NULL != m_hWorkThread)
  215. {
  216. CloseHandle(m_hWorkThread);
  217. m_hWorkThread = NULL;
  218. }
  219. if (NULL != m_hViewEvent)
  220. {
  221. CloseHandle(m_hViewEvent);
  222. m_hViewEvent = NULL;
  223. }
  224. if (NULL != m_hReturnEvent)
  225. {
  226. CloseHandle(m_hReturnEvent);
  227. m_hReturnEvent = NULL;
  228. }
  229. }
  230. if (NULL != m_pdb)
  231. {
  232. m_pdb->Release();
  233. m_pdb = NULL;
  234. }
  235. }
  236. VOID
  237. CEnumCERTDBRESULTROW::_ThreadCleanup(
  238. IN DWORD /* dwCallerThreadID */ )
  239. {
  240. HRESULT hr;
  241. DWORD i;
  242. if (NULL != m_pdb)
  243. {
  244. if (NULL != m_pcs)
  245. {
  246. hr = ((CCertDB *) m_pdb)->CloseTables(m_pcs);
  247. _PrintIfError(hr, "CloseTables");
  248. hr = ((CCertDB *) m_pdb)->CommitTransaction(m_pcs, FALSE, FALSE);
  249. _PrintIfError(hr, "CommitTransaction");
  250. hr = ((CCertDB *) m_pdb)->ReleaseSession(m_pcs);
  251. _PrintIfError(hr, "ReleaseSession");
  252. m_pcs = NULL;
  253. }
  254. if (NULL != m_aRestriction)
  255. {
  256. for (i = 0; i < m_cRestriction; i++)
  257. {
  258. if (NULL != m_aRestriction[i].pbValue)
  259. {
  260. LocalFree(m_aRestriction[i].pbValue);
  261. }
  262. }
  263. LocalFree(m_aRestriction);
  264. m_aRestriction = NULL;
  265. }
  266. if (NULL != m_acolOut)
  267. {
  268. LocalFree(m_acolOut);
  269. m_acolOut = NULL;
  270. }
  271. }
  272. }
  273. HRESULT
  274. CEnumCERTDBRESULTROW::_HandleThreadError()
  275. {
  276. HRESULT hr = S_OK;
  277. HANDLE ahEvents[] = { m_hReturnEvent, m_hWorkThread };
  278. // need to handle error
  279. DWORD dwWaitState = WaitForMultipleObjects(
  280. ARRAYSIZE(ahEvents),
  281. ahEvents,
  282. FALSE,
  283. THREAD_TIMEOUT);
  284. // reset
  285. m_pThreadParam = NULL;
  286. //#if DBG_CERTSRV
  287. //m_dwCallerThreadId = 0;
  288. //#endif
  289. if (WAIT_OBJECT_0 == dwWaitState)
  290. {
  291. // signaled from work thread
  292. hr = m_hrThread;
  293. }
  294. else if (WAIT_OBJECT_0 + 1 == dwWaitState)
  295. {
  296. // work thread ended unexpectedly
  297. hr = E_UNEXPECTED;
  298. _JumpError(hr, error, "work thread is ended unexpectedly");
  299. }
  300. else if (WAIT_TIMEOUT == dwWaitState)
  301. {
  302. hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
  303. _JumpError(hr, error, "WaitForSingleObject(timeout)");
  304. }
  305. else if (WAIT_FAILED == dwWaitState)
  306. {
  307. hr = myHLastError();
  308. _JumpError(hr, error, "WaitForSingleObject");
  309. }
  310. error:
  311. return(hr);
  312. }
  313. // Truncate FILETIME to next lower minute and add lMinuteCount minutes (if !0)
  314. HRESULT
  315. myMakeExprDateMinuteRound(
  316. IN OUT FILETIME *pft,
  317. IN LONG lMinuteCount)
  318. {
  319. HRESULT hr;
  320. SYSTEMTIME st;
  321. #if DBG_CERTSRV
  322. dbDumpFileTime(DBG_SS_CERTDBI, "MinuteRound(IN): ", pft);
  323. #endif
  324. FileTimeToSystemTime(pft, &st);
  325. st.wSecond = 0;
  326. st.wMilliseconds = 0;
  327. if (!SystemTimeToFileTime(&st, pft))
  328. {
  329. hr = myHLastError();
  330. _JumpError(hr, error, "SystemTimeToFileTime");
  331. }
  332. if (0 != lMinuteCount)
  333. {
  334. myMakeExprDateTime(pft, lMinuteCount, ENUM_PERIOD_MINUTES);
  335. }
  336. #if DBG_CERTSRV
  337. dbDumpFileTime(DBG_SS_CERTDBI, "MinuteRound(OUT): ", pft);
  338. #endif
  339. hr = S_OK;
  340. error:
  341. return(hr);
  342. }
  343. HRESULT
  344. CEnumCERTDBRESULTROW::_SetTable(
  345. IN LONG ColumnIndex,
  346. OUT LONG *pColumnIndexDefault)
  347. {
  348. HRESULT hr;
  349. DWORD dwTable;
  350. LONG ColumnIndexDefault;
  351. if (0 > ColumnIndex)
  352. {
  353. switch (ColumnIndex)
  354. {
  355. case CV_COLUMN_LOG_DEFAULT:
  356. case CV_COLUMN_LOG_FAILED_DEFAULT:
  357. case CV_COLUMN_LOG_REVOKED_DEFAULT:
  358. case CV_COLUMN_QUEUE_DEFAULT:
  359. ColumnIndex = DTI_REQUESTTABLE;
  360. break;
  361. case CV_COLUMN_EXTENSION_DEFAULT:
  362. ColumnIndex = DTI_EXTENSIONTABLE;
  363. break;
  364. case CV_COLUMN_ATTRIBUTE_DEFAULT:
  365. ColumnIndex = DTI_ATTRIBUTETABLE;
  366. break;
  367. case CV_COLUMN_CRL_DEFAULT:
  368. ColumnIndex = DTI_CRLTABLE;
  369. break;
  370. default:
  371. hr = E_INVALIDARG;
  372. _JumpError(hr, error, "bad negative ColumnIndex");
  373. }
  374. }
  375. if (~(DTI_COLUMNMASK | DTI_TABLEMASK) & ColumnIndex)
  376. {
  377. hr = E_INVALIDARG;
  378. _JumpError(hr, error, "invalid bits");
  379. }
  380. switch (DTI_TABLEMASK & ColumnIndex)
  381. {
  382. case DTI_REQUESTTABLE:
  383. case DTI_CERTIFICATETABLE:
  384. dwTable = TABLE_REQCERTS;
  385. ColumnIndexDefault = DTI_REQUESTTABLE | DTR_REQUESTID;
  386. break;
  387. case DTI_EXTENSIONTABLE:
  388. dwTable = TABLE_EXTENSIONS;
  389. ColumnIndexDefault = DTI_EXTENSIONTABLE | DTE_REQUESTID;
  390. break;
  391. case DTI_ATTRIBUTETABLE:
  392. dwTable = TABLE_ATTRIBUTES;
  393. ColumnIndexDefault = DTI_ATTRIBUTETABLE | DTA_REQUESTID;
  394. break;
  395. case DTI_CRLTABLE:
  396. dwTable = TABLE_CRLS;
  397. ColumnIndexDefault = DTI_CRLTABLE | DTL_ROWID;
  398. break;
  399. default:
  400. hr = E_INVALIDARG;
  401. _JumpError(hr, error, "bad table");
  402. }
  403. if (CSF_TABLESET & m_pcs->SesFlags)
  404. {
  405. if ((CSF_TABLEMASK & m_pcs->SesFlags) != dwTable)
  406. {
  407. DBGPRINT((
  408. DBG_SS_CERTVIEW,
  409. "_SetTable: Table=%x <- %x\n",
  410. CSF_TABLEMASK & m_pcs->SesFlags,
  411. dwTable));
  412. hr = E_INVALIDARG;
  413. _JumpError(hr, error, "mixed tables");
  414. }
  415. }
  416. else
  417. {
  418. CSASSERT(0 == (CSF_TABLEMASK & m_pcs->SesFlags));
  419. m_pcs->SesFlags |= CSF_TABLESET | dwTable;
  420. }
  421. *pColumnIndexDefault = ColumnIndexDefault;
  422. hr = S_OK;
  423. error:
  424. return(hr);
  425. }
  426. HRESULT
  427. CEnumCERTDBRESULTROW::_SaveRestrictions(
  428. IN DWORD ccvrIn,
  429. IN CERTVIEWRESTRICTION const *acvrIn,
  430. IN LONG ColumnIndexDefault)
  431. {
  432. HRESULT hr;
  433. DWORD ccvrAlloc;
  434. CERTVIEWRESTRICTION const *pcvr;
  435. CERTVIEWRESTRICTION const *pcvrEnd;
  436. CERTVIEWRESTRICTION const *pcvrIndexed;
  437. CERTVIEWRESTRICTION *pcvrDst;
  438. BOOL fFoundSortOrder;
  439. BOOL fDefault;
  440. DWORD dwDefaultValue;
  441. DWORD Type;
  442. FILETIME ft;
  443. ccvrAlloc = ccvrIn;
  444. pcvrIndexed = NULL;
  445. fFoundSortOrder = FALSE;
  446. Type = 0;
  447. pcvrEnd = &acvrIn[ccvrIn];
  448. for (pcvr = acvrIn; pcvr < pcvrEnd; pcvr++) // for each restriction
  449. {
  450. fDefault = 0 > (LONG) pcvr->ColumnIndex;
  451. if (!fDefault)
  452. {
  453. hr = ((CCertDB *) m_pdb)->GetColumnType(pcvr->ColumnIndex, &Type);
  454. _JumpIfError(hr, error, "GetColumnType");
  455. }
  456. if (fDefault || (PROPFLAGS_INDEXED & Type))
  457. {
  458. if (!fFoundSortOrder && CVR_SORT_NONE != pcvr->SortOrder)
  459. {
  460. // if the first indexed column with sort order, save this one.
  461. fFoundSortOrder = TRUE;
  462. pcvrIndexed = pcvr;
  463. }
  464. else
  465. if (NULL == pcvrIndexed)
  466. {
  467. // if the first indexed column, save this one.
  468. pcvrIndexed = pcvr;
  469. }
  470. }
  471. if (CVR_SORT_NONE != pcvr->SortOrder && pcvrIndexed != pcvr)
  472. {
  473. hr = E_INVALIDARG;
  474. DBGPRINT((DBG_SS_CERTDB, "_SaveRestrictions(%x)\n", pcvr->ColumnIndex));
  475. _JumpError(hr, error, "multiple SortOrders or non-indexed column");
  476. }
  477. if (!fDefault &&
  478. PROPTYPE_DATE == (PROPTYPE_MASK & Type) &&
  479. CVR_SEEK_EQ == pcvr->SeekOperator)
  480. {
  481. ccvrAlloc++; // Turn Date == value into a range restriction
  482. }
  483. }
  484. if (NULL == pcvrIndexed)
  485. {
  486. ccvrAlloc++; // No indexed column: add RequestId >= 0
  487. }
  488. m_aRestriction = (CERTVIEWRESTRICTION *) LocalAlloc(
  489. LMEM_FIXED | LMEM_ZEROINIT,
  490. ccvrAlloc * sizeof(m_aRestriction[0]));
  491. if (NULL == m_aRestriction)
  492. {
  493. hr = E_OUTOFMEMORY;
  494. _JumpError(hr, error, "LocalAlloc");
  495. }
  496. m_cRestriction = ccvrAlloc;
  497. pcvrDst = m_aRestriction;
  498. // If no indexed restriction, add one.
  499. if (NULL == pcvrIndexed)
  500. {
  501. pcvrDst->ColumnIndex = ColumnIndexDefault;
  502. pcvrDst->SeekOperator = CVR_SEEK_NONE;
  503. pcvrDst->SortOrder = CVR_SORT_ASCEND;
  504. pcvrDst->cbValue = 0;
  505. pcvrDst->pbValue = NULL;
  506. pcvrDst++;
  507. }
  508. for (pcvr = acvrIn; pcvr < pcvrEnd; pcvr++)
  509. {
  510. CERTVIEWRESTRICTION const *pcvrSrc = pcvr;
  511. BYTE *pbValue;
  512. // Swap the first restriction with the first indexed restriction
  513. if (NULL != pcvrIndexed)
  514. {
  515. if (pcvrSrc == acvrIn)
  516. {
  517. pcvrSrc = pcvrIndexed;
  518. }
  519. else if (pcvrSrc == pcvrIndexed)
  520. {
  521. pcvrSrc = acvrIn;
  522. }
  523. }
  524. *pcvrDst = *pcvrSrc;
  525. if (pcvrSrc == pcvrIndexed && CVR_SORT_NONE == pcvrSrc->SortOrder)
  526. {
  527. pcvrDst->SortOrder = CVR_SORT_ASCEND;
  528. }
  529. pcvrDst->pbValue = NULL;
  530. fDefault = 0 > (LONG) pcvr->ColumnIndex;
  531. if (fDefault)
  532. {
  533. pcvrDst->SeekOperator = CVR_SEEK_GE; // default seek operator
  534. dwDefaultValue = 1; // default RequestId/Rowid
  535. switch (pcvr->ColumnIndex)
  536. {
  537. case CV_COLUMN_QUEUE_DEFAULT:
  538. case CV_COLUMN_LOG_DEFAULT:
  539. case CV_COLUMN_LOG_FAILED_DEFAULT:
  540. case CV_COLUMN_LOG_REVOKED_DEFAULT:
  541. pcvrDst->ColumnIndex = DTI_REQUESTTABLE | DTR_REQUESTDISPOSITION;
  542. if (CV_COLUMN_QUEUE_DEFAULT == pcvr->ColumnIndex)
  543. {
  544. dwDefaultValue = DB_DISP_QUEUE_MAX;
  545. pcvrDst->SeekOperator = CVR_SEEK_LE;
  546. }
  547. else if (CV_COLUMN_LOG_DEFAULT == pcvr->ColumnIndex)
  548. {
  549. dwDefaultValue = DB_DISP_LOG_MIN;
  550. }
  551. else if (CV_COLUMN_LOG_REVOKED_DEFAULT == pcvr->ColumnIndex)
  552. {
  553. dwDefaultValue = DB_DISP_REVOKED;
  554. pcvrDst->SeekOperator = CVR_SEEK_EQ;
  555. }
  556. else
  557. {
  558. dwDefaultValue = DB_DISP_LOG_FAILED_MIN;
  559. }
  560. break;
  561. case CV_COLUMN_EXTENSION_DEFAULT:
  562. pcvrDst->ColumnIndex = DTI_EXTENSIONTABLE | DTE_REQUESTID;
  563. break;
  564. case CV_COLUMN_ATTRIBUTE_DEFAULT:
  565. pcvrDst->ColumnIndex = DTI_ATTRIBUTETABLE | DTA_REQUESTID;
  566. break;
  567. case CV_COLUMN_CRL_DEFAULT:
  568. pcvrDst->ColumnIndex = DTI_CRLTABLE | DTL_ROWID;
  569. break;
  570. default:
  571. hr = E_INVALIDARG;
  572. _JumpError(hr, error, "bad default restriction column");
  573. break;
  574. }
  575. pcvrDst->cbValue = sizeof(dwDefaultValue);
  576. pbValue = (BYTE *) &dwDefaultValue;
  577. }
  578. else
  579. {
  580. // To handle rounding errors, modify date restrictions as follows:
  581. //
  582. // DateColumn == Constant ==> two restrictions:
  583. // DateColumn < Ceiling(Constant) &&
  584. // DateColumn >= Floor(Constant)
  585. //
  586. // DateColumn > Constant ==> DateColumn >= Ceiling(Constant)
  587. // DateColumn >= Constant ==> DateColumn >= Floor(Constant)
  588. //
  589. // DateColumn < Constant ==> DateColumn < Floor(Constant)
  590. // DateColumn <= Constant ==> DateColumn < Ceiling(Constant)
  591. hr = ((CCertDB *) m_pdb)->GetColumnType(
  592. pcvrDst->ColumnIndex,
  593. &Type);
  594. _JumpIfError(hr, error, "GetColumnType");
  595. pbValue = pcvrSrc->pbValue;
  596. if (PROPTYPE_DATE == (PROPTYPE_MASK & Type) &&
  597. 0 == (CVR_SEEK_NODELTA & pcvrDst->SeekOperator) &&
  598. CVR_SEEK_NONE != (CVR_SEEK_MASK & pcvrDst->SeekOperator))
  599. {
  600. LONG lMinuteCount = 0; // assume truncate to lower minute
  601. if(NULL == pcvrSrc->pbValue)
  602. {
  603. hr = E_INVALIDARG;
  604. _JumpError(hr, error, "restriction value is null");
  605. }
  606. ft = *(FILETIME *) pcvrSrc->pbValue;
  607. pbValue = (BYTE *) &ft;
  608. switch (CVR_SEEK_MASK & pcvrDst->SeekOperator)
  609. {
  610. FILETIME ftCeiling;
  611. case CVR_SEEK_EQ:
  612. ftCeiling = ft;
  613. hr = myMakeExprDateMinuteRound(&ftCeiling, 1);
  614. _JumpIfError(hr, error, "myMakeExprDateMinuteRound");
  615. pcvrDst->pbValue = (BYTE *) LocalAlloc(
  616. LMEM_FIXED,
  617. sizeof(ft));
  618. if (NULL == pcvrDst->pbValue)
  619. {
  620. hr = E_OUTOFMEMORY;
  621. _JumpError(hr, error, "LocalAlloc");
  622. }
  623. CopyMemory(pcvrDst->pbValue, &ftCeiling, pcvrDst->cbValue);
  624. pcvrDst->SeekOperator = CVR_SEEK_LT | CVR_SEEK_NODELTA;
  625. pcvrDst++;
  626. *pcvrDst = *pcvrSrc;
  627. pcvrDst->pbValue = NULL;
  628. pcvrDst->SeekOperator = CVR_SEEK_GE | CVR_SEEK_NODELTA;
  629. hr = myMakeExprDateMinuteRound(&ft, 0);
  630. _JumpIfError(hr, error, "myMakeExprDateMinuteRound");
  631. break;
  632. case CVR_SEEK_GT:
  633. lMinuteCount = 1; // round to next higher minute
  634. // FALL THROUGH
  635. case CVR_SEEK_GE:
  636. pcvrDst->SeekOperator = CVR_SEEK_GE | CVR_SEEK_NODELTA;
  637. hr = myMakeExprDateMinuteRound(&ft, lMinuteCount);
  638. _JumpIfError(hr, error, "myMakeExprDateMinuteRound");
  639. break;
  640. case CVR_SEEK_LE:
  641. lMinuteCount = 1; // round to next higher minute
  642. // FALL THROUGH
  643. case CVR_SEEK_LT:
  644. pcvrDst->SeekOperator = CVR_SEEK_LT | CVR_SEEK_NODELTA;
  645. hr = myMakeExprDateMinuteRound(&ft, lMinuteCount);
  646. _JumpIfError(hr, error, "myMakeExprDateMinuteRound");
  647. break;
  648. default:
  649. hr = E_INVALIDARG;
  650. _JumpError(hr, error, "invalid seek operator");
  651. }
  652. }
  653. }
  654. // either nonzero or SEEK_NONE
  655. CSASSERT((0 != pcvrDst->cbValue) || ((CVR_SEEK_MASK & pcvrDst->SeekOperator) == CVR_SEEK_NONE));
  656. if (0 != pcvrDst->cbValue)
  657. {
  658. pcvrDst->pbValue = (BYTE *) LocalAlloc(LMEM_FIXED, pcvrDst->cbValue);
  659. if (NULL == pcvrDst->pbValue)
  660. {
  661. hr = E_OUTOFMEMORY;
  662. _JumpError(hr, error, "alloc value");
  663. }
  664. CopyMemory(pcvrDst->pbValue, pbValue, pcvrDst->cbValue);
  665. }
  666. pcvrDst++;
  667. }
  668. CSASSERT(pcvrDst == &m_aRestriction[m_cRestriction]);
  669. #if DBG_CERTSRV
  670. pcvrEnd = &m_aRestriction[m_cRestriction];
  671. for (pcvr = m_aRestriction; pcvr < pcvrEnd; pcvr++)
  672. {
  673. ((CCertDB *) m_pdb)->DumpRestriction(
  674. DBG_SS_CERTDBI,
  675. SAFE_SUBTRACT_POINTERS(pcvr, m_aRestriction),
  676. pcvr);
  677. }
  678. #endif // DBG_CERTSRV
  679. hr = S_OK;
  680. error:
  681. return(hr);
  682. }
  683. HRESULT
  684. CEnumCERTDBRESULTROW::Open(
  685. IN CERTSESSION *pcs,
  686. IN ICertDB *pdb,
  687. IN DWORD ccvr,
  688. IN CERTVIEWRESTRICTION const *acvr,
  689. IN DWORD ccolOut,
  690. IN DWORD const *acolOut)
  691. {
  692. HRESULT hr;
  693. THREAD_PARAM_OPEN tpOpen;
  694. CSASSERT(NULL == m_hViewEvent);
  695. CSASSERT(NULL == m_hReturnEvent);
  696. CSASSERT(NULL == m_hWorkThread);
  697. if (NULL != m_hViewEvent ||
  698. NULL != m_hReturnEvent ||
  699. NULL != m_hWorkThread)
  700. {
  701. hr = E_UNEXPECTED;
  702. _JumpError(hr, error, "unexpected thread sync. state");
  703. }
  704. hr = ((CCertDB *) pdb)->TestShutDownState();
  705. _JumpIfError(hr, error, "TestShutDownState");
  706. // call cleanup before worker thread is created
  707. _Cleanup();
  708. tpOpen.pcs = pcs;
  709. tpOpen.pdb = pdb;
  710. tpOpen.ccvr = ccvr;
  711. tpOpen.acvr = acvr;
  712. tpOpen.ccolOut = ccolOut;
  713. tpOpen.acolOut = acolOut;
  714. m_pThreadParam = (void*)&tpOpen;
  715. //#if DBG_CERTSRV
  716. m_dwCallerThreadId = GetCurrentThreadId();
  717. DBGPRINT((s_ssDB, "CEnumCERTDBRESULTROW::Open(tid=%d) (this=0x%x)\n", m_dwCallerThreadId, this));
  718. //#endif
  719. if (m_fThreading)
  720. {
  721. m_hViewEvent = CreateEvent(
  722. NULL, //child inheritance
  723. FALSE, //manual reset
  724. FALSE, //initial signaled
  725. NULL); //name
  726. if (NULL == m_hViewEvent)
  727. {
  728. hr = myHLastError();
  729. _JumpError(hr, error, "CreateEvent");
  730. }
  731. m_hReturnEvent = CreateEvent(
  732. NULL, //child inheritance
  733. FALSE, //manual reset
  734. FALSE, //initial signaled
  735. NULL); //name
  736. if (NULL == m_hReturnEvent)
  737. {
  738. hr = myHLastError();
  739. _JumpError(hr, error, "CreateEvent");
  740. }
  741. m_hWorkThread = CreateThread(
  742. NULL, //no child inheritance
  743. 0, //use default stack size
  744. _ViewWorkThreadFunctionHelper, // thread function
  745. this, //pass this pointer
  746. 0, //run immediately
  747. &pcs->dwThreadId); //session thread id is overwritten
  748. if (NULL == m_hWorkThread)
  749. {
  750. hr = myHLastError();
  751. _JumpError(hr, error, "CreateThread");
  752. }
  753. m_enumViewCall = ENUMTHREAD_OPEN;
  754. //set open event
  755. if (!SetEvent(m_hViewEvent))
  756. {
  757. hr = myHLastError();
  758. _JumpError(hr, error, "SetEvent");
  759. }
  760. else
  761. {
  762. hr = _HandleThreadError();
  763. }
  764. }
  765. else
  766. {
  767. // don't go through worker thread
  768. hr = _ThreadOpen(0);
  769. }
  770. //hr = S_OK;
  771. error:
  772. return(hr);
  773. }
  774. HRESULT
  775. CEnumCERTDBRESULTROW::_ThreadOpen(
  776. IN DWORD /* dwCallerThreadID */ )
  777. {
  778. HRESULT hr;
  779. THREAD_PARAM_OPEN *ptpOpen = (THREAD_PARAM_OPEN *)m_pThreadParam;
  780. LONG ColumnIndexDefault = DTI_REQUESTTABLE | DTR_REQUESTID;
  781. DWORD i;
  782. DBGPRINT((s_ssDB, "CEnumCERTDBRESULTROW::ThreadOpen(tid=%d) from (tid=%d)\n", GetCurrentThreadId(), m_dwCallerThreadId));
  783. CSASSERT(NULL != ptpOpen);
  784. CSASSERTTHREAD(ptpOpen->pcs);
  785. if (NULL == ptpOpen->pcs ||
  786. NULL == ptpOpen->pdb ||
  787. (NULL == ptpOpen->acvr && 0 != ptpOpen->ccvr) ||
  788. NULL == ptpOpen->acolOut)
  789. {
  790. hr = E_POINTER;
  791. _JumpError(hr, error, "NULL parm");
  792. }
  793. m_fNoMoreData = FALSE;
  794. m_pcs = ptpOpen->pcs;
  795. m_pdb = ptpOpen->pdb;
  796. m_pdb->AddRef();
  797. m_ielt = 0;
  798. m_cskip = 0;
  799. CSASSERT(0 == m_pcs->cTransact);
  800. if (NULL != ptpOpen->acolOut)
  801. {
  802. for (i = 0; i < ptpOpen->ccolOut; i++)
  803. {
  804. hr = _SetTable(ptpOpen->acolOut[i], &ColumnIndexDefault);
  805. _JumpIfError(hr, error, "_SetTable");
  806. }
  807. }
  808. for (i = 0; i < ptpOpen->ccvr; i++)
  809. {
  810. hr = _SetTable(ptpOpen->acvr[i].ColumnIndex, &ColumnIndexDefault);
  811. _JumpIfError(hr, error, "_SetTable");
  812. }
  813. hr = _SaveRestrictions(ptpOpen->ccvr, ptpOpen->acvr, ColumnIndexDefault);
  814. _JumpIfError(hr, error, "_SaveRestrictions");
  815. m_ccolOut = ptpOpen->ccolOut;
  816. if (NULL != ptpOpen->acolOut)
  817. {
  818. m_acolOut = (DWORD *) LocalAlloc(
  819. LMEM_FIXED,
  820. sizeof(m_acolOut[0]) * m_ccolOut);
  821. if (NULL == m_acolOut)
  822. {
  823. hr = E_OUTOFMEMORY;
  824. _JumpError(hr, error, "alloc output columns");
  825. }
  826. CopyMemory(m_acolOut, ptpOpen->acolOut, sizeof(m_acolOut[0]) * m_ccolOut);
  827. }
  828. //if (!(CSF_READONLY & ptpOpen->pcs->SesFlags))
  829. {
  830. hr = ((CCertDB *) m_pdb)->BeginTransaction(m_pcs, FALSE);
  831. _JumpIfError(hr, error, "BeginTransaction");
  832. }
  833. hr = ((CCertDB *) m_pdb)->OpenTables(m_pcs, &m_aRestriction[0]);
  834. _PrintIfError2(hr, "OpenTables", CERTSRV_E_PROPERTY_EMPTY);
  835. if (CERTSRV_E_PROPERTY_EMPTY == hr)
  836. {
  837. m_fNoMoreData = TRUE;
  838. m_ieltMax = 0;
  839. hr = S_OK;
  840. }
  841. _JumpIfError(hr, error, "OpenTables");
  842. error:
  843. return(hr);
  844. }
  845. STDMETHODIMP
  846. CEnumCERTDBRESULTROW::Next(
  847. /* [in] */ ICertDBComputedColumn *pIComputedColumn,
  848. /* [in] */ ULONG celt,
  849. /* [out] */ CERTDBRESULTROW *rgelt,
  850. /* [out] */ ULONG *pceltFetched)
  851. {
  852. HRESULT hr;
  853. THREAD_PARAM_NEXT tpNext;
  854. tpNext.pIComputedColumn = pIComputedColumn;
  855. tpNext.celt = celt;
  856. tpNext.rgelt = rgelt;
  857. tpNext.pceltFetched = pceltFetched;
  858. m_pThreadParam = (void*)&tpNext;
  859. //#if DBG_CERTSRV
  860. m_dwCallerThreadId = GetCurrentThreadId();
  861. DBGPRINT((s_ssDB, "CEnumCERTDBRESULTROW::Next(tid=%d) (this=0x%x)\n", m_dwCallerThreadId, this));
  862. //#endif
  863. CSASSERT(NULL != m_pdb);
  864. if (NULL == m_pdb)
  865. {
  866. hr = E_UNEXPECTED;
  867. _JumpError(hr, error, "NULL m_pdb");
  868. }
  869. hr = ((CCertDB *) m_pdb)->TestShutDownState();
  870. _JumpIfError(hr, error, "TestShutDownState");
  871. if (m_fThreading)
  872. {
  873. CSASSERT(NULL != m_hViewEvent);
  874. CSASSERT(NULL != m_hReturnEvent);
  875. CSASSERT(NULL != m_hWorkThread);
  876. if (NULL == m_hViewEvent ||
  877. NULL == m_hReturnEvent ||
  878. NULL == m_hWorkThread)
  879. {
  880. hr = E_UNEXPECTED;
  881. _JumpError(hr, error, "unexpected thread sync. state");
  882. }
  883. m_enumViewCall = ENUMTHREAD_NEXT;
  884. // set next event
  885. if (!SetEvent(m_hViewEvent))
  886. {
  887. hr = myHLastError();
  888. _JumpError(hr, error, "SetEvent");
  889. }
  890. else
  891. {
  892. hr = _HandleThreadError();
  893. }
  894. }
  895. else
  896. {
  897. // don't go through worker thread
  898. hr = _ThreadNext(0);
  899. }
  900. //hr = S_OK;
  901. error:
  902. return(hr);
  903. }
  904. HRESULT
  905. CEnumCERTDBRESULTROW::_ThreadNext(
  906. IN DWORD /* dwCallerThreadID */ )
  907. {
  908. HRESULT hr;
  909. LONG cskip;
  910. LONG cskipped;
  911. THREAD_PARAM_NEXT *ptpNext = (THREAD_PARAM_NEXT *)m_pThreadParam;
  912. DBGPRINT((s_ssDB, "CEnumCERTDBRESULTROW::ThreadNext(tid=%d) from (tid=%d)\n", GetCurrentThreadId(), m_dwCallerThreadId));
  913. CSASSERT(NULL != ptpNext);
  914. DBGPRINT((
  915. DBG_SS_CERTDBI,
  916. "Trace: hr = penum->Next(%d, arow, &crow);\t_PrintIfError(hr, \"Next\");\n",
  917. ptpNext->celt));
  918. if (NULL == ptpNext->rgelt || NULL == ptpNext->pceltFetched)
  919. {
  920. hr = E_POINTER;
  921. _JumpError(hr, error, "NULL parm");
  922. }
  923. *ptpNext->pceltFetched = 0;
  924. if (NULL == m_pdb)
  925. {
  926. hr = E_UNEXPECTED;
  927. _JumpError(hr, error, "NULL m_pdb");
  928. }
  929. ZeroMemory(ptpNext->rgelt, ptpNext->celt * sizeof(ptpNext->rgelt[0]));
  930. CSASSERT(0 <= m_ielt);
  931. CSASSERT(0 <= m_ielt + m_cskip);
  932. DBGPRINT((
  933. DBG_SS_CERTDBI,
  934. "Next(celt=%d) ielt=%d, skip=%d\n",
  935. ptpNext->celt,
  936. m_ielt,
  937. m_cskip));
  938. hr = S_FALSE;
  939. if (m_fNoMoreData)
  940. {
  941. // We know no additional data can be returned until Reset is called or
  942. // until Skip is called with a negative skip count. Don't bother...
  943. _JumpError2(hr, error, "NoMoreData", S_FALSE);
  944. }
  945. // If we have previously computed the end of the data set, ...
  946. cskip = m_cskip;
  947. if (0 != m_ieltMax)
  948. {
  949. if (m_ielt + cskip >= m_ieltMax)
  950. {
  951. // The requested data lies past the computed end of the data set.
  952. CSASSERT(S_FALSE == hr);
  953. m_fNoMoreData = TRUE;
  954. _JumpError2(hr, error, "past end", S_FALSE);
  955. }
  956. DBGPRINT((
  957. DBG_SS_CERTDBI,
  958. "cskip = %d m_ielt = %d m_ieltMax = %d\n",
  959. cskip,
  960. m_ielt,
  961. m_ieltMax));
  962. if (0 > cskip && m_ielt > m_ieltMax)
  963. {
  964. // We're skiping backwards. If we started out past the end of the
  965. // data set, we must reduce the negative skip count passed to the
  966. // DB layer to position the index cursor correctly.
  967. cskip += m_ielt - m_ieltMax;
  968. DBGPRINT((
  969. DBG_SS_CERTDBI,
  970. "MODIFIED: cskip = %d m_ielt = %d m_ieltMax = %d\n",
  971. cskip,
  972. m_ielt,
  973. m_ieltMax));
  974. }
  975. }
  976. hr = ((CCertDB *) m_pdb)->EnumCertDBResultRowNext(
  977. m_pcs,
  978. m_cRestriction,
  979. m_aRestriction,
  980. m_ccolOut,
  981. m_acolOut,
  982. cskip,
  983. ptpNext->pIComputedColumn,
  984. ptpNext->celt,
  985. ptpNext->rgelt,
  986. ptpNext->pceltFetched,
  987. &cskipped);
  988. if (S_FALSE == hr)
  989. {
  990. // Only set m_ieltMax the first time we run off the end, when we will
  991. // be guaranteed that we are moving forward through the DB index.
  992. // Otherwise the math is too complicated and would be redundant anyway.
  993. if (0 == m_ieltMax)
  994. {
  995. CSASSERT(0 <= cskip);
  996. CSASSERT(0 <= cskipped);
  997. m_ieltMax = m_ielt + cskipped;
  998. }
  999. DBGPRINT((
  1000. DBG_SS_CERTDBI,
  1001. "Next: ieltMax=%d ielt=%d, cskipped=%d\n",
  1002. m_ieltMax,
  1003. m_ielt,
  1004. cskipped));
  1005. m_fNoMoreData = TRUE;
  1006. }
  1007. else
  1008. {
  1009. _JumpIfError(hr, error, "EnumCertDBResultRowNext");
  1010. }
  1011. DBGPRINT((
  1012. DBG_SS_CERTDBI,
  1013. "Next: ielt=%d -> %d cskip=%d, *pceltFetched=%d\n",
  1014. m_ielt,
  1015. m_ielt + m_cskip + *ptpNext->pceltFetched,
  1016. m_cskip,
  1017. *ptpNext->pceltFetched));
  1018. m_ielt += m_cskip;
  1019. m_ielt += *ptpNext->pceltFetched;
  1020. m_cskip = 0;
  1021. error:
  1022. if (S_FALSE == hr)
  1023. {
  1024. CSASSERT(NULL != ptpNext->rgelt);
  1025. CSASSERT(NULL != ptpNext->pceltFetched);
  1026. CSASSERT(*ptpNext->pceltFetched < ptpNext->celt);
  1027. CERTDBRESULTROW *peltMaxIndex = &ptpNext->rgelt[*ptpNext->pceltFetched];
  1028. peltMaxIndex->rowid = m_ieltMax;
  1029. peltMaxIndex->ccol = ~m_ieltMax;
  1030. }
  1031. return(hr);
  1032. }
  1033. STDMETHODIMP
  1034. CEnumCERTDBRESULTROW::ReleaseResultRow(
  1035. /* [in] */ ULONG celt,
  1036. /* [in, out] */ CERTDBRESULTROW *rgelt)
  1037. {
  1038. HRESULT hr;
  1039. if (NULL == rgelt)
  1040. {
  1041. hr = E_POINTER;
  1042. _JumpError(hr, error, "NULL parm");
  1043. }
  1044. if (NULL == m_pdb)
  1045. {
  1046. hr = E_UNEXPECTED;
  1047. _JumpError(hr, error, "NULL m_pdb");
  1048. }
  1049. hr = ((CCertDB *) m_pdb)->ReleaseResultRow(celt, rgelt);
  1050. _JumpIfError(hr, error, "ReleaseResultRow");
  1051. error:
  1052. return(hr);
  1053. }
  1054. STDMETHODIMP
  1055. CEnumCERTDBRESULTROW::Skip(
  1056. /* [in] */ LONG celt,
  1057. /* [out] */ LONG *pielt)
  1058. {
  1059. HRESULT hr;
  1060. LONG cskipnew;
  1061. DBGPRINT((
  1062. DBG_SS_CERTDBI,
  1063. "Trace: hr = penum->Skip(%d, &irow);\t_PrintIfError(hr, \"Skip\");\n",
  1064. celt));
  1065. if (NULL == pielt)
  1066. {
  1067. hr = E_POINTER;
  1068. _JumpError(hr, error, "NULL parm");
  1069. }
  1070. cskipnew = m_cskip + celt;
  1071. DBGPRINT((
  1072. DBG_SS_CERTDBI,
  1073. "Skip(%d) ielt=%d: %d --> %d, skip=%d --> %d\n",
  1074. celt,
  1075. m_ielt,
  1076. m_ielt + m_cskip,
  1077. m_ielt + cskipnew,
  1078. m_cskip,
  1079. cskipnew));
  1080. CSASSERT(0 <= m_ielt);
  1081. if (0 > celt)
  1082. {
  1083. if (0 > m_ielt + cskipnew)
  1084. {
  1085. hr = E_INVALIDARG;
  1086. _JumpError(hr, error, "Skip back to before start");
  1087. }
  1088. m_fNoMoreData = FALSE;
  1089. }
  1090. *pielt = m_ielt + cskipnew;
  1091. m_cskip = cskipnew;
  1092. hr = S_OK;
  1093. error:
  1094. return(hr);
  1095. }
  1096. STDMETHODIMP
  1097. CEnumCERTDBRESULTROW::Reset(VOID)
  1098. {
  1099. HRESULT hr;
  1100. LONG iDummy;
  1101. DBGPRINT((
  1102. DBG_SS_CERTDBI,
  1103. "Trace: hr = penum->Reset();\t_PrintIfError(hr, \"Reset\");\n// "));
  1104. hr = Skip(-(m_ielt + m_cskip), &iDummy);
  1105. _JumpIfError(hr, error, "Skip");
  1106. CSASSERT(0 == iDummy);
  1107. error:
  1108. return(hr);
  1109. }
  1110. STDMETHODIMP
  1111. CEnumCERTDBRESULTROW::Clone(
  1112. /* [out] */ IEnumCERTDBRESULTROW **ppenum)
  1113. {
  1114. HRESULT hr;
  1115. LONG iDummy;
  1116. if (NULL == ppenum)
  1117. {
  1118. hr = E_POINTER;
  1119. _JumpError(hr, error, "NULL parm");
  1120. }
  1121. *ppenum = NULL;
  1122. if (NULL == m_pdb)
  1123. {
  1124. hr = E_UNEXPECTED;
  1125. _JumpError(hr, error, "NULL m_pdb");
  1126. }
  1127. hr = ((CCertDB *) m_pdb)->TestShutDownState();
  1128. _JumpIfError(hr, error, "TestShutDownState");
  1129. hr = ((CCertDB *) m_pdb)->OpenView(
  1130. m_cRestriction,
  1131. m_aRestriction,
  1132. m_ccolOut,
  1133. m_acolOut,
  1134. m_fThreading,
  1135. ppenum);
  1136. _JumpIfError(hr, error, "OpenView");
  1137. (*ppenum)->Skip(m_ielt + m_cskip, &iDummy);
  1138. error:
  1139. return(hr);
  1140. }
  1141. // IUnknown implementation
  1142. STDMETHODIMP
  1143. CEnumCERTDBRESULTROW::QueryInterface(
  1144. const IID& iid,
  1145. void **ppv)
  1146. {
  1147. HRESULT hr;
  1148. if (NULL == ppv)
  1149. {
  1150. hr = E_POINTER;
  1151. _JumpError(hr, error, "NULL parm");
  1152. }
  1153. if (iid == IID_IUnknown)
  1154. {
  1155. *ppv = static_cast<IEnumCERTDBRESULTROW *>(this);
  1156. }
  1157. else if (iid == IID_IEnumCERTDBRESULTROW)
  1158. {
  1159. *ppv = static_cast<IEnumCERTDBRESULTROW *>(this);
  1160. }
  1161. else
  1162. {
  1163. *ppv = NULL;
  1164. hr = E_NOINTERFACE;
  1165. _JumpError(hr, error, "IID");
  1166. }
  1167. reinterpret_cast<IUnknown *>(*ppv)->AddRef();
  1168. hr = S_OK;
  1169. error:
  1170. return(hr);
  1171. }
  1172. ULONG STDMETHODCALLTYPE
  1173. CEnumCERTDBRESULTROW::AddRef()
  1174. {
  1175. return(InterlockedIncrement(&m_cRef));
  1176. }
  1177. ULONG STDMETHODCALLTYPE
  1178. CEnumCERTDBRESULTROW::Release()
  1179. {
  1180. ULONG cRef = InterlockedDecrement(&m_cRef);
  1181. if (0 == cRef)
  1182. {
  1183. delete this;
  1184. }
  1185. return(cRef);
  1186. }
  1187. #if 0
  1188. STDMETHODIMP
  1189. CEnumCERTDBRESULTROW::InterfaceSupportsErrorInfo(
  1190. IN REFIID riid)
  1191. {
  1192. static const IID *arr[] =
  1193. {
  1194. &IID_IEnumCERTDBRESULTROW,
  1195. };
  1196. for (int i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
  1197. {
  1198. if (InlineIsEqualGUID(*arr[i], riid))
  1199. {
  1200. return(S_OK);
  1201. }
  1202. }
  1203. return(S_FALSE);
  1204. }
  1205. #endif