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.

1493 lines
40 KiB

  1. /*---------------------------------------------------------------------------
  2. File: VSet.cpp
  3. Comments: Implementation of IVarSet interface.
  4. (c) Copyright 1995-1998, Mission Critical Software, Inc., All Rights Reserved
  5. Proprietary and confidential to Mission Critical Software, Inc.
  6. REVISION LOG ENTRY
  7. Revision By: Christy Boles
  8. Revised on 11/19/98 19:44:06
  9. ---------------------------------------------------------------------------
  10. */
  11. // VSet.cpp : Implementation of CVSet
  12. #include "stdafx.h"
  13. #ifdef STRIPPED_VARSET
  14. #include "NoMcs.h"
  15. #include <comdef.h>
  16. #include "Err.hpp"
  17. #include "Varset.h"
  18. #else
  19. #endif
  20. #include "VarSetI.h"
  21. #include "VSet.h"
  22. #include "VarMap.h"
  23. #include "DotStr.hpp"
  24. #ifdef _DEBUG
  25. #define new DEBUG_NEW
  26. #undef THIS_FILE
  27. static char THIS_FILE[] = __FILE__;
  28. #endif
  29. /////////////////////////////////////////////////////////////////////////////
  30. // CVSet
  31. /////////////////////////////////////////////////////////////////////
  32. // IVarSet
  33. /////////////////////////////////////////////////////////////////////
  34. // Gets the number of items in the map and all sub-maps
  35. STDMETHODIMP CVSet::get_Count(/* [retval][out] */long* retval)
  36. {
  37. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  38. MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::get_Count");
  39. if (retval == NULL)
  40. {
  41. MCSVERIFYSZ(FALSE,"get_Count: output pointer was null, returning E_POINTER");
  42. return E_POINTER;
  43. }
  44. m_cs.Lock();
  45. *retval = m_nItems;
  46. MCSASSERTSZ(! m_nItems || m_nItems == m_data->CountItems() - (m_data->HasData()?0:1),"get_Count:Item count consistency check failed.");
  47. m_cs.Unlock();
  48. return S_OK;
  49. }
  50. STDMETHODIMP CVSet::get_NumChildren(/* [in] */BSTR parentKey,/* [out,retval] */long*count)
  51. {
  52. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  53. MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::get_NumChildren");
  54. HRESULT hr = S_OK;
  55. CVarData * pVar;
  56. CString parent;
  57. parent = parentKey;
  58. if ( count == NULL )
  59. {
  60. MCSVERIFYSZ(FALSE,"get_NumChildren: output pointer was null, returning E_POINTER");
  61. hr = E_POINTER;
  62. }
  63. else
  64. {
  65. m_cs.Lock();
  66. pVar = GetItem(parent,FALSE);
  67. if ( pVar )
  68. {
  69. if ( pVar->HasChildren() )
  70. {
  71. (*count) = pVar->GetChildren()->GetCount();
  72. }
  73. else
  74. {
  75. (*count) = 0;
  76. }
  77. }
  78. else
  79. {
  80. // The parent key does not exist
  81. (*count) = 0;
  82. }
  83. m_cs.Unlock();
  84. }
  85. return hr;
  86. }
  87. // Adds or changes a value in the map
  88. STDMETHODIMP CVSet::putObject(/* [in] */BSTR property,/* [in] */VARIANT value)
  89. {
  90. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  91. MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::putObject");
  92. CVarData * pVar = NULL;
  93. HRESULT hr = S_OK;
  94. if ( m_Restrictions & VARSET_RESTRICT_NOCHANGEDATA )
  95. {
  96. hr = E_ACCESSDENIED;
  97. }
  98. else
  99. {
  100. m_cs.Lock();
  101. m_bNeedToSave = TRUE;
  102. pVar = GetItem(property,TRUE);
  103. if ( pVar )
  104. {
  105. MC_LOG("set value for " << McString::String(property));
  106. m_nItems+=pVar->SetData("",&value,FALSE,&hr);
  107. }
  108. else
  109. {
  110. MCSASSERTSZ(FALSE,"VarSet internal error creating or retrieving node");
  111. // GetItem failed - cannot add item to property
  112. hr = E_FAIL;
  113. }
  114. m_cs.Unlock();
  115. }
  116. return hr;
  117. }
  118. // Adds or changes a value in the map
  119. STDMETHODIMP CVSet::put(/* [in] */BSTR property,/* [in] */VARIANT value)
  120. {
  121. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  122. MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::put");
  123. CVarData * pVar = NULL;
  124. HRESULT hr = S_OK;
  125. if ( m_Restrictions & VARSET_RESTRICT_NOCHANGEDATA )
  126. {
  127. hr = E_ACCESSDENIED;
  128. }
  129. else
  130. {
  131. m_cs.Lock();
  132. m_bNeedToSave = TRUE;
  133. pVar = GetItem(property,TRUE);
  134. if ( pVar )
  135. {
  136. MC_LOG("set value for " << McString::String(property));
  137. m_nItems+=pVar->SetData("",&value,TRUE,&hr);
  138. }
  139. else
  140. {
  141. MCSASSERTSZ(FALSE,"VarSet internal error creating or retrieving node");
  142. // GetItem failed - cannot add item to property
  143. hr = E_FAIL;
  144. }
  145. m_cs.Unlock();
  146. }
  147. return hr;
  148. }
  149. CVarData * // ret- pointer to item in varset
  150. CVSet::GetItem(
  151. CString str, // in - key to look for
  152. BOOL addToMap, // in - if TRUE, adds the key to the map if it does not exist
  153. CVarData * base // in - starting point
  154. )
  155. {
  156. MC_LOGBLOCKIF(VARSET_LOGLEVEL_INTERNAL,"CVSet::GetItem");
  157. CVarData * curr = base;
  158. CVarData * result = NULL;
  159. CDottedString s(str);
  160. CString seg;
  161. CString next;
  162. if ( ! curr )
  163. {
  164. curr = m_data;
  165. MC_LOG("No basepoint provided, using root element");
  166. }
  167. if ( str.IsEmpty() )
  168. {
  169. result = curr;
  170. MC_LOG("Returning current node");
  171. }
  172. else
  173. {
  174. for ( int i = 0 ; curr && i < s.NumSegments(); i++ )
  175. {
  176. s.GetSegment(i,seg);
  177. MC_LOG("Looking for key segment "<< McString::String(seg) );
  178. curr->SetCaseSensitive(m_CaseSensitive);
  179. if ( ! curr->Lookup(seg,result) )
  180. {
  181. if ( addToMap )
  182. {
  183. MC_LOG(McString::String(seg) << " not found, creating new node");
  184. result = new CVarData;
  185. if (!result)
  186. break;
  187. try {
  188. result->SetCaseSensitive(m_CaseSensitive);
  189. result->SetIndexed(m_Indexed);
  190. curr->SetAt(seg,result);
  191. m_nItems++;
  192. }
  193. catch(...)
  194. {
  195. delete result;
  196. result = NULL;
  197. throw;
  198. }
  199. }
  200. else
  201. {
  202. MC_LOG(McString::String(seg) << " not found, aborting");
  203. result = NULL;
  204. break;
  205. }
  206. }
  207. curr = result;
  208. }
  209. }
  210. return result;
  211. }
  212. // Retrieves a value from the map
  213. STDMETHODIMP CVSet::get(/* [in] */BSTR property,/* [retval][out] */VARIANT * value)
  214. {
  215. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  216. MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::get");
  217. CVarData * pVar;
  218. HRESULT hr = S_OK;
  219. CComVariant var;
  220. CString s;
  221. if (property == NULL )
  222. {
  223. MCSVERIFYSZ(FALSE,"CVSet::get - output pointer is NULL, returning E_POINTER");
  224. hr = E_POINTER;
  225. }
  226. else
  227. {
  228. m_cs.Lock();
  229. s = property;
  230. pVar = GetItem(s);
  231. var.Attach(value);
  232. if ( pVar )
  233. {
  234. MC_LOG("got value for " << McString::String(property));
  235. var.Copy(pVar->GetData());
  236. }
  237. else
  238. {
  239. MC_LOG("CVSet::get " << McString::String(property) << " was not found, returning empty variant");
  240. }
  241. // if the item was not found, set the variant to VT_EMPTY
  242. var.Detach(value);
  243. m_cs.Unlock();
  244. }
  245. return hr;
  246. }
  247. STDMETHODIMP CVSet::put_CaseSensitive(/* [in] */BOOL newVal)
  248. {
  249. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  250. MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::put_CaseSensitive");
  251. HRESULT hr = S_OK;
  252. if ( m_Restrictions & VARSET_RESTRICT_NOCHANGEPROPS )
  253. {
  254. hr = E_ACCESSDENIED;
  255. }
  256. else
  257. {
  258. m_cs.Lock();
  259. m_bNeedToSave = TRUE;
  260. m_CaseSensitive = newVal;
  261. m_cs.Unlock();
  262. }
  263. return hr;
  264. }
  265. STDMETHODIMP CVSet::get_CaseSensitive(/* [retval][out] */BOOL * isCaseSensitive)
  266. {
  267. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  268. MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::get_CaseSensitive");
  269. if ( ! isCaseSensitive )
  270. {
  271. MCSVERIFYSZ(FALSE,"CVSet::get_CaseSensitive - output pointer is NULL, returning E_POINTER");
  272. return E_POINTER;
  273. }
  274. else
  275. {
  276. m_cs.Lock();
  277. (*isCaseSensitive) = m_CaseSensitive;
  278. m_cs.Unlock();
  279. }
  280. return S_OK;
  281. }
  282. // This function is used to sort the keys being returned from an enum.
  283. int __cdecl SortComVariantStrings(const void * v1, const void * v2)
  284. {
  285. CComVariant * var1 = (CComVariant*)v1;
  286. CComVariant * var2 = (CComVariant*)v2;
  287. if ( var1->vt == VT_BSTR && var2->vt == VT_BSTR )
  288. {
  289. return wcscmp(var1->bstrVal,var2->bstrVal);
  290. }
  291. return 0;
  292. }
  293. // This returns an IEnumVARIANT interface. It is used by the VB For Each command.
  294. // This enumerates only the keys, not the values. It is not very efficient, especially for large sets.
  295. STDMETHODIMP CVSet::get__NewEnum(/* [retval][out] */IUnknown** retval)
  296. {
  297. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  298. MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::get_NewEnum");
  299. if (retval == NULL)
  300. {
  301. MCSVERIFYSZ(FALSE,"CVSet::get_NewEnum - output pointer is NULL, returning E_POINTER");
  302. return E_POINTER;
  303. }
  304. // initialize output parameter
  305. (*retval) = NULL;
  306. typedef CComObject<CComEnum<IEnumVARIANT, &IID_IEnumVARIANT, VARIANT,
  307. _Copy<VARIANT> > > enumvar;
  308. HRESULT hRes = S_OK;
  309. enumvar * p = new enumvar;
  310. if (p == NULL)
  311. {
  312. MCSVERIFYSZ(FALSE,"CVSet::get_NewEnum - Could not create IEnumVARIANT object");
  313. hRes = E_OUTOFMEMORY;
  314. }
  315. else
  316. {
  317. hRes = p->FinalConstruct();
  318. if (hRes == S_OK)
  319. {
  320. m_cs.Lock();
  321. CVarData * map = m_data;
  322. CString start;
  323. CString seg;
  324. // Build an array of variants to hold the keys
  325. CComVariant * pVars = new CComVariant[m_data->CountItems()+1];
  326. CString key;
  327. int offset = 0;
  328. key = _T("");
  329. if ( map->GetData() && map->GetData()->vt != VT_EMPTY )
  330. {
  331. pVars[offset] = key;
  332. offset++;
  333. }
  334. if ( map->HasChildren() )
  335. {
  336. BuildVariantKeyArray(key,map->GetChildren(),pVars,&offset);
  337. }
  338. if ( ! m_Indexed )
  339. {
  340. // Sort the results
  341. qsort(pVars,offset,(sizeof CComVariant),&SortComVariantStrings);
  342. }
  343. hRes = p->Init(pVars, &pVars[offset], NULL,AtlFlagCopy);
  344. if (hRes == S_OK)
  345. hRes = p->QueryInterface(IID_IUnknown, (void**)retval);
  346. delete [] pVars;
  347. m_cs.Unlock();
  348. }
  349. }
  350. if (hRes != S_OK)
  351. delete p;
  352. return hRes;
  353. }
  354. // Helper function for get__NewEnum
  355. // copies all the keys in the map, and all sub-maps, into a CComVariant array.
  356. // the values are then sorted if necessary.
  357. void
  358. CVSet::BuildVariantKeyArray(
  359. CString prefix, // in - string to tack on to the beginning of each key (used when enumerating subkeys)
  360. CMapStringToVar * map, // in - map containing data
  361. CComVariant * pVars, // i/o- array that will contain all the keys
  362. int * offset // i/o- number of keys copied to pVars (index to use for next insertion)
  363. )
  364. {
  365. MC_LOGBLOCKIF(VARSET_LOGLEVEL_INTERNAL,"CVSet::BuildVariantKeyArray");
  366. int i;
  367. int nItems;
  368. CVarData * pObj;
  369. CString key;
  370. CComVariant var;
  371. CString val;
  372. if ( ! map )
  373. return; // no data =>no work to do
  374. nItems = map->GetCount();
  375. if ( ! m_Indexed )
  376. {
  377. POSITION pos;
  378. pos = map->GetStartPosition();
  379. for ( i = 0 ; pos && i < nItems ; i++ )
  380. {
  381. map->GetNextAssoc(pos,key,pObj);
  382. if ( ! prefix.IsEmpty() )
  383. {
  384. var = prefix + L"." + key;
  385. }
  386. else
  387. {
  388. var = key;
  389. }
  390. // add each key to the array
  391. var.Detach(&pVars[(*offset)]);
  392. (*offset)++;
  393. if ( pObj->HasChildren() )
  394. {
  395. // Recursively do the sub-map
  396. if ( ! prefix.IsEmpty() )
  397. {
  398. BuildVariantKeyArray(prefix+L"."+key,pObj->GetChildren(),pVars,offset);
  399. }
  400. else
  401. {
  402. BuildVariantKeyArray(key,pObj->GetChildren(),pVars,offset);
  403. }
  404. }
  405. }
  406. }
  407. else
  408. {
  409. CIndexItem * item;
  410. CIndexTree * index = map->GetIndex();
  411. ASSERT(index);
  412. if ( ! index )
  413. return;
  414. item = index->GetFirstItem();
  415. for ( i = 0 ; item && i < nItems ; i++ )
  416. {
  417. key = item->GetKey();
  418. pObj = item->GetValue();
  419. if ( ! prefix.IsEmpty() )
  420. {
  421. var = prefix + L"." + key;
  422. }
  423. else
  424. {
  425. var = key;
  426. }
  427. // add each key to the array
  428. var.Detach(&pVars[(*offset)]);
  429. (*offset)++;
  430. if ( pObj->HasChildren() )
  431. {
  432. // Recursively do the sub-map
  433. if ( ! prefix.IsEmpty() )
  434. {
  435. BuildVariantKeyArray(prefix+L"."+key,pObj->GetChildren(),pVars,offset);
  436. }
  437. else
  438. {
  439. BuildVariantKeyArray(key,pObj->GetChildren(),pVars,offset);
  440. }
  441. }
  442. item = index->GetNextItem(item);
  443. }
  444. }
  445. }
  446. STDMETHODIMP
  447. CVSet::getItems2(
  448. /* [in] */VARIANT basepoint, // in - if specified, only children of this node will be enumerated
  449. /* [in] */VARIANT startAfter, // in - the enumeration will begin with the next item in the map following this key.
  450. /* [in] */VARIANT bRecursive, // in - TRUE includes all sub-items, FALSE enumerates one level only.
  451. /* [in] */VARIANT bSize, // in - max number of elements to return (the size of the arrays)
  452. /* [out] */VARIANT * keyVar, // out- array of keys
  453. /* [out] */VARIANT * valVar, // out- array of values
  454. /* [in,out] */VARIANT* nReturned // out- number of items copied
  455. )
  456. {
  457. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  458. MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::getItems2");
  459. HRESULT hr = S_OK;
  460. LONG n = 0;
  461. LONG size = bSize.pvarVal->iVal;
  462. // TODO: Verify that all the arguments are the correct type!
  463. // Allocate SAFEARRAYs for the keys and values
  464. SAFEARRAY * keys = NULL;
  465. SAFEARRAY * values= NULL;
  466. _variant_t key;
  467. _variant_t val;
  468. _variant_t num;
  469. if ( ! keys || !values )
  470. {
  471. hr = E_OUTOFMEMORY;
  472. }
  473. else
  474. {
  475. hr = getItems(basepoint.bstrVal,startAfter.bstrVal,bRecursive.boolVal,size,&keys,&values,&n);
  476. key.vt = VT_ARRAY | VT_VARIANT;
  477. key.parray = keys;
  478. val.vt = VT_ARRAY | VT_VARIANT;
  479. val.parray = values;
  480. num.vt = VT_I4;
  481. num.lVal = n;
  482. (*keyVar) = key.Detach();
  483. (*valVar) = val.Detach();
  484. (*nReturned) = num.Detach();
  485. }
  486. return hr;
  487. }
  488. STDMETHODIMP
  489. CVSet::getItems(
  490. /* [in] */BSTR basepoint, // in - if specified, only children of this node will be enumerated
  491. /* [in] */BSTR startAfter, // in - the enumeration will begin with the next item in the map following this key.
  492. /* [in] */BOOL bRecursive, // in - TRUE includes all sub-items, FALSE enumerates one level only.
  493. /* [in] */ULONG bSize, // in - max number of elements to return (the size of the arrays)
  494. /* [out] */SAFEARRAY ** keys, // out- array of keys
  495. /* [out] */SAFEARRAY ** values, // out- array of values
  496. /* [out] */LONG * nReturned // out- number of items copied
  497. )
  498. {
  499. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  500. MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::getItems");
  501. HRESULT hr = S_OK;
  502. (*nReturned) = 0;
  503. (*keys) = 0;
  504. (*values) = 0;
  505. m_cs.Lock();
  506. CVarData * map = m_data;
  507. CString base;
  508. CString start;
  509. CString seg;
  510. // Find the map to enumerate
  511. base = basepoint;
  512. if ( base.GetLength() > 0 )
  513. {
  514. map = GetItem(base);
  515. }
  516. if ( ! map )
  517. {
  518. // not found
  519. (*nReturned) = 0;
  520. }
  521. else
  522. {
  523. // Build an array of variants to hold the keys
  524. int offset = 0;
  525. SAFEARRAYBOUND bound[1];
  526. LONG n = 0;
  527. LONG size = bSize;
  528. bound[0].lLbound = 0;
  529. bound[0].cElements = size;
  530. // Allocate SAFEARRAYs for the keys and values
  531. (*keys) = SafeArrayCreate(VT_VARIANT, 1, bound);
  532. (*values) = SafeArrayCreate(VT_VARIANT, 1, bound);
  533. start = startAfter;
  534. if ( base.GetLength() && start.GetLength() )
  535. {
  536. // ASSERT( that LEFT(start,LEN(base)) = base
  537. //start = start.Right(start.GetLength() - base.GetLength() - 1);
  538. }
  539. if ( base.IsEmpty() && start.IsEmpty() )
  540. {
  541. if ( map->GetData() && map->GetData()->vt != VT_EMPTY )
  542. {
  543. long index[1];
  544. index[0] = 0;
  545. // add the root element to the results
  546. if ( (*keys)->fFeatures & FADF_BSTR )
  547. {
  548. SafeArrayPutElement((*keys),index,_T(""));
  549. }
  550. else
  551. {
  552. // VBScript can only use VARIANT arrays (see getItems2)
  553. _variant_t tempKey;
  554. tempKey = _T("");
  555. SafeArrayPutElement((*keys),index,&tempKey);
  556. }
  557. SafeArrayPutElement((*values),index,map->GetData());
  558. offset++;
  559. }
  560. }
  561. if ( map->HasChildren() )
  562. {
  563. BuildVariantKeyValueArray(base,start,map->GetChildren(),(*keys),
  564. (*values),&offset,bSize,bRecursive);
  565. }
  566. (*nReturned) = offset;
  567. }
  568. m_cs.Unlock();
  569. return hr;
  570. }
  571. // helper function for getItems. Fills SAFEARRAYs of keys and values
  572. // if the varset is indexed, the items will be returned in sorted order, o.w. they will be in arbitrary (but consistent) order.
  573. void
  574. CVSet::BuildVariantKeyValueArray(
  575. CString prefix, // in - string to tack on to the beginning of each key (used when enumerating subkeys)
  576. CString startAfter, // in - optional, enumerates only those items that alphabetically follow this one.
  577. CMapStringToVar * map, // in - map containing the data
  578. SAFEARRAY * keys, // i/o- array that will contain the key values for the requested items
  579. SAFEARRAY * pVars, // i/o- array that will contain the data values for the requested items
  580. int * offset, // i/o- number of items copied to the arrays (index to use for next insertion)
  581. int maxOffset, // in - allocated size of the arrays
  582. BOOL bRecurse // in - whether to recursively process children
  583. )
  584. {
  585. MC_LOGBLOCKIF(VARSET_LOGLEVEL_INTERNAL,"CVSet::BuildVariantKeyValueArray");
  586. int i;
  587. int nItems;
  588. CVarData * pObj;
  589. CString key; // last segment of key name
  590. POSITION pos;
  591. CComBSTR val; // fully qualified key name to add to array (val = prefix.key)
  592. CComVariant var; // value to add to array
  593. BOOL includeSomeChildrenOnly;
  594. CDottedString dBase(prefix);
  595. CDottedString dStartItem(startAfter);
  596. int depth = dBase.NumSegments();
  597. if ( ! map )
  598. return; // no data => nothing to do
  599. if ( (*offset) >= maxOffset )
  600. return; // the arrays are full
  601. includeSomeChildrenOnly = dStartItem.NumSegments() > depth;
  602. nItems = map->GetCount();
  603. // If we're not using an index, the items will be returned in arbitrary order
  604. if ( ! m_Indexed )
  605. {
  606. if ( includeSomeChildrenOnly && bRecurse )
  607. {
  608. // the startAfter item is in a subtree. Find the appropriate element at this level and recursively continue the search
  609. dStartItem.GetSegment(depth,key);
  610. if ( map->Lookup(key,pObj) )
  611. {
  612. // found the object
  613. if ( ! prefix.IsEmpty() )
  614. {
  615. BuildVariantKeyValueArray(prefix+_T(".")+key,startAfter,pObj->GetChildren(),keys,pVars,offset,maxOffset,bRecurse);
  616. }
  617. else
  618. {
  619. BuildVariantKeyValueArray(key,startAfter,pObj->GetChildren(),keys,pVars,offset,maxOffset,bRecurse);
  620. }
  621. }
  622. // we've included the children of this item that come after 'startAfter',
  623. // now process the rest of the items at this level
  624. // make sure there's still room
  625. if ( (*offset) >= maxOffset )
  626. return; // the arrays are full
  627. }
  628. // this is the usual case. process the items at this level, starting with the element following StartAfter.
  629. // Get a pointer to that first element
  630. if ( startAfter.GetLength() > prefix.GetLength())
  631. {
  632. CString startItem;
  633. dStartItem.GetSegment(depth,startItem);
  634. // this returns the position before startItem
  635. pos = (POSITION)map->GetPositionAt(startItem);
  636. if (!pos)
  637. return;
  638. map->GetNextAssoc(pos,key,pObj);
  639. }
  640. else
  641. {
  642. pos = map->GetStartPosition();
  643. }
  644. for ( i = 0 ; pos && i < nItems ; i++ )
  645. {
  646. map->GetNextAssoc(pos,key,pObj);
  647. if ( ! prefix.IsEmpty() )
  648. {
  649. val = prefix + L"." + key;
  650. }
  651. else
  652. {
  653. val = key;
  654. }
  655. // copy each item into the arrays
  656. ASSERT((*offset) < maxOffset);
  657. var.Copy(pObj->GetData());
  658. LONG index[1];
  659. index[0] = (*offset);
  660. SafeArrayPutElement(pVars,index,&var);
  661. if ( keys->fFeatures & FADF_BSTR )
  662. {
  663. SafeArrayPutElement(keys,index,val);
  664. }
  665. else
  666. {
  667. // VBScript can only use VARIANT arrays (see getItems2)
  668. _variant_t tempKey;
  669. tempKey = val;
  670. SafeArrayPutElement(keys,index,&tempKey);
  671. }
  672. var.Clear();
  673. (*offset)++;
  674. if ( *offset >= maxOffset )
  675. break; // arrays are full - stop
  676. if ( bRecurse && pObj->HasChildren() )
  677. {
  678. // Recursively do the sub-map
  679. if ( ! prefix.IsEmpty() )
  680. {
  681. BuildVariantKeyValueArray(prefix+L"."+key,"",pObj->GetChildren(),keys,pVars,offset,maxOffset,bRecurse);
  682. }
  683. else
  684. {
  685. BuildVariantKeyValueArray(key,"",pObj->GetChildren(),keys,pVars,offset,maxOffset,bRecurse);
  686. }
  687. if ( *offset >= maxOffset )
  688. break; // arrays are full - stop
  689. }
  690. }
  691. }
  692. else
  693. {
  694. // Use the index to enumerate the items in alphabetical order
  695. CIndexItem * curr;
  696. CIndexTree * ndx = map->GetIndex();
  697. ASSERT (ndx != NULL);
  698. if ( includeSomeChildrenOnly && bRecurse )
  699. {
  700. // the startAfter item is in a subtree. Find the appropriate element at this level and recursively continue the search
  701. dStartItem.GetSegment(depth,key);
  702. if ( map->Lookup(key,pObj) )
  703. {
  704. // found the object
  705. if ( ! prefix.IsEmpty() )
  706. {
  707. BuildVariantKeyValueArray(prefix+_T(".")+key,startAfter,pObj->GetChildren(),keys,pVars,offset,maxOffset,bRecurse);
  708. }
  709. else
  710. {
  711. BuildVariantKeyValueArray(key,startAfter,pObj->GetChildren(),keys,pVars,offset,maxOffset,bRecurse);
  712. }
  713. }
  714. // we've included the children of this item that come after 'startAfter',
  715. // now process the rest of the items at this level
  716. // make sure there's still room
  717. if ( (*offset) >= maxOffset )
  718. return; // the arrays are full
  719. }
  720. // Get a pointer to the first item at this level AFTER startAfter
  721. if ( startAfter.GetLength() > prefix.GetLength() )
  722. {
  723. CString startItem;
  724. dStartItem.GetSegment(depth,startItem);
  725. // if a starting item is specified, try using the hash function to find it in the table
  726. curr = map->GetIndexAt(startItem);
  727. if ( curr )
  728. {
  729. curr = ndx->GetNextItem(curr);
  730. }
  731. else
  732. {
  733. // The startAfter item is not in the table. Search the tree to find
  734. // the first item that would follow it if it were there
  735. curr = ndx->GetFirstAfter(startItem);
  736. }
  737. }
  738. else
  739. {
  740. curr = ndx->GetFirstItem();
  741. }
  742. // Process all the items
  743. while ( curr )
  744. {
  745. pObj = curr->GetValue();
  746. key = curr->GetKey();
  747. curr = ndx->GetNextItem(curr);
  748. if ( ! prefix.IsEmpty() )
  749. {
  750. val = prefix + L"." + key;
  751. }
  752. else
  753. {
  754. val = key;
  755. }
  756. // add each item to the arrays
  757. ASSERT((*offset) < maxOffset);
  758. var.Copy(pObj->GetData());
  759. LONG index[1];
  760. index[0] = (*offset);
  761. SafeArrayPutElement(pVars,index,&var);
  762. if ( keys->fFeatures & FADF_BSTR )
  763. {
  764. SafeArrayPutElement(keys,index,val);
  765. }
  766. else
  767. {
  768. // VBScript can only use VARIANT arrays (see getItems2)
  769. _variant_t tempKey;
  770. tempKey = val;
  771. SafeArrayPutElement(keys,index,&tempKey);
  772. }
  773. var.Clear();
  774. (*offset)++;
  775. if ( *offset >= maxOffset )
  776. break; // arrays are full - stop
  777. if ( bRecurse && pObj->HasChildren() )
  778. {
  779. // Recursively do the sub-map
  780. if ( ! prefix.IsEmpty() )
  781. {
  782. BuildVariantKeyValueArray(prefix+L"."+key,"",pObj->GetChildren(),keys,pVars,offset,maxOffset,bRecurse);
  783. }
  784. else
  785. {
  786. BuildVariantKeyValueArray(key,"",pObj->GetChildren(),keys,pVars,offset,maxOffset,bRecurse);
  787. }
  788. if ( *offset >= maxOffset )
  789. break; // arrays are full - stop
  790. }
  791. }
  792. }
  793. }
  794. STDMETHODIMP CVSet::Clear()
  795. {
  796. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  797. MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::Clear");
  798. HRESULT hr = S_OK;
  799. if ( m_Restrictions & VARSET_RESTRICT_NOCHANGEDATA )
  800. {
  801. hr = E_ACCESSDENIED;
  802. }
  803. else
  804. {
  805. m_cs.Lock();
  806. m_bNeedToSave = TRUE;
  807. m_data->RemoveAll();
  808. m_data->GetData()->Clear();
  809. m_data->GetData()->ChangeType(VT_EMPTY);
  810. m_nItems = 0;
  811. m_cs.Unlock();
  812. }
  813. return hr;
  814. }
  815. //////////////IPersistStreamInit//////////////////////////////////////////////////////
  816. STDMETHODIMP CVSet::GetClassID(CLSID __RPC_FAR *pClassID)
  817. {
  818. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  819. (*pClassID) = CLSID_VarSet;
  820. return S_OK;
  821. }
  822. STDMETHODIMP CVSet::IsDirty()
  823. {
  824. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  825. if ( m_bNeedToSave )
  826. {
  827. return S_OK;
  828. }
  829. else
  830. {
  831. return S_FALSE;
  832. }
  833. }
  834. STDMETHODIMP CVSet::Load(LPSTREAM pStm)
  835. {
  836. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  837. ULONG result = 0;
  838. HRESULT hr;
  839. m_cs.Lock();
  840. do { // once
  841. hr = pStm->Read(&m_nItems,(sizeof m_nItems),&result);
  842. if ( FAILED(hr) )
  843. break;
  844. hr = pStm->Read(&m_CaseSensitive,(sizeof m_CaseSensitive),&result);
  845. if ( FAILED(hr) )
  846. break;
  847. hr = pStm->Read(&m_Indexed,(sizeof m_Indexed),&result);
  848. if ( FAILED(hr) )
  849. break;
  850. hr = m_data->ReadFromStream(pStm);
  851. m_bNeedToSave = FALSE;
  852. m_bLoaded = TRUE;
  853. }
  854. while (FALSE);
  855. m_cs.Unlock();
  856. return hr;
  857. }
  858. STDMETHODIMP CVSet::Save(LPSTREAM pStm,BOOL fClearDirty)
  859. {
  860. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  861. ULONG result = 0;
  862. HRESULT hr;
  863. m_cs.Lock();
  864. do { // once
  865. hr = pStm->Write(&m_nItems,(sizeof m_nItems),&result);
  866. if ( FAILED(hr) )
  867. break;
  868. hr = pStm->Write(&m_CaseSensitive,(sizeof m_CaseSensitive),&result);
  869. if ( FAILED(hr) )
  870. break;
  871. hr = pStm->Write(&m_Indexed,(sizeof m_Indexed),&result);
  872. if ( FAILED(hr) )
  873. break;
  874. hr = m_data->WriteToStream(pStm);
  875. if ( fClearDirty )
  876. {
  877. m_bNeedToSave = FALSE;
  878. }
  879. }while (FALSE);
  880. m_cs.Unlock();
  881. return hr;
  882. }
  883. STDMETHODIMP CVSet::GetSizeMax(ULARGE_INTEGER __RPC_FAR *pCbSize)
  884. {
  885. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  886. MC_LOGBLOCKIF(VARSET_LOGLEVEL_INTERNAL,"VarSet GetSizeMax");
  887. HRESULT hr = S_OK;
  888. if ( pCbSize == NULL )
  889. {
  890. return E_POINTER;
  891. }
  892. else
  893. {
  894. LPSTREAM pStr = NULL;
  895. DWORD rc;
  896. STATSTG stats;
  897. DWORD requiredLength = 0;
  898. rc = CreateStreamOnHGlobal(NULL,TRUE,&pStr);
  899. if ( ! rc )
  900. {
  901. hr = Save(pStr,FALSE);
  902. if (SUCCEEDED(hr) )
  903. {
  904. hr = pStr->Stat(&stats,STATFLAG_NONAME);
  905. if (SUCCEEDED(hr) )
  906. {
  907. requiredLength = stats.cbSize.LowPart;
  908. }
  909. }
  910. pStr->Release();
  911. }
  912. pCbSize->LowPart = requiredLength;
  913. MC_LOG("Size is " << McString::makeStr(requiredLength) );
  914. pCbSize->HighPart = 0;
  915. }
  916. return hr;
  917. }
  918. STDMETHODIMP CVSet::InitNew()
  919. {
  920. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  921. if ( m_bLoaded )
  922. {
  923. return E_UNEXPECTED;
  924. }
  925. else
  926. {
  927. m_cs.Lock();
  928. InitProperties();
  929. m_cs.Unlock();
  930. return S_OK;
  931. }
  932. }
  933. STDMETHODIMP CVSet::ImportSubTree(/*[in] */ BSTR key, /* [in] */ IVarSet * pVarSet)
  934. {
  935. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  936. MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::ImportSubTree");
  937. HRESULT hr = S_OK;
  938. VARIANT value;
  939. ULONG nGot;
  940. _bstr_t keyB;
  941. _bstr_t newkey;
  942. // make sure the varset is valid
  943. // enumerate the varset, inserting each item into the tree as key.item
  944. IEnumVARIANT * varEnum = NULL;
  945. IUnknown * pUnk = NULL;
  946. // TODO: need to speed this up by using getItems
  947. hr = pVarSet->get__NewEnum(&pUnk);
  948. if ( SUCCEEDED(hr) )
  949. {
  950. hr = pUnk->QueryInterface(IID_IEnumVARIANT,(void**)&varEnum);
  951. pUnk->Release();
  952. }
  953. if ( SUCCEEDED(hr))
  954. {
  955. value.vt = VT_EMPTY;
  956. while ( SUCCEEDED(hr = varEnum->Next(1,&value,&nGot)) )
  957. {
  958. if ( nGot==1 )
  959. {
  960. keyB = value.bstrVal;
  961. newkey = key;
  962. if ( newkey.length() )
  963. {
  964. newkey += _T(".");
  965. }
  966. newkey += keyB;
  967. hr = pVarSet->get(keyB,&value);
  968. if ( SUCCEEDED(hr ) )
  969. {
  970. hr = put(newkey,value);
  971. }
  972. }
  973. else
  974. {
  975. break;
  976. }
  977. if ( FAILED(hr) )
  978. break;
  979. }
  980. if ( varEnum )
  981. varEnum->Release();
  982. }
  983. varEnum = NULL;
  984. // clean up
  985. return hr;
  986. }
  987. STDMETHODIMP CVSet::getReference( /* [in] */ BSTR key, /* [out,retval] */IVarSet ** ppVarSet)
  988. {
  989. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  990. MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::getReference");
  991. HRESULT hr = S_OK;
  992. CVarData * item = GetItem(key);
  993. typedef CComObject<CVSet> myvset;
  994. myvset * pVarSet;
  995. if ( ! ppVarSet )
  996. {
  997. hr = E_POINTER;
  998. }
  999. else
  1000. {
  1001. if ( item )
  1002. {
  1003. pVarSet = new myvset;
  1004. AddRef();
  1005. ((CVSet*)pVarSet)->SetData(this,item,m_Restrictions);
  1006. hr = pVarSet->QueryInterface(IID_IVarSet,(void**)ppVarSet);
  1007. }
  1008. else
  1009. {
  1010. hr = TYPE_E_ELEMENTNOTFOUND;
  1011. }
  1012. }
  1013. return hr;
  1014. }
  1015. STDMETHODIMP CVSet::DumpToFile(BSTR filename)
  1016. {
  1017. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  1018. HRESULT hr = S_OK;
  1019. #ifdef STRIPPED_VARSET
  1020. USES_CONVERSION;
  1021. TError errLog;
  1022. errLog.LogOpen((WCHAR*)filename,1,0);
  1023. errLog.MsgWrite(0,L"VarSet");
  1024. errLog.MsgWrite(0,L"Case Sensitive: %s, Indexed: %s",(m_CaseSensitive ? L"Yes" : L"No"),(m_Indexed ? L"Yes" : L"No") );
  1025. errLog.MsgWrite(0,L"User Data ( %ld ) items",m_nItems);
  1026. #else
  1027. #endif
  1028. m_cs.Lock();
  1029. CVarData * map = m_data;
  1030. CString start;
  1031. CString seg;
  1032. // Build an array of variants to hold the keys
  1033. CComVariant * pVars = new CComVariant[m_data->CountItems()+1];
  1034. CString key;
  1035. int offset = 1;
  1036. key = _T("");
  1037. if (!pVars)
  1038. {
  1039. m_cs.Unlock();
  1040. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  1041. }
  1042. // include the root item in the list
  1043. pVars[0] = key;
  1044. if ( map->HasChildren() )
  1045. {
  1046. BuildVariantKeyArray(key,map->GetChildren(),pVars,&offset);
  1047. }
  1048. m_cs.Unlock();
  1049. if ( ! m_Indexed )
  1050. {
  1051. // Sort the results
  1052. qsort(pVars,offset,(sizeof CComVariant),&SortComVariantStrings);
  1053. }
  1054. for ( int i = 0 ; i < offset ; i++ )
  1055. {
  1056. CVarData * data;
  1057. CString value;
  1058. CString key;
  1059. key = pVars[i].bstrVal;
  1060. data = GetItem(key);
  1061. if ( data )
  1062. {
  1063. switch ( data->GetData()->vt )
  1064. {
  1065. case VT_EMPTY:
  1066. value = _T("<Empty>");
  1067. break;
  1068. case VT_NULL:
  1069. value = _T("<Null>");
  1070. break;
  1071. case VT_I2:
  1072. value.Format(_T("%ld"),data->GetData()->iVal);
  1073. break;
  1074. case VT_I4:
  1075. value.Format(_T("%ld"),data->GetData()->lVal);
  1076. break;
  1077. case VT_BSTR:
  1078. value = data->GetData()->bstrVal;
  1079. break;
  1080. default:
  1081. value.Format(_T("variant type=0x%lx"),data->GetData()->vt);
  1082. break;
  1083. }
  1084. #ifdef STRIPPED_VARSET
  1085. errLog.MsgWrite(0,L" [%ls] %ls",key.GetBuffer(0),value.GetBuffer(0));
  1086. #else
  1087. #endif
  1088. }
  1089. else
  1090. {
  1091. #ifdef STRIPPED_VARSET
  1092. errLog.MsgWrite(0,L" [%ls] <No Value>",key.GetBuffer(0));
  1093. #else
  1094. #endif
  1095. }
  1096. }
  1097. delete [] pVars;
  1098. return hr;
  1099. }
  1100. STDMETHODIMP CVSet::get_Indexed(BOOL *pVal)
  1101. {
  1102. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  1103. MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::get_Indexed");
  1104. if ( pVal == NULL )
  1105. return E_POINTER;
  1106. m_cs.Lock();
  1107. (*pVal) = m_Indexed;
  1108. m_cs.Unlock();
  1109. return S_OK;
  1110. }
  1111. STDMETHODIMP CVSet::put_Indexed(BOOL newVal)
  1112. {
  1113. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  1114. MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::put_Indexed");
  1115. HRESULT hr = S_OK;
  1116. if ( m_Restrictions & VARSET_RESTRICT_NOCHANGEPROPS )
  1117. {
  1118. hr = E_ACCESSDENIED;
  1119. }
  1120. else
  1121. {
  1122. m_cs.Lock();
  1123. m_bNeedToSave = TRUE;
  1124. m_Indexed = newVal;
  1125. m_data->SetIndexed(m_Indexed);
  1126. m_cs.Unlock();
  1127. }
  1128. return hr;
  1129. }
  1130. STDMETHODIMP CVSet::get_AllowRehashing(BOOL *pVal)
  1131. {
  1132. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  1133. MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::get_AllowRehashing");
  1134. if ( pVal == NULL )
  1135. return E_POINTER;
  1136. m_cs.Lock();
  1137. (*pVal) = m_AllowRehashing;
  1138. m_cs.Unlock();
  1139. return S_OK;
  1140. }
  1141. STDMETHODIMP CVSet::put_AllowRehashing(BOOL newVal)
  1142. {
  1143. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  1144. MC_LOGBLOCKIF(VARSET_LOGLEVEL_CLIENT,"CVSet::put_AllowRehashing");
  1145. HRESULT hr = S_OK;
  1146. if ( m_Restrictions & VARSET_RESTRICT_NOCHANGEPROPS )
  1147. {
  1148. hr = E_ACCESSDENIED;
  1149. }
  1150. else
  1151. {
  1152. m_cs.Lock();
  1153. m_bNeedToSave = TRUE;
  1154. m_AllowRehashing = newVal;
  1155. m_data->SetAllowRehashing(newVal);
  1156. m_cs.Unlock();
  1157. }
  1158. return hr;
  1159. }
  1160. STDMETHODIMP CVSet::get_Restrictions(DWORD * restrictions)
  1161. {
  1162. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  1163. HRESULT hr = S_OK;
  1164. if ( restrictions == NULL )
  1165. {
  1166. hr = E_POINTER;
  1167. }
  1168. else
  1169. {
  1170. (*restrictions) = m_Restrictions;
  1171. }
  1172. return hr;
  1173. }
  1174. STDMETHODIMP CVSet::put_Restrictions(DWORD newRestrictions)
  1175. {
  1176. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  1177. HRESULT hr = S_OK;
  1178. DWORD rAdding = newRestrictions & ~m_Restrictions;
  1179. DWORD rRemoving = ~newRestrictions & m_Restrictions;
  1180. // Can't remove any restrictions passed down from parent.
  1181. if ( ( rRemoving & m_ImmutableRestrictions) )
  1182. {
  1183. hr = E_ACCESSDENIED;
  1184. }
  1185. else if ( rAdding & ! VARSET_RESTRICT_ALL )
  1186. {
  1187. hr = E_NOTIMPL;
  1188. }
  1189. else
  1190. {
  1191. // the change is OK.
  1192. m_Restrictions = newRestrictions;
  1193. }
  1194. return hr;
  1195. }
  1196. // IMarshal implemention
  1197. // This marshals the varset to a stream that is then sent across the wire
  1198. STDMETHODIMP CVSet::GetUnmarshalClass(REFIID riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, CLSID *pCid)
  1199. {
  1200. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  1201. *pCid = GetObjectCLSID();
  1202. return S_OK;
  1203. }
  1204. STDMETHODIMP CVSet::GetMarshalSizeMax(REFIID riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, DWORD *pSize)
  1205. {
  1206. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  1207. HRESULT hr = S_OK;
  1208. ULARGE_INTEGER uli;
  1209. hr = GetSizeMax(&uli);
  1210. if (SUCCEEDED(hr))
  1211. {
  1212. *pSize = uli.LowPart;
  1213. }
  1214. return hr;
  1215. }
  1216. STDMETHODIMP CVSet::MarshalInterface(IStream *pStm, REFIID riid, void *pv, DWORD dwDestContext, void *pvDestCtx, DWORD mshlflags)
  1217. {
  1218. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  1219. HRESULT hr = S_OK;
  1220. // Save the varset's data to a stream
  1221. hr = Save(pStm, FALSE);
  1222. return hr;
  1223. }
  1224. STDMETHODIMP CVSet::UnmarshalInterface(IStream *pStm, REFIID riid, void **ppv)
  1225. {
  1226. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  1227. HRESULT hr = S_OK;
  1228. // Load up the data from the stream using our IPersistStream implementation
  1229. hr = Load(pStm);
  1230. if ( SUCCEEDED(hr) )
  1231. {
  1232. hr = QueryInterface(riid,ppv);
  1233. }
  1234. return hr;
  1235. }
  1236. STDMETHODIMP CVSet::ReleaseMarshalData(IStream * /*pStmNotNeeded*/)
  1237. {
  1238. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  1239. // we don't keep any state data, so there's nothing to release
  1240. // since we just read the object from the stream, the stream's pointer should already be at the end,
  1241. // so there's nothing left for us to do here
  1242. return S_OK;
  1243. }
  1244. STDMETHODIMP CVSet::DisconnectObject(DWORD /*dwReservedNotUsed*/)
  1245. {
  1246. AFX_MANAGE_STATE(AfxGetStaticModuleState())
  1247. return S_OK;
  1248. }