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.

852 lines
22 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1995
  5. //
  6. // File: cenumdom.cxx
  7. //
  8. // Contents: LDAP Object Enumeration Code
  9. //
  10. // CLDAPGenObjectEnum::CLDAPGenObjectEnum()
  11. // CLDAPGenObjectEnum::CLDAPGenObjectEnum
  12. //
  13. // History:
  14. //----------------------------------------------------------------------------
  15. #include "ldap.hxx"
  16. #pragma hdrstop
  17. HRESULT
  18. BuildLDAPFilterArray(
  19. VARIANT vFilter,
  20. LPTSTR *ppszFilter
  21. );
  22. #define FILTER_BUFFER_LEN 256
  23. //+---------------------------------------------------------------------------
  24. //
  25. // Function: CLDAPEnumVariant::Create
  26. //
  27. // Synopsis:
  28. //
  29. // Arguments: [pCollection]
  30. // [ppEnumVariant]
  31. //
  32. // Returns: HRESULT
  33. //
  34. // Modifies:
  35. //
  36. // History: 01-30-95 krishnag Created.
  37. //
  38. //----------------------------------------------------------------------------
  39. HRESULT
  40. CLDAPGenObjectEnum::Create(
  41. CLDAPGenObjectEnum FAR* FAR* ppenumvariant,
  42. BSTR ADsPath,
  43. PADSLDP pLdapHandle,
  44. IADs *pADs,
  45. VARIANT vFilter,
  46. CCredentials& Credentials,
  47. DWORD dwOptReferral,
  48. DWORD dwPageSize
  49. )
  50. {
  51. HRESULT hr = NOERROR;
  52. CLDAPGenObjectEnum FAR* penumvariant = NULL;
  53. TCHAR *pszLDAPServer = NULL;
  54. DWORD dwModificationTime = 0L;
  55. DWORD dwNumberOfEntries = 0L;
  56. LPTSTR aStrings[2];
  57. DWORD dwPort = 0;
  58. WCHAR **aValues = NULL;
  59. int nCount = 0;
  60. DWORD totalCount = 0;
  61. DWORD dwcEntriesReturned = 0;
  62. PLDAPControl pPagedControl = NULL;
  63. LDAPControl referralControl =
  64. {
  65. LDAP_CONTROL_REFERRALS_W,
  66. {
  67. sizeof( DWORD ), (PCHAR) &dwOptReferral
  68. },
  69. TRUE
  70. };
  71. PLDAPControl clientControls[2] =
  72. {
  73. &referralControl,
  74. NULL
  75. };
  76. PLDAPControl serverControls[2] = {NULL, NULL};
  77. PLDAPControl *serverReturnedControls = NULL;
  78. penumvariant = new CLDAPGenObjectEnum();
  79. if (!penumvariant) {
  80. hr = E_OUTOFMEMORY;
  81. BAIL_ON_FAILURE(hr);
  82. }
  83. hr = ADsAllocString( ADsPath, &penumvariant->_ADsPath);
  84. BAIL_ON_FAILURE(hr);
  85. penumvariant->_dwOptReferral = dwOptReferral;
  86. penumvariant->_dwPageSize = dwPageSize;
  87. hr = BuildLDAPFilterArray(
  88. vFilter,
  89. &penumvariant->_pszFilter
  90. );
  91. if ( FAILED(hr) || !penumvariant->_pszFilter ) // this might happen when vFilter is empty
  92. {
  93. hr = S_OK;
  94. penumvariant->_pszFilter = AllocADsStr(TEXT("(objectClass=*)"));
  95. if ( penumvariant->_pszFilter == NULL )
  96. {
  97. hr = E_OUTOFMEMORY;
  98. BAIL_ON_FAILURE(hr);
  99. }
  100. }
  101. penumvariant->_Credentials = Credentials;
  102. hr = BuildLDAPPathFromADsPath2(
  103. ADsPath,
  104. &pszLDAPServer,
  105. &penumvariant->_pszLDAPDn,
  106. &dwPort
  107. );
  108. BAIL_ON_FAILURE(hr);
  109. penumvariant->_pLdapHandle = pLdapHandle;
  110. // Need to AddRef since we are storing the ld that is owned by the object
  111. pADs->AddRef();
  112. penumvariant->_pADs = pADs;
  113. aStrings[0] = TEXT("objectClass");
  114. aStrings[1] = NULL;
  115. //
  116. // Check if paged search control is supported; if so, we will use it.
  117. //
  118. hr = ReadPagingSupportedAttr(
  119. pszLDAPServer,
  120. &penumvariant->_fPagedSearch,
  121. Credentials,
  122. dwPort
  123. ) ;
  124. if (penumvariant->_fPagedSearch) {
  125. //
  126. // In this case we wont to use the ldap paging API
  127. // wrapper rather than use cookies directly.
  128. //
  129. hr = LdapSearchInitPage(
  130. penumvariant->_pLdapHandle,
  131. penumvariant->_pszLDAPDn,
  132. LDAP_SCOPE_ONELEVEL,
  133. penumvariant->_pszFilter,
  134. aStrings,
  135. 0,
  136. NULL,
  137. clientControls,
  138. 0,
  139. 0,
  140. NULL,
  141. &penumvariant->_phPagedSearch
  142. );
  143. if (FAILED(hr) || (penumvariant->_phPagedSearch == NULL)) {
  144. //
  145. // Some error in setting up the paged control, default to non
  146. // paged search.
  147. //
  148. penumvariant->_fPagedSearch = FALSE;
  149. }
  150. }
  151. if (!penumvariant->_fPagedSearch) {
  152. hr = LdapSearchExtS(
  153. penumvariant->_pLdapHandle,
  154. penumvariant->_pszLDAPDn,
  155. LDAP_SCOPE_ONELEVEL,
  156. penumvariant->_pszFilter,
  157. aStrings,
  158. 0,
  159. NULL,
  160. clientControls,
  161. NULL,
  162. 0,
  163. &penumvariant->_res
  164. );
  165. //
  166. // Error out only if there are no entries returned.
  167. //
  168. dwcEntriesReturned = LdapCountEntries(
  169. penumvariant->_pLdapHandle,
  170. penumvariant->_res
  171. );
  172. if (FAILED(hr) && !dwcEntriesReturned) {
  173. BAIL_ON_FAILURE(hr);
  174. }
  175. } // if not do the paged search case
  176. else {
  177. if (penumvariant->_fPagedSearch) {
  178. //
  179. // Get the first page full of results.
  180. //
  181. hr = LdapGetNextPageS(
  182. penumvariant->_pLdapHandle,
  183. penumvariant->_phPagedSearch,
  184. NULL,
  185. penumvariant->_dwPageSize,
  186. &totalCount,
  187. &penumvariant->_res
  188. );
  189. if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_RESULTS_RETURNED)) {
  190. penumvariant->_fLastPage = TRUE;
  191. goto error;
  192. } else if ((hr == HRESULT_FROM_WIN32(ERROR_DS_SIZELIMIT_EXCEEDED))
  193. && totalCount != 0) {
  194. penumvariant->_fLastPage = TRUE;
  195. hr = S_OK;
  196. }
  197. BAIL_ON_FAILURE(hr);
  198. }
  199. } // the paged search case
  200. error:
  201. if ( pszLDAPServer )
  202. FreeADsStr( pszLDAPServer);
  203. if (pPagedControl) {
  204. LdapControlFree(pPagedControl);
  205. }
  206. if (serverReturnedControls) {
  207. LdapControlsFree(serverReturnedControls);
  208. }
  209. if (FAILED(hr) && !dwcEntriesReturned) {
  210. if (penumvariant) delete penumvariant;
  211. *ppenumvariant = NULL;
  212. } else {
  213. //
  214. // entries successfully retrieved from server, return them
  215. // to client even if error and let client decide
  216. //
  217. *ppenumvariant = penumvariant;
  218. }
  219. RRETURN_EXP_IF_ERR(hr);
  220. }
  221. CLDAPGenObjectEnum::CLDAPGenObjectEnum():
  222. _ADsPath(NULL),
  223. _fAllEntriesReturned(FALSE),
  224. _fPagedSearch(FALSE),
  225. _fLastPage(FALSE),
  226. _dwOptReferral(LDAP_CHASE_EXTERNAL_REFERRALS),
  227. _pszFilter(NULL),
  228. _pszLDAPDn(NULL),
  229. _phPagedSearch(NULL)
  230. {
  231. _pObjList = NULL;
  232. _pLdapHandle = NULL;
  233. _pADs = NULL;
  234. _res = NULL;
  235. _entry = NULL;
  236. }
  237. CLDAPGenObjectEnum::~CLDAPGenObjectEnum()
  238. {
  239. if ( _res )
  240. LdapMsgFree( _res );
  241. if (_pszFilter) {
  242. FreeADsMem(_pszFilter);
  243. }
  244. if (_pszLDAPDn) {
  245. FreeADsMem(_pszLDAPDn);
  246. }
  247. if (_fPagedSearch) {
  248. LdapSearchAbandonPage(_pLdapHandle, _phPagedSearch);
  249. }
  250. if ( _pADs )
  251. _pADs->Release();
  252. if ( _ADsPath )
  253. ADsFreeString( _ADsPath );
  254. if ( _pObjList )
  255. delete _pObjList;
  256. _pLdapHandle = NULL;
  257. _phPagedSearch = NULL;
  258. }
  259. HRESULT
  260. CLDAPGenObjectEnum::EnumGenericObjects(
  261. ULONG cElements,
  262. VARIANT FAR* pvar,
  263. ULONG FAR* pcElementFetched
  264. )
  265. {
  266. HRESULT hr = S_OK;
  267. IDispatch *pDispatch = NULL;
  268. DWORD i = 0;
  269. BOOL fRepeat = FALSE;
  270. DWORD dwFailureCount = 0;
  271. DWORD dwPermitFailure = 1000;
  272. while (i < cElements) {
  273. hr = GetGenObject(&pDispatch);
  274. if (hr == S_FALSE) {
  275. break;
  276. }
  277. else if (FAILED(hr)) {
  278. //
  279. // Got an error while retrieving the object, ignore the
  280. // error and continue with the next object.
  281. // If continuously getting error more than dwPermitFailure,
  282. // make the return value S_FALSE, leave the loop.
  283. //
  284. if (fRepeat) {
  285. dwFailureCount++;
  286. if(dwFailureCount > dwPermitFailure) {
  287. hr = S_FALSE;
  288. break;
  289. }
  290. }
  291. else {
  292. fRepeat = TRUE;
  293. dwFailureCount = 1;
  294. }
  295. hr = S_OK;
  296. continue;
  297. }
  298. if (fRepeat) {
  299. fRepeat = FALSE;
  300. }
  301. VariantInit(&pvar[i]);
  302. pvar[i].vt = VT_DISPATCH;
  303. pvar[i].pdispVal = pDispatch;
  304. (*pcElementFetched)++;
  305. i++;
  306. }
  307. return(hr);
  308. }
  309. HRESULT
  310. CLDAPGenObjectEnum::GetGenObject(
  311. IDispatch ** ppDispatch
  312. )
  313. {
  314. HRESULT hr = S_OK;
  315. LPTSTR pszObjectName = NULL;
  316. TCHAR **aValues = NULL;
  317. int nCount = 0;
  318. DWORD totalCount = 0;
  319. TCHAR szADsClassName[64];
  320. LPWSTR aStrings[2] = {TEXT("objectClass"), NULL};
  321. DWORD dwPort;
  322. LPWSTR pszLDAPServer = NULL;
  323. LPWSTR pszLDAPDn = NULL;
  324. LPWSTR pszADsPathParent = NULL;
  325. BOOL fGCNameSpace = FALSE;
  326. PLDAPControl pPagedControl = NULL;
  327. PLDAPControl serverControls[2] = {NULL, NULL};
  328. PLDAPControl *serverReturnedControls = NULL;
  329. LDAPControl referralControl =
  330. {
  331. LDAP_CONTROL_REFERRALS_W,
  332. {
  333. sizeof( DWORD ), (PCHAR) &_dwOptReferral
  334. },
  335. TRUE
  336. };
  337. PLDAPControl clientControls[2] =
  338. {
  339. &referralControl,
  340. NULL
  341. };
  342. OBJECTINFO ObjectInfoLocal;
  343. memset(&ObjectInfoLocal, 0, sizeof(OBJECTINFO));
  344. *ppDispatch = NULL;
  345. if ( _fAllEntriesReturned )
  346. {
  347. hr = S_FALSE;
  348. goto error;
  349. }
  350. if ( _entry == NULL )
  351. hr = LdapFirstEntry( _pLdapHandle, _res, &_entry );
  352. else
  353. hr = LdapNextEntry( _pLdapHandle, _entry, &_entry );
  354. if (FAILED(hr) ) {
  355. _fAllEntriesReturned = TRUE;
  356. BAIL_ON_FAILURE(hr);
  357. }
  358. else if ( _entry == NULL ) {
  359. if (!_fPagedSearch || _fLastPage) { // reached the end of enumeration
  360. hr = S_FALSE;
  361. _fAllEntriesReturned = TRUE;
  362. goto error;
  363. }
  364. else { // more pages are remaining
  365. //
  366. // release this page
  367. //
  368. if (_res) {
  369. LdapMsgFree(
  370. _res
  371. );
  372. _res = NULL;
  373. }
  374. //
  375. // Get the next page of results
  376. //
  377. //
  378. // Get the first page full of results.
  379. //
  380. hr = LdapGetNextPageS(
  381. _pLdapHandle,
  382. _phPagedSearch,
  383. NULL,
  384. _dwPageSize,
  385. &totalCount,
  386. &_res
  387. );
  388. if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_RESULTS_RETURNED)) {
  389. _fLastPage = TRUE;
  390. hr = S_FALSE;
  391. goto error;
  392. }
  393. BAIL_ON_FAILURE(hr);
  394. hr = LdapFirstEntry(_pLdapHandle, _res, &_entry );
  395. if (FAILED(hr) ) {
  396. _fAllEntriesReturned = TRUE;
  397. BAIL_ON_FAILURE(hr);
  398. }
  399. else if ( (_entry == NULL) ) { // reached the end of enumeration
  400. hr = S_FALSE;
  401. _fAllEntriesReturned = TRUE;
  402. goto error;
  403. }
  404. }
  405. }
  406. hr = LdapGetDn( _pLdapHandle, _entry, &pszObjectName );
  407. if (FAILED(hr) ) {
  408. BAIL_ON_FAILURE(hr);
  409. }
  410. hr = LdapGetValues( _pLdapHandle, _entry, TEXT("objectClass"),
  411. &aValues, &nCount );
  412. BAIL_ON_FAILURE(hr);
  413. if ( nCount == 0 )
  414. {
  415. // This object exists but does not contain objectClass attribute
  416. // which is required for all DS objects. Hence, ignore the object
  417. // and return error.
  418. hr = E_ADS_PROPERTY_NOT_FOUND;
  419. BAIL_ON_FAILURE(hr);
  420. }
  421. //
  422. // Now send back the current object
  423. //
  424. {
  425. CLexer Lexer;
  426. hr = Lexer.InitializePath(pszObjectName);
  427. BAIL_ON_FAILURE(hr);
  428. hr = InitObjectInfo(pszObjectName,
  429. &ObjectInfoLocal);
  430. BAIL_ON_FAILURE(hr);
  431. Lexer.SetAtDisabler(TRUE);
  432. Lexer.SetFSlashDisabler(TRUE);
  433. hr = PathName(&Lexer,
  434. &ObjectInfoLocal);
  435. BAIL_ON_FAILURE(hr);
  436. Lexer.SetFSlashDisabler(FALSE);
  437. if (ObjectInfoLocal.ComponentArray[0].szValue == NULL) {
  438. BAIL_ON_FAILURE(hr=E_ADS_BAD_PATHNAME);
  439. }
  440. DWORD len;
  441. len = wcslen(ObjectInfoLocal.ComponentArray[0].szComponent) +
  442. wcslen(ObjectInfoLocal.ComponentArray[0].szValue) +
  443. 1; // For equal to sign
  444. pszObjectName[len] = '\0';
  445. //
  446. // We form the ADsPath to the parent by taking the _ADsPath of this
  447. // object (which is the parent ADsPath), chopping off the DN portion,
  448. // and attaching the parent DN portion of the object retrieved. This
  449. // is so that the enumerated child objects get a proper parent ADsPath
  450. // even if the parent object was bound using a GUID.
  451. //
  452. hr = BuildLDAPPathFromADsPath2(
  453. _ADsPath,
  454. &pszLDAPServer,
  455. &pszLDAPDn,
  456. &dwPort
  457. );
  458. BAIL_ON_FAILURE(hr);
  459. // _ADsPath could not be NULL
  460. if(!_wcsnicmp(L"GC:", _ADsPath, wcslen(L"GC:")))
  461. {
  462. fGCNameSpace = TRUE;
  463. }
  464. if (ObjectInfoLocal.NumComponents > 1) {
  465. //
  466. // pszObjectName[0 ... len] = object name (e.g., "CN=child")
  467. // pszObjectName[len+1 ....] = parent name (e.g., "OU=parent, DC=....")
  468. //
  469. hr = BuildADsPathFromLDAPPath2(
  470. (pszLDAPServer ? TRUE : FALSE),
  471. fGCNameSpace ? L"GC:" : L"LDAP:",
  472. pszLDAPServer,
  473. dwPort,
  474. &(pszObjectName[len+1]),
  475. &pszADsPathParent
  476. );
  477. }
  478. else {
  479. //
  480. // If the count is zero or less, then the objectName will
  481. // need to be created with a parent path of NULL. In this case
  482. // pszObjectName[len+1] is not necessarily valid. This is
  483. // espescially true when we have a defaultNamingContext
  484. // that is NULL or no defaultNamingContext with an object
  485. // directly underneath it (say dn is just o=testObject.
  486. //
  487. hr = BuildADsPathFromLDAPPath2(
  488. (pszLDAPServer ? TRUE : FALSE),
  489. fGCNameSpace ? L"GC:" : L"LDAP:",
  490. pszLDAPServer,
  491. dwPort,
  492. NULL,
  493. &pszADsPathParent
  494. );
  495. }
  496. BAIL_ON_FAILURE(hr);
  497. }
  498. hr = CLDAPGenObject::CreateGenericObject(
  499. pszADsPathParent,
  500. pszObjectName,
  501. aValues,
  502. nCount,
  503. _Credentials,
  504. ADS_OBJECT_BOUND,
  505. IID_IDispatch,
  506. (void **) ppDispatch
  507. );
  508. BAIL_ON_FAILURE(hr);
  509. error:
  510. FreeObjectInfo(&ObjectInfoLocal);
  511. if ( pszObjectName )
  512. LdapMemFree( pszObjectName );
  513. if ( aValues )
  514. LdapValueFree( aValues );
  515. if (pPagedControl) {
  516. LdapControlFree(pPagedControl);
  517. }
  518. if (serverReturnedControls) {
  519. LdapControlsFree(serverReturnedControls);
  520. }
  521. if (pszADsPathParent) {
  522. FreeADsMem(pszADsPathParent);
  523. }
  524. if (pszLDAPServer) {
  525. FreeADsMem(pszLDAPServer);
  526. }
  527. if (pszLDAPDn) {
  528. FreeADsMem(pszLDAPDn);
  529. }
  530. RRETURN_EXP_IF_ERR(hr);
  531. }
  532. //+---------------------------------------------------------------------------
  533. //
  534. // Function: CLDAPGenObjectEnum::Next
  535. //
  536. // Synopsis: Returns cElements number of requested NetOle objects in the
  537. // array supplied in pvar.
  538. //
  539. // Arguments: [cElements] -- The number of elements requested by client
  540. // [pvar] -- ptr to array of VARIANTs to for return objects
  541. // [pcElementFetched] -- if non-NULL, then number of elements
  542. // -- actually returned is placed here
  543. //
  544. // Returns: HRESULT -- S_OK if number of elements requested are returned
  545. // -- S_FALSE if number of elements is < requested
  546. //
  547. // Modifies:
  548. //
  549. // History: 11-3-95 krishnag Created.
  550. //
  551. //----------------------------------------------------------------------------
  552. STDMETHODIMP
  553. CLDAPGenObjectEnum::Next(
  554. ULONG cElements,
  555. VARIANT FAR* pvar,
  556. ULONG FAR* pcElementFetched
  557. )
  558. {
  559. ULONG cElementFetched = 0;
  560. HRESULT hr = S_OK;
  561. hr = EnumGenericObjects(
  562. cElements,
  563. pvar,
  564. &cElementFetched
  565. );
  566. if (pcElementFetched) {
  567. *pcElementFetched = cElementFetched;
  568. }
  569. RRETURN_EXP_IF_ERR(hr);
  570. }
  571. HRESULT
  572. BuildLDAPFilterArray(
  573. VARIANT var,
  574. LPTSTR *ppszFilter
  575. )
  576. {
  577. HRESULT hr = S_OK;
  578. LONG dwSLBound = 0;
  579. LONG dwSUBound = 0;
  580. VARIANT v;
  581. LONG i;
  582. LONG dwBracketCount = 0;
  583. DWORD dwFilterBufLen = 0;
  584. DWORD dwCurrentFilterLen = 0;
  585. LPTSTR pszFilter = NULL, pszTempFilter = NULL;
  586. *ppszFilter = NULL;
  587. if(!(V_VT(&var) == (VT_VARIANT|VT_ARRAY))) {
  588. RRETURN(E_FAIL);
  589. }
  590. //
  591. // Check that there is only one dimension in this array
  592. //
  593. if ((V_ARRAY(&var))->cDims != 1) {
  594. hr = E_FAIL;
  595. BAIL_ON_FAILURE(hr);
  596. }
  597. //
  598. // Check that there is at least one element in this array
  599. //
  600. if ((V_ARRAY(&var))->rgsabound[0].cElements == 0){
  601. hr = E_FAIL;
  602. BAIL_ON_FAILURE(hr);
  603. }
  604. //
  605. // We know that this is a valid single dimension array
  606. //
  607. hr = SafeArrayGetLBound(V_ARRAY(&var),
  608. 1,
  609. (long FAR *)&dwSLBound
  610. );
  611. BAIL_ON_FAILURE(hr);
  612. hr = SafeArrayGetUBound(V_ARRAY(&var),
  613. 1,
  614. (long FAR *)&dwSUBound
  615. );
  616. BAIL_ON_FAILURE(hr);
  617. dwCurrentFilterLen = 0;
  618. dwFilterBufLen = FILTER_BUFFER_LEN;
  619. pszFilter = (LPTSTR) AllocADsMem( (dwFilterBufLen + 1) * sizeof(WCHAR));
  620. if ( pszFilter == NULL )
  621. {
  622. hr = E_OUTOFMEMORY;
  623. BAIL_ON_FAILURE(hr);
  624. }
  625. for (i = dwSLBound; i <= dwSUBound; i++) {
  626. VariantInit(&v);
  627. hr = SafeArrayGetElement(V_ARRAY(&var),
  628. (long FAR *)&i,
  629. &v
  630. );
  631. BAIL_ON_FAILURE(hr);
  632. // The last length below is the string length of
  633. // "(| (objectClass=))" which is 18.
  634. //
  635. while ( dwCurrentFilterLen + _tcslen(V_BSTR(&v)) + 18 > dwFilterBufLen)
  636. {
  637. pszTempFilter = (LPTSTR) ReallocADsMem(
  638. pszFilter,
  639. (dwFilterBufLen + 1) * sizeof(WCHAR),
  640. (dwFilterBufLen*2 + 1) * sizeof(WCHAR));
  641. if ( pszTempFilter == NULL )
  642. {
  643. hr = E_OUTOFMEMORY;
  644. BAIL_ON_FAILURE(hr);
  645. }
  646. pszFilter = pszTempFilter;
  647. dwFilterBufLen *= 2;
  648. }
  649. if ( i == dwSUBound )
  650. {
  651. _tcscat( pszFilter, TEXT("(objectClass="));
  652. }
  653. else
  654. {
  655. dwBracketCount++;
  656. _tcscat( pszFilter, TEXT("(| (objectClass="));
  657. }
  658. _tcscat( pszFilter, V_BSTR(&v));
  659. _tcscat( pszFilter, TEXT(")"));
  660. dwCurrentFilterLen = _tcslen(pszFilter);
  661. VariantClear(&v);
  662. }
  663. for ( i = 0; i < dwBracketCount; i++ )
  664. _tcscat( pszFilter, TEXT(")"));
  665. *ppszFilter = pszFilter;
  666. RRETURN(S_OK);
  667. error:
  668. VariantClear(&v);
  669. if ( pszFilter != NULL)
  670. FreeADsMem( pszFilter );
  671. RRETURN(hr);
  672. }