WindowsXP-SP1/com/ole2ui32/strcache.cpp

462 lines
15 KiB
C++

//+----------------------------------------------------------------------------
//
// File: strcache.cpp
//
// Contents: String cache for insert object dialog.
//
// Classes: CStringCache
//
// History: 02-May-99 MPrabhu Created
//
//-----------------------------------------------------------------------------
#include "precomp.h"
#include "common.h"
#include "strcache.h"
#if USE_STRING_CACHE==1
// Global instance of the string Cache object.
CStringCache gInsObjStringCache;
// Was the cache initialized successfully?
BOOL gbCacheInit = FALSE;
// Is the cache in good shape currently?
// This is needed because errors may occur post-initialization
// during caching strings, setting up RegNotify etc.
// If there is any error we do not take further risk and flag the cache
// as useless all the way till process detach.
BOOL gbOKToUseCache = FALSE;
// REVIEW: the above two globals could probably be folded into a single
// dwFlags member in the cache.
//+-------------------------------------------------------------------------
//
// Function: InsertObjCacheInitialize, public
//
// Synopsis: Calls Init() method on the string cache and records
// success/failure for later use.
//
// History: 02-May-99 MPrabhu Created.
//
//--------------------------------------------------------------------------
BOOL InsertObjCacheInitialize()
{
OleDbgAssert(gbCacheInit == FALSE);
OleDbgAssert(gInsObjStringCache);
if (gInsObjStringCache.Init())
{
gbCacheInit = TRUE;
gbOKToUseCache = TRUE;
}
return gbCacheInit;
}
//+-------------------------------------------------------------------------
//
// Function: InsertObjCacheUninitialize, public
//
// Synopsis: Calls CleanUp method on the string cache if it was
// successfully initialized.
//
// History: 02-May-99 MPrabhu Created.
//
//--------------------------------------------------------------------------
void InsertObjCacheUninitialize()
{
OleDbgAssert(gInsObjStringCache);
if (gbCacheInit)
{
gInsObjStringCache.CleanUp();
}
}
//+-------------------------------------------------------------------------
//
// Method: CStringCache::CStringCache, Public
//
// Synopsis: Ctor (empty)
//
// History: 02-May-99 MPrabhu Created
//
//+-------------------------------------------------------------------------
CStringCache::CStringCache()
{
}
//+-------------------------------------------------------------------------
//
// Method: CStringCache::~CStringCache, Public
//
// Synopsis: Dtor (empty)
//
// History: 02-May-99 MPrabhu Created
//
//+-------------------------------------------------------------------------
CStringCache::~CStringCache()
{
}
//+-------------------------------------------------------------------------
//
// Method: CStringCache::Init, Public
//
// Synopsis: Called during dll_proc_attach to set up the initial state
// and allocate memory for the cache.
//
// History: 02-May-99 MPrabhu Created
//
//+-------------------------------------------------------------------------
BOOL CStringCache::Init()
{
m_ulMaxBytes = 0; //We will alloc this below
m_ulMaxStringCount = 0;
m_ulNextStringNum = 1;
m_ulStringCount = 0;
m_pOffsetTable = NULL;
m_pStrings = NULL;
m_cClsidExcludePrev = 0xFFFFFFFF; // bogus initial values
m_ioFlagsPrev = 0xFFFFFFFF;
m_hRegEvent = CreateEventW( NULL, // pointer to security attributes
// (NULL=>can't inherit)
FALSE, // not Manual Reset
FALSE, // not Signaled initially
NULL ); // pointer to event-object name
LONG ret = RegOpenKeyW( HKEY_CLASSES_ROOT,
L"CLSID", // szSubKey
&m_hRegKey );
if ( (!m_hRegEvent) || ((LONG)ERROR_SUCCESS!=ret) )
{
// No point in using the cache if we cannot watch key changes.
return FALSE;
}
ret = RegNotifyChangeKeyValue( m_hRegKey, // key to watch
TRUE, // watch subTree
REG_NOTIFY_CHANGE_NAME // name
| REG_NOTIFY_CHANGE_LAST_SET, // value
m_hRegEvent, // event to signal
TRUE ); // report asynchronously
if (ERROR_SUCCESS!=ret)
{
// No point in using the cache if we cannot watch key changes.
return FALSE;
}
return (ExpandStringTable() && ExpandOffsetTable());
}
//+-------------------------------------------------------------------------
//
// Method: CStringCache::CleanUp, Public
//
// Synopsis: Called during dll_proc_detach to clean up the state
// and free the memory allocated for the cache.
//
// History: 02-May-99 MPrabhu Created
//
//+-------------------------------------------------------------------------
void CStringCache::CleanUp()
{
FlushCache();
CoTaskMemFree(m_pStrings);
CoTaskMemFree(m_pOffsetTable);
if (m_hRegEvent)
CloseHandle(m_hRegEvent);
if (m_hRegKey)
CloseHandle(m_hRegKey);
gbCacheInit = FALSE;
gbOKToUseCache = FALSE;
}
//+-------------------------------------------------------------------------
//
// Method: CStringCache::ExpandStringTable, Private
//
// Synopsis: Called to expand the memory block used to keep strings
//
// History: 02-May-99 MPrabhu Created
//
// Notes: This relies on MemRealloc to copy the existing contents.
// Caller *must* mark cache state as bad if this fails.
//+-------------------------------------------------------------------------
BOOL CStringCache::ExpandStringTable()
{
// Note: we rely on the constructor to set m_ulMaxBytes to 0.
if (m_ulMaxBytes == 0) //first expansion
{
OleDbgAssert(m_pStrings==NULL);
m_ulMaxBytes = CACHE_MAX_BYTES_INITIAL;
}
else
{
// Each expansion doubles the current size.
m_ulMaxBytes = m_ulMaxBytes*2;
}
// CoTaskMemRealloc does a simple alloc when m_pStrings is NULL.
BYTE *pStrings = (BYTE *)CoTaskMemRealloc( m_pStrings, m_ulMaxBytes);
if (!pStrings)
{
// Caller must mark cache as bad.
return FALSE;
}
m_pStrings = pStrings;
return TRUE;
}
//+-------------------------------------------------------------------------
//
// Method: CStringCache::ExpandOffsetTable, Private
//
// Synopsis: Called to expand the memory block used to keep strings
//
// History: 02-May-99 MPrabhu Created
//
// Notes: This relies on MemRealloc to copy the existing contents.
// Caller *must* mark cache state as bad if this fails.
//+-------------------------------------------------------------------------
BOOL CStringCache::ExpandOffsetTable()
{
// Note: we rely on the contructor to set m_ulMaxStringCount to 0.
if (m_ulMaxStringCount == 0)
{
// first expansion
OleDbgAssert(m_pOffsetTable==NULL);
m_ulMaxStringCount = MAX_INDEX_ENTRIES_INITIAL;
}
else
{
// at each expansion we double the current size.
m_ulMaxStringCount = m_ulMaxStringCount*2;
}
// CoTaskMemRealloc does a simple alloc when m_pOffsetTable is NULL.
ULONG *pTable = (ULONG *) CoTaskMemRealloc( m_pOffsetTable,
sizeof(ULONG)*(m_ulMaxStringCount+1));
if (!pTable)
{
// Caller must mark the cache as bad.
return FALSE;
}
m_pOffsetTable = pTable;
if (m_ulMaxStringCount == (ULONG) MAX_INDEX_ENTRIES_INITIAL)
{
// initial expansion case
m_pOffsetTable[0] = 0; //byte offset for first string
}
return TRUE;
}
//+-------------------------------------------------------------------------
//
// Method: CStringCache::NewCall, Public
//
// Synopsis: Called to notify the cache of a fresh OleUIInsertObject call
//
// Parameters: [idFlags] - dwFlags passed in LPOLEUIINSERTOBJECT struct
// [cClsidExclude] - cClsidExclude - do -
//
// History: 02-May-99 MPrabhu Created
//
//+-------------------------------------------------------------------------
void CStringCache::NewCall(DWORD ioFlags, DWORD cClsidExclude)
{
if ( (ioFlags != m_ioFlagsPrev)
||(cClsidExclude != m_cClsidExcludePrev) )
{
// We clear cache state if either:
// i) InsertObject call flags change from previous call
// ii) Number of clsIds to exclude has changed
m_ioFlagsPrev = ioFlags;
m_cClsidExcludePrev = cClsidExclude;
FlushCache();
}
}
//+-------------------------------------------------------------------------
//
// Method: CStringCache::IsUptodate, Public
//
// Synopsis: Called to check if the cache is up to date.
//
// History: 02-May-99 MPrabhu Created
//
//+-------------------------------------------------------------------------
BOOL CStringCache::IsUptodate()
{
if (m_ulStringCount==0)
{
// The cache has never been setup or has been Flushed recently
return FALSE;
}
BOOL bUptodate;
// Check the notify event if it has fired since we set it up.
DWORD res = WaitForSingleObject( m_hRegEvent,
0 ); // timeout for wait
if (res == WAIT_TIMEOUT)
{
// Wait timed out => the reg key sub-tree has not changed
// Our cache is up to date.
bUptodate = TRUE;
}
else if (res == WAIT_OBJECT_0)
{
// Some CLSID must have changed => cache not up to date.
bUptodate = FALSE;
// We have to re-Register for the notification!
ResetEvent(m_hRegEvent);
res = RegNotifyChangeKeyValue( m_hRegKey,
TRUE, // watch sub-tree
REG_NOTIFY_CHANGE_NAME
| REG_NOTIFY_CHANGE_LAST_SET,
m_hRegEvent,
TRUE ); // asynchronous call
if (res != ERROR_SUCCESS)
{
// Cache is useless if we cannot watch CLSID sub-tree.
gbOKToUseCache = FALSE;
}
}
else
{
OleDbgAssert(!"Unexpected return from WaitForSingleObject");
bUptodate = FALSE;
}
return bUptodate;
}
//+-------------------------------------------------------------------------
//
// Method: CStringCache::AddString, Public
//
// Synopsis: Called to notify the cache of a fresh OleUIInsertObject call
//
// Parameters: [lpStrAdd] - String to add to the cache.
//
// History: 02-May-99 MPrabhu Created
//
//+-------------------------------------------------------------------------
BOOL CStringCache::AddString(LPTSTR lpStrAdd)
{
if (m_ulStringCount+2 == m_ulMaxStringCount)
{
// The offset array stores the offset of all the existing strings and
// the next one to be added!
// Hence at start of AddString, we must have enough space for the new
// string being added *and* the next one (hence the +2 above)
if (!ExpandOffsetTable())
{
// Something is really wrong.
// Mark the cache as useless hereafter.
gbOKToUseCache = FALSE;
return FALSE;
}
}
ULONG cbStrAdd = sizeof(TCHAR)*(lstrlen(lpStrAdd) + 1);
ULONG offset = m_pOffsetTable[m_ulStringCount];
if ( offset + cbStrAdd > m_ulMaxBytes )
{
// not enough space in the string block
if (!ExpandStringTable())
{
// Something is really wrong.
// Mark the cache as useless hereafter.
gbOKToUseCache = FALSE;
return FALSE;
}
}
if (! lstrcpy( (TCHAR *)(m_pStrings+offset), lpStrAdd))
{
// Mark the cache as useless hereafter.
gbOKToUseCache = FALSE;
return FALSE;
}
// We have successfully added one more string to the cache.
m_ulStringCount++;
// Next string goes at this byte offset in m_pStrings.
m_pOffsetTable[m_ulStringCount] = offset + cbStrAdd;
return TRUE;
}
//+-------------------------------------------------------------------------
//
// Method: CStringCache::NextString, Public
//
// Synopsis: Used to obtain a pointer to the next string during
// during cache enumeration.
//
// History: 02-May-99 MPrabhu Created
//
//+-------------------------------------------------------------------------
LPCTSTR CStringCache::NextString()
{
if (m_ulNextStringNum > m_ulStringCount)
{
return NULL;
}
return (LPCTSTR) (m_pStrings+m_pOffsetTable[m_ulNextStringNum++-1]);
}
//+-------------------------------------------------------------------------
//
// Method: CStringCache::ResetEnumerator, Public
//
// Synopsis: Used to reset the enumerator.
//
// History: 02-May-99 MPrabhu Created
//
//+-------------------------------------------------------------------------
void CStringCache::ResetEnumerator()
{
m_ulNextStringNum = 1;
}
//+-------------------------------------------------------------------------
//
// Method: CStringCache::FlushCache, Public
//
// Synopsis: Invalidates the cache by clearing the counters.
//
// History: 02-May-99 MPrabhu Created
//
//+-------------------------------------------------------------------------
BOOL CStringCache::FlushCache()
{
m_ulNextStringNum = 1;
m_ulStringCount = 0;
return TRUE;
}
//+-------------------------------------------------------------------------
//
// Method: CStringCache::OKToUse, Public
//
// Synopsis: Used to check if cache is in good shape.
//
// History: 02-May-99 MPrabhu Created
//
//+-------------------------------------------------------------------------
BOOL CStringCache::OKToUse()
{
return gbOKToUseCache;
}
#endif // USE_STRING_CACHE==1