Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1475 lines
38 KiB

/*--------------------------------------------------------------------------*
*
* 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)
{
DECLARE_SC(sc, TEXT("CStringEnumerator::Next"));
/*
* validate the parameters
*/
if ((celt > 0) && IsBadWritePtr (rgelt, celt * sizeof (*rgelt)))
return (sc = E_INVALIDARG).ToHr();
if ((pceltFetched != NULL) && IsBadWritePtr (pceltFetched, sizeof (*pceltFetched)))
return (sc = E_INVALIDARG).ToHr();
IMallocPtr spMalloc;
sc = CoGetMalloc (1, &spMalloc);
if (sc)
return sc.ToHr();
/*
* 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 (sc = E_OUTOFMEMORY).ToHr();
}
/*
* copy this string and bump to the next one
*/
sc = StringCbCopyW(rgelt[i], cbAlloc, m_Strings[m_nCurrentIndex].data());
if (sc)
return sc.ToHr();
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)));
}