Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

943 lines
32 KiB

  1. #include "priv.h"
  2. #include "comcatex.h"
  3. #include "runtask.h"
  4. //------------------//
  5. // Misc constants
  6. static LPCTSTR
  7. #ifdef _WIN64
  8. REGKEY_COMCATEX = STRREG_DISCARDABLE STRREG_POSTSETUP TEXT("\\Component Categories64"),
  9. #else
  10. REGKEY_COMCATEX = STRREG_DISCARDABLE STRREG_POSTSETUP TEXT("\\Component Categories"),
  11. #endif
  12. REGKEY_COMCATEX_ENUM = TEXT("Enum"), // HKCR\ComponentClasses\{catid}\Enum
  13. REGVAL_COMCATEX_IMPLEMENTING = TEXT("Implementing"),// HKCR\ComponentClasses\{catid}\Enum\Implementing
  14. REGVAL_COMCATEX_REQUIRING = TEXT("Requiring"); // HKCR\ComponentClasses\{catid}\Enum\Requiring
  15. static const ULONG
  16. COMCAT_CACHE_CURRENTVERSION = MAKELONG(1,0); // current cache version.
  17. //-------------//
  18. // Cache header
  19. typedef struct {
  20. ULONG cbStruct; // structure size
  21. ULONG ver; // version string (COMCAT_CACHE_CURRENTVERSION)
  22. SYSTEMTIME stLastUpdate; // UTC date, time of last update.
  23. ULONG cClsid; // number of CLSIDs to follow
  24. CLSID clsid[]; // array of CLSIDs
  25. } COMCAT_CACHE_HEADER;
  26. //----------------//
  27. // Impl helpers
  28. STDMETHODIMP _EnumerateGuids( IN IEnumGUID* pEnumGUID, OUT HDSA* phdsa );
  29. STDMETHODIMP _ComCatCacheFromDSA( IN HDSA hdsa, OUT LPBYTE* pBuf, OUT LPDWORD pcbBuf );
  30. STDMETHODIMP _DSAFromComCatCache( IN LPBYTE pBuf, IN ULONG cbBuf, OUT HDSA* phdsa );
  31. STDMETHODIMP _MakeComCatCacheKey( IN REFCATID refcatid, OUT LPTSTR pszKey, IN ULONG cchKey );
  32. STDMETHODIMP _ReadClassesOfCategory( IN REFCATID refcatid, OUT HDSA* phdsa, LPCTSTR pszRegValueName );
  33. STDMETHODIMP _WriteImplementingClassesOfCategory( IN REFCATID refcatid, IN HDSA hdsa );
  34. STDMETHODIMP _WriteRequiringClassesOfCategory( IN REFCATID refcatid, IN HDSA hdsa );
  35. STDMETHODIMP _WriteClassesOfCategories( IN ULONG, IN CATID [], IN ULONG, IN CATID [], BOOL );
  36. STDMETHODIMP _BuildCacheIfNecessary( IN REFCATID refcatid, BOOL fImplementing);
  37. STDAPI _CComCatCache_CommonCreateInstance( BOOL, OUT void**);
  38. //-----------------------//
  39. // Higher-level methods
  40. STDMETHODIMP SHReadImplementingClassesOfCategory( REFCATID refcatid, OUT HDSA* phdsa );
  41. STDMETHODIMP SHReadRequiringClassesOfCategory( REFCATID refcatid, OUT HDSA* phdsa );
  42. STDMETHODIMP SHWriteImplementingClassesOfCategory( REFCATID refcatid );
  43. STDMETHODIMP SHWriteRequiringClassesOfCategory( REFCATID refcatid );
  44. #define SAFE_DESTROY_CLSID_DSA(hdsa) \
  45. if((hdsa)) { DSA_Destroy((hdsa)); (hdsa)=NULL; }
  46. //-------------------------------------------------------------------------//
  47. // Cache-aware component categories enumerator object
  48. class CSHEnumClassesOfCategories : public IEnumGUID
  49. //-------------------------------------------------------------------------//
  50. {
  51. public:
  52. // IUnknown methods
  53. STDMETHOD_ (ULONG, AddRef)() {
  54. return InterlockedIncrement( &_cRef );
  55. }
  56. STDMETHOD_ (ULONG, Release)() {
  57. if( InterlockedDecrement( &_cRef )==0 ) {
  58. delete this; return 0;
  59. }
  60. return _cRef;
  61. }
  62. STDMETHOD (QueryInterface)( REFIID riid, void **ppvObj);
  63. // IEnum methods
  64. STDMETHOD (Next)( ULONG celt, GUID* rgelt, ULONG* pceltFetched );
  65. STDMETHOD (Skip)( ULONG celt );
  66. STDMETHOD (Reset)();
  67. STDMETHOD (Clone)( IEnumGUID ** ppenum );
  68. protected:
  69. CSHEnumClassesOfCategories();
  70. virtual ~CSHEnumClassesOfCategories();
  71. STDMETHOD (Initialize)( ULONG cImpl, CATID rgcatidImpl[], ULONG cReq, CATID rgcatidReq[]);
  72. // invoke immediately after construction for arg validation.
  73. LONG _cRef, // ref count
  74. _iEnum; // enumerator index
  75. HDSA _hdsa; // CLSID DSA handle
  76. ULONG _cImpl, // count of catids to enumerate for implementing classes
  77. _cReq; // count of catids to enumerate for requiring classes
  78. CATID *_rgcatidImpl, // catids to enumerate for implementing classes
  79. *_rgcatidReq; // catids to enumerate for requiring classes
  80. friend STDMETHODIMP SHEnumClassesOfCategories( ULONG, CATID[], ULONG, CATID[], IEnumGUID**);
  81. };
  82. //-------------------------------------------------------------------------//
  83. // IRunnableTask derivative for asynchronous update of
  84. // component categories cache.
  85. class CComCatCacheTask : public CRunnableTask
  86. //-------------------------------------------------------------------------//
  87. {
  88. public:
  89. CComCatCacheTask();
  90. virtual ~CComCatCacheTask();
  91. STDMETHOD (Initialize)( ULONG cImplemented,
  92. CATID rgcatidImpl[],
  93. ULONG cRequired,
  94. CATID rgcatidReq[],
  95. BOOL bForceUpdate,
  96. HANDLE hEvent );
  97. STDMETHOD (Go)();
  98. protected:
  99. STDMETHOD (RunInitRT)()
  100. {
  101. HRESULT hr = _WriteClassesOfCategories( _cImpl, _rgcatidImpl,
  102. _cReq, _rgcatidReq, _bForceUpdate );
  103. if (_hEvent)
  104. SetEvent(_hEvent);
  105. return hr;
  106. }
  107. ULONG _cImpl, _cReq;
  108. CATID *_rgcatidImpl,
  109. *_rgcatidReq;
  110. BOOL _bForceUpdate;
  111. HANDLE _hEvent;
  112. friend HRESULT _CComCatCache_CommonCreateInstance( BOOL, OUT void**);
  113. };
  114. //-------------------------------------------------------------------------//
  115. // Entrypoint: retrieves cache-aware enumerator over classes which require or
  116. // implement the specified component catagory(ies).
  117. STDMETHODIMP SHEnumClassesOfCategories(
  118. ULONG cImplemented, //Number of category IDs in the rgcatidImpl array
  119. CATID rgcatidImpl[], //Array of category identifiers
  120. ULONG cRequired, //Number of category IDs in the rgcatidReq array
  121. CATID rgcatidReq[], //Array of category identifiers
  122. IEnumGUID** ppenumGUID ) //Location in which to return an IEnumGUID interface
  123. {
  124. HRESULT hr = S_OK;
  125. CSHEnumClassesOfCategories* pEnum = NULL;
  126. if( NULL == ppenumGUID )
  127. return E_INVALIDARG;
  128. *ppenumGUID = NULL;
  129. // Construct and initialize enumerator object
  130. if( NULL == (pEnum = new CSHEnumClassesOfCategories) )
  131. return E_OUTOFMEMORY;
  132. if( FAILED( (hr = pEnum->Initialize(
  133. cImplemented, rgcatidImpl, cRequired, rgcatidReq )) ) )
  134. {
  135. pEnum->Release();
  136. return hr;
  137. }
  138. *ppenumGUID = pEnum;
  139. return hr;
  140. }
  141. //-------------------------------------------------------------------------//
  142. // Determines whether a cache exists for the indicated CATID.
  143. // If bImplementing is TRUE, the function checks for a cache of
  144. // implementing classes; otherwise the function checks for a cache of
  145. // requiring classes.
  146. STDMETHODIMP SHDoesComCatCacheExist( REFCATID refcatid, BOOL bImplementing )
  147. {
  148. TCHAR szKey[MAX_PATH];
  149. HRESULT hr;
  150. if( SUCCEEDED( (hr = _MakeComCatCacheKey( refcatid, szKey, ARRAYSIZE(szKey) )) ) )
  151. {
  152. HKEY hkeyCache;
  153. DWORD dwRet = RegOpenKeyEx( HKEY_CURRENT_USER, szKey, 0L, KEY_READ, &hkeyCache );
  154. hr = S_FALSE;
  155. if( ERROR_SUCCESS == dwRet )
  156. {
  157. DWORD dwType, cbData = 0;
  158. dwRet = RegQueryValueEx( hkeyCache,
  159. bImplementing ? REGVAL_COMCATEX_IMPLEMENTING :
  160. REGVAL_COMCATEX_REQUIRING,
  161. 0L, &dwType, NULL, &cbData );
  162. // We'll confirm only on value type and size of data.
  163. if( ERROR_SUCCESS == dwRet &&
  164. dwType == REG_BINARY &&
  165. sizeof(COMCAT_CACHE_HEADER) <= cbData )
  166. {
  167. hr = S_OK;
  168. }
  169. RegCloseKey( hkeyCache );
  170. }
  171. }
  172. return hr;
  173. }
  174. //-------------------------------------------------------------------------//
  175. // Entrypoint: Caches implementing and requiring classes for the
  176. // specified categories with asynchronous option.
  177. STDMETHODIMP SHWriteClassesOfCategories(
  178. ULONG cImplemented, //Number of category IDs in the rgcatidImpl array
  179. CATID rgcatidImpl[], //Array of category identifiers
  180. ULONG cRequired, //Number of category IDs in the rgcatidReq array
  181. CATID rgcatidReq[], //Array of category identifiers
  182. BOOL bForceUpdate, // TRUE: Unconditionally update the cache; FALSE: create cache iif doesn't exist.
  183. BOOL bWait, //If FALSE, the function returns immediately and the
  184. // caching occurs asynchronously; otherwise
  185. // the function returns only after the caching
  186. // operation has completed.
  187. HANDLE hEvent //(optional) Event to be signalled when cache update is done
  188. )
  189. {
  190. HRESULT hr;
  191. if( bWait )
  192. {
  193. // Synchronous update
  194. hr = _WriteClassesOfCategories( cImplemented, rgcatidImpl, cRequired, rgcatidReq, bForceUpdate );
  195. if (hEvent)
  196. SetEvent(hEvent);
  197. }
  198. else
  199. {
  200. // Asynchronous update
  201. CComCatCacheTask* pTask = new CComCatCacheTask();
  202. if (pTask)
  203. {
  204. // Initialize with caller's args:
  205. if( SUCCEEDED( (hr = pTask->Initialize(
  206. cImplemented, rgcatidImpl, cRequired, rgcatidReq, bForceUpdate, hEvent )) ) )
  207. {
  208. hr = pTask->Go();
  209. }
  210. pTask->Release();
  211. }
  212. else
  213. hr = E_OUTOFMEMORY;
  214. }
  215. return hr;
  216. }
  217. //-------------------------------------------------------------------------//
  218. // CSHEnumClassesOfCategories class implementation
  219. //-------------------------------------------------------------------------//
  220. //-------------------------------------------------------------------------//
  221. inline CSHEnumClassesOfCategories::CSHEnumClassesOfCategories()
  222. : _cImpl(0), _rgcatidImpl(NULL),
  223. _cReq(0), _rgcatidReq(NULL),
  224. _cRef(1), _iEnum(0), _hdsa(NULL)
  225. {
  226. DllAddRef();
  227. }
  228. //-------------------------------------------------------------------------//
  229. CSHEnumClassesOfCategories::~CSHEnumClassesOfCategories()
  230. {
  231. delete [] _rgcatidImpl;
  232. delete [] _rgcatidReq;
  233. SAFE_DESTROY_CLSID_DSA( _hdsa );
  234. DllRelease();
  235. }
  236. //-------------------------------------------------------------------------//
  237. STDMETHODIMP CSHEnumClassesOfCategories::QueryInterface( REFIID riid, void **ppvObj )
  238. {
  239. static const QITAB qit[] = {
  240. QITABENT(CSHEnumClassesOfCategories, IEnumGUID),
  241. { 0 },
  242. };
  243. return QISearch(this, qit, riid, ppvObj);
  244. }
  245. //-------------------------------------------------------------------------//
  246. STDMETHODIMP CSHEnumClassesOfCategories::Initialize(
  247. ULONG cImplemented,
  248. CATID rgcatidImpl[],
  249. ULONG cRequired,
  250. CATID rgcatidReq[]
  251. )
  252. {
  253. // Disallow multiple initialization.
  254. if( _hdsa || _rgcatidImpl || _rgcatidReq )
  255. return S_FALSE;
  256. // Superficial arg validation:
  257. if( (0==cImplemented && 0==cRequired) ||
  258. (cImplemented && NULL == rgcatidImpl) ||
  259. (cRequired && NULL == rgcatidReq) )
  260. {
  261. return E_INVALIDARG;
  262. }
  263. // Allocate and make copies of CATID arrays
  264. if( cImplemented )
  265. {
  266. if( NULL == (_rgcatidImpl = new CATID[cImplemented]) )
  267. return E_OUTOFMEMORY;
  268. CopyMemory( _rgcatidImpl, rgcatidImpl, sizeof(CATID) * cImplemented );
  269. }
  270. _cImpl = cImplemented;
  271. if( cRequired )
  272. {
  273. if( NULL == (_rgcatidReq = new CATID[cRequired]) )
  274. return E_OUTOFMEMORY;
  275. CopyMemory( _rgcatidReq, rgcatidReq, sizeof(CATID) * cRequired );
  276. }
  277. _cReq = cRequired;
  278. return S_OK;
  279. }
  280. //-------------------------------------------------------------------------//
  281. // Iterates implementing and/or requiring classes for the caller-specified
  282. // component categories.
  283. STDMETHODIMP CSHEnumClassesOfCategories::Next(
  284. ULONG celt,
  285. GUID* rgelt,
  286. ULONG* pceltFetched )
  287. {
  288. if( pceltFetched )
  289. *pceltFetched = 0;
  290. HRESULT hr = S_FALSE;
  291. ULONG celtFetched = 0;
  292. // Have we assembled our collection?
  293. if( NULL == _hdsa )
  294. {
  295. _iEnum = 0;
  296. ULONG i;
  297. for( i=0; SUCCEEDED( hr ) && i < _cImpl; i++ )
  298. {
  299. // Try reading implementing classes from cache
  300. if( FAILED( (hr = SHReadImplementingClassesOfCategory( _rgcatidImpl[i], &_hdsa )) ) )
  301. {
  302. // Uncached; try caching and then re-read.
  303. if( FAILED( (hr = SHWriteImplementingClassesOfCategory( _rgcatidImpl[i] )) ) ||
  304. FAILED( (hr = SHReadImplementingClassesOfCategory( _rgcatidImpl[i], &_hdsa )) ) )
  305. break;
  306. }
  307. }
  308. for( i=0; SUCCEEDED( hr ) && i < _cReq; i++ )
  309. {
  310. // Try reading requiring classes from cache
  311. if( FAILED( (hr = SHReadRequiringClassesOfCategory( _rgcatidReq[i], &_hdsa )) ) )
  312. {
  313. // Uncached; try caching and then re-read.
  314. if( FAILED( (hr = SHWriteRequiringClassesOfCategory( _rgcatidReq[i] )) ) ||
  315. FAILED( (hr = SHReadRequiringClassesOfCategory( _rgcatidReq[i], &_hdsa )) ) )
  316. break;
  317. }
  318. }
  319. }
  320. if( NULL != _hdsa )
  321. {
  322. LONG count = DSA_GetItemCount( _hdsa );
  323. while( celtFetched < celt && _iEnum < count )
  324. {
  325. if( DSA_GetItem( _hdsa, _iEnum, &rgelt[celtFetched] ) )
  326. celtFetched++;
  327. _iEnum++;
  328. }
  329. return celtFetched == celt ? S_OK : S_FALSE;
  330. }
  331. return SUCCEEDED( hr ) ? S_FALSE : hr;
  332. }
  333. //-------------------------------------------------------------------------//
  334. inline STDMETHODIMP CSHEnumClassesOfCategories::Skip( ULONG celt )
  335. {
  336. InterlockedExchange( &_iEnum, _iEnum + celt );
  337. return S_OK;
  338. }
  339. //-------------------------------------------------------------------------//
  340. inline STDMETHODIMP CSHEnumClassesOfCategories::Reset( void )
  341. {
  342. InterlockedExchange( &_iEnum, 0 );
  343. return S_OK;
  344. }
  345. //-------------------------------------------------------------------------//
  346. inline STDMETHODIMP CSHEnumClassesOfCategories::Clone( IEnumGUID ** ppenum )
  347. {
  348. return E_NOTIMPL;
  349. }
  350. //-------------------------------------------------------------------------//
  351. // CComCatCacheTask class implementation
  352. //-------------------------------------------------------------------------//
  353. //-------------------------------------------------------------------------//
  354. STDAPI CComCatConditionalCacheTask_CreateInstance( IN IUnknown*, OUT void** ppOut, LPCOBJECTINFO )
  355. {
  356. return _CComCatCache_CommonCreateInstance( FALSE /* iif not exists */, ppOut );
  357. }
  358. //-------------------------------------------------------------------------//
  359. STDAPI CComCatCacheTask_CreateInstance( IN IUnknown*, OUT void** ppOut, LPCOBJECTINFO poi )
  360. {
  361. return _CComCatCache_CommonCreateInstance( TRUE /* unconditionally update */, ppOut );
  362. }
  363. //-------------------------------------------------------------------------//
  364. STDAPI _CComCatCache_CommonCreateInstance(
  365. BOOL bForceUpdate,
  366. OUT void** ppOut )
  367. {
  368. CComCatCacheTask* pTask;
  369. if( NULL == (pTask = new CComCatCacheTask) )
  370. return E_OUTOFMEMORY;
  371. HRESULT hr = S_OK;
  372. // We're being CoCreated without args, so we'll use
  373. // a hard-coded list of likely suspects (catids) to cache.
  374. static CATID rgcatid[2];
  375. rgcatid[0] = CATID_InfoBand;
  376. rgcatid[1] = CATID_CommBand;
  377. if( FAILED( (hr = pTask->Initialize( ARRAYSIZE(rgcatid), rgcatid, 0, NULL, bForceUpdate, NULL )) ) )
  378. {
  379. pTask->Release();
  380. return hr;
  381. }
  382. *ppOut = SAFECAST( pTask, IRunnableTask*);
  383. return hr;
  384. }
  385. //-------------------------------------------------------------------------//
  386. inline CComCatCacheTask::CComCatCacheTask()
  387. : CRunnableTask( RTF_DEFAULT ),
  388. _cImpl(0), _cReq(0), _rgcatidImpl(NULL), _rgcatidReq(NULL), _bForceUpdate(TRUE)
  389. {
  390. }
  391. //-------------------------------------------------------------------------//
  392. inline CComCatCacheTask::~CComCatCacheTask()
  393. {
  394. delete [] _rgcatidImpl;
  395. delete [] _rgcatidReq;
  396. if (_hEvent)
  397. CloseHandle(_hEvent);
  398. }
  399. //-------------------------------------------------------------------------//
  400. STDMETHODIMP CComCatCacheTask::Initialize(
  401. ULONG cImplemented,
  402. CATID rgcatidImpl[],
  403. ULONG cRequired,
  404. CATID rgcatidReq[],
  405. BOOL bForceUpdate,
  406. HANDLE hEvent)
  407. {
  408. // Superficial arg validation:
  409. if( (0==cImplemented && 0==cRequired) ||
  410. (cImplemented && NULL == rgcatidImpl) ||
  411. (cRequired && NULL == rgcatidReq) )
  412. {
  413. return E_INVALIDARG;
  414. }
  415. // Disallow multiple initialization.
  416. if( _rgcatidImpl || _rgcatidReq )
  417. return S_FALSE;
  418. // Allocate and make copies of CATID arrays
  419. if( cImplemented )
  420. {
  421. if( NULL == (_rgcatidImpl = new CATID[cImplemented]) )
  422. return E_OUTOFMEMORY;
  423. CopyMemory( _rgcatidImpl, rgcatidImpl, sizeof(CATID) * cImplemented );
  424. }
  425. _cImpl = cImplemented;
  426. if( cRequired )
  427. {
  428. if( NULL == (_rgcatidReq = new CATID[cRequired]) )
  429. return E_OUTOFMEMORY;
  430. CopyMemory( _rgcatidReq, rgcatidReq, sizeof(CATID) * cRequired );
  431. }
  432. _cReq = cRequired;
  433. _bForceUpdate = bForceUpdate;
  434. if (hEvent)
  435. {
  436. HANDLE hProcess = GetCurrentProcess();
  437. DuplicateHandle(hProcess, hEvent, hProcess, &_hEvent, 0, FALSE, DUPLICATE_SAME_ACCESS);
  438. }
  439. return S_OK;
  440. }
  441. //-------------------------------------------------------------------------//
  442. // Initiates asynchronous update of component categories cache.
  443. STDMETHODIMP CComCatCacheTask::Go()
  444. {
  445. // Run the task from the shared thread pool
  446. IShellTaskScheduler* pScheduler;
  447. HRESULT hr = CoCreateInstance( CLSID_SharedTaskScheduler, NULL, CLSCTX_INPROC_SERVER,
  448. IID_IShellTaskScheduler, (LPVOID*)&pScheduler );
  449. if( SUCCEEDED( hr ) )
  450. {
  451. hr = pScheduler->AddTask( this, CLSID_ComCatCacheTask, 0L, ITSAT_DEFAULT_PRIORITY );
  452. // heap alloc'd memory belongs to scheduler thread.
  453. pScheduler->Release(); // OK to release shared scheduler before task has completed.
  454. }
  455. return hr;
  456. }
  457. //-------------------------------------------------------------------------//
  458. // Component cache implementation
  459. //-------------------------------------------------------------------------//
  460. STDMETHODIMP _BuildCacheIfNecessary(
  461. IN REFCATID refcatid,
  462. BOOL fImplementing)
  463. {
  464. HRESULT hr = S_OK;
  465. if (S_OK != SHDoesComCatCacheExist(refcatid, fImplementing))
  466. {
  467. hr = fImplementing ? SHWriteImplementingClassesOfCategory(refcatid)
  468. : SHWriteRequiringClassesOfCategory(refcatid);
  469. }
  470. return hr;
  471. }
  472. //-------------------------------------------------------------------------//
  473. // Reads a series of CLSIDs from a registry-based cache of
  474. // implementing classes for the specified component category into a DSA.
  475. // If the DSA is NULL, a new DSA is created; otherwise the CLSIDS are appended to
  476. // the provided DSA.
  477. inline STDMETHODIMP SHReadImplementingClassesOfCategory(
  478. IN REFCATID refcatid,
  479. OUT HDSA* phdsa )
  480. {
  481. HRESULT hr = _BuildCacheIfNecessary(refcatid, TRUE);
  482. if (SUCCEEDED(hr))
  483. {
  484. hr = _ReadClassesOfCategory( refcatid, phdsa, REGVAL_COMCATEX_IMPLEMENTING );
  485. }
  486. return hr;
  487. }
  488. //-------------------------------------------------------------------------//
  489. // Reads a series of CLSIDs from a registry-based cache of
  490. // requiring classes for the specified component category into a DSA.
  491. // If the DSA is NULL, a new DSA is created; otherwise the CLSIDS are appended to
  492. // the provided DSA.
  493. inline STDMETHODIMP SHReadRequiringClassesOfCategory(
  494. IN REFCATID refcatid,
  495. OUT HDSA* phdsa )
  496. {
  497. HRESULT hr = _BuildCacheIfNecessary(refcatid, FALSE);
  498. if (SUCCEEDED(hr))
  499. {
  500. hr = _ReadClassesOfCategory( refcatid, phdsa, REGVAL_COMCATEX_REQUIRING );
  501. }
  502. return hr;
  503. }
  504. //-------------------------------------------------------------------------//
  505. // Caches a list of classes which implement the indicated component category.
  506. STDMETHODIMP SHWriteImplementingClassesOfCategory( IN REFCATID refcatid )
  507. {
  508. HRESULT hr;
  509. // Retrieve OLE component category manager
  510. ICatInformation* pci;
  511. if( SUCCEEDED( (hr = CoCreateInstance( CLSID_StdComponentCategoriesMgr,
  512. NULL, CLSCTX_INPROC_SERVER,
  513. IID_ICatInformation, (LPVOID*)&pci)) ) )
  514. {
  515. // Retrieve enumerator over classes that implement the category
  516. IEnumGUID* pEnumGUID;
  517. if( SUCCEEDED( (hr = pci->EnumClassesOfCategories( 1, (CATID*)&refcatid,
  518. 0, NULL, &pEnumGUID )) ) )
  519. {
  520. HDSA hdsa = NULL;
  521. if( SUCCEEDED( (hr = _EnumerateGuids( pEnumGUID, &hdsa )) ) )
  522. {
  523. // Write to cache
  524. hr = _WriteImplementingClassesOfCategory( refcatid, hdsa );
  525. SAFE_DESTROY_CLSID_DSA( hdsa );
  526. }
  527. pEnumGUID->Release();
  528. }
  529. pci->Release();
  530. }
  531. return hr;
  532. }
  533. //-------------------------------------------------------------------------//
  534. // Caches a list of classes which require the indicated component category.
  535. STDMETHODIMP SHWriteRequiringClassesOfCategory( IN REFCATID refcatid )
  536. {
  537. HRESULT hr;
  538. // Retrieve OLE component category manager
  539. ICatInformation* pci;
  540. if( SUCCEEDED( (hr = CoCreateInstance( CLSID_StdComponentCategoriesMgr,
  541. NULL, CLSCTX_INPROC_SERVER,
  542. IID_ICatInformation, (LPVOID*)&pci)) ) )
  543. {
  544. // Retrieve enumerator over classes that require the category
  545. IEnumGUID* pEnumGUID;
  546. if( SUCCEEDED( (hr = pci->EnumClassesOfCategories( 0, NULL, 1,
  547. (CLSID*)&refcatid,
  548. &pEnumGUID )) ) )
  549. {
  550. HDSA hdsa = NULL;
  551. if( SUCCEEDED( (hr = _EnumerateGuids( pEnumGUID, &hdsa )) ) )
  552. {
  553. // Write to cache
  554. hr = _WriteRequiringClassesOfCategory( refcatid, hdsa );
  555. SAFE_DESTROY_CLSID_DSA( hdsa );
  556. }
  557. pEnumGUID->Release();
  558. }
  559. pci->Release();
  560. }
  561. return hr;
  562. }
  563. //-------------------------------------------------------------------------//
  564. // Accepts a valid GUID enumerator and constructs an HDSA containing the GUIDS.
  565. // The caller is responsible for freeing the HDSA which may or may not
  566. // have been allocated.
  567. STDMETHODIMP _EnumerateGuids( IEnumGUID* pEnumGUID, OUT HDSA* phdsa )
  568. {
  569. ASSERT( pEnumGUID );
  570. ASSERT( phdsa );
  571. ULONG celtFetched;
  572. CLSID clsid;
  573. HRESULT hr;
  574. while( SUCCEEDED( (hr = pEnumGUID->Next( 1, &clsid, &celtFetched )) ) &&
  575. celtFetched > 0 )
  576. {
  577. if( NULL == *phdsa &&
  578. NULL == (*phdsa = DSA_Create( sizeof(CLSID), 4 )) )
  579. {
  580. hr = E_OUTOFMEMORY;
  581. break;
  582. }
  583. DSA_AppendItem( *phdsa, &clsid );
  584. }
  585. // translate S_FALSE.
  586. return SUCCEEDED( hr ) ? S_OK : hr;
  587. }
  588. //-------------------------------------------------------------------------//
  589. // Generates a persistable cache of CLSIDs derived from the CLSID* DSA.
  590. STDMETHODIMP _ComCatCacheFromDSA( IN HDSA hdsa, OUT LPBYTE* pBuf, OUT LPDWORD pcbBuf )
  591. {
  592. ASSERT( pBuf );
  593. ASSERT( pcbBuf );
  594. ULONG cClsid = hdsa ? DSA_GetItemCount( hdsa ) : 0,
  595. cbBuf = sizeof(COMCAT_CACHE_HEADER) + (cClsid * sizeof(CLSID));
  596. HRESULT hr = S_OK;
  597. // Allocate blob
  598. *pcbBuf = 0;
  599. if( NULL != (*pBuf = new BYTE[cbBuf]) )
  600. {
  601. // Initialize header
  602. COMCAT_CACHE_HEADER* pCache = (COMCAT_CACHE_HEADER*)(*pBuf);
  603. pCache->cbStruct = sizeof(*pCache);
  604. pCache->ver = COMCAT_CACHE_CURRENTVERSION;
  605. pCache->cClsid = 0;
  606. GetSystemTime( &pCache->stLastUpdate );
  607. // Copy CLSIDs
  608. for( ULONG i = 0; i< cClsid; i++ )
  609. DSA_GetItem( hdsa, i, &pCache->clsid[pCache->cClsid++] );
  610. // Adjust output size.
  611. *pcbBuf = sizeof(*pCache) + (pCache->cClsid * sizeof(CLSID));
  612. }
  613. else
  614. hr = E_OUTOFMEMORY;
  615. return hr;
  616. }
  617. //-------------------------------------------------------------------------//
  618. // Appends CLSIDS from the cache buffer to the specified DSA. If the DSA is
  619. // NULL, a new DSA is created.
  620. STDMETHODIMP _DSAFromComCatCache( IN LPBYTE pBuf, IN ULONG cbBuf, OUT HDSA* phdsa )
  621. {
  622. ASSERT( pBuf );
  623. ASSERT( phdsa );
  624. HRESULT hr = S_OK;
  625. COMCAT_CACHE_HEADER* pCache = (COMCAT_CACHE_HEADER*)pBuf;
  626. // Validate header
  627. if( !( sizeof(*pCache) <= cbBuf &&
  628. sizeof(*pCache) == pCache->cbStruct &&
  629. COMCAT_CACHE_CURRENTVERSION == pCache->ver ) )
  630. return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
  631. // Create the DSA if necessary
  632. if( 0 == pCache->cClsid )
  633. return S_FALSE;
  634. if( NULL == *phdsa && NULL == (*phdsa = DSA_Create( sizeof(CLSID), 4 )) )
  635. return E_OUTOFMEMORY;
  636. // Copy CLSIDs from the cache to the DSA.
  637. for( ULONG i = 0; i< pCache->cClsid; i++ )
  638. DSA_AppendItem( *phdsa, &pCache->clsid[i] );
  639. return hr;
  640. }
  641. //-------------------------------------------------------------------------//
  642. // Constructs a component category registry cache key based on the
  643. // specified CATID.
  644. STDMETHODIMP _MakeComCatCacheKey(
  645. IN REFCATID refcatid,
  646. OUT LPTSTR pszKey,
  647. IN ULONG cchKey )
  648. {
  649. TCHAR szCLSID[GUIDSTR_MAX];
  650. if( SHStringFromGUID( refcatid, szCLSID, ARRAYSIZE(szCLSID) )<=0 )
  651. return E_INVALIDARG;
  652. ASSERT( cchKey > (ULONG)(lstrlen( REGKEY_COMCATEX ) + GUIDSTR_MAX) );
  653. // "Component Categories\{clsid}\Enum"
  654. if( wnsprintf( pszKey, cchKey, TEXT("%s\\%s\\%s"),
  655. REGKEY_COMCATEX, szCLSID, REGKEY_COMCATEX_ENUM ) > 0 )
  656. return S_OK;
  657. return E_FAIL;
  658. }
  659. //-------------------------------------------------------------------------//
  660. // Reads a cache of implementing or requiring classes info a CLSID DSA.
  661. STDMETHODIMP _ReadClassesOfCategory(
  662. IN REFCATID refcatid,
  663. OUT HDSA* phdsa,
  664. LPCTSTR pszRegValueName /*REGVAL_COMCATEX_IMPLEMENTING/REQUIRING*/ )
  665. {
  666. TCHAR szKey[MAX_PATH];
  667. HRESULT hr;
  668. // Create/Open key HKCR\Component Categories\{catid}\Enum
  669. if( SUCCEEDED( (hr = _MakeComCatCacheKey( refcatid, szKey, ARRAYSIZE(szKey) )) ) )
  670. {
  671. HKEY hkeyCache = NULL;
  672. DWORD dwRet = RegOpenKeyEx( HKEY_CURRENT_USER, szKey, 0L, KEY_READ, &hkeyCache );
  673. hr = HRESULT_FROM_WIN32( dwRet );
  674. if( SUCCEEDED( hr ) )
  675. {
  676. // Determine required buffer size.
  677. LPBYTE pBuf = NULL;
  678. ULONG cbBuf = 0,
  679. dwType,
  680. dwRet = RegQueryValueEx( hkeyCache, pszRegValueName, 0L,
  681. &dwType, NULL, &cbBuf );
  682. hr = HRESULT_FROM_WIN32( dwRet );
  683. if (SUCCEEDED(hr))
  684. {
  685. // Allocate buffer and read
  686. if( NULL != (pBuf = new BYTE[cbBuf]) )
  687. {
  688. dwRet = RegQueryValueEx( hkeyCache, pszRegValueName, 0L,
  689. &dwType, pBuf, &cbBuf );
  690. hr = HRESULT_FROM_WIN32( dwRet );
  691. }
  692. else
  693. hr = E_OUTOFMEMORY;
  694. }
  695. if( SUCCEEDED( hr ) )
  696. {
  697. // Gather CLSIDs into the DSA
  698. hr = REG_BINARY == dwType ?
  699. _DSAFromComCatCache( pBuf, cbBuf, phdsa ) : E_ABORT;
  700. }
  701. if( pBuf ) delete [] pBuf;
  702. RegCloseKey( hkeyCache );
  703. }
  704. }
  705. return hr;
  706. }
  707. //-------------------------------------------------------------------------//
  708. // Writes a series of CLSIDs from a DSA to a registry-based cache of
  709. // implementing classes for the specified component category.
  710. STDMETHODIMP _WriteImplementingClassesOfCategory(
  711. IN REFCATID refcatid,
  712. IN HDSA hdsa )
  713. {
  714. TCHAR szKey[MAX_PATH];
  715. HRESULT hr;
  716. // Create/Open key HKCR\Component Categories\{catid}\Enum
  717. if( SUCCEEDED( (hr = _MakeComCatCacheKey( refcatid, szKey, ARRAYSIZE(szKey) )) ) )
  718. {
  719. HKEY hkeyCache = NULL;
  720. ULONG dwRet, dwDisposition;
  721. dwRet = RegCreateKeyEx( HKEY_CURRENT_USER, szKey, 0L,
  722. NULL, 0L, KEY_WRITE, NULL,
  723. &hkeyCache, &dwDisposition );
  724. hr = HRESULT_FROM_WIN32( dwRet );
  725. if( SUCCEEDED( hr ) )
  726. {
  727. // Construct a blob containing cache data.
  728. LPBYTE pBuf;
  729. ULONG cbBuf;
  730. if( SUCCEEDED( (hr = _ComCatCacheFromDSA( hdsa, &pBuf, &cbBuf )) ) )
  731. {
  732. // Write it to 'Implementing' reg value
  733. hr = RegSetValueEx( hkeyCache, REGVAL_COMCATEX_IMPLEMENTING, 0L,
  734. REG_BINARY, pBuf, cbBuf );
  735. if( pBuf )
  736. delete [] pBuf;
  737. }
  738. RegCloseKey( hkeyCache );
  739. }
  740. }
  741. return hr;
  742. }
  743. //-------------------------------------------------------------------------//
  744. // Writes a series of CLSIDs from a DSA to a registry-based cache of
  745. // requiring classes for the specified component category.
  746. STDMETHODIMP _WriteRequiringClassesOfCategory(
  747. IN REFCATID refcatid,
  748. IN HDSA hdsa )
  749. {
  750. TCHAR szKey[MAX_PATH];
  751. HRESULT hr;
  752. // Create/Open key HKCR\Component Categories\{catid}\Enum
  753. if( SUCCEEDED( (hr = _MakeComCatCacheKey( refcatid, szKey, ARRAYSIZE(szKey) )) ) )
  754. {
  755. HKEY hkeyCache = NULL;
  756. ULONG dwRet,
  757. dwDisposition;
  758. dwRet = RegCreateKeyEx( HKEY_CURRENT_USER, szKey, 0L,
  759. NULL, 0L, KEY_WRITE, NULL,
  760. &hkeyCache, &dwDisposition );
  761. hr = HRESULT_FROM_WIN32( dwRet );
  762. if( SUCCEEDED( hr ) )
  763. {
  764. // Construct a blob containing cache data.
  765. LPBYTE pBuf;
  766. ULONG cbBuf;
  767. if( SUCCEEDED( (hr = _ComCatCacheFromDSA( hdsa, &pBuf, &cbBuf )) ) )
  768. {
  769. // Write it to 'Requirng' reg value
  770. hr = RegSetValueEx( hkeyCache, REGVAL_COMCATEX_REQUIRING, 0L,
  771. REG_BINARY, pBuf, cbBuf );
  772. if( pBuf )
  773. delete [] pBuf;
  774. }
  775. RegCloseKey( hkeyCache );
  776. }
  777. }
  778. return hr;
  779. }
  780. //-------------------------------------------------------------------------//
  781. // Does work of caching implementing and requiring classes for the specified categories
  782. STDMETHODIMP _WriteClassesOfCategories(
  783. ULONG cImplemented, //Number of category IDs in the rgcatidImpl array
  784. CATID rgcatidImpl[], //Array of category identifiers
  785. ULONG cRequired, //Number of category IDs in the rgcatidReq array
  786. CATID rgcatidReq[], //Array of category identifiers
  787. BOOL bForceUpdate ) //TRUE: unconditionally update the cache; otherwise
  788. // update iif the cache doesn't exist.
  789. {
  790. HRESULT hr = S_OK;
  791. ULONG i;
  792. // Cache implementing classes of each category.
  793. for( i = 0; i< cImplemented; i++ )
  794. {
  795. if( bForceUpdate || S_OK != SHDoesComCatCacheExist( rgcatidImpl[i], TRUE ) )
  796. {
  797. HRESULT hrCatid;
  798. if( FAILED( (hrCatid = SHWriteImplementingClassesOfCategory( rgcatidImpl[i] )) ) )
  799. hr = hrCatid;
  800. }
  801. }
  802. // Cache requiring classes of each category.
  803. for( i = 0; i< cRequired; i++ )
  804. {
  805. if( bForceUpdate || S_OK != SHDoesComCatCacheExist( rgcatidReq[i], FALSE ) )
  806. {
  807. HRESULT hrCatid;
  808. if( FAILED( (hrCatid = SHWriteRequiringClassesOfCategory( rgcatidReq[i] )) ) )
  809. hr = hrCatid;
  810. }
  811. }
  812. return hr;
  813. }