#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; }