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.

1470 lines
36 KiB

  1. /*--------------------------------------------------------------------------*
  2. *
  3. * Microsoft Windows
  4. * Copyright (C) Microsoft Corporation, 1992 - 1999
  5. *
  6. * File: strtable.cpp
  7. *
  8. * Contents: Implementation file for CStringTable
  9. *
  10. * History: 25-Jun-98 jeffro Created
  11. *
  12. *--------------------------------------------------------------------------*/
  13. #include "stdafx.h"
  14. #include "strtable.h"
  15. #include "macros.h"
  16. #include "comdbg.h"
  17. #include "amcdoc.h"
  18. // {71E5B33E-1064-11d2-808F-0000F875A9CE}
  19. const CLSID CLSID_MMC =
  20. { 0x71e5b33e, 0x1064, 0x11d2, { 0x80, 0x8f, 0x0, 0x0, 0xf8, 0x75, 0xa9, 0xce } };
  21. const WCHAR CMasterStringTable::s_pszIDPoolStream[] = L"ID Pool";
  22. const WCHAR CMasterStringTable::s_pszStringsStream[] = L"Strings";
  23. #ifdef DBG
  24. CTraceTag tagStringTable (_T("StringTable"), _T("StringTable"));
  25. #endif // DBG
  26. /*+-------------------------------------------------------------------------*
  27. * IsBadString
  28. *
  29. *
  30. *--------------------------------------------------------------------------*/
  31. inline static bool IsBadString (LPCWSTR psz)
  32. {
  33. if (psz == NULL)
  34. return (true);
  35. return (::IsBadStringPtrW (psz, -1) != 0);
  36. }
  37. /*+-------------------------------------------------------------------------*
  38. * TStringFromCLSID
  39. *
  40. *
  41. *--------------------------------------------------------------------------*/
  42. static LPTSTR TStringFromCLSID (LPTSTR pszClsid, const CLSID& clsid)
  43. {
  44. const int cchClass = 40;
  45. #ifdef UNICODE
  46. StringFromGUID2 (clsid, pszClsid, cchClass);
  47. #else
  48. USES_CONVERSION;
  49. WCHAR wzClsid[cchClass];
  50. StringFromGUID2 (clsid, wzClsid, cchClass);
  51. _tcscpy (pszClsid, W2T (wzClsid));
  52. #endif
  53. return (pszClsid);
  54. }
  55. /*+-------------------------------------------------------------------------*
  56. * operator>>
  57. *
  58. *
  59. *--------------------------------------------------------------------------*/
  60. inline IStream& operator>> (IStream& stm, CEntry& entry)
  61. {
  62. return (stm >> entry.m_id >> entry.m_cRefs >> entry.m_str);
  63. }
  64. /*+-------------------------------------------------------------------------*
  65. * operator<<
  66. *
  67. * Writes a CEntry to a stream. The format is:
  68. *
  69. * DWORD string ID
  70. * DWORD reference count
  71. * DWORD string length (character count)
  72. * WCHAR[] characters in the strings, *not* NULL-terminated
  73. *
  74. *--------------------------------------------------------------------------*/
  75. inline IStream& operator<< (IStream& stm, const CEntry& entry)
  76. {
  77. return (stm << entry.m_id << entry.m_cRefs << entry.m_str);
  78. }
  79. /*+-------------------------------------------------------------------------*
  80. * CEntry::Persist
  81. *
  82. *
  83. *--------------------------------------------------------------------------*/
  84. void CEntry::Persist(CPersistor &persistor)
  85. {
  86. persistor.PersistAttribute(XML_ATTR_STRING_TABLE_STR_ID, m_id);
  87. persistor.PersistAttribute(XML_ATTR_STRING_TABLE_STR_REFS, m_cRefs);
  88. persistor.PersistContents(m_str);
  89. }
  90. /*+-------------------------------------------------------------------------*
  91. * CEntry::Dump
  92. *
  93. *
  94. *--------------------------------------------------------------------------*/
  95. #ifdef DBG
  96. void CEntry::Dump () const
  97. {
  98. USES_CONVERSION;
  99. Trace (tagStringTable, _T("id=%d, refs=%d, string=\"%s\""),
  100. m_id, m_cRefs, W2CT (m_str.data()));
  101. }
  102. #endif
  103. /*+-------------------------------------------------------------------------*
  104. * CMasterStringTable::CMasterStringTable
  105. *
  106. * Even though a MMC_STRING_ID is a DWORD, we want to make sure the high
  107. * word is 0, to keep open the possibility that we can use something like
  108. * MAKEINTRESOURCE in the future. To do this, set USHRT_MAX as the
  109. * maximum string ID.
  110. *--------------------------------------------------------------------------*/
  111. CMasterStringTable::CMasterStringTable ()
  112. : m_IDPool (1, USHRT_MAX)
  113. {
  114. }
  115. /*+-------------------------------------------------------------------------*
  116. * CMasterStringTable::~CMasterStringTable
  117. *
  118. *
  119. *--------------------------------------------------------------------------*/
  120. CMasterStringTable::~CMasterStringTable ()
  121. {
  122. }
  123. /*+-------------------------------------------------------------------------*
  124. * CMasterStringTable::AddString
  125. *
  126. *
  127. *--------------------------------------------------------------------------*/
  128. STDMETHODIMP CMasterStringTable::AddString (
  129. LPCOLESTR pszAdd,
  130. MMC_STRING_ID* pID,
  131. const CLSID* pclsid)
  132. {
  133. if (pclsid == NULL)
  134. pclsid = &CLSID_MMC;
  135. if (IsBadReadPtr (pclsid, sizeof(*pclsid)))
  136. return (E_INVALIDARG);
  137. CStringTable* pStringTable = LookupStringTableByCLSID (pclsid);
  138. /*
  139. * If this the first string added for this CLSID,
  140. * we need to create a new string table.
  141. */
  142. if (pStringTable == NULL)
  143. {
  144. CStringTable table (&m_IDPool);
  145. TableMapValue value (*pclsid, table);
  146. CLSIDToStringTableMap::_Pairib rc = m_TableMap.insert (value);
  147. /*
  148. * we should have actually inserted the new table
  149. */
  150. ASSERT (rc.second);
  151. pStringTable = &(rc.first->second);
  152. ASSERT (pStringTable != NULL);
  153. }
  154. HRESULT hr = pStringTable->AddString (pszAdd, pID);
  155. #ifdef DBG
  156. if (SUCCEEDED (hr))
  157. {
  158. USES_CONVERSION;
  159. TCHAR szClsid[40];
  160. Trace (tagStringTable, _T("Added \"%s\" (id=%d) for %s"),
  161. W2CT(pszAdd), (int) *pID, TStringFromCLSID (szClsid, *pclsid));
  162. Dump();
  163. }
  164. #endif
  165. return (hr);
  166. }
  167. /*+-------------------------------------------------------------------------*
  168. * CMasterStringTable::GetString
  169. *
  170. *
  171. *--------------------------------------------------------------------------*/
  172. STDMETHODIMP CMasterStringTable::GetString (
  173. MMC_STRING_ID id,
  174. ULONG cchBuffer,
  175. LPOLESTR lpBuffer,
  176. ULONG* pcchOut,
  177. const CLSID* pclsid)
  178. {
  179. if (pclsid == NULL)
  180. pclsid = &CLSID_MMC;
  181. if (IsBadReadPtr (pclsid, sizeof(*pclsid)))
  182. return (E_INVALIDARG);
  183. CStringTable* pStringTable = LookupStringTableByCLSID (pclsid);
  184. if (pStringTable == NULL)
  185. return (E_FAIL);
  186. return (pStringTable->GetString (id, cchBuffer, lpBuffer, pcchOut));
  187. }
  188. /*+-------------------------------------------------------------------------*
  189. * CMasterStringTable::GetStringLength
  190. *
  191. *
  192. *--------------------------------------------------------------------------*/
  193. STDMETHODIMP CMasterStringTable::GetStringLength (
  194. MMC_STRING_ID id,
  195. ULONG* pcchString,
  196. const CLSID* pclsid)
  197. {
  198. if (pclsid == NULL)
  199. pclsid = &CLSID_MMC;
  200. if (IsBadReadPtr (pclsid, sizeof(*pclsid)))
  201. return (E_INVALIDARG);
  202. CStringTable* pStringTable = LookupStringTableByCLSID (pclsid);
  203. if (pStringTable == NULL)
  204. return (E_FAIL);
  205. return (pStringTable->GetStringLength (id, pcchString));
  206. }
  207. /*+-------------------------------------------------------------------------*
  208. * CMasterStringTable::DeleteString
  209. *
  210. *
  211. *--------------------------------------------------------------------------*/
  212. STDMETHODIMP CMasterStringTable::DeleteString (
  213. MMC_STRING_ID id,
  214. const CLSID* pclsid)
  215. {
  216. if (pclsid == NULL)
  217. pclsid = &CLSID_MMC;
  218. if (IsBadReadPtr (pclsid, sizeof(*pclsid)))
  219. return (E_INVALIDARG);
  220. CStringTable* pStringTable = LookupStringTableByCLSID (pclsid);
  221. if (pStringTable == NULL)
  222. return (E_FAIL);
  223. HRESULT hr = pStringTable->DeleteString (id);
  224. TCHAR szClsid[40];
  225. Trace (tagStringTable, _T("Deleted string %d for %s"), (int) id, TStringFromCLSID (szClsid, *pclsid));
  226. Dump();
  227. return (hr);
  228. }
  229. /*+-------------------------------------------------------------------------*
  230. * CMasterStringTable::DeleteAllStrings
  231. *
  232. *
  233. *--------------------------------------------------------------------------*/
  234. STDMETHODIMP CMasterStringTable::DeleteAllStrings (
  235. const CLSID* pclsid)
  236. {
  237. if (pclsid == NULL)
  238. pclsid = &CLSID_MMC;
  239. if (IsBadReadPtr (pclsid, sizeof(*pclsid)))
  240. return (E_INVALIDARG);
  241. CStringTable* pStringTable = LookupStringTableByCLSID (pclsid);
  242. if (pStringTable == NULL)
  243. return (E_FAIL);
  244. #include "pushwarn.h"
  245. #pragma warning(disable: 4553) // "==" operator has no effect
  246. VERIFY (pStringTable->DeleteAllStrings () == S_OK);
  247. VERIFY (m_TableMap.erase (*pclsid) == 1);
  248. #include "popwarn.h"
  249. TCHAR szClsid[40];
  250. Trace (tagStringTable, _T("Deleted all strings for %s"), TStringFromCLSID (szClsid, *pclsid));
  251. Dump();
  252. return (S_OK);
  253. }
  254. /*+-------------------------------------------------------------------------*
  255. * CMasterStringTable::FindString
  256. *
  257. *
  258. *--------------------------------------------------------------------------*/
  259. STDMETHODIMP CMasterStringTable::FindString (
  260. LPCOLESTR pszFind,
  261. MMC_STRING_ID* pID,
  262. const CLSID* pclsid)
  263. {
  264. if (pclsid == NULL)
  265. pclsid = &CLSID_MMC;
  266. if (IsBadReadPtr (pclsid, sizeof(*pclsid)))
  267. return (E_INVALIDARG);
  268. CStringTable* pStringTable = LookupStringTableByCLSID (pclsid);
  269. if (pStringTable == NULL)
  270. return (E_FAIL);
  271. return (pStringTable->FindString (pszFind, pID));
  272. }
  273. /*+-------------------------------------------------------------------------*
  274. * CMasterStringTable::Enumerate
  275. *
  276. *
  277. *--------------------------------------------------------------------------*/
  278. STDMETHODIMP CMasterStringTable::Enumerate (
  279. IEnumString** ppEnum,
  280. const CLSID* pclsid)
  281. {
  282. if (pclsid == NULL)
  283. pclsid = &CLSID_MMC;
  284. if (IsBadReadPtr (pclsid, sizeof(*pclsid)))
  285. return (E_INVALIDARG);
  286. CStringTable* pStringTable = LookupStringTableByCLSID (pclsid);
  287. if (pStringTable == NULL)
  288. return (E_FAIL);
  289. return (pStringTable->Enumerate (ppEnum));
  290. }
  291. /*+-------------------------------------------------------------------------*
  292. * CMasterStringTable::LookupStringTableByCLSID
  293. *
  294. * Returns a pointer to the string table for a given CLSID, or NULL if
  295. * there isn't a corresponding string in the string table.
  296. *--------------------------------------------------------------------------*/
  297. CStringTable* CMasterStringTable::LookupStringTableByCLSID (const CLSID* pclsid) const
  298. {
  299. CLSIDToStringTableMap::iterator it = m_TableMap.find (*pclsid);
  300. if (it == m_TableMap.end())
  301. return (NULL);
  302. return (&it->second);
  303. }
  304. /*+-------------------------------------------------------------------------*
  305. * operator>>
  306. *
  307. * Reads a CMasterStringTable from a storage.
  308. *--------------------------------------------------------------------------*/
  309. IStorage& operator>> (IStorage& stg, CMasterStringTable& mst)
  310. {
  311. DECLARE_SC (sc, _T("operator>> (IStorage& stg, CMasterStringTable& mst)"));
  312. HRESULT hr;
  313. IStreamPtr spStream;
  314. /*
  315. * read the available IDs
  316. */
  317. hr = OpenDebugStream (&stg, CMasterStringTable::s_pszIDPoolStream,
  318. STGM_SHARE_EXCLUSIVE | STGM_READ,
  319. &spStream);
  320. THROW_ON_FAIL (hr);
  321. spStream >> mst.m_IDPool;
  322. /*
  323. * read the CLSIDs and the strings
  324. */
  325. hr = OpenDebugStream (&stg, CMasterStringTable::s_pszStringsStream,
  326. STGM_SHARE_EXCLUSIVE | STGM_READ,
  327. &spStream);
  328. THROW_ON_FAIL (hr);
  329. #if 1
  330. /*
  331. * clear out the current table
  332. */
  333. mst.m_TableMap.clear();
  334. /*
  335. * read the CLSID count
  336. */
  337. DWORD cClasses;
  338. *spStream >> cClasses;
  339. while (cClasses-- > 0)
  340. {
  341. /*
  342. * read the CLSID...
  343. */
  344. CLSID clsid;
  345. spStream >> clsid;
  346. /*
  347. * ...and the string table
  348. */
  349. CStringTable table (&mst.m_IDPool, spStream);
  350. /*
  351. * insert the string table into the CLSID map
  352. */
  353. TableMapValue value (clsid, table);
  354. VERIFY (mst.m_TableMap.insert(value).second);
  355. }
  356. #else
  357. /*
  358. * Can't use this because there's no default ctor for CStringTable
  359. */
  360. *spStream >> mst.m_TableMap;
  361. #endif
  362. /*
  363. * Generate the list of stale IDs.
  364. */
  365. sc = mst.ScGenerateIDPool ();
  366. if (sc)
  367. return (stg);
  368. mst.Dump();
  369. return (stg);
  370. }
  371. /*+-------------------------------------------------------------------------*
  372. * CMasterStringTable::ScGenerateIDPool
  373. *
  374. * Generates the list of stale string IDs for this CMasterStringTable.
  375. * The set of stale IDs is the entire set of IDs, minus the available IDs,
  376. * minus the in-use IDs.
  377. *--------------------------------------------------------------------------*/
  378. SC CMasterStringTable::ScGenerateIDPool ()
  379. {
  380. /*
  381. * Step 1: build up a RangeList of the in-use IDs
  382. */
  383. DECLARE_SC (sc, _T("CMasterStringTable::ScGenerateIDPool"));
  384. CStringIDPool::RangeList lInUseIDs;
  385. CLSIDToStringTableMap::const_iterator itTable;
  386. for (itTable = m_TableMap.begin(); itTable != m_TableMap.end(); ++itTable)
  387. {
  388. const CStringTable& st = itTable->second;
  389. sc = st.ScCollectInUseIDs (lInUseIDs);
  390. if (sc)
  391. return (sc);
  392. }
  393. /*
  394. * Step 2: give the in-use IDs to the ID pool so it can merge it
  395. * with the available IDs (which it already has) to generate the
  396. * list of stale IDs
  397. */
  398. sc = m_IDPool.ScGenerate (lInUseIDs);
  399. if (sc)
  400. return (sc);
  401. return (sc);
  402. }
  403. /*+-------------------------------------------------------------------------*
  404. *
  405. * CMasterStringTable::Persist
  406. *
  407. * PURPOSE: persists the CMasterStringTable object to the specified persistor.
  408. *
  409. * PARAMETERS:
  410. * CPersistor & persistor :
  411. *
  412. * RETURNS:
  413. * void
  414. *
  415. *+-------------------------------------------------------------------------*/
  416. void
  417. CMasterStringTable::Persist(CPersistor & persistor)
  418. {
  419. DECLARE_SC(sc, TEXT("CMasterStringTable::Persist"));
  420. // purge unused snapins not to save what's already gone
  421. sc = ScPurgeUnusedStrings();
  422. if (sc)
  423. sc.Throw();
  424. persistor.Persist(m_IDPool);
  425. m_TableMap.PersistSelf(&m_IDPool, persistor);
  426. if (persistor.IsLoading())
  427. ScGenerateIDPool ();
  428. }
  429. /***************************************************************************\
  430. *
  431. * METHOD: CMasterStringTable::ScPurgeUnusedStrings
  432. *
  433. * PURPOSE: removes entries for snapins what aren't in use anymore
  434. *
  435. * PARAMETERS:
  436. *
  437. * RETURNS:
  438. * SC - result code
  439. *
  440. \***************************************************************************/
  441. SC CMasterStringTable::ScPurgeUnusedStrings()
  442. {
  443. DECLARE_SC(sc, TEXT("CMasterStringTable::ScPurgeUnusedStrings"));
  444. // det to the currfent document
  445. CAMCDoc* pAMCDoc = CAMCDoc::GetDocument();
  446. sc = ScCheckPointers(pAMCDoc, E_UNEXPECTED);
  447. if (sc)
  448. return sc;
  449. // get the access to scope tree
  450. IScopeTree *pScopeTree = pAMCDoc->GetScopeTree();
  451. sc = ScCheckPointers(pScopeTree, E_UNEXPECTED);
  452. if (sc)
  453. return sc;
  454. // now iterate thru entries removing those belonging
  455. // to snapins already gone.
  456. CLSIDToStringTableMap::iterator it = m_TableMap.begin();
  457. while (it != m_TableMap.end())
  458. {
  459. // special case for internal guid
  460. if (IsEqualGUID(it->first, CLSID_MMC))
  461. {
  462. ++it; // simply skip own stuff
  463. }
  464. else
  465. {
  466. // ask the scope tree if snapin is in use
  467. BOOL bInUse = FALSE;
  468. sc = pScopeTree->IsSnapinInUse(it->first, &bInUse);
  469. if (sc)
  470. return sc;
  471. // act depending on usage
  472. if (bInUse)
  473. {
  474. ++it; // skip also the stuff currently in use
  475. }
  476. else
  477. {
  478. // to the trash can
  479. sc = it->second.DeleteAllStrings();
  480. if (sc)
  481. return sc;
  482. it = m_TableMap.erase(it);
  483. }
  484. }
  485. }
  486. return sc;
  487. }
  488. /*+-------------------------------------------------------------------------*
  489. * operator<<
  490. *
  491. * Writes a CMasterStringTable to a storage.
  492. *
  493. * It is written into two streams: "ID Pool" and "Strings".
  494. *
  495. * "ID Pool" contains the list of available string IDs remaining in the
  496. * string table. Its format is defined by CIdentifierPool.
  497. *
  498. * "Strings" contains the strings. The format is:
  499. *
  500. * DWORD count of string tables
  501. * [n string tables]
  502. *
  503. * The format for each string is defined by operator<<(TableMapValue).
  504. *--------------------------------------------------------------------------*/
  505. IStorage& operator<< (IStorage& stg, const CMasterStringTable& mst)
  506. {
  507. HRESULT hr;
  508. IStreamPtr spStream;
  509. /*
  510. * write the available IDs
  511. */
  512. hr = CreateDebugStream (&stg, CMasterStringTable::s_pszIDPoolStream,
  513. STGM_SHARE_EXCLUSIVE | STGM_CREATE | STGM_WRITE,
  514. &spStream);
  515. THROW_ON_FAIL (hr);
  516. spStream << mst.m_IDPool;
  517. /*
  518. * write the string tables
  519. */
  520. hr = CreateDebugStream (&stg, CMasterStringTable::s_pszStringsStream,
  521. STGM_SHARE_EXCLUSIVE | STGM_CREATE | STGM_WRITE,
  522. &spStream);
  523. THROW_ON_FAIL (hr);
  524. *spStream << mst.m_TableMap;
  525. return (stg);
  526. }
  527. /*+-------------------------------------------------------------------------*
  528. * CMasterStringTable::Dump
  529. *
  530. *
  531. *--------------------------------------------------------------------------*/
  532. #ifdef DBG
  533. void CMasterStringTable::Dump () const
  534. {
  535. Trace (tagStringTable, _T("Contents of CMasterStringTable at 0x08%x"), this);
  536. m_IDPool.Dump();
  537. CLSIDToStringTableMap::const_iterator it;
  538. for (it = m_TableMap.begin(); it != m_TableMap.end(); ++it)
  539. {
  540. TCHAR szClsid[40];
  541. const CLSID& clsid = it->first;
  542. const CStringTable& st = it->second;
  543. Trace (tagStringTable, _T("%d strings for %s:"),
  544. st.size(), TStringFromCLSID (szClsid, clsid));
  545. st.Dump();
  546. }
  547. }
  548. #endif
  549. /*+-------------------------------------------------------------------------*
  550. * CStringTable::CStringTable
  551. *
  552. *
  553. *--------------------------------------------------------------------------*/
  554. CStringTable::CStringTable (CStringIDPool* pIDPool)
  555. : m_pIDPool (pIDPool),
  556. CStringTable_base(m_Entries, XML_TAG_STRING_TABLE)
  557. {
  558. ASSERT_VALID_(this);
  559. }
  560. CStringTable::CStringTable (CStringIDPool* pIDPool, IStream& stm)
  561. : m_pIDPool (pIDPool),
  562. CStringTable_base(m_Entries, XML_TAG_STRING_TABLE)
  563. {
  564. stm >> *this;
  565. ASSERT_VALID_(this);
  566. }
  567. /*+-------------------------------------------------------------------------*
  568. * CStringTable::~CStringTable
  569. *
  570. *
  571. *--------------------------------------------------------------------------*/
  572. CStringTable::~CStringTable ()
  573. {
  574. }
  575. /*+-------------------------------------------------------------------------*
  576. * CStringTable::CStringTable
  577. *
  578. * Copy constructor
  579. *--------------------------------------------------------------------------*/
  580. CStringTable::CStringTable (const CStringTable& other)
  581. : m_Entries (other.m_Entries),
  582. m_pIDPool (other.m_pIDPool),
  583. CStringTable_base(m_Entries, XML_TAG_STRING_TABLE)
  584. {
  585. ASSERT_VALID_(&other);
  586. IndexAllEntries ();
  587. ASSERT_VALID_(this);
  588. }
  589. /*+-------------------------------------------------------------------------*
  590. * CStringTable::operator=
  591. *
  592. * Assignment operator
  593. *--------------------------------------------------------------------------*/
  594. CStringTable& CStringTable::operator= (const CStringTable& other)
  595. {
  596. ASSERT_VALID_(&other);
  597. if (&other != this)
  598. {
  599. m_Entries = other.m_Entries;
  600. m_pIDPool = other.m_pIDPool;
  601. IndexAllEntries ();
  602. }
  603. ASSERT_VALID_(this);
  604. return (*this);
  605. }
  606. /*+-------------------------------------------------------------------------*
  607. * CStringTable::AddString
  608. *
  609. *
  610. *--------------------------------------------------------------------------*/
  611. STDMETHODIMP CStringTable::AddString (
  612. LPCOLESTR pszAdd,
  613. MMC_STRING_ID* pID)
  614. {
  615. /*
  616. * validate the parameters
  617. */
  618. if (IsBadString (pszAdd))
  619. return (E_INVALIDARG);
  620. if (IsBadWritePtr (pID, sizeof (*pID)))
  621. return (E_INVALIDARG);
  622. std::wstring strAdd = pszAdd;
  623. /*
  624. * check to see if there's already an entry for this string
  625. */
  626. EntryList::iterator itEntry = LookupEntryByString (strAdd);
  627. /*
  628. * if there's not an entry for this string, add one
  629. */
  630. if (itEntry == m_Entries.end())
  631. {
  632. /*
  633. * add the entry to the list
  634. */
  635. try
  636. {
  637. CEntry EntryToInsert (strAdd, m_pIDPool->Reserve());
  638. itEntry = m_Entries.insert (FindInsertionPointForEntry (EntryToInsert),
  639. EntryToInsert);
  640. ASSERT (itEntry->m_cRefs == 0);
  641. }
  642. catch (CStringIDPool::pool_exhausted&)
  643. {
  644. return (E_OUTOFMEMORY);
  645. }
  646. /*
  647. * add the new entry to the indices
  648. */
  649. IndexEntry (itEntry);
  650. }
  651. /*
  652. * Bump the ref count for this string. The ref count for
  653. * new strings is 0, so we won't have ref counting problems.
  654. */
  655. ASSERT (itEntry != m_Entries.end());
  656. itEntry->m_cRefs++;
  657. *pID = itEntry->m_id;
  658. ASSERT_VALID_(this);
  659. return (S_OK);
  660. }
  661. /*+-------------------------------------------------------------------------*
  662. * CStringTable::GetString
  663. *
  664. *
  665. *--------------------------------------------------------------------------*/
  666. STDMETHODIMP CStringTable::GetString (
  667. MMC_STRING_ID id,
  668. ULONG cchBuffer,
  669. LPOLESTR lpBuffer,
  670. ULONG* pcchOut) const
  671. {
  672. ASSERT_VALID_(this);
  673. /*
  674. * validate the parameters
  675. */
  676. if (cchBuffer == 0)
  677. return (E_INVALIDARG);
  678. if (IsBadWritePtr (lpBuffer, cchBuffer * sizeof (*lpBuffer)))
  679. return (E_INVALIDARG);
  680. if ((pcchOut != NULL) && IsBadWritePtr (pcchOut, sizeof (*pcchOut)))
  681. return (E_INVALIDARG);
  682. /*
  683. * find the entry for this string ID
  684. */
  685. EntryList::iterator itEntry = LookupEntryByID (id);
  686. if (itEntry == m_Entries.end())
  687. return (E_FAIL);
  688. /*
  689. * copy to the user's buffer and make sure it's terminated
  690. */
  691. wcsncpy (lpBuffer, itEntry->m_str.data(), cchBuffer);
  692. lpBuffer[cchBuffer-1] = 0;
  693. /*
  694. * if the caller wants the write count, give it to him
  695. */
  696. if ( pcchOut != NULL)
  697. *pcchOut = wcslen (lpBuffer);
  698. return (S_OK);
  699. }
  700. /*+-------------------------------------------------------------------------*
  701. * CStringTable::GetStringLength
  702. *
  703. *
  704. *--------------------------------------------------------------------------*/
  705. STDMETHODIMP CStringTable::GetStringLength (
  706. MMC_STRING_ID id,
  707. ULONG* pcchString) const
  708. {
  709. ASSERT_VALID_(this);
  710. /*
  711. * validate the parameters
  712. */
  713. if (IsBadWritePtr (pcchString, sizeof (*pcchString)))
  714. return (E_INVALIDARG);
  715. /*
  716. * find the entry for this string ID
  717. */
  718. EntryList::iterator itEntry = LookupEntryByID (id);
  719. if (itEntry == m_Entries.end())
  720. return (E_FAIL);
  721. *pcchString = itEntry->m_str.length();
  722. return (S_OK);
  723. }
  724. /*+-------------------------------------------------------------------------*
  725. * CStringTable::DeleteString
  726. *
  727. *
  728. *--------------------------------------------------------------------------*/
  729. STDMETHODIMP CStringTable::DeleteString (
  730. MMC_STRING_ID id)
  731. {
  732. /*
  733. * find the entry for this string ID
  734. */
  735. EntryList::iterator itEntry = LookupEntryByID (id);
  736. if (itEntry == m_Entries.end())
  737. return (E_FAIL);
  738. /*
  739. * Decrement the ref count. If it goes to zero, we can remove the
  740. * string entirely.
  741. */
  742. if (--itEntry->m_cRefs == 0)
  743. {
  744. /*
  745. * remove the string from the indices
  746. */
  747. m_StringIndex.erase (itEntry->m_str);
  748. m_IDIndex.erase (itEntry->m_id);
  749. /*
  750. * return the string ID to the ID pool and remove the entry
  751. */
  752. VERIFY (m_pIDPool->Release (itEntry->m_id));
  753. m_Entries.erase (itEntry);
  754. }
  755. ASSERT_VALID_(this);
  756. return (S_OK);
  757. }
  758. /*+-------------------------------------------------------------------------*
  759. * CStringTable::DeleteAllStrings
  760. *
  761. *
  762. *--------------------------------------------------------------------------*/
  763. STDMETHODIMP CStringTable::DeleteAllStrings ()
  764. {
  765. /*
  766. * return all string IDs to the ID pool
  767. */
  768. std::for_each (m_Entries.begin(), m_Entries.end(),
  769. IdentifierReleaser (*m_pIDPool));
  770. /*
  771. * wipe everything clean
  772. */
  773. m_Entries.clear ();
  774. m_StringIndex.clear ();
  775. m_IDIndex.clear ();
  776. ASSERT_VALID_(this);
  777. return (S_OK);
  778. }
  779. /*+-------------------------------------------------------------------------*
  780. * CStringTable::FindString
  781. *
  782. *
  783. *--------------------------------------------------------------------------*/
  784. STDMETHODIMP CStringTable::FindString (
  785. LPCOLESTR pszFind,
  786. MMC_STRING_ID* pID) const
  787. {
  788. ASSERT_VALID_(this);
  789. /*
  790. * validate the parameters
  791. */
  792. if (IsBadString (pszFind))
  793. return (E_INVALIDARG);
  794. if (IsBadWritePtr (pID, sizeof (*pID)))
  795. return (E_INVALIDARG);
  796. /*
  797. * look up the string
  798. */
  799. EntryList::iterator itEntry = LookupEntryByString (pszFind);
  800. /*
  801. * no entry? fail
  802. */
  803. if (itEntry == m_Entries.end())
  804. return (E_FAIL);
  805. *pID = itEntry->m_id;
  806. return (S_OK);
  807. }
  808. /*+-------------------------------------------------------------------------*
  809. * CStringTable::Enumerate
  810. *
  811. *
  812. *--------------------------------------------------------------------------*/
  813. STDMETHODIMP CStringTable::Enumerate (
  814. IEnumString** ppEnum) const
  815. {
  816. ASSERT_VALID_(this);
  817. /*
  818. * validate the parameters
  819. */
  820. if (IsBadWritePtr (ppEnum, sizeof (*ppEnum)))
  821. return (E_INVALIDARG);
  822. /*
  823. * Create the new CStringEnumerator object
  824. */
  825. CComObject<CStringEnumerator>* pEnumerator;
  826. HRESULT hr = CStringEnumerator::CreateInstanceWrapper(&pEnumerator, ppEnum);
  827. if (FAILED (hr))
  828. return (hr);
  829. /*
  830. * initialize it
  831. */
  832. ASSERT (pEnumerator != NULL);
  833. pEnumerator->Init (m_Entries);
  834. return (S_OK);
  835. }
  836. /*+-------------------------------------------------------------------------*
  837. * CStringTable::IndexEntry
  838. *
  839. * Adds an EntryList entry to the by-string and by-ID indices maintained
  840. * for the EntryList.
  841. *--------------------------------------------------------------------------*/
  842. void CStringTable::IndexEntry (EntryList::iterator itEntry)
  843. {
  844. /*
  845. * the entry shouldn't be in any of the indices yet
  846. */
  847. ASSERT (m_StringIndex.find (itEntry->m_str) == m_StringIndex.end());
  848. ASSERT (m_IDIndex.find (itEntry->m_id) == m_IDIndex.end());
  849. /*
  850. * add the entry to the indices
  851. */
  852. m_StringIndex[itEntry->m_str] = itEntry;
  853. m_IDIndex [itEntry->m_id] = itEntry;
  854. }
  855. /*+-------------------------------------------------------------------------*
  856. * CStringTable::LookupEntryByString
  857. *
  858. * Returns an iterator to the string table entry for a given string, or
  859. * m_Entries.end() if there isn't an entry for the ID.
  860. *--------------------------------------------------------------------------*/
  861. EntryList::iterator
  862. CStringTable::LookupEntryByString (const std::wstring& str) const
  863. {
  864. StringToEntryMap::iterator it = m_StringIndex.find (str);
  865. if (it == m_StringIndex.end())
  866. return (m_Entries.end());
  867. return (it->second);
  868. }
  869. /*+-------------------------------------------------------------------------*
  870. * CStringTable::LookupEntryByID
  871. *
  872. * Returns an iterator to the string table entry for a given string ID, or
  873. * m_Entries.end() if there isn't an entry for the ID.
  874. *--------------------------------------------------------------------------*/
  875. EntryList::iterator
  876. CStringTable::LookupEntryByID (MMC_STRING_ID id) const
  877. {
  878. IDToEntryMap::iterator it = m_IDIndex.find (id);
  879. if (it == m_IDIndex.end())
  880. return (m_Entries.end());
  881. return (it->second);
  882. }
  883. /*+-------------------------------------------------------------------------*
  884. * operator>>
  885. *
  886. * Reads a CStringTable from a storage.
  887. *--------------------------------------------------------------------------*/
  888. IStream& operator>> (IStream& stm, CStringTable& table)
  889. {
  890. stm >> table.m_Entries;
  891. /*
  892. * rebuild the by-string and by-ID indices
  893. */
  894. EntryList::iterator it;
  895. table.m_StringIndex.clear();
  896. table.m_IDIndex.clear();
  897. for (it = table.m_Entries.begin(); it != table.m_Entries.end(); ++it)
  898. {
  899. table.IndexEntry (it);
  900. }
  901. #ifdef DBG
  902. CStringTable::AssertValid (&table);
  903. #endif
  904. return (stm);
  905. }
  906. /*+-------------------------------------------------------------------------*
  907. * operator<<
  908. *
  909. * Writes a CStringTable to a stream. The format is:
  910. *
  911. * DWORD count of string entries
  912. * [n string entries]
  913. *
  914. * The format of each string entry is controled by operator<<(CEntry).
  915. *--------------------------------------------------------------------------*/
  916. IStream& operator<< (IStream& stm, const CStringTable& table)
  917. {
  918. return (stm << table.m_Entries);
  919. }
  920. /*+-------------------------------------------------------------------------*
  921. * CStringTable::FindInsertionPointForEntry
  922. *
  923. *
  924. *--------------------------------------------------------------------------*/
  925. EntryList::iterator CStringTable::FindInsertionPointForEntry (
  926. const CEntry& entry) const
  927. {
  928. return (std::lower_bound (m_Entries.begin(), m_Entries.end(),
  929. entry, CompareEntriesByID()));
  930. }
  931. /*+-------------------------------------------------------------------------*
  932. * CStringTable::ScCollectInUseIDs
  933. *
  934. *
  935. *--------------------------------------------------------------------------*/
  936. SC CStringTable::ScCollectInUseIDs (CStringIDPool::RangeList& rl) const
  937. {
  938. DECLARE_SC (sc, _T("CStringTable::ScCollectInUseIDs"));
  939. EntryList::iterator it;
  940. for (it = m_Entries.begin(); it != m_Entries.end(); ++it)
  941. {
  942. if (!CStringIDPool::AddToRangeList (rl, it->m_id))
  943. return (sc = E_FAIL);
  944. }
  945. return (sc);
  946. }
  947. /*+-------------------------------------------------------------------------*
  948. * CStringTable::Dump
  949. *
  950. *
  951. *--------------------------------------------------------------------------*/
  952. #ifdef DBG
  953. void CStringTable::Dump () const
  954. {
  955. EntryList::const_iterator it;
  956. for (it = m_Entries.begin(); it != m_Entries.end(); ++it)
  957. {
  958. it->Dump();
  959. }
  960. }
  961. #endif
  962. /*+-------------------------------------------------------------------------*
  963. * CStringTable::AssertValid
  964. *
  965. * Asserts the validity of a CStringTable object. It is pretty slow,
  966. * O(n * logn)
  967. *--------------------------------------------------------------------------*/
  968. #ifdef DBG
  969. void CStringTable::AssertValid (const CStringTable* pTable)
  970. {
  971. ASSERT (pTable != NULL);
  972. ASSERT (pTable->m_pIDPool != NULL);
  973. ASSERT (pTable->m_Entries.size() == pTable->m_StringIndex.size());
  974. ASSERT (pTable->m_Entries.size() == pTable->m_IDIndex.size());
  975. EntryList::iterator it;
  976. EntryList::iterator itPrev;
  977. /*
  978. * for each string in the list, make sure the string index
  979. * and the ID index point to the string
  980. */
  981. for (it = pTable->m_Entries.begin(); it != pTable->m_Entries.end(); ++it)
  982. {
  983. /*
  984. * there should be at least one reference to the string
  985. */
  986. ASSERT (it->m_cRefs > 0);
  987. /*
  988. * make sure the IDs are in ascending order (to aid debugging)
  989. */
  990. if (it != pTable->m_Entries.begin())
  991. ASSERT (it->m_id > itPrev->m_id);
  992. /*
  993. * validate the string index
  994. */
  995. ASSERT (pTable->LookupEntryByString (it->m_str) == it);
  996. /*
  997. * validate the ID index
  998. */
  999. ASSERT (pTable->LookupEntryByID (it->m_id) == it);
  1000. itPrev = it;
  1001. }
  1002. }
  1003. #endif // DBG
  1004. /*+-------------------------------------------------------------------------*
  1005. * CStringEnumerator::CStringEnumerator
  1006. *
  1007. *
  1008. *--------------------------------------------------------------------------*/
  1009. CStringEnumerator::CStringEnumerator ()
  1010. {
  1011. }
  1012. /*+-------------------------------------------------------------------------*
  1013. * CStringEnumerator::~CStringEnumerator
  1014. *
  1015. *
  1016. *--------------------------------------------------------------------------*/
  1017. CStringEnumerator::~CStringEnumerator ()
  1018. {
  1019. }
  1020. /*+-------------------------------------------------------------------------*
  1021. * CStringEnumerator::Init
  1022. *
  1023. *
  1024. *--------------------------------------------------------------------------*/
  1025. bool CStringEnumerator::Init (const EntryList& entries)
  1026. {
  1027. m_cStrings = entries.size();
  1028. m_nCurrentIndex = 0;
  1029. if (m_cStrings > 0)
  1030. {
  1031. /*
  1032. * pre-set the size of the vector to optimize allocation
  1033. */
  1034. m_Strings.reserve (m_cStrings);
  1035. for (EntryList::iterator it = entries.begin(); it != entries.end(); ++it)
  1036. m_Strings.push_back (it->m_str);
  1037. }
  1038. return (true);
  1039. }
  1040. /*+-------------------------------------------------------------------------*
  1041. * CStringEnumerator::Next
  1042. *
  1043. *
  1044. *--------------------------------------------------------------------------*/
  1045. STDMETHODIMP CStringEnumerator::Next (ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched)
  1046. {
  1047. /*
  1048. * validate the parameters
  1049. */
  1050. if ((celt > 0) && IsBadWritePtr (rgelt, celt * sizeof (*rgelt)))
  1051. return (E_INVALIDARG);
  1052. if ((pceltFetched != NULL) && IsBadWritePtr (pceltFetched, sizeof (*pceltFetched)))
  1053. return (E_INVALIDARG);
  1054. IMallocPtr spMalloc;
  1055. HRESULT hr = CoGetMalloc (1, &spMalloc);
  1056. if (FAILED (hr))
  1057. return (hr);
  1058. /*
  1059. * allocate copies of the next celt strings
  1060. */
  1061. for (int i = 0; (celt > 0) && (m_nCurrentIndex < m_Strings.size()); i++)
  1062. {
  1063. int cchString = m_Strings[m_nCurrentIndex].length();
  1064. int cbAlloc = (cchString + 1) * sizeof (WCHAR);
  1065. rgelt[i] = (LPOLESTR) spMalloc->Alloc (cbAlloc);
  1066. /*
  1067. * couldn't get the buffer, free the ones we've allocated so far
  1068. */
  1069. if (rgelt[i] == NULL)
  1070. {
  1071. while (--i >= 0)
  1072. spMalloc->Free (rgelt[i]);
  1073. return (E_OUTOFMEMORY);
  1074. }
  1075. /*
  1076. * copy this string and bump to the next one
  1077. */
  1078. wcscpy (rgelt[i], m_Strings[m_nCurrentIndex].data());
  1079. m_nCurrentIndex++;
  1080. celt--;
  1081. }
  1082. if ( pceltFetched != NULL)
  1083. *pceltFetched = i;
  1084. return ((celt == 0) ? S_OK : S_FALSE);
  1085. }
  1086. /*+-------------------------------------------------------------------------*
  1087. * CStringEnumerator::Skip
  1088. *
  1089. *
  1090. *--------------------------------------------------------------------------*/
  1091. STDMETHODIMP CStringEnumerator::Skip (ULONG celt)
  1092. {
  1093. ULONG cSkip = min (celt, m_cStrings - m_nCurrentIndex);
  1094. m_nCurrentIndex += cSkip;
  1095. ASSERT (m_nCurrentIndex <= m_cStrings);
  1096. return ((cSkip == celt) ? S_OK : S_FALSE);
  1097. }
  1098. /*+-------------------------------------------------------------------------*
  1099. * CStringEnumerator::Reset
  1100. *
  1101. *
  1102. *--------------------------------------------------------------------------*/
  1103. STDMETHODIMP CStringEnumerator::Reset ()
  1104. {
  1105. m_nCurrentIndex = 0;
  1106. return (S_OK);
  1107. }
  1108. /*+-------------------------------------------------------------------------*
  1109. * CStringEnumerator::Clone
  1110. *
  1111. *
  1112. *--------------------------------------------------------------------------*/
  1113. STDMETHODIMP CStringEnumerator::Clone (IEnumString **ppEnum)
  1114. {
  1115. /*
  1116. * Create the new CStringEnumerator object
  1117. */
  1118. CComObject<CStringEnumerator>* pEnumerator;
  1119. HRESULT hr = CStringEnumerator::CreateInstanceWrapper (&pEnumerator, ppEnum);
  1120. if (FAILED (hr))
  1121. return (hr);
  1122. /*
  1123. * copy to the CStringEnuerator part of the new CComObect from this
  1124. */
  1125. ASSERT (pEnumerator != NULL);
  1126. CStringEnumerator& rEnum = *pEnumerator;
  1127. rEnum.m_cStrings = m_cStrings;
  1128. rEnum.m_nCurrentIndex = m_nCurrentIndex;
  1129. rEnum.m_Strings = m_Strings;
  1130. return (S_OK);
  1131. }
  1132. /*+-------------------------------------------------------------------------*
  1133. * CStringEnumerator::CreateInstance
  1134. *
  1135. *
  1136. *--------------------------------------------------------------------------*/
  1137. HRESULT CStringEnumerator::CreateInstanceWrapper(
  1138. CComObject<CStringEnumerator>** ppEnumObject,
  1139. IEnumString** ppEnumIface)
  1140. {
  1141. /*
  1142. * Create the new CStringEnumerator object
  1143. */
  1144. HRESULT hr = CComObject<CStringEnumerator>::CreateInstance(ppEnumObject);
  1145. if (FAILED (hr))
  1146. return (hr);
  1147. /*
  1148. * get the IEnumString interface for the caller
  1149. */
  1150. ASSERT ((*ppEnumObject) != NULL);
  1151. return ((*ppEnumObject)->QueryInterface (IID_IEnumString,
  1152. reinterpret_cast<void**>(ppEnumIface)));
  1153. }