|
|
/*--------------------------------------------------------------------------*
* * Microsoft Windows * Copyright (C) Microsoft Corporation, 1992 - 1999 * * File: strtable.cpp * * Contents: Implementation file for CStringTable * * History: 25-Jun-98 jeffro Created * *--------------------------------------------------------------------------*/
#include "stdafx.h"
#include "strtable.h"
#include "macros.h"
#include "comdbg.h"
#include "amcdoc.h"
// {71E5B33E-1064-11d2-808F-0000F875A9CE}
const CLSID CLSID_MMC = { 0x71e5b33e, 0x1064, 0x11d2, { 0x80, 0x8f, 0x0, 0x0, 0xf8, 0x75, 0xa9, 0xce } };
const WCHAR CMasterStringTable::s_pszIDPoolStream[] = L"ID Pool"; const WCHAR CMasterStringTable::s_pszStringsStream[] = L"Strings";
#ifdef DBG
CTraceTag tagStringTable (_T("StringTable"), _T("StringTable")); #endif // DBG
/*+-------------------------------------------------------------------------*
* IsBadString * * *--------------------------------------------------------------------------*/
inline static bool IsBadString (LPCWSTR psz) { if (psz == NULL) return (true);
return (::IsBadStringPtrW (psz, -1) != 0); }
/*+-------------------------------------------------------------------------*
* TStringFromCLSID * * *--------------------------------------------------------------------------*/
static LPTSTR TStringFromCLSID (LPTSTR pszClsid, const CLSID& clsid) { const int cchClass = 40;
#ifdef UNICODE
StringFromGUID2 (clsid, pszClsid, cchClass); #else
USES_CONVERSION; WCHAR wzClsid[cchClass]; StringFromGUID2 (clsid, wzClsid, cchClass); _tcscpy (pszClsid, W2T (wzClsid)); #endif
return (pszClsid); }
/*+-------------------------------------------------------------------------*
* operator>> * * *--------------------------------------------------------------------------*/
inline IStream& operator>> (IStream& stm, CEntry& entry) { return (stm >> entry.m_id >> entry.m_cRefs >> entry.m_str); }
/*+-------------------------------------------------------------------------*
* operator<< * * Writes a CEntry to a stream. The format is: * * DWORD string ID * DWORD reference count * DWORD string length (character count) * WCHAR[] characters in the strings, *not* NULL-terminated * *--------------------------------------------------------------------------*/
inline IStream& operator<< (IStream& stm, const CEntry& entry) { return (stm << entry.m_id << entry.m_cRefs << entry.m_str); }
/*+-------------------------------------------------------------------------*
* CEntry::Persist * * *--------------------------------------------------------------------------*/ void CEntry::Persist(CPersistor &persistor) { persistor.PersistAttribute(XML_ATTR_STRING_TABLE_STR_ID, m_id); persistor.PersistAttribute(XML_ATTR_STRING_TABLE_STR_REFS, m_cRefs); persistor.PersistContents(m_str); }
/*+-------------------------------------------------------------------------*
* CEntry::Dump * * *--------------------------------------------------------------------------*/
#ifdef DBG
void CEntry::Dump () const { USES_CONVERSION; Trace (tagStringTable, _T("id=%d, refs=%d, string=\"%s\""), m_id, m_cRefs, W2CT (m_str.data())); }
#endif
/*+-------------------------------------------------------------------------*
* CMasterStringTable::CMasterStringTable * * Even though a MMC_STRING_ID is a DWORD, we want to make sure the high * word is 0, to keep open the possibility that we can use something like * MAKEINTRESOURCE in the future. To do this, set USHRT_MAX as the * maximum string ID. *--------------------------------------------------------------------------*/
CMasterStringTable::CMasterStringTable () : m_IDPool (1, USHRT_MAX) { }
/*+-------------------------------------------------------------------------*
* CMasterStringTable::~CMasterStringTable * * *--------------------------------------------------------------------------*/
CMasterStringTable::~CMasterStringTable () { }
/*+-------------------------------------------------------------------------*
* CMasterStringTable::AddString * * *--------------------------------------------------------------------------*/
STDMETHODIMP CMasterStringTable::AddString ( LPCOLESTR pszAdd, MMC_STRING_ID* pID, const CLSID* pclsid) { if (pclsid == NULL) pclsid = &CLSID_MMC;
if (IsBadReadPtr (pclsid, sizeof(*pclsid))) return (E_INVALIDARG);
CStringTable* pStringTable = LookupStringTableByCLSID (pclsid);
/*
* If this the first string added for this CLSID, * we need to create a new string table. */ if (pStringTable == NULL) { CStringTable table (&m_IDPool); TableMapValue value (*pclsid, table);
CLSIDToStringTableMap::_Pairib rc = m_TableMap.insert (value);
/*
* we should have actually inserted the new table */ ASSERT (rc.second);
pStringTable = &(rc.first->second); ASSERT (pStringTable != NULL); }
HRESULT hr = pStringTable->AddString (pszAdd, pID);
#ifdef DBG
if (SUCCEEDED (hr)) { USES_CONVERSION; TCHAR szClsid[40]; Trace (tagStringTable, _T("Added \"%s\" (id=%d) for %s"), W2CT(pszAdd), (int) *pID, TStringFromCLSID (szClsid, *pclsid)); Dump(); } #endif
return (hr); }
/*+-------------------------------------------------------------------------*
* CMasterStringTable::GetString * * *--------------------------------------------------------------------------*/
STDMETHODIMP CMasterStringTable::GetString ( MMC_STRING_ID id, ULONG cchBuffer, LPOLESTR lpBuffer, ULONG* pcchOut, const CLSID* pclsid) { if (pclsid == NULL) pclsid = &CLSID_MMC;
if (IsBadReadPtr (pclsid, sizeof(*pclsid))) return (E_INVALIDARG);
CStringTable* pStringTable = LookupStringTableByCLSID (pclsid);
if (pStringTable == NULL) return (E_FAIL);
return (pStringTable->GetString (id, cchBuffer, lpBuffer, pcchOut)); }
/*+-------------------------------------------------------------------------*
* CMasterStringTable::GetStringLength * * *--------------------------------------------------------------------------*/
STDMETHODIMP CMasterStringTable::GetStringLength ( MMC_STRING_ID id, ULONG* pcchString, const CLSID* pclsid) { if (pclsid == NULL) pclsid = &CLSID_MMC;
if (IsBadReadPtr (pclsid, sizeof(*pclsid))) return (E_INVALIDARG);
CStringTable* pStringTable = LookupStringTableByCLSID (pclsid);
if (pStringTable == NULL) return (E_FAIL);
return (pStringTable->GetStringLength (id, pcchString)); }
/*+-------------------------------------------------------------------------*
* CMasterStringTable::DeleteString * * *--------------------------------------------------------------------------*/
STDMETHODIMP CMasterStringTable::DeleteString ( MMC_STRING_ID id, const CLSID* pclsid) { if (pclsid == NULL) pclsid = &CLSID_MMC;
if (IsBadReadPtr (pclsid, sizeof(*pclsid))) return (E_INVALIDARG);
CStringTable* pStringTable = LookupStringTableByCLSID (pclsid);
if (pStringTable == NULL) return (E_FAIL);
HRESULT hr = pStringTable->DeleteString (id);
TCHAR szClsid[40]; Trace (tagStringTable, _T("Deleted string %d for %s"), (int) id, TStringFromCLSID (szClsid, *pclsid)); Dump();
return (hr); }
/*+-------------------------------------------------------------------------*
* CMasterStringTable::DeleteAllStrings * * *--------------------------------------------------------------------------*/
STDMETHODIMP CMasterStringTable::DeleteAllStrings ( const CLSID* pclsid) { if (pclsid == NULL) pclsid = &CLSID_MMC;
if (IsBadReadPtr (pclsid, sizeof(*pclsid))) return (E_INVALIDARG);
CStringTable* pStringTable = LookupStringTableByCLSID (pclsid);
if (pStringTable == NULL) return (E_FAIL);
#include "pushwarn.h"
#pragma warning(disable: 4553) // "==" operator has no effect
VERIFY (pStringTable->DeleteAllStrings () == S_OK); VERIFY (m_TableMap.erase (*pclsid) == 1); #include "popwarn.h"
TCHAR szClsid[40]; Trace (tagStringTable, _T("Deleted all strings for %s"), TStringFromCLSID (szClsid, *pclsid)); Dump();
return (S_OK); }
/*+-------------------------------------------------------------------------*
* CMasterStringTable::FindString * * *--------------------------------------------------------------------------*/
STDMETHODIMP CMasterStringTable::FindString ( LPCOLESTR pszFind, MMC_STRING_ID* pID, const CLSID* pclsid) { if (pclsid == NULL) pclsid = &CLSID_MMC;
if (IsBadReadPtr (pclsid, sizeof(*pclsid))) return (E_INVALIDARG);
CStringTable* pStringTable = LookupStringTableByCLSID (pclsid);
if (pStringTable == NULL) return (E_FAIL);
return (pStringTable->FindString (pszFind, pID)); }
/*+-------------------------------------------------------------------------*
* CMasterStringTable::Enumerate * * *--------------------------------------------------------------------------*/
STDMETHODIMP CMasterStringTable::Enumerate ( IEnumString** ppEnum, const CLSID* pclsid) { if (pclsid == NULL) pclsid = &CLSID_MMC;
if (IsBadReadPtr (pclsid, sizeof(*pclsid))) return (E_INVALIDARG);
CStringTable* pStringTable = LookupStringTableByCLSID (pclsid);
if (pStringTable == NULL) return (E_FAIL);
return (pStringTable->Enumerate (ppEnum)); }
/*+-------------------------------------------------------------------------*
* CMasterStringTable::LookupStringTableByCLSID * * Returns a pointer to the string table for a given CLSID, or NULL if * there isn't a corresponding string in the string table. *--------------------------------------------------------------------------*/
CStringTable* CMasterStringTable::LookupStringTableByCLSID (const CLSID* pclsid) const { CLSIDToStringTableMap::iterator it = m_TableMap.find (*pclsid);
if (it == m_TableMap.end()) return (NULL);
return (&it->second); }
/*+-------------------------------------------------------------------------*
* operator>> * * Reads a CMasterStringTable from a storage. *--------------------------------------------------------------------------*/
IStorage& operator>> (IStorage& stg, CMasterStringTable& mst) { DECLARE_SC (sc, _T("operator>> (IStorage& stg, CMasterStringTable& mst)"));
HRESULT hr; IStreamPtr spStream;
/*
* read the available IDs */ hr = OpenDebugStream (&stg, CMasterStringTable::s_pszIDPoolStream, STGM_SHARE_EXCLUSIVE | STGM_READ, &spStream);
THROW_ON_FAIL (hr); spStream >> mst.m_IDPool;
/*
* read the CLSIDs and the strings */ hr = OpenDebugStream (&stg, CMasterStringTable::s_pszStringsStream, STGM_SHARE_EXCLUSIVE | STGM_READ, &spStream);
THROW_ON_FAIL (hr);
#if 1
/*
* clear out the current table */ mst.m_TableMap.clear();
/*
* read the CLSID count */ DWORD cClasses; *spStream >> cClasses;
while (cClasses-- > 0) { /*
* read the CLSID... */ CLSID clsid; spStream >> clsid;
/*
* ...and the string table */ CStringTable table (&mst.m_IDPool, spStream);
/*
* insert the string table into the CLSID map */ TableMapValue value (clsid, table); VERIFY (mst.m_TableMap.insert(value).second); } #else
/*
* Can't use this because there's no default ctor for CStringTable */ *spStream >> mst.m_TableMap; #endif
/*
* Generate the list of stale IDs. */
sc = mst.ScGenerateIDPool (); if (sc) return (stg);
mst.Dump(); return (stg); }
/*+-------------------------------------------------------------------------*
* CMasterStringTable::ScGenerateIDPool * * Generates the list of stale string IDs for this CMasterStringTable. * The set of stale IDs is the entire set of IDs, minus the available IDs, * minus the in-use IDs. *--------------------------------------------------------------------------*/
SC CMasterStringTable::ScGenerateIDPool () { /*
* Step 1: build up a RangeList of the in-use IDs */ DECLARE_SC (sc, _T("CMasterStringTable::ScGenerateIDPool")); CStringIDPool::RangeList lInUseIDs; CLSIDToStringTableMap::const_iterator itTable;
for (itTable = m_TableMap.begin(); itTable != m_TableMap.end(); ++itTable) { const CStringTable& st = itTable->second;
sc = st.ScCollectInUseIDs (lInUseIDs); if (sc) return (sc); }
/*
* Step 2: give the in-use IDs to the ID pool so it can merge it * with the available IDs (which it already has) to generate the * list of stale IDs */ sc = m_IDPool.ScGenerate (lInUseIDs); if (sc) return (sc);
return (sc); }
/*+-------------------------------------------------------------------------*
* * CMasterStringTable::Persist * * PURPOSE: persists the CMasterStringTable object to the specified persistor. * * PARAMETERS: * CPersistor & persistor : * * RETURNS: * void * *+-------------------------------------------------------------------------*/ void CMasterStringTable::Persist(CPersistor & persistor) { DECLARE_SC(sc, TEXT("CMasterStringTable::Persist"));
// purge unused snapins not to save what's already gone
sc = ScPurgeUnusedStrings(); if (sc) sc.Throw();
persistor.Persist(m_IDPool); m_TableMap.PersistSelf(&m_IDPool, persistor); if (persistor.IsLoading()) ScGenerateIDPool (); }
/***************************************************************************\
* * METHOD: CMasterStringTable::ScPurgeUnusedStrings * * PURPOSE: removes entries for snapins what aren't in use anymore * * PARAMETERS: * * RETURNS: * SC - result code * \***************************************************************************/ SC CMasterStringTable::ScPurgeUnusedStrings() { DECLARE_SC(sc, TEXT("CMasterStringTable::ScPurgeUnusedStrings"));
// det to the currfent document
CAMCDoc* pAMCDoc = CAMCDoc::GetDocument(); sc = ScCheckPointers(pAMCDoc, E_UNEXPECTED); if (sc) return sc;
// get the access to scope tree
IScopeTree *pScopeTree = pAMCDoc->GetScopeTree(); sc = ScCheckPointers(pScopeTree, E_UNEXPECTED); if (sc) return sc;
// now iterate thru entries removing those belonging
// to snapins already gone.
CLSIDToStringTableMap::iterator it = m_TableMap.begin(); while (it != m_TableMap.end()) { // special case for internal guid
if (IsEqualGUID(it->first, CLSID_MMC)) { ++it; // simply skip own stuff
} else { // ask the scope tree if snapin is in use
BOOL bInUse = FALSE; sc = pScopeTree->IsSnapinInUse(it->first, &bInUse); if (sc) return sc;
// act depending on usage
if (bInUse) { ++it; // skip also the stuff currently in use
} else { // to the trash can
sc = it->second.DeleteAllStrings(); if (sc) return sc;
it = m_TableMap.erase(it); } } }
return sc; }
/*+-------------------------------------------------------------------------*
* operator<< * * Writes a CMasterStringTable to a storage. * * It is written into two streams: "ID Pool" and "Strings". * * "ID Pool" contains the list of available string IDs remaining in the * string table. Its format is defined by CIdentifierPool. * * "Strings" contains the strings. The format is: * * DWORD count of string tables * [n string tables] * * The format for each string is defined by operator<<(TableMapValue). *--------------------------------------------------------------------------*/
IStorage& operator<< (IStorage& stg, const CMasterStringTable& mst) { HRESULT hr; IStreamPtr spStream;
/*
* write the available IDs */ hr = CreateDebugStream (&stg, CMasterStringTable::s_pszIDPoolStream, STGM_SHARE_EXCLUSIVE | STGM_CREATE | STGM_WRITE, &spStream);
THROW_ON_FAIL (hr); spStream << mst.m_IDPool;
/*
* write the string tables */ hr = CreateDebugStream (&stg, CMasterStringTable::s_pszStringsStream, STGM_SHARE_EXCLUSIVE | STGM_CREATE | STGM_WRITE, &spStream);
THROW_ON_FAIL (hr); *spStream << mst.m_TableMap;
return (stg); }
/*+-------------------------------------------------------------------------*
* CMasterStringTable::Dump * * *--------------------------------------------------------------------------*/
#ifdef DBG
void CMasterStringTable::Dump () const { Trace (tagStringTable, _T("Contents of CMasterStringTable at 0x08%x"), this);
m_IDPool.Dump();
CLSIDToStringTableMap::const_iterator it;
for (it = m_TableMap.begin(); it != m_TableMap.end(); ++it) { TCHAR szClsid[40]; const CLSID& clsid = it->first; const CStringTable& st = it->second;
Trace (tagStringTable, _T("%d strings for %s:"), st.size(), TStringFromCLSID (szClsid, clsid)); st.Dump(); } }
#endif
/*+-------------------------------------------------------------------------*
* CStringTable::CStringTable * * *--------------------------------------------------------------------------*/
CStringTable::CStringTable (CStringIDPool* pIDPool) : m_pIDPool (pIDPool), CStringTable_base(m_Entries, XML_TAG_STRING_TABLE) { ASSERT_VALID_(this); }
CStringTable::CStringTable (CStringIDPool* pIDPool, IStream& stm) : m_pIDPool (pIDPool), CStringTable_base(m_Entries, XML_TAG_STRING_TABLE) { stm >> *this; ASSERT_VALID_(this); }
/*+-------------------------------------------------------------------------*
* CStringTable::~CStringTable * * *--------------------------------------------------------------------------*/
CStringTable::~CStringTable () { }
/*+-------------------------------------------------------------------------*
* CStringTable::CStringTable * * Copy constructor *--------------------------------------------------------------------------*/
CStringTable::CStringTable (const CStringTable& other) : m_Entries (other.m_Entries), m_pIDPool (other.m_pIDPool), CStringTable_base(m_Entries, XML_TAG_STRING_TABLE) { ASSERT_VALID_(&other); IndexAllEntries (); ASSERT_VALID_(this); }
/*+-------------------------------------------------------------------------*
* CStringTable::operator= * * Assignment operator *--------------------------------------------------------------------------*/
CStringTable& CStringTable::operator= (const CStringTable& other) { ASSERT_VALID_(&other);
if (&other != this) { m_Entries = other.m_Entries; m_pIDPool = other.m_pIDPool; IndexAllEntries (); }
ASSERT_VALID_(this); return (*this); }
/*+-------------------------------------------------------------------------*
* CStringTable::AddString * * *--------------------------------------------------------------------------*/
STDMETHODIMP CStringTable::AddString ( LPCOLESTR pszAdd, MMC_STRING_ID* pID) { /*
* validate the parameters */ if (IsBadString (pszAdd)) return (E_INVALIDARG);
if (IsBadWritePtr (pID, sizeof (*pID))) return (E_INVALIDARG);
std::wstring strAdd = pszAdd;
/*
* check to see if there's already an entry for this string */ EntryList::iterator itEntry = LookupEntryByString (strAdd);
/*
* if there's not an entry for this string, add one */ if (itEntry == m_Entries.end()) { /*
* add the entry to the list */ try { CEntry EntryToInsert (strAdd, m_pIDPool->Reserve());
itEntry = m_Entries.insert (FindInsertionPointForEntry (EntryToInsert), EntryToInsert); ASSERT (itEntry->m_cRefs == 0); } catch (CStringIDPool::pool_exhausted&) { return (E_OUTOFMEMORY); }
/*
* add the new entry to the indices */ IndexEntry (itEntry); }
/*
* Bump the ref count for this string. The ref count for * new strings is 0, so we won't have ref counting problems. */ ASSERT (itEntry != m_Entries.end()); itEntry->m_cRefs++;
*pID = itEntry->m_id;
ASSERT_VALID_(this); return (S_OK); }
/*+-------------------------------------------------------------------------*
* CStringTable::GetString * * *--------------------------------------------------------------------------*/
STDMETHODIMP CStringTable::GetString ( MMC_STRING_ID id, ULONG cchBuffer, LPOLESTR lpBuffer, ULONG* pcchOut) const { ASSERT_VALID_(this);
/*
* validate the parameters */ if (cchBuffer == 0) return (E_INVALIDARG);
if (IsBadWritePtr (lpBuffer, cchBuffer * sizeof (*lpBuffer))) return (E_INVALIDARG);
if ((pcchOut != NULL) && IsBadWritePtr (pcchOut, sizeof (*pcchOut))) return (E_INVALIDARG);
/*
* find the entry for this string ID */ EntryList::iterator itEntry = LookupEntryByID (id);
if (itEntry == m_Entries.end()) return (E_FAIL);
/*
* copy to the user's buffer and make sure it's terminated */ wcsncpy (lpBuffer, itEntry->m_str.data(), cchBuffer); lpBuffer[cchBuffer-1] = 0;
/*
* if the caller wants the write count, give it to him */ if ( pcchOut != NULL) *pcchOut = wcslen (lpBuffer);
return (S_OK); }
/*+-------------------------------------------------------------------------*
* CStringTable::GetStringLength * * *--------------------------------------------------------------------------*/
STDMETHODIMP CStringTable::GetStringLength ( MMC_STRING_ID id, ULONG* pcchString) const { ASSERT_VALID_(this);
/*
* validate the parameters */ if (IsBadWritePtr (pcchString, sizeof (*pcchString))) return (E_INVALIDARG);
/*
* find the entry for this string ID */ EntryList::iterator itEntry = LookupEntryByID (id);
if (itEntry == m_Entries.end()) return (E_FAIL);
*pcchString = itEntry->m_str.length();
return (S_OK); }
/*+-------------------------------------------------------------------------*
* CStringTable::DeleteString * * *--------------------------------------------------------------------------*/
STDMETHODIMP CStringTable::DeleteString ( MMC_STRING_ID id) { /*
* find the entry for this string ID */ EntryList::iterator itEntry = LookupEntryByID (id);
if (itEntry == m_Entries.end()) return (E_FAIL);
/*
* Decrement the ref count. If it goes to zero, we can remove the * string entirely. */ if (--itEntry->m_cRefs == 0) { /*
* remove the string from the indices */ m_StringIndex.erase (itEntry->m_str); m_IDIndex.erase (itEntry->m_id);
/*
* return the string ID to the ID pool and remove the entry */ VERIFY (m_pIDPool->Release (itEntry->m_id)); m_Entries.erase (itEntry); }
ASSERT_VALID_(this); return (S_OK); }
/*+-------------------------------------------------------------------------*
* CStringTable::DeleteAllStrings * * *--------------------------------------------------------------------------*/
STDMETHODIMP CStringTable::DeleteAllStrings () { /*
* return all string IDs to the ID pool */ std::for_each (m_Entries.begin(), m_Entries.end(), IdentifierReleaser (*m_pIDPool));
/*
* wipe everything clean */ m_Entries.clear (); m_StringIndex.clear (); m_IDIndex.clear ();
ASSERT_VALID_(this); return (S_OK); }
/*+-------------------------------------------------------------------------*
* CStringTable::FindString * * *--------------------------------------------------------------------------*/
STDMETHODIMP CStringTable::FindString ( LPCOLESTR pszFind, MMC_STRING_ID* pID) const { ASSERT_VALID_(this);
/*
* validate the parameters */ if (IsBadString (pszFind)) return (E_INVALIDARG);
if (IsBadWritePtr (pID, sizeof (*pID))) return (E_INVALIDARG);
/*
* look up the string */ EntryList::iterator itEntry = LookupEntryByString (pszFind);
/*
* no entry? fail */ if (itEntry == m_Entries.end()) return (E_FAIL);
*pID = itEntry->m_id;
return (S_OK); }
/*+-------------------------------------------------------------------------*
* CStringTable::Enumerate * * *--------------------------------------------------------------------------*/
STDMETHODIMP CStringTable::Enumerate ( IEnumString** ppEnum) const { ASSERT_VALID_(this);
/*
* validate the parameters */ if (IsBadWritePtr (ppEnum, sizeof (*ppEnum))) return (E_INVALIDARG);
/*
* Create the new CStringEnumerator object */ CComObject<CStringEnumerator>* pEnumerator; HRESULT hr = CStringEnumerator::CreateInstanceWrapper(&pEnumerator, ppEnum);
if (FAILED (hr)) return (hr);
/*
* initialize it */ ASSERT (pEnumerator != NULL); pEnumerator->Init (m_Entries); return (S_OK); }
/*+-------------------------------------------------------------------------*
* CStringTable::IndexEntry * * Adds an EntryList entry to the by-string and by-ID indices maintained * for the EntryList. *--------------------------------------------------------------------------*/
void CStringTable::IndexEntry (EntryList::iterator itEntry) { /*
* the entry shouldn't be in any of the indices yet */ ASSERT (m_StringIndex.find (itEntry->m_str) == m_StringIndex.end()); ASSERT (m_IDIndex.find (itEntry->m_id) == m_IDIndex.end());
/*
* add the entry to the indices */ m_StringIndex[itEntry->m_str] = itEntry; m_IDIndex [itEntry->m_id] = itEntry; }
/*+-------------------------------------------------------------------------*
* CStringTable::LookupEntryByString * * Returns an iterator to the string table entry for a given string, or * m_Entries.end() if there isn't an entry for the ID. *--------------------------------------------------------------------------*/
EntryList::iterator CStringTable::LookupEntryByString (const std::wstring& str) const { StringToEntryMap::iterator it = m_StringIndex.find (str);
if (it == m_StringIndex.end()) return (m_Entries.end());
return (it->second); }
/*+-------------------------------------------------------------------------*
* CStringTable::LookupEntryByID * * Returns an iterator to the string table entry for a given string ID, or * m_Entries.end() if there isn't an entry for the ID. *--------------------------------------------------------------------------*/
EntryList::iterator CStringTable::LookupEntryByID (MMC_STRING_ID id) const { IDToEntryMap::iterator it = m_IDIndex.find (id);
if (it == m_IDIndex.end()) return (m_Entries.end());
return (it->second); }
/*+-------------------------------------------------------------------------*
* operator>> * * Reads a CStringTable from a storage. *--------------------------------------------------------------------------*/
IStream& operator>> (IStream& stm, CStringTable& table) { stm >> table.m_Entries;
/*
* rebuild the by-string and by-ID indices */ EntryList::iterator it; table.m_StringIndex.clear(); table.m_IDIndex.clear();
for (it = table.m_Entries.begin(); it != table.m_Entries.end(); ++it) { table.IndexEntry (it); }
#ifdef DBG
CStringTable::AssertValid (&table); #endif
return (stm); }
/*+-------------------------------------------------------------------------*
* operator<< * * Writes a CStringTable to a stream. The format is: * * DWORD count of string entries * [n string entries] * * The format of each string entry is controled by operator<<(CEntry). *--------------------------------------------------------------------------*/
IStream& operator<< (IStream& stm, const CStringTable& table) { return (stm << table.m_Entries); }
/*+-------------------------------------------------------------------------*
* CStringTable::FindInsertionPointForEntry * * *--------------------------------------------------------------------------*/
EntryList::iterator CStringTable::FindInsertionPointForEntry ( const CEntry& entry) const { return (std::lower_bound (m_Entries.begin(), m_Entries.end(), entry, CompareEntriesByID())); }
/*+-------------------------------------------------------------------------*
* CStringTable::ScCollectInUseIDs * * *--------------------------------------------------------------------------*/
SC CStringTable::ScCollectInUseIDs (CStringIDPool::RangeList& rl) const { DECLARE_SC (sc, _T("CStringTable::ScCollectInUseIDs")); EntryList::iterator it;
for (it = m_Entries.begin(); it != m_Entries.end(); ++it) { if (!CStringIDPool::AddToRangeList (rl, it->m_id)) return (sc = E_FAIL); }
return (sc); }
/*+-------------------------------------------------------------------------*
* CStringTable::Dump * * *--------------------------------------------------------------------------*/
#ifdef DBG
void CStringTable::Dump () const { EntryList::const_iterator it;
for (it = m_Entries.begin(); it != m_Entries.end(); ++it) { it->Dump(); } }
#endif
/*+-------------------------------------------------------------------------*
* CStringTable::AssertValid * * Asserts the validity of a CStringTable object. It is pretty slow, * O(n * logn) *--------------------------------------------------------------------------*/
#ifdef DBG
void CStringTable::AssertValid (const CStringTable* pTable) { ASSERT (pTable != NULL); ASSERT (pTable->m_pIDPool != NULL); ASSERT (pTable->m_Entries.size() == pTable->m_StringIndex.size()); ASSERT (pTable->m_Entries.size() == pTable->m_IDIndex.size());
EntryList::iterator it; EntryList::iterator itPrev;
/*
* for each string in the list, make sure the string index * and the ID index point to the string */ for (it = pTable->m_Entries.begin(); it != pTable->m_Entries.end(); ++it) { /*
* there should be at least one reference to the string */ ASSERT (it->m_cRefs > 0);
/*
* make sure the IDs are in ascending order (to aid debugging) */ if (it != pTable->m_Entries.begin()) ASSERT (it->m_id > itPrev->m_id);
/*
* validate the string index */ ASSERT (pTable->LookupEntryByString (it->m_str) == it);
/*
* validate the ID index */ ASSERT (pTable->LookupEntryByID (it->m_id) == it);
itPrev = it; } }
#endif // DBG
/*+-------------------------------------------------------------------------*
* CStringEnumerator::CStringEnumerator * * *--------------------------------------------------------------------------*/
CStringEnumerator::CStringEnumerator () { }
/*+-------------------------------------------------------------------------*
* CStringEnumerator::~CStringEnumerator * * *--------------------------------------------------------------------------*/
CStringEnumerator::~CStringEnumerator () { }
/*+-------------------------------------------------------------------------*
* CStringEnumerator::Init * * *--------------------------------------------------------------------------*/
bool CStringEnumerator::Init (const EntryList& entries) { m_cStrings = entries.size(); m_nCurrentIndex = 0;
if (m_cStrings > 0) { /*
* pre-set the size of the vector to optimize allocation */ m_Strings.reserve (m_cStrings);
for (EntryList::iterator it = entries.begin(); it != entries.end(); ++it) m_Strings.push_back (it->m_str); }
return (true); }
/*+-------------------------------------------------------------------------*
* CStringEnumerator::Next * * *--------------------------------------------------------------------------*/
STDMETHODIMP CStringEnumerator::Next (ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched) { /*
* validate the parameters */ if ((celt > 0) && IsBadWritePtr (rgelt, celt * sizeof (*rgelt))) return (E_INVALIDARG);
if ((pceltFetched != NULL) && IsBadWritePtr (pceltFetched, sizeof (*pceltFetched))) return (E_INVALIDARG);
IMallocPtr spMalloc; HRESULT hr = CoGetMalloc (1, &spMalloc);
if (FAILED (hr)) return (hr);
/*
* allocate copies of the next celt strings */ for (int i = 0; (celt > 0) && (m_nCurrentIndex < m_Strings.size()); i++) { int cchString = m_Strings[m_nCurrentIndex].length(); int cbAlloc = (cchString + 1) * sizeof (WCHAR); rgelt[i] = (LPOLESTR) spMalloc->Alloc (cbAlloc);
/*
* couldn't get the buffer, free the ones we've allocated so far */ if (rgelt[i] == NULL) { while (--i >= 0) spMalloc->Free (rgelt[i]);
return (E_OUTOFMEMORY); }
/*
* copy this string and bump to the next one */ wcscpy (rgelt[i], m_Strings[m_nCurrentIndex].data()); m_nCurrentIndex++; celt--; }
if ( pceltFetched != NULL) *pceltFetched = i;
return ((celt == 0) ? S_OK : S_FALSE); }
/*+-------------------------------------------------------------------------*
* CStringEnumerator::Skip * * *--------------------------------------------------------------------------*/
STDMETHODIMP CStringEnumerator::Skip (ULONG celt) { ULONG cSkip = min (celt, m_cStrings - m_nCurrentIndex); m_nCurrentIndex += cSkip; ASSERT (m_nCurrentIndex <= m_cStrings);
return ((cSkip == celt) ? S_OK : S_FALSE); }
/*+-------------------------------------------------------------------------*
* CStringEnumerator::Reset * * *--------------------------------------------------------------------------*/
STDMETHODIMP CStringEnumerator::Reset () { m_nCurrentIndex = 0; return (S_OK); }
/*+-------------------------------------------------------------------------*
* CStringEnumerator::Clone * * *--------------------------------------------------------------------------*/
STDMETHODIMP CStringEnumerator::Clone (IEnumString **ppEnum) { /*
* Create the new CStringEnumerator object */ CComObject<CStringEnumerator>* pEnumerator; HRESULT hr = CStringEnumerator::CreateInstanceWrapper (&pEnumerator, ppEnum);
if (FAILED (hr)) return (hr);
/*
* copy to the CStringEnuerator part of the new CComObect from this */ ASSERT (pEnumerator != NULL); CStringEnumerator& rEnum = *pEnumerator;
rEnum.m_cStrings = m_cStrings; rEnum.m_nCurrentIndex = m_nCurrentIndex; rEnum.m_Strings = m_Strings;
return (S_OK); }
/*+-------------------------------------------------------------------------*
* CStringEnumerator::CreateInstance * * *--------------------------------------------------------------------------*/
HRESULT CStringEnumerator::CreateInstanceWrapper( CComObject<CStringEnumerator>** ppEnumObject, IEnumString** ppEnumIface) { /*
* Create the new CStringEnumerator object */ HRESULT hr = CComObject<CStringEnumerator>::CreateInstance(ppEnumObject);
if (FAILED (hr)) return (hr);
/*
* get the IEnumString interface for the caller */ ASSERT ((*ppEnumObject) != NULL); return ((*ppEnumObject)->QueryInterface (IID_IEnumString, reinterpret_cast<void**>(ppEnumIface))); }
|