Leaked source code of windows server 2003
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.

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