|
|
//+----------------------------------------------------------------------------
//
// 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
|