NT4/private/ole32/com/class/classmap.hxx
2020-09-30 17:12:29 +02:00

254 lines
6.8 KiB
C++

//+---------------------------------------------------------------------------//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1993 - 1993.
//
// File: classmap.hxx
//
// Contents: File extension to CLSID cache
//
// Classes: CClassExtMap
//
// Functions: none
//
// History: 20-Apr-94 Rickhi Created
//
//----------------------------------------------------------------------------
#ifndef __CLASSMAP__
#define __CLASSMAP__
#include <sem.hxx>
// function prototypes
INTERNAL RegGetClassExt(LPCWSTR lpszExt, LPCLSID pclsid);
// structure for one entry in the cache. the structure is variable sized
// with the variable sized data being the filename extension at the end
// of the structure.
typedef struct SClassEntry
{
CLSID Clsid; // clsid the extension maps to
ULONG ulEntryLen; // length of this entry
WCHAR wszExt[1]; // start of filename extension
} SClassEntry;
// amount to grow the cache by
#define CLASSMAP_GROW_SIZE (4 * (sizeof(SClassEntry)+(4*sizeof(WCHAR))) )
//+-------------------------------------------------------------------------
//
// class: CClassExtMap
//
// purpose: Holds a cache of file extension to clsid mappings (saves
// two registry hits for each lookup). The cache helps reduce
// the working set by avoiding paging in the registry.
//
// notes: The cache is expected to typically be small, hence the small
// growth rate (5 entries at a time), linear search, and no
// attempt is made at reordering the entries based on lookup
// frequency.
//
// The entries are stored in a single block of memory which is
// realloced when it needs to grow.
//
//--------------------------------------------------------------------------
class CClassExtMap : public CPrivAlloc
{
public:
CClassExtMap();
~CClassExtMap();
HRESULT FindClassExt(LPCWSTR pwszExt, CLSID *pClsid);
private:
SClassEntry *Search(LPCWSTR pwszExt);
HRESULT Add(LPCWSTR pwszExt, CLSID *pClsid);
BYTE *_pStart; // ptr to first entry in the memory block
BYTE *_pFree; // ptr to first free byte in the memory block
ULONG _ulSize; // size of the memory block
CMutexSem _mxs; // mutex for single threaded access
};
// global class map pointer
extern CClassExtMap *g_pClassExtMap;
//+-------------------------------------------------------------------------
//
// member: CClassExtMap::CClassExtMap
//
// Synopsis: constructor for the cache.
//
//--------------------------------------------------------------------------
inline CClassExtMap::CClassExtMap() :
_pStart(NULL),
_pFree(NULL),
_ulSize(0)
{
}
//+-------------------------------------------------------------------------
//
// member: CClassExtMap::~CClassExtMap
//
// Synopsis: destructor for the cache. Delete the entries.
//
//--------------------------------------------------------------------------
inline CClassExtMap::~CClassExtMap()
{
// delete the memory block
PrivMemFree(_pStart);
_pStart = NULL;
}
//+-------------------------------------------------------------------------
//
// member: CClassExtMap::FindClassExt
//
// Synopsis: Finds the clsid that maps to the given file extension
//
// Arguments: [pszExt] - the file extension to look up
// [pclsid] - where to return the clsid
//
// Returns: S_OK if successfull
//
//--------------------------------------------------------------------------
inline HRESULT CClassExtMap::FindClassExt(LPCWSTR pszExt, CLSID *pClsid)
{
// single case the string now to save comparison time.
WCHAR wszExt[MAX_PATH];
lstrcpyW(wszExt, pszExt);
CharLowerW(wszExt);
// single thread access
CLock lck(_mxs);
// look in the existing cache first
SClassEntry *pEntry = Search(wszExt);
if (pEntry)
{
// already in the cache, return the classid
memcpy(pClsid, &pEntry->Clsid, sizeof(CLSID));
return S_OK;
}
else
{
// not found, try to add an entry for this file extension
return Add(wszExt, pClsid);
}
}
//+-------------------------------------------------------------------------
//
// member: CClassExtMap::Search
//
// Synopsis: looks in the cache for an entry with the given file extension
//
// Arguments: [pszExt] - the file extension to look up
//
// Returns: pEntry if found, NULL otherwise.
//
//--------------------------------------------------------------------------
inline SClassEntry *CClassExtMap::Search(LPCWSTR pwszExt)
{
BYTE *pEntry = _pStart;
while (pEntry < _pFree)
{
// compare the class extensions
if (!lstrcmpW(pwszExt, ((SClassEntry *)pEntry)->wszExt))
{
return (SClassEntry *)pEntry;
}
Win4Assert((((SClassEntry *)pEntry)->ulEntryLen & 0x07) == 0);
pEntry += ((SClassEntry *)pEntry)->ulEntryLen;
}
return NULL;
}
//+-------------------------------------------------------------------------
//
// member: CClassExtMap::Add
//
// Synopsis: reads the registry to get the mapping, then creates a
// cache node and adds it to the list.
//
// Arguments: [pszExt] - the file extension to look up
// [pClsid] - where to return the clsid
//
// Returns: S_OK if successfull,
// REG error otherwise
//
//--------------------------------------------------------------------------
inline HRESULT CClassExtMap::Add(LPCWSTR pwszExt, CLSID *pClsid)
{
// read the clsid from the registry
HRESULT hr = RegGetClassExt(pwszExt, pClsid);
if (SUCCEEDED(hr))
{
// compute how much space we need for this entry. Note that the
// terminating NULL is accounted for in the sizeof(SClassEntry).
// we also keep the structures 8 byte aligned.
ULONG ulStrLen = lstrlenW(pwszExt) * sizeof(WCHAR);
ULONG ulEntryLen = (sizeof(SClassEntry) + ulStrLen + 7) & 0xfffffff8;
// see if it will fit in the currently allocated block.
if (_pFree + ulEntryLen > _pStart + _ulSize)
{
// this entry wont fit in the currently allocated block,
// so grow the block.
ULONG ulNewSize = _ulSize + ulEntryLen + CLASSMAP_GROW_SIZE;
BYTE *pNew = (BYTE *) PrivMemAlloc(ulNewSize);
if (pNew)
{
// update the free pointer
_pFree = pNew + (_pFree - _pStart);
// copy the old to the new, and free the old
memcpy(pNew, _pStart, _ulSize);
PrivMemFree(_pStart);
// update the pointers and sizes
_pStart = pNew;
_ulSize = ulNewSize;
}
else
{
// could not allocate the memory. we got the clsid above
// so we still return OK, we just cant cache the data.
return S_OK;
}
}
// copy in the guid and the extension
SClassEntry *pEntry = (SClassEntry *)_pFree;
memcpy(&pEntry->Clsid, pClsid, sizeof(CLSID));
memcpy(&pEntry->wszExt, pwszExt, ulStrLen + 1);
pEntry->ulEntryLen = ulEntryLen;
// update the free ptr
_pFree += ulEntryLen;
}
return hr;
}
#endif // __CLASSMAP__