windows-nt/Source/XPSP1/NT/shell/browseui/comcatex.cpp
2020-09-26 16:20:57 +08:00

944 lines
32 KiB
C++

#include "priv.h"
#include "comcatex.h"
#include "runtask.h"
//------------------//
// Misc constants
static LPCTSTR
#ifdef _WIN64
REGKEY_COMCATEX = STRREG_DISCARDABLE STRREG_POSTSETUP TEXT("\\Component Categories64"),
#else
REGKEY_COMCATEX = STRREG_DISCARDABLE STRREG_POSTSETUP TEXT("\\Component Categories"),
#endif
REGKEY_COMCATEX_ENUM = TEXT("Enum"), // HKCR\ComponentClasses\{catid}\Enum
REGVAL_COMCATEX_IMPLEMENTING = TEXT("Implementing"),// HKCR\ComponentClasses\{catid}\Enum\Implementing
REGVAL_COMCATEX_REQUIRING = TEXT("Requiring"); // HKCR\ComponentClasses\{catid}\Enum\Requiring
static const ULONG
COMCAT_CACHE_CURRENTVERSION = MAKELONG(1,0); // current cache version.
//-------------//
// Cache header
typedef struct {
ULONG cbStruct; // structure size
ULONG ver; // version string (COMCAT_CACHE_CURRENTVERSION)
SYSTEMTIME stLastUpdate; // UTC date, time of last update.
ULONG cClsid; // number of CLSIDs to follow
CLSID clsid[]; // array of CLSIDs
} COMCAT_CACHE_HEADER;
//----------------//
// Impl helpers
STDMETHODIMP _EnumerateGuids( IN IEnumGUID* pEnumGUID, OUT HDSA* phdsa );
STDMETHODIMP _ComCatCacheFromDSA( IN HDSA hdsa, OUT LPBYTE* pBuf, OUT LPDWORD pcbBuf );
STDMETHODIMP _DSAFromComCatCache( IN LPBYTE pBuf, IN ULONG cbBuf, OUT HDSA* phdsa );
STDMETHODIMP _MakeComCatCacheKey( IN REFCATID refcatid, OUT LPTSTR pszKey, IN ULONG cchKey );
STDMETHODIMP _ReadClassesOfCategory( IN REFCATID refcatid, OUT HDSA* phdsa, LPCTSTR pszRegValueName );
STDMETHODIMP _WriteImplementingClassesOfCategory( IN REFCATID refcatid, IN HDSA hdsa );
STDMETHODIMP _WriteRequiringClassesOfCategory( IN REFCATID refcatid, IN HDSA hdsa );
STDMETHODIMP _WriteClassesOfCategories( IN ULONG, IN CATID [], IN ULONG, IN CATID [], BOOL );
STDMETHODIMP _BuildCacheIfNecessary( IN REFCATID refcatid, BOOL fImplementing);
STDAPI _CComCatCache_CommonCreateInstance( BOOL, OUT void**);
//-----------------------//
// Higher-level methods
STDMETHODIMP SHReadImplementingClassesOfCategory( REFCATID refcatid, OUT HDSA* phdsa );
STDMETHODIMP SHReadRequiringClassesOfCategory( REFCATID refcatid, OUT HDSA* phdsa );
STDMETHODIMP SHWriteImplementingClassesOfCategory( REFCATID refcatid );
STDMETHODIMP SHWriteRequiringClassesOfCategory( REFCATID refcatid );
#define SAFE_DESTROY_CLSID_DSA(hdsa) \
if((hdsa)) { DSA_Destroy((hdsa)); (hdsa)=NULL; }
//-------------------------------------------------------------------------//
// Cache-aware component categories enumerator object
class CSHEnumClassesOfCategories : public IEnumGUID
//-------------------------------------------------------------------------//
{
public:
// IUnknown methods
STDMETHOD_ (ULONG, AddRef)() {
return InterlockedIncrement( &_cRef );
}
STDMETHOD_ (ULONG, Release)() {
if( InterlockedDecrement( &_cRef )==0 ) {
delete this; return 0;
}
return _cRef;
}
STDMETHOD (QueryInterface)( REFIID riid, void **ppvObj);
// IEnum methods
STDMETHOD (Next)( ULONG celt, GUID* rgelt, ULONG* pceltFetched );
STDMETHOD (Skip)( ULONG celt );
STDMETHOD (Reset)();
STDMETHOD (Clone)( IEnumGUID ** ppenum );
protected:
CSHEnumClassesOfCategories();
virtual ~CSHEnumClassesOfCategories();
STDMETHOD (Initialize)( ULONG cImpl, CATID rgcatidImpl[], ULONG cReq, CATID rgcatidReq[]);
// invoke immediately after construction for arg validation.
LONG _cRef, // ref count
_iEnum; // enumerator index
HDSA _hdsa; // CLSID DSA handle
ULONG _cImpl, // count of catids to enumerate for implementing classes
_cReq; // count of catids to enumerate for requiring classes
CATID *_rgcatidImpl, // catids to enumerate for implementing classes
*_rgcatidReq; // catids to enumerate for requiring classes
friend STDMETHODIMP SHEnumClassesOfCategories( ULONG, CATID[], ULONG, CATID[], IEnumGUID**);
};
//-------------------------------------------------------------------------//
// IRunnableTask derivative for asynchronous update of
// component categories cache.
class CComCatCacheTask : public CRunnableTask
//-------------------------------------------------------------------------//
{
public:
CComCatCacheTask();
virtual ~CComCatCacheTask();
STDMETHOD (Initialize)( ULONG cImplemented,
CATID rgcatidImpl[],
ULONG cRequired,
CATID rgcatidReq[],
BOOL bForceUpdate,
HANDLE hEvent );
STDMETHOD (Go)();
protected:
STDMETHOD (RunInitRT)()
{
HRESULT hr = _WriteClassesOfCategories( _cImpl, _rgcatidImpl,
_cReq, _rgcatidReq, _bForceUpdate );
if (_hEvent)
SetEvent(_hEvent);
return hr;
}
ULONG _cImpl, _cReq;
CATID *_rgcatidImpl,
*_rgcatidReq;
BOOL _bForceUpdate;
HANDLE _hEvent;
friend HRESULT _CComCatCache_CommonCreateInstance( BOOL, OUT void**);
};
//-------------------------------------------------------------------------//
// Entrypoint: retrieves cache-aware enumerator over classes which require or
// implement the specified component catagory(ies).
STDMETHODIMP SHEnumClassesOfCategories(
ULONG cImplemented, //Number of category IDs in the rgcatidImpl array
CATID rgcatidImpl[], //Array of category identifiers
ULONG cRequired, //Number of category IDs in the rgcatidReq array
CATID rgcatidReq[], //Array of category identifiers
IEnumGUID** ppenumGUID ) //Location in which to return an IEnumGUID interface
{
HRESULT hr = S_OK;
CSHEnumClassesOfCategories* pEnum = NULL;
if( NULL == ppenumGUID )
return E_INVALIDARG;
*ppenumGUID = NULL;
// Construct and initialize enumerator object
if( NULL == (pEnum = new CSHEnumClassesOfCategories) )
return E_OUTOFMEMORY;
if( FAILED( (hr = pEnum->Initialize(
cImplemented, rgcatidImpl, cRequired, rgcatidReq )) ) )
{
pEnum->Release();
return hr;
}
*ppenumGUID = pEnum;
return hr;
}
//-------------------------------------------------------------------------//
// Determines whether a cache exists for the indicated CATID.
// If bImplementing is TRUE, the function checks for a cache of
// implementing classes; otherwise the function checks for a cache of
// requiring classes.
STDMETHODIMP SHDoesComCatCacheExist( REFCATID refcatid, BOOL bImplementing )
{
TCHAR szKey[MAX_PATH];
HRESULT hr;
if( SUCCEEDED( (hr = _MakeComCatCacheKey( refcatid, szKey, ARRAYSIZE(szKey) )) ) )
{
HKEY hkeyCache;
DWORD dwRet = RegOpenKeyEx( HKEY_CURRENT_USER, szKey, 0L, KEY_READ, &hkeyCache );
hr = S_FALSE;
if( ERROR_SUCCESS == dwRet )
{
DWORD dwType, cbData = 0;
dwRet = RegQueryValueEx( hkeyCache,
bImplementing ? REGVAL_COMCATEX_IMPLEMENTING :
REGVAL_COMCATEX_REQUIRING,
0L, &dwType, NULL, &cbData );
// We'll confirm only on value type and size of data.
if( ERROR_SUCCESS == dwRet &&
dwType == REG_BINARY &&
sizeof(COMCAT_CACHE_HEADER) <= cbData )
{
hr = S_OK;
}
RegCloseKey( hkeyCache );
}
}
return hr;
}
//-------------------------------------------------------------------------//
// Entrypoint: Caches implementing and requiring classes for the
// specified categories with asynchronous option.
STDMETHODIMP SHWriteClassesOfCategories(
ULONG cImplemented, //Number of category IDs in the rgcatidImpl array
CATID rgcatidImpl[], //Array of category identifiers
ULONG cRequired, //Number of category IDs in the rgcatidReq array
CATID rgcatidReq[], //Array of category identifiers
BOOL bForceUpdate, // TRUE: Unconditionally update the cache; FALSE: create cache iif doesn't exist.
BOOL bWait, //If FALSE, the function returns immediately and the
// caching occurs asynchronously; otherwise
// the function returns only after the caching
// operation has completed.
HANDLE hEvent //(optional) Event to be signalled when cache update is done
)
{
HRESULT hr;
if( bWait )
{
// Synchronous update
hr = _WriteClassesOfCategories( cImplemented, rgcatidImpl, cRequired, rgcatidReq, bForceUpdate );
if (hEvent)
SetEvent(hEvent);
}
else
{
// Asynchronous update
CComCatCacheTask* pTask = new CComCatCacheTask();
if (pTask)
{
// Initialize with caller's args:
if( SUCCEEDED( (hr = pTask->Initialize(
cImplemented, rgcatidImpl, cRequired, rgcatidReq, bForceUpdate, hEvent )) ) )
{
hr = pTask->Go();
}
pTask->Release();
}
else
hr = E_OUTOFMEMORY;
}
return hr;
}
//-------------------------------------------------------------------------//
// CSHEnumClassesOfCategories class implementation
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
inline CSHEnumClassesOfCategories::CSHEnumClassesOfCategories()
: _cImpl(0), _rgcatidImpl(NULL),
_cReq(0), _rgcatidReq(NULL),
_cRef(1), _iEnum(0), _hdsa(NULL)
{
DllAddRef();
}
//-------------------------------------------------------------------------//
CSHEnumClassesOfCategories::~CSHEnumClassesOfCategories()
{
delete [] _rgcatidImpl;
delete [] _rgcatidReq;
SAFE_DESTROY_CLSID_DSA( _hdsa );
DllRelease();
}
//-------------------------------------------------------------------------//
STDMETHODIMP CSHEnumClassesOfCategories::QueryInterface( REFIID riid, void **ppvObj )
{
static const QITAB qit[] = {
QITABENT(CSHEnumClassesOfCategories, IEnumGUID),
{ 0 },
};
return QISearch(this, qit, riid, ppvObj);
}
//-------------------------------------------------------------------------//
STDMETHODIMP CSHEnumClassesOfCategories::Initialize(
ULONG cImplemented,
CATID rgcatidImpl[],
ULONG cRequired,
CATID rgcatidReq[]
)
{
// Disallow multiple initialization.
if( _hdsa || _rgcatidImpl || _rgcatidReq )
return S_FALSE;
// Superficial arg validation:
if( (0==cImplemented && 0==cRequired) ||
(cImplemented && NULL == rgcatidImpl) ||
(cRequired && NULL == rgcatidReq) )
{
return E_INVALIDARG;
}
// Allocate and make copies of CATID arrays
if( cImplemented )
{
if( NULL == (_rgcatidImpl = new CATID[cImplemented]) )
return E_OUTOFMEMORY;
CopyMemory( _rgcatidImpl, rgcatidImpl, sizeof(CATID) * cImplemented );
}
_cImpl = cImplemented;
if( cRequired )
{
if( NULL == (_rgcatidReq = new CATID[cRequired]) )
return E_OUTOFMEMORY;
CopyMemory( _rgcatidReq, rgcatidReq, sizeof(CATID) * cRequired );
}
_cReq = cRequired;
return S_OK;
}
//-------------------------------------------------------------------------//
// Iterates implementing and/or requiring classes for the caller-specified
// component categories.
STDMETHODIMP CSHEnumClassesOfCategories::Next(
ULONG celt,
GUID* rgelt,
ULONG* pceltFetched )
{
if( pceltFetched )
*pceltFetched = 0;
HRESULT hr = S_FALSE;
ULONG celtFetched = 0;
// Have we assembled our collection?
if( NULL == _hdsa )
{
_iEnum = 0;
ULONG i;
for( i=0; SUCCEEDED( hr ) && i < _cImpl; i++ )
{
// Try reading implementing classes from cache
if( FAILED( (hr = SHReadImplementingClassesOfCategory( _rgcatidImpl[i], &_hdsa )) ) )
{
// Uncached; try caching and then re-read.
if( FAILED( (hr = SHWriteImplementingClassesOfCategory( _rgcatidImpl[i] )) ) ||
FAILED( (hr = SHReadImplementingClassesOfCategory( _rgcatidImpl[i], &_hdsa )) ) )
break;
}
}
for( i=0; SUCCEEDED( hr ) && i < _cReq; i++ )
{
// Try reading requiring classes from cache
if( FAILED( (hr = SHReadRequiringClassesOfCategory( _rgcatidReq[i], &_hdsa )) ) )
{
// Uncached; try caching and then re-read.
if( FAILED( (hr = SHWriteRequiringClassesOfCategory( _rgcatidReq[i] )) ) ||
FAILED( (hr = SHReadRequiringClassesOfCategory( _rgcatidReq[i], &_hdsa )) ) )
break;
}
}
}
if( NULL != _hdsa )
{
LONG count = DSA_GetItemCount( _hdsa );
while( celtFetched < celt && _iEnum < count )
{
if( DSA_GetItem( _hdsa, _iEnum, &rgelt[celtFetched] ) )
celtFetched++;
_iEnum++;
}
return celtFetched == celt ? S_OK : S_FALSE;
}
return SUCCEEDED( hr ) ? S_FALSE : hr;
}
//-------------------------------------------------------------------------//
inline STDMETHODIMP CSHEnumClassesOfCategories::Skip( ULONG celt )
{
InterlockedExchange( &_iEnum, _iEnum + celt );
return S_OK;
}
//-------------------------------------------------------------------------//
inline STDMETHODIMP CSHEnumClassesOfCategories::Reset( void )
{
InterlockedExchange( &_iEnum, 0 );
return S_OK;
}
//-------------------------------------------------------------------------//
inline STDMETHODIMP CSHEnumClassesOfCategories::Clone( IEnumGUID ** ppenum )
{
return E_NOTIMPL;
}
//-------------------------------------------------------------------------//
// CComCatCacheTask class implementation
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
STDAPI CComCatConditionalCacheTask_CreateInstance( IN IUnknown*, OUT void** ppOut, LPCOBJECTINFO )
{
return _CComCatCache_CommonCreateInstance( FALSE /* iif not exists */, ppOut );
}
//-------------------------------------------------------------------------//
STDAPI CComCatCacheTask_CreateInstance( IN IUnknown*, OUT void** ppOut, LPCOBJECTINFO poi )
{
return _CComCatCache_CommonCreateInstance( TRUE /* unconditionally update */, ppOut );
}
//-------------------------------------------------------------------------//
STDAPI _CComCatCache_CommonCreateInstance(
BOOL bForceUpdate,
OUT void** ppOut )
{
CComCatCacheTask* pTask;
if( NULL == (pTask = new CComCatCacheTask) )
return E_OUTOFMEMORY;
HRESULT hr = S_OK;
// We're being CoCreated without args, so we'll use
// a hard-coded list of likely suspects (catids) to cache.
static CATID rgcatid[2];
rgcatid[0] = CATID_InfoBand;
rgcatid[1] = CATID_CommBand;
if( FAILED( (hr = pTask->Initialize( ARRAYSIZE(rgcatid), rgcatid, 0, NULL, bForceUpdate, NULL )) ) )
{
pTask->Release();
return hr;
}
*ppOut = SAFECAST( pTask, IRunnableTask*);
return hr;
}
//-------------------------------------------------------------------------//
inline CComCatCacheTask::CComCatCacheTask()
: CRunnableTask( RTF_DEFAULT ),
_cImpl(0), _cReq(0), _rgcatidImpl(NULL), _rgcatidReq(NULL), _bForceUpdate(TRUE)
{
}
//-------------------------------------------------------------------------//
inline CComCatCacheTask::~CComCatCacheTask()
{
delete [] _rgcatidImpl;
delete [] _rgcatidReq;
if (_hEvent)
CloseHandle(_hEvent);
}
//-------------------------------------------------------------------------//
STDMETHODIMP CComCatCacheTask::Initialize(
ULONG cImplemented,
CATID rgcatidImpl[],
ULONG cRequired,
CATID rgcatidReq[],
BOOL bForceUpdate,
HANDLE hEvent)
{
// Superficial arg validation:
if( (0==cImplemented && 0==cRequired) ||
(cImplemented && NULL == rgcatidImpl) ||
(cRequired && NULL == rgcatidReq) )
{
return E_INVALIDARG;
}
// Disallow multiple initialization.
if( _rgcatidImpl || _rgcatidReq )
return S_FALSE;
// Allocate and make copies of CATID arrays
if( cImplemented )
{
if( NULL == (_rgcatidImpl = new CATID[cImplemented]) )
return E_OUTOFMEMORY;
CopyMemory( _rgcatidImpl, rgcatidImpl, sizeof(CATID) * cImplemented );
}
_cImpl = cImplemented;
if( cRequired )
{
if( NULL == (_rgcatidReq = new CATID[cRequired]) )
return E_OUTOFMEMORY;
CopyMemory( _rgcatidReq, rgcatidReq, sizeof(CATID) * cRequired );
}
_cReq = cRequired;
_bForceUpdate = bForceUpdate;
if (hEvent)
{
HANDLE hProcess = GetCurrentProcess();
DuplicateHandle(hProcess, hEvent, hProcess, &_hEvent, 0, FALSE, DUPLICATE_SAME_ACCESS);
}
return S_OK;
}
//-------------------------------------------------------------------------//
// Initiates asynchronous update of component categories cache.
STDMETHODIMP CComCatCacheTask::Go()
{
// Run the task from the shared thread pool
IShellTaskScheduler* pScheduler;
HRESULT hr = CoCreateInstance( CLSID_SharedTaskScheduler, NULL, CLSCTX_INPROC_SERVER,
IID_IShellTaskScheduler, (LPVOID*)&pScheduler );
if( SUCCEEDED( hr ) )
{
hr = pScheduler->AddTask( this, CLSID_ComCatCacheTask, 0L, ITSAT_DEFAULT_PRIORITY );
// heap alloc'd memory belongs to scheduler thread.
pScheduler->Release(); // OK to release shared scheduler before task has completed.
}
return hr;
}
//-------------------------------------------------------------------------//
// Component cache implementation
//-------------------------------------------------------------------------//
STDMETHODIMP _BuildCacheIfNecessary(
IN REFCATID refcatid,
BOOL fImplementing)
{
HRESULT hr = S_OK;
if (S_OK != SHDoesComCatCacheExist(refcatid, fImplementing))
{
hr = fImplementing ? SHWriteImplementingClassesOfCategory(refcatid)
: SHWriteRequiringClassesOfCategory(refcatid);
}
return hr;
}
//-------------------------------------------------------------------------//
// Reads a series of CLSIDs from a registry-based cache of
// implementing classes for the specified component category into a DSA.
// If the DSA is NULL, a new DSA is created; otherwise the CLSIDS are appended to
// the provided DSA.
inline STDMETHODIMP SHReadImplementingClassesOfCategory(
IN REFCATID refcatid,
OUT HDSA* phdsa )
{
HRESULT hr = _BuildCacheIfNecessary(refcatid, TRUE);
if (SUCCEEDED(hr))
{
hr = _ReadClassesOfCategory( refcatid, phdsa, REGVAL_COMCATEX_IMPLEMENTING );
}
return hr;
}
//-------------------------------------------------------------------------//
// Reads a series of CLSIDs from a registry-based cache of
// requiring classes for the specified component category into a DSA.
// If the DSA is NULL, a new DSA is created; otherwise the CLSIDS are appended to
// the provided DSA.
inline STDMETHODIMP SHReadRequiringClassesOfCategory(
IN REFCATID refcatid,
OUT HDSA* phdsa )
{
HRESULT hr = _BuildCacheIfNecessary(refcatid, FALSE);
if (SUCCEEDED(hr))
{
hr = _ReadClassesOfCategory( refcatid, phdsa, REGVAL_COMCATEX_REQUIRING );
}
return hr;
}
//-------------------------------------------------------------------------//
// Caches a list of classes which implement the indicated component category.
STDMETHODIMP SHWriteImplementingClassesOfCategory( IN REFCATID refcatid )
{
HRESULT hr;
// Retrieve OLE component category manager
ICatInformation* pci;
if( SUCCEEDED( (hr = CoCreateInstance( CLSID_StdComponentCategoriesMgr,
NULL, CLSCTX_INPROC_SERVER,
IID_ICatInformation, (LPVOID*)&pci)) ) )
{
// Retrieve enumerator over classes that implement the category
IEnumGUID* pEnumGUID;
if( SUCCEEDED( (hr = pci->EnumClassesOfCategories( 1, (CATID*)&refcatid,
0, NULL, &pEnumGUID )) ) )
{
HDSA hdsa = NULL;
if( SUCCEEDED( (hr = _EnumerateGuids( pEnumGUID, &hdsa )) ) )
{
// Write to cache
hr = _WriteImplementingClassesOfCategory( refcatid, hdsa );
SAFE_DESTROY_CLSID_DSA( hdsa );
}
pEnumGUID->Release();
}
pci->Release();
}
return hr;
}
//-------------------------------------------------------------------------//
// Caches a list of classes which require the indicated component category.
STDMETHODIMP SHWriteRequiringClassesOfCategory( IN REFCATID refcatid )
{
HRESULT hr;
// Retrieve OLE component category manager
ICatInformation* pci;
if( SUCCEEDED( (hr = CoCreateInstance( CLSID_StdComponentCategoriesMgr,
NULL, CLSCTX_INPROC_SERVER,
IID_ICatInformation, (LPVOID*)&pci)) ) )
{
// Retrieve enumerator over classes that require the category
IEnumGUID* pEnumGUID;
if( SUCCEEDED( (hr = pci->EnumClassesOfCategories( 0, NULL, 1,
(CLSID*)&refcatid,
&pEnumGUID )) ) )
{
HDSA hdsa = NULL;
if( SUCCEEDED( (hr = _EnumerateGuids( pEnumGUID, &hdsa )) ) )
{
// Write to cache
hr = _WriteRequiringClassesOfCategory( refcatid, hdsa );
SAFE_DESTROY_CLSID_DSA( hdsa );
}
pEnumGUID->Release();
}
pci->Release();
}
return hr;
}
//-------------------------------------------------------------------------//
// Accepts a valid GUID enumerator and constructs an HDSA containing the GUIDS.
// The caller is responsible for freeing the HDSA which may or may not
// have been allocated.
STDMETHODIMP _EnumerateGuids( IEnumGUID* pEnumGUID, OUT HDSA* phdsa )
{
ASSERT( pEnumGUID );
ASSERT( phdsa );
ULONG celtFetched;
CLSID clsid;
HRESULT hr;
while( SUCCEEDED( (hr = pEnumGUID->Next( 1, &clsid, &celtFetched )) ) &&
celtFetched > 0 )
{
if( NULL == *phdsa &&
NULL == (*phdsa = DSA_Create( sizeof(CLSID), 4 )) )
{
hr = E_OUTOFMEMORY;
break;
}
DSA_AppendItem( *phdsa, &clsid );
}
// translate S_FALSE.
return SUCCEEDED( hr ) ? S_OK : hr;
}
//-------------------------------------------------------------------------//
// Generates a persistable cache of CLSIDs derived from the CLSID* DSA.
STDMETHODIMP _ComCatCacheFromDSA( IN HDSA hdsa, OUT LPBYTE* pBuf, OUT LPDWORD pcbBuf )
{
ASSERT( pBuf );
ASSERT( pcbBuf );
ULONG cClsid = hdsa ? DSA_GetItemCount( hdsa ) : 0,
cbBuf = sizeof(COMCAT_CACHE_HEADER) + (cClsid * sizeof(CLSID));
HRESULT hr = S_OK;
// Allocate blob
*pcbBuf = 0;
if( NULL != (*pBuf = new BYTE[cbBuf]) )
{
// Initialize header
COMCAT_CACHE_HEADER* pCache = (COMCAT_CACHE_HEADER*)(*pBuf);
pCache->cbStruct = sizeof(*pCache);
pCache->ver = COMCAT_CACHE_CURRENTVERSION;
pCache->cClsid = 0;
GetSystemTime( &pCache->stLastUpdate );
// Copy CLSIDs
for( ULONG i = 0; i< cClsid; i++ )
DSA_GetItem( hdsa, i, &pCache->clsid[pCache->cClsid++] );
// Adjust output size.
*pcbBuf = sizeof(*pCache) + (pCache->cClsid * sizeof(CLSID));
}
else
hr = E_OUTOFMEMORY;
return hr;
}
//-------------------------------------------------------------------------//
// Appends CLSIDS from the cache buffer to the specified DSA. If the DSA is
// NULL, a new DSA is created.
STDMETHODIMP _DSAFromComCatCache( IN LPBYTE pBuf, IN ULONG cbBuf, OUT HDSA* phdsa )
{
ASSERT( pBuf );
ASSERT( phdsa );
HRESULT hr = S_OK;
COMCAT_CACHE_HEADER* pCache = (COMCAT_CACHE_HEADER*)pBuf;
// Validate header
if( !( sizeof(*pCache) <= cbBuf &&
sizeof(*pCache) == pCache->cbStruct &&
COMCAT_CACHE_CURRENTVERSION == pCache->ver ) )
return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
// Create the DSA if necessary
if( 0 == pCache->cClsid )
return S_FALSE;
if( NULL == *phdsa && NULL == (*phdsa = DSA_Create( sizeof(CLSID), 4 )) )
return E_OUTOFMEMORY;
// Copy CLSIDs from the cache to the DSA.
for( ULONG i = 0; i< pCache->cClsid; i++ )
DSA_AppendItem( *phdsa, &pCache->clsid[i] );
return hr;
}
//-------------------------------------------------------------------------//
// Constructs a component category registry cache key based on the
// specified CATID.
STDMETHODIMP _MakeComCatCacheKey(
IN REFCATID refcatid,
OUT LPTSTR pszKey,
IN ULONG cchKey )
{
TCHAR szCLSID[GUIDSTR_MAX];
if( SHStringFromGUID( refcatid, szCLSID, ARRAYSIZE(szCLSID) )<=0 )
return E_INVALIDARG;
ASSERT( cchKey > (ULONG)(lstrlen( REGKEY_COMCATEX ) + GUIDSTR_MAX) );
// "Component Categories\{clsid}\Enum"
if( wnsprintf( pszKey, cchKey, TEXT("%s\\%s\\%s"),
REGKEY_COMCATEX, szCLSID, REGKEY_COMCATEX_ENUM ) > 0 )
return S_OK;
return E_FAIL;
}
//-------------------------------------------------------------------------//
// Reads a cache of implementing or requiring classes info a CLSID DSA.
STDMETHODIMP _ReadClassesOfCategory(
IN REFCATID refcatid,
OUT HDSA* phdsa,
LPCTSTR pszRegValueName /*REGVAL_COMCATEX_IMPLEMENTING/REQUIRING*/ )
{
TCHAR szKey[MAX_PATH];
HRESULT hr;
// Create/Open key HKCR\Component Categories\{catid}\Enum
if( SUCCEEDED( (hr = _MakeComCatCacheKey( refcatid, szKey, ARRAYSIZE(szKey) )) ) )
{
HKEY hkeyCache = NULL;
DWORD dwRet = RegOpenKeyEx( HKEY_CURRENT_USER, szKey, 0L, KEY_READ, &hkeyCache );
hr = HRESULT_FROM_WIN32( dwRet );
if( SUCCEEDED( hr ) )
{
// Determine required buffer size.
LPBYTE pBuf = NULL;
ULONG cbBuf = 0,
dwType,
dwRet = RegQueryValueEx( hkeyCache, pszRegValueName, 0L,
&dwType, NULL, &cbBuf );
hr = HRESULT_FROM_WIN32( dwRet );
if (SUCCEEDED(hr))
{
// Allocate buffer and read
if( NULL != (pBuf = new BYTE[cbBuf]) )
{
dwRet = RegQueryValueEx( hkeyCache, pszRegValueName, 0L,
&dwType, pBuf, &cbBuf );
hr = HRESULT_FROM_WIN32( dwRet );
}
else
hr = E_OUTOFMEMORY;
}
if( SUCCEEDED( hr ) )
{
// Gather CLSIDs into the DSA
hr = REG_BINARY == dwType ?
_DSAFromComCatCache( pBuf, cbBuf, phdsa ) : E_ABORT;
}
if( pBuf ) delete [] pBuf;
RegCloseKey( hkeyCache );
}
}
return hr;
}
//-------------------------------------------------------------------------//
// Writes a series of CLSIDs from a DSA to a registry-based cache of
// implementing classes for the specified component category.
STDMETHODIMP _WriteImplementingClassesOfCategory(
IN REFCATID refcatid,
IN HDSA hdsa )
{
TCHAR szKey[MAX_PATH];
HRESULT hr;
// Create/Open key HKCR\Component Categories\{catid}\Enum
if( SUCCEEDED( (hr = _MakeComCatCacheKey( refcatid, szKey, ARRAYSIZE(szKey) )) ) )
{
HKEY hkeyCache = NULL;
ULONG dwRet, dwDisposition;
dwRet = RegCreateKeyEx( HKEY_CURRENT_USER, szKey, 0L,
NULL, 0L, KEY_WRITE, NULL,
&hkeyCache, &dwDisposition );
hr = HRESULT_FROM_WIN32( dwRet );
if( SUCCEEDED( hr ) )
{
// Construct a blob containing cache data.
LPBYTE pBuf;
ULONG cbBuf;
if( SUCCEEDED( (hr = _ComCatCacheFromDSA( hdsa, &pBuf, &cbBuf )) ) )
{
// Write it to 'Implementing' reg value
hr = RegSetValueEx( hkeyCache, REGVAL_COMCATEX_IMPLEMENTING, 0L,
REG_BINARY, pBuf, cbBuf );
if( pBuf )
delete [] pBuf;
}
RegCloseKey( hkeyCache );
}
}
return hr;
}
//-------------------------------------------------------------------------//
// Writes a series of CLSIDs from a DSA to a registry-based cache of
// requiring classes for the specified component category.
STDMETHODIMP _WriteRequiringClassesOfCategory(
IN REFCATID refcatid,
IN HDSA hdsa )
{
TCHAR szKey[MAX_PATH];
HRESULT hr;
// Create/Open key HKCR\Component Categories\{catid}\Enum
if( SUCCEEDED( (hr = _MakeComCatCacheKey( refcatid, szKey, ARRAYSIZE(szKey) )) ) )
{
HKEY hkeyCache = NULL;
ULONG dwRet,
dwDisposition;
dwRet = RegCreateKeyEx( HKEY_CURRENT_USER, szKey, 0L,
NULL, 0L, KEY_WRITE, NULL,
&hkeyCache, &dwDisposition );
hr = HRESULT_FROM_WIN32( dwRet );
if( SUCCEEDED( hr ) )
{
// Construct a blob containing cache data.
LPBYTE pBuf;
ULONG cbBuf;
if( SUCCEEDED( (hr = _ComCatCacheFromDSA( hdsa, &pBuf, &cbBuf )) ) )
{
// Write it to 'Requirng' reg value
hr = RegSetValueEx( hkeyCache, REGVAL_COMCATEX_REQUIRING, 0L,
REG_BINARY, pBuf, cbBuf );
if( pBuf )
delete [] pBuf;
}
RegCloseKey( hkeyCache );
}
}
return hr;
}
//-------------------------------------------------------------------------//
// Does work of caching implementing and requiring classes for the specified categories
STDMETHODIMP _WriteClassesOfCategories(
ULONG cImplemented, //Number of category IDs in the rgcatidImpl array
CATID rgcatidImpl[], //Array of category identifiers
ULONG cRequired, //Number of category IDs in the rgcatidReq array
CATID rgcatidReq[], //Array of category identifiers
BOOL bForceUpdate ) //TRUE: unconditionally update the cache; otherwise
// update iif the cache doesn't exist.
{
HRESULT hr = S_OK;
ULONG i;
// Cache implementing classes of each category.
for( i = 0; i< cImplemented; i++ )
{
if( bForceUpdate || S_OK != SHDoesComCatCacheExist( rgcatidImpl[i], TRUE ) )
{
HRESULT hrCatid;
if( FAILED( (hrCatid = SHWriteImplementingClassesOfCategory( rgcatidImpl[i] )) ) )
hr = hrCatid;
}
}
// Cache requiring classes of each category.
for( i = 0; i< cRequired; i++ )
{
if( bForceUpdate || S_OK != SHDoesComCatCacheExist( rgcatidReq[i], FALSE ) )
{
HRESULT hrCatid;
if( FAILED( (hrCatid = SHWriteRequiringClassesOfCategory( rgcatidReq[i] )) ) )
hr = hrCatid;
}
}
return hr;
}