Source code of Windows XP (NT5)
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.

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