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.

1499 lines
39 KiB

  1. //+--------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1994 - 2001.
  5. //
  6. // File: msiclass.cpp
  7. //
  8. // Contents: msi class collection abstraction
  9. //
  10. // Classes:
  11. //
  12. //
  13. // History: 4-14-2000 adamed Created
  14. //
  15. //---------------------------------------------------------------------------
  16. #include "precomp.hxx"
  17. WCHAR* CClassCollection::_wszQueries[ TYPE_COUNT ] =
  18. {
  19. QUERY_EXTENSIONS,
  20. QUERY_CLSIDS,
  21. QUERY_VERSION_INDEPENDENT_PROGIDS
  22. };
  23. CClassCollection::CClassCollection( PACKAGEDETAIL* pPackageDetail ) :
  24. _pPackageDetail( pPackageDetail ),
  25. _cMaxClsids( 0 ),
  26. _cMaxExtensions( 0 ),
  27. _InstallLevel( 0 )
  28. {
  29. //
  30. // All memory referenced by the pPackageDetail must be
  31. // freed by the caller after the GetClasses method is called,
  32. // even if the call fails.
  33. //
  34. //
  35. // We need to clear any existing class information in the
  36. // PACKAGEDETAIL structure since we are going to overwrite
  37. // it eventually anyway
  38. //
  39. //
  40. // First clear the clsid's
  41. //
  42. DWORD iClass;
  43. //
  44. // Free each individual class
  45. //
  46. for ( iClass = 0; iClass < _pPackageDetail->pActInfo->cClasses; iClass++ )
  47. {
  48. FreeClassDetail( &(_pPackageDetail->pActInfo->pClasses[ iClass ]) );
  49. }
  50. //
  51. // Now free the vector that held the classes
  52. //
  53. LocalFree( _pPackageDetail->pActInfo->pClasses );
  54. //
  55. // Set our vector reference to the initial state of none
  56. //
  57. _pPackageDetail->pActInfo->pClasses = NULL;
  58. //
  59. // Set the initial state of no clsid's since they have all been freed
  60. //
  61. _pPackageDetail->pActInfo->cClasses = 0;
  62. //
  63. // Now clear the extensions
  64. //
  65. DWORD iExtension;
  66. //
  67. // For each individual extension
  68. //
  69. for ( iExtension = 0; iExtension < _pPackageDetail->pActInfo->cShellFileExt; iExtension++ )
  70. {
  71. LocalFree( _pPackageDetail->pActInfo->prgShellFileExt[ iExtension ] );
  72. }
  73. //
  74. // Free the vector that held the extensions
  75. //
  76. LocalFree( _pPackageDetail->pActInfo->prgShellFileExt );
  77. //
  78. // Also destroy the vector that held extension priorities
  79. //
  80. LocalFree( _pPackageDetail->pActInfo->prgPriority );
  81. //
  82. // Set our vector references to the initial state of none
  83. //
  84. _pPackageDetail->pActInfo->prgShellFileExt = NULL;
  85. _pPackageDetail->pActInfo->prgPriority = NULL;
  86. //
  87. // Set the initial state of no file extensions since they have all been freed
  88. //
  89. _pPackageDetail->pActInfo->cShellFileExt = 0;
  90. }
  91. HRESULT
  92. CClassCollection::GetClasses( BOOL bFileExtensionsOnly )
  93. {
  94. HRESULT hr;
  95. LONG Status;
  96. DWORD cTransforms;
  97. //
  98. // This method obtains the class metadata from an msi package + transforms.
  99. // The goal is to approximate the set of class data that would be advertised
  100. // on any system (regardless of system configuration) if the package were
  101. // advertised.
  102. //
  103. //
  104. // The classes will all be stored in the PACKAGEDETAIL structure. The caller
  105. // must free this memory after finishing with the structure, even if this
  106. // method fails
  107. //
  108. //
  109. // First, we must create a database representation of the package + transforms
  110. //
  111. //
  112. // The source list vector contains the package + transforms in application order --
  113. // we must subtract one source since the original package is included in the list
  114. //
  115. cTransforms = _pPackageDetail->cSources - 1;
  116. //
  117. // Now we create a database out of the package plus transforms. Since the
  118. // first item in the source list is the package, we pass that in as the package,
  119. // and all other items after it in the vector are passed in as the transform vector
  120. //
  121. Status = _Database.Open(
  122. _pPackageDetail->pszSourceList[0],
  123. cTransforms,
  124. cTransforms ? &(_pPackageDetail->pszSourceList[1]) : NULL );
  125. if ( ERROR_SUCCESS == Status )
  126. {
  127. //
  128. // We've successfully opened the package, now obtain its friendly name.
  129. //
  130. Status = GetFriendlyName();
  131. if (ERROR_SUCCESS == Status)
  132. {
  133. //
  134. // Now obtain its install level.
  135. // The install level affects whether or not a class will get advertised
  136. //
  137. Status = GetInstallLevel();
  138. }
  139. if ( ERROR_SUCCESS == Status )
  140. {
  141. //
  142. // Now that we know the install level of the package, we have
  143. // enough information to flag each advertisable feature in the database.
  144. // We need this because a class is only advertised if its associated
  145. // feature is advertised.
  146. //
  147. Status = FlagAdvertisableFeatures();
  148. }
  149. //
  150. // We may now retrieve the set of classes that will be advertised based
  151. // on the set of advertised features we flagged earlier. We care only
  152. // about 3 types of classes: Clsid's, ProgId's, and File Extenions.
  153. //
  154. if ( ! bFileExtensionsOnly )
  155. {
  156. if ( ERROR_SUCCESS == Status )
  157. {
  158. Status = GetClsids();
  159. }
  160. if ( ERROR_SUCCESS == Status )
  161. {
  162. Status = GetProgIds();
  163. }
  164. }
  165. if ( ERROR_SUCCESS == Status )
  166. {
  167. Status = GetExtensions();
  168. }
  169. LONG StatusFree;
  170. //
  171. // We must remove the scratch flags we added to the database
  172. //
  173. StatusFree = RemoveAdvertisableFeatureFlags();
  174. if ( ERROR_SUCCESS == Status )
  175. {
  176. //
  177. // Take care to preserve the return value -- a failure
  178. // before cleaning up the database takes precedence over
  179. // a failure in cleaning up the database.
  180. //
  181. Status = StatusFree;
  182. }
  183. }
  184. return HRESULT_FROM_WIN32(Status);
  185. }
  186. LONG
  187. CClassCollection::GetExtensions()
  188. {
  189. LONG Status;
  190. BOOL bTableExists;
  191. //
  192. // First check to see if we even have an extension table to query
  193. //
  194. Status = _Database.TableExists( TABLE_FILE_EXTENSIONS, &bTableExists );
  195. if ( ( ERROR_SUCCESS == Status ) && bTableExists )
  196. {
  197. //
  198. // Set up a destination in the user's PACKAGEDETAIL structure
  199. // for the shell extension class data
  200. //
  201. DataDestination Destination(
  202. TYPE_EXTENSION,
  203. (void**)&(_pPackageDetail->pActInfo->prgShellFileExt),
  204. &(_pPackageDetail->pActInfo->cShellFileExt),
  205. (UINT*) &_cMaxExtensions);
  206. //
  207. // Now retrieve the shell extensions
  208. //
  209. Status = GetElements(
  210. TYPE_EXTENSION,
  211. &Destination );
  212. if ( ERROR_SUCCESS == Status )
  213. {
  214. //
  215. // We've successfully retrieved the shell extensions --
  216. // the caller also expects a parallel array of priorities
  217. // with each shell extension -- the values are unimportant
  218. // since the caller will fill those in, but the memory must
  219. // exist, so we will allocate it.
  220. //
  221. _pPackageDetail->pActInfo->prgPriority =
  222. (UINT*) LocalAlloc(
  223. 0,
  224. sizeof(UINT) *
  225. _pPackageDetail->pActInfo->cShellFileExt );
  226. if ( ! _pPackageDetail->pActInfo->prgPriority )
  227. {
  228. Status = ERROR_NOT_ENOUGH_MEMORY;
  229. }
  230. }
  231. }
  232. return Status;
  233. }
  234. LONG
  235. CClassCollection::GetClsids()
  236. {
  237. LONG Status;
  238. BOOL bTableExists;
  239. //
  240. // First check to see if we even have a clsid table to query
  241. //
  242. Status = _Database.TableExists( TABLE_CLSIDS, &bTableExists );
  243. if ( ( ERROR_SUCCESS == Status ) && bTableExists )
  244. {
  245. //
  246. // Set the destination for the clsid's to a location
  247. // in the caller's PACKAGEDETAIL structure
  248. //
  249. DataDestination Destination(
  250. TYPE_CLSID,
  251. (void**)&(_pPackageDetail->pActInfo->pClasses),
  252. &(_pPackageDetail->pActInfo->cClasses),
  253. (UINT*) &_cMaxClsids);
  254. //
  255. // Now retrieve the clsid's for each package
  256. //
  257. Status = GetElements(
  258. TYPE_CLSID,
  259. &Destination );
  260. }
  261. return Status;
  262. }
  263. LONG
  264. CClassCollection::GetProgIds()
  265. {
  266. LONG Status;
  267. BOOL bTableExists;
  268. //
  269. // First check to see if we even have a ProgId table to query
  270. //
  271. Status = _Database.TableExists( TABLE_PROGIDS, &bTableExists );
  272. if ( ( ERROR_SUCCESS == Status ) && bTableExists )
  273. {
  274. //
  275. // This method MUST be called AFTER GetClsids -- progid's
  276. // are stored within their associated clsid's, so we will
  277. // not have a place to store the progid's unless we've
  278. // already obtained the clsid's.
  279. //
  280. //
  281. // At this point, we know only that we want to retrieve ProgId's --
  282. // we do not know their destination because this differs for
  283. // each progid depending on the associated clsid -- the NULL
  284. // parameters indicate that some callee will need to determine
  285. // the location for this data.
  286. //
  287. DataDestination Destination(
  288. TYPE_PROGID,
  289. NULL,
  290. NULL,
  291. NULL);
  292. //
  293. // Retrieve the progid's into the appropriate locations in the structure
  294. //
  295. Status = GetElements(
  296. TYPE_PROGID,
  297. &Destination );
  298. }
  299. return ERROR_SUCCESS;
  300. }
  301. LONG
  302. CClassCollection::GetElements(
  303. DWORD dwType,
  304. DataDestination* pDestination )
  305. {
  306. LONG Status;
  307. CMsiQuery ElementQuery;
  308. //
  309. // Perform the query for the class elements
  310. //
  311. Status = _Database.GetQueryResults(
  312. _wszQueries[ dwType ],
  313. &ElementQuery);
  314. if ( ERROR_SUCCESS != Status )
  315. {
  316. return Status;
  317. }
  318. for (;;)
  319. {
  320. //
  321. // We've obtained the results -- now we enumerate them so
  322. // that we can persist them in the caller's PACKAGEDETAIL
  323. // structure.
  324. //
  325. //
  326. // Note that we start a new scope so that our record object
  327. // will automatically free its resources
  328. //
  329. {
  330. CMsiRecord CurrentRecord;
  331. //
  332. // Enumerate the next record in the query result set
  333. //
  334. Status = ElementQuery.GetNextRecord( &CurrentRecord );
  335. if ( ERROR_SUCCESS != Status )
  336. {
  337. if ( ERROR_NO_MORE_ITEMS == Status )
  338. {
  339. Status = ERROR_SUCCESS;
  340. }
  341. break;
  342. }
  343. //
  344. // Now attempt to add the class data from this record into
  345. // the PACKAGEDETAIL structure
  346. //
  347. Status = ProcessElement(
  348. dwType,
  349. &CurrentRecord,
  350. pDestination);
  351. }
  352. if ( ERROR_SUCCESS != Status )
  353. {
  354. break;
  355. }
  356. }
  357. return Status;
  358. }
  359. LONG
  360. CClassCollection::FlagAdvertisableFeatures()
  361. {
  362. LONG Status;
  363. CMsiQuery FeatureQueryCreate;
  364. //
  365. // We will attempt to mark each feature in the database
  366. // with a flag indicating whether or not it will be advertised
  367. //
  368. //
  369. // First, add a column to the feature table of the database
  370. // so that we can use the column to flag whether or not the
  371. // feature is advertised.
  372. //
  373. Status = _Database.GetQueryResults(
  374. QUERY_ADVERTISED_FEATURES_CREATE,
  375. &FeatureQueryCreate);
  376. CMsiQuery FeatureQueryInit;
  377. //
  378. // Now intialize the new column's flags to 0 which
  379. // indicates that no features will be advertised (yet)
  380. //
  381. if ( ERROR_SUCCESS == Status )
  382. {
  383. Status = _Database.GetQueryResults(
  384. QUERY_ADVERTISED_FEATURES_INIT,
  385. &FeatureQueryInit);
  386. }
  387. CMsiQuery AllFeatures;
  388. //
  389. // Now we perform the query to retrieve all features --
  390. // records in this query will contain the newly added
  391. // flag column.
  392. //
  393. if ( ERROR_SUCCESS == Status )
  394. {
  395. Status = _Database.GetQueryResults(
  396. QUERY_ADVERTISED_FEATURES_RESULT,
  397. &AllFeatures);
  398. }
  399. CMsiQuery SetAdvertised;
  400. //
  401. // Create a query that will allow us to set the flag --
  402. // this query is not yet computed, simply initialized
  403. //
  404. if ( ERROR_SUCCESS == Status )
  405. {
  406. Status = _Database.OpenQuery(
  407. QUERY_FEATURES_SET,
  408. &SetAdvertised);
  409. }
  410. //
  411. // Now we enumerate through all the features and
  412. // set the flag for each feature that passes the tests
  413. // for advertisability.
  414. //
  415. for (; ERROR_SUCCESS == Status ;)
  416. {
  417. CMsiRecord CurrentRecord;
  418. BOOL bAdvertised;
  419. //
  420. // Retrieve the current feature
  421. //
  422. Status = AllFeatures.GetNextRecord(
  423. &CurrentRecord);
  424. if ( ERROR_SUCCESS != Status )
  425. {
  426. if ( ERROR_NO_MORE_ITEMS == Status )
  427. {
  428. Status = ERROR_SUCCESS;
  429. }
  430. break;
  431. }
  432. //
  433. // Determine whether or not this feature should be advertised
  434. //
  435. Status = GetFeatureAdvertiseState(
  436. &CurrentRecord,
  437. &bAdvertised );
  438. if ( ( ERROR_SUCCESS == Status ) &&
  439. bAdvertised )
  440. {
  441. //
  442. // This feature is advertisable -- use our SetAdvertised query
  443. // to set the advertisability flag to true.
  444. //
  445. Status = SetAdvertised.UpdateQueryFromFilter( &CurrentRecord );
  446. }
  447. }
  448. return Status;
  449. }
  450. LONG
  451. CClassCollection::RemoveAdvertisableFeatureFlags()
  452. {
  453. LONG Status;
  454. CMsiQuery FreeQuery;
  455. //
  456. // Retrieving the results of this query will
  457. // eliminate the extra flag column we added
  458. // to the feature table to flag advertisable
  459. // features.
  460. //
  461. Status = _Database.GetQueryResults(
  462. QUERY_ADVERTISED_FEATURES_DESTROY,
  463. &FreeQuery);
  464. return Status;
  465. }
  466. LONG
  467. CClassCollection::GetInstallLevel()
  468. {
  469. LONG Status;
  470. CMsiQuery InstallLevelQuery;
  471. //
  472. // Perform a query which retrieves the install level
  473. // property from the package's property table
  474. //
  475. Status = _Database.GetQueryResults(
  476. QUERY_INSTALLLEVEL,
  477. &InstallLevelQuery);
  478. CMsiRecord InstallLevelRecord;
  479. //
  480. // This query should only have one record in it since
  481. // it was targeted at the specific record for install level --
  482. // we now read that record
  483. //
  484. if ( ERROR_SUCCESS == Status )
  485. {
  486. Status = InstallLevelQuery.GetNextRecord(
  487. &InstallLevelRecord);
  488. }
  489. if ( ERROR_SUCCESS == Status )
  490. {
  491. CMsiValue InstallLevelProperty;
  492. //
  493. // We now attempt to obtain the installlevel property value
  494. // from the retrieved record.
  495. //
  496. Status = InstallLevelRecord.GetValue(
  497. CMsiValue::TYPE_DWORD,
  498. PROPERTY_COLUMN_VALUE,
  499. &InstallLevelProperty);
  500. if ( ERROR_SUCCESS == Status )
  501. {
  502. //
  503. // We've successfully obtained the value, so we set it
  504. //
  505. _InstallLevel = InstallLevelProperty.GetDWORDValue();
  506. }
  507. }
  508. else if ( ERROR_NO_MORE_ITEMS == Status )
  509. {
  510. //
  511. // This will only happen if the install level property
  512. // is not present. As fundamental as this property is,
  513. // some packages do not specify it. The Darwin engine
  514. // treats this case as an implicit install level of 1, so
  515. // we must do the same here
  516. //
  517. _InstallLevel = 1;
  518. Status = ERROR_SUCCESS;
  519. }
  520. return Status;
  521. }
  522. LONG
  523. CClassCollection::GetFriendlyName()
  524. {
  525. LONG Status;
  526. CMsiQuery FriendlyNameQuery;
  527. //
  528. // Perform a query which retrieves the install level
  529. // property from the package's property table
  530. //
  531. Status = _Database.GetQueryResults(
  532. QUERY_FRIENDLYNAME,
  533. &FriendlyNameQuery);
  534. CMsiRecord FriendlyNameRecord;
  535. //
  536. // This query should only have one record in it since
  537. // it was targeted at the specific record
  538. // we now read that record
  539. //
  540. if ( ERROR_SUCCESS == Status )
  541. {
  542. Status = FriendlyNameQuery.GetNextRecord(
  543. &FriendlyNameRecord);
  544. }
  545. if ( ERROR_SUCCESS == Status )
  546. {
  547. CMsiValue FriendlyNameProperty;
  548. //
  549. // We now attempt to obtain the property value
  550. // from the retrieved record.
  551. //
  552. Status = FriendlyNameRecord.GetValue(
  553. CMsiValue::TYPE_STRING,
  554. PROPERTY_COLUMN_VALUE,
  555. &FriendlyNameProperty);
  556. if ( ERROR_SUCCESS == Status )
  557. {
  558. //
  559. // We've successfully obtained the value, so we set it
  560. //
  561. CString szName = FriendlyNameProperty.GetStringValue();
  562. OLESAFE_DELETE(_pPackageDetail->pszPackageName);
  563. OLESAFE_COPYSTRING(_pPackageDetail->pszPackageName, szName);
  564. }
  565. }
  566. return Status;
  567. }
  568. LONG
  569. CClassCollection::GetFeatureAdvertiseState(
  570. CMsiRecord* pFeatureRecord,
  571. BOOL* pbAdvertised )
  572. {
  573. LONG Status;
  574. CMsiValue Attributes;
  575. CMsiValue InstallLevel;
  576. //
  577. // Set the out paramter's initial value to FALSE,
  578. // indicating that the feature is not advertised
  579. //
  580. *pbAdvertised = FALSE;
  581. //
  582. // The Attributes column of the feature table
  583. // contains a flag indicating that a feature
  584. // should be not advertised
  585. //
  586. Status = pFeatureRecord->GetValue(
  587. CMsiValue::TYPE_DWORD,
  588. FEATURE_COLUMN_ATTRIBUTES,
  589. &Attributes);
  590. if ( ERROR_SUCCESS == Status )
  591. {
  592. //
  593. // If the disable advertise flag is set, this feature
  594. // cannot be advertised
  595. //
  596. if ( Attributes.GetDWORDValue() & MSI_DISABLEADVERTISE )
  597. {
  598. return ERROR_SUCCESS;
  599. }
  600. //
  601. // The disable flag was not set -- that still does not mean that
  602. // the feature is advertised -- we must check the install level.
  603. // We retrieve the install level for this feature here
  604. //
  605. Status = pFeatureRecord->GetValue(
  606. CMsiValue::TYPE_DWORD,
  607. FEATURE_COLUMN_LEVEL,
  608. &InstallLevel);
  609. }
  610. if ( ERROR_SUCCESS == Status )
  611. {
  612. DWORD dwInstallLevel;
  613. //
  614. // Obtain the value for the install level so
  615. // we can compare against the package install level
  616. //
  617. dwInstallLevel = InstallLevel.GetDWORDValue();
  618. //
  619. // An install level of 0 indicates that the package will
  620. // not be advertised. The install level of the feature
  621. // must be no higher than the package's global install
  622. // level
  623. //
  624. if ( ( 0 != dwInstallLevel ) &&
  625. ( dwInstallLevel <= _InstallLevel ) )
  626. {
  627. //
  628. // This feature passes the tests -- set the out parameter
  629. // to TRUE to indicate that the feature should be advertised
  630. //
  631. *pbAdvertised = TRUE;
  632. }
  633. }
  634. return Status;
  635. }
  636. LONG
  637. CClassCollection::AddElement(
  638. void* pvDataSource,
  639. DataDestination* pDataDestination)
  640. {
  641. DWORD* pcMax;
  642. BYTE* pNewResults;
  643. DWORD cCurrent;
  644. //
  645. // We attempt to add an element to a vector
  646. //
  647. //
  648. // Set the count for how many elements are stored in the vector to
  649. // that specified by the caller
  650. //
  651. cCurrent = *(pDataDestination->_pcCurrent);
  652. //
  653. // Set the element count for the maximum number of elements that
  654. // will fit in the vector currently to that specified by the caller
  655. //
  656. pcMax = (DWORD*) pDataDestination->_pcMax;
  657. //
  658. // Set our results to point to the vector specified by the caller
  659. //
  660. pNewResults = (BYTE*) pDataDestination->_ppvData;
  661. //
  662. // If we already have the maximum number of elements in the vector,
  663. // we will have to make room for more
  664. //
  665. if ( *pcMax >= cCurrent)
  666. {
  667. DWORD cbSize;
  668. //
  669. // Calculate the new size in bytes so that we can ask the system
  670. // for memory. We take our current size in elements and add on a fixed
  671. // allocation increment. The caller has specified the size
  672. // of each individual element, so we use that to turn the number
  673. // of elements to a memory size.
  674. //
  675. cbSize = ( *pcMax + CLASS_ALLOC_SIZE ) *
  676. pDataDestination->_cbElementSize;
  677. //
  678. // Make the request for memory
  679. //
  680. pNewResults = (BYTE*) LocalAlloc( 0, cbSize );
  681. if ( ! pNewResults )
  682. {
  683. return ERROR_NOT_ENOUGH_MEMORY;
  684. }
  685. //
  686. // Clear the memory -- any data structures embedded in the element
  687. // will have NULL references and thus will be properly initialized
  688. //
  689. memset( pNewResults, 0, cbSize );
  690. //
  691. // If the original maximum size of the vector was nonzero, then we must
  692. // copy to original contents of the vector to the newly allocated memory
  693. // location.
  694. //
  695. if ( *pcMax )
  696. {
  697. memcpy(
  698. pNewResults,
  699. *(pDataDestination->_ppvData),
  700. *pcMax * pDataDestination->_cbElementSize);
  701. }
  702. //
  703. // Free the original vector as it is no longer needed
  704. //
  705. LocalFree( *(pDataDestination->_ppvData) );
  706. //
  707. // Change the caller's reference to point to the new vector
  708. //
  709. *(pDataDestination->_ppvData) = pNewResults;
  710. //
  711. // Set the new maximum size (in elements) to that of the newly allocated vector
  712. //
  713. *pcMax += CLASS_ALLOC_SIZE;
  714. }
  715. //
  716. // At this point, we know we have a memory location in the vector into
  717. // which we can safely copy the new element
  718. //
  719. memcpy(
  720. pNewResults + ( cCurrent * pDataDestination->_cbElementSize ),
  721. pvDataSource,
  722. pDataDestination->_cbElementSize);
  723. //
  724. // Update the count of elements currently stored in the vector
  725. //
  726. *(pDataDestination->_pcCurrent) = cCurrent + 1;
  727. return ERROR_SUCCESS;
  728. }
  729. LONG
  730. CClassCollection::ProcessElement(
  731. DWORD dwType,
  732. CMsiRecord* pRecord,
  733. DataDestination* pDataDestination)
  734. {
  735. LONG Status = ERROR_SUCCESS;
  736. void* pvData;
  737. WCHAR* wszData;
  738. CLASSDETAIL ClassDetail;
  739. pvData = NULL;
  740. wszData = NULL;
  741. //
  742. // We attempt to create a new class element based
  743. // on the record passed in by the caller, and then
  744. // add that element to the caller's PACKAGEDETAIL structure
  745. //
  746. //
  747. // The type of element to be added depends on the type
  748. // of class requested by the caller. The pvData variable
  749. // will point to the element to be added if we can successfully
  750. // create a representation for it.
  751. //
  752. switch ( dwType )
  753. {
  754. case TYPE_EXTENSION:
  755. //
  756. // Get a file extension representation from the record --
  757. // note that wszData points to memory allocated by the callee
  758. // on success, so it must be freed by this function.
  759. //
  760. Status = ProcessExtension(
  761. pRecord,
  762. &wszData);
  763. if ( ERROR_SUCCESS == Status )
  764. {
  765. pvData = &wszData;
  766. }
  767. break;
  768. case TYPE_CLSID:
  769. //
  770. // Get a clsid representation from the record --
  771. // in this case, the ClassDetail itself does not
  772. // need to be freed since it does not contain any references
  773. // to memory after this call
  774. //
  775. BOOL bIgnoreClsid;
  776. Status = ProcessClsid(
  777. pRecord,
  778. &ClassDetail,
  779. &bIgnoreClsid);
  780. if ( ERROR_SUCCESS == Status )
  781. {
  782. //
  783. // Check to see if we should add this clsid -- we may be prohibited from
  784. // this because it is a duplicate of an exsting clsid, which would be
  785. // redundant and furthermore the PACKAGEDETAIL format requires
  786. // that all (clsid, clsctx) pairs be unique. Or the clsid itself
  787. // may have an unsupported clsctx. This is not a failure
  788. // case, so we return success here and simply avoid addding this
  789. // class
  790. //
  791. if ( bIgnoreClsid )
  792. {
  793. return ERROR_SUCCESS;
  794. }
  795. pvData = &ClassDetail;
  796. }
  797. break;
  798. case TYPE_PROGID:
  799. //
  800. // Get a progid representation from the record. In addition
  801. // to retrieving the progid in the form of an allocated string
  802. // which must be freed by this funciton, we also retrieve the
  803. // location at which to add the progid to the caller's
  804. // PACKAGEDETAIL structure. This is necessary since the
  805. // ProgId must be part of the CLASSDETAIL structure with which
  806. // it is associated.
  807. //
  808. Status = ProcessProgId(
  809. pRecord,
  810. pDataDestination,
  811. &wszData);
  812. if ( ( ERROR_SUCCESS == Status ) &&
  813. wszData )
  814. {
  815. pvData = &wszData;
  816. }
  817. break;
  818. default:
  819. ASSERT(FALSE);
  820. break;
  821. }
  822. //
  823. // If we were successful in obtaining a representation of the record
  824. // that can be stored in the caller's PACKAGEDETAIL structure, attempt
  825. // to add it to the structure
  826. //
  827. if ( pvData )
  828. {
  829. Status = AddElement(
  830. pvData,
  831. pDataDestination);
  832. }
  833. //
  834. // Be sure that in the failure case, we free any memory
  835. // that may have been allocated.
  836. //
  837. if ( ERROR_SUCCESS != Status )
  838. {
  839. if (wszData )
  840. {
  841. LocalFree( wszData );
  842. }
  843. }
  844. return Status;
  845. }
  846. LONG
  847. CClassCollection::ProcessExtension(
  848. CMsiRecord* pRecord,
  849. WCHAR** ppwszExtension)
  850. {
  851. LONG Status;
  852. CMsiValue FileExtension;
  853. *ppwszExtension = NULL;
  854. //
  855. // We retrieve the actual file extension string
  856. //
  857. Status = pRecord->GetValue(
  858. CMsiValue::TYPE_STRING,
  859. EXTENSION_COLUMN_EXTENSION,
  860. &FileExtension);
  861. if ( ERROR_SUCCESS == Status )
  862. {
  863. ULONG ulNoChars;
  864. HRESULT hr;
  865. //
  866. // We have the value. Note that it does not contain
  867. // an initial '.', but the usage of the PACKAGEDETAIL
  868. // structure mandates that file extensions begin with the '.'
  869. // char, so we will have to prepend the '.' here.
  870. //
  871. //
  872. // First, get space for a copy of the string that includes
  873. // the '.' as well as the zero terminator.
  874. //
  875. ulNoChars = FileExtension.GetStringSize() + 1 + 1;
  876. *ppwszExtension = (WCHAR*) LocalAlloc(
  877. 0,
  878. ulNoChars * sizeof(WCHAR) );
  879. if ( ! *ppwszExtension )
  880. {
  881. Status = ERROR_NOT_ENOUGH_MEMORY;
  882. return Status;
  883. }
  884. //
  885. // Set the first char to be '.'
  886. //
  887. **ppwszExtension = L'.';
  888. //
  889. // Now append the actual extension to the '.'
  890. //
  891. hr = StringCchCopy( *ppwszExtension + 1, ulNoChars - 1, FileExtension.GetStringValue() );
  892. ASSERT(SUCCEEDED(hr));
  893. }
  894. return Status;
  895. }
  896. LONG
  897. CClassCollection::ProcessClsid(
  898. CMsiRecord* pRecord,
  899. CLASSDETAIL* pClsid,
  900. BOOL* pbIgnoreClsid)
  901. {
  902. LONG Status;
  903. DWORD dwClsCtx;
  904. CMsiValue GuidString;
  905. CMsiValue ClassContext;
  906. //
  907. // Clear the clsid to a safe state
  908. //
  909. memset( pClsid, 0, sizeof( *pClsid ) );
  910. //
  911. // Reset out parameters
  912. //
  913. *pbIgnoreClsid = FALSE;
  914. dwClsCtx = 0;
  915. //
  916. // Retrieve the actual clsid
  917. //
  918. Status = pRecord->GetValue(
  919. CMsiValue::TYPE_STRING,
  920. CLSID_COLUMN_CLSID,
  921. &GuidString);
  922. if ( ERROR_SUCCESS == Status )
  923. {
  924. //
  925. // Get the clsctx for this clsid
  926. //
  927. Status = pRecord->GetValue(
  928. CMsiValue::TYPE_STRING,
  929. CLSID_COLUMN_CONTEXT,
  930. &ClassContext);
  931. }
  932. if ( ERROR_SUCCESS == Status )
  933. {
  934. CMsiValue Attribute;
  935. WCHAR* wszClassContext;
  936. DWORD dwInprocClsCtx;
  937. dwInprocClsCtx = 0;
  938. //
  939. // Retrieve a string representation of the clsctx for this clsid
  940. //
  941. wszClassContext = ClassContext.GetStringValue();
  942. //
  943. // Now map the clsctx strings to COM CLSCTX_* values
  944. //
  945. if (CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, wszClassContext, -1, COM_INPROC_CONTEXT, -1) == CSTR_EQUAL)
  946. {
  947. dwInprocClsCtx |= CLSCTX_INPROC_SERVER;
  948. }
  949. else if (CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, wszClassContext, -1, COM_INPROCHANDLER_CONTEXT, -1) == CSTR_EQUAL)
  950. {
  951. dwInprocClsCtx |= CLSCTX_INPROC_HANDLER;
  952. }
  953. else if (CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, wszClassContext, -1, COM_LOCALSERVER_CONTEXT, -1) == CSTR_EQUAL)
  954. {
  955. dwClsCtx |= CLSCTX_LOCAL_SERVER;
  956. }
  957. else if (CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, wszClassContext, -1, COM_REMOTESERVER_CONTEXT, -1) == CSTR_EQUAL)
  958. {
  959. dwClsCtx |= CLSCTX_REMOTE_SERVER;
  960. }
  961. else
  962. {
  963. //
  964. // If the clsctx is one we do not support, we will ignore it
  965. //
  966. *pbIgnoreClsid = TRUE;
  967. return ERROR_SUCCESS;
  968. }
  969. BOOL b64Bit;
  970. b64Bit = FALSE;
  971. //
  972. // We must disginguish between 32-bit and 64-bit in-process servers, since
  973. // 64-bit Windows does not allows modules of different bitness to coexist in the
  974. // same process. If this is an in-process component, we will also check to see
  975. // whether it is 64-bit or not.
  976. //
  977. if ( ( dwInprocClsCtx & CLSCTX_INPROC_HANDLER ) ||
  978. ( dwInprocClsCtx & CLSCTX_INPROC_SERVER ) )
  979. {
  980. //
  981. // The Attributes column of the record has a flag indicating bitness -- this
  982. // will only fail if the property is NULL
  983. //
  984. Status = pRecord->GetValue(
  985. CMsiValue::TYPE_DWORD,
  986. CLSID_COLUMN_ATTRIBUTES,
  987. &Attribute);
  988. //
  989. // Check the flag to see if this is 64-bit
  990. //
  991. if ( ERROR_SUCCESS == Status )
  992. {
  993. b64Bit = Attribute.GetDWORDValue() & MSI_64BIT_CLASS;
  994. }
  995. else
  996. {
  997. //
  998. // This means the property is NULL, so we interpret that as
  999. // meaning the application is not 64 bit
  1000. //
  1001. Status = ERROR_SUCCESS;
  1002. }
  1003. //
  1004. // Map this 64-bit clsctx to a custom (non-COM) CLSCTX that
  1005. // indicates that this is a 64-bit-only in-process class.
  1006. //
  1007. if ( ( ERROR_SUCCESS == Status ) && b64Bit )
  1008. {
  1009. if ( dwInprocClsCtx & CLSCTX_INPROC_SERVER )
  1010. {
  1011. dwClsCtx |= CLSCTX64_INPROC_SERVER;
  1012. }
  1013. if ( dwInprocClsCtx & CLSCTX_INPROC_HANDLER )
  1014. {
  1015. dwClsCtx |= CLSCTX64_INPROC_HANDLER;
  1016. }
  1017. }
  1018. }
  1019. //
  1020. // In the 32-bit case, just or in the values we already computed for
  1021. // inproc case
  1022. //
  1023. if ( ! b64Bit )
  1024. {
  1025. dwClsCtx |= dwInprocClsCtx;
  1026. }
  1027. }
  1028. //
  1029. // Check to see if this is a duplicate -- we do this because our query
  1030. // returned results distinct in (clsid, clsctx, attribute). Since we
  1031. // are mapping attribute to clsctx above and we only support 1 attribute
  1032. // flag (the 64-bit flag) out of several, we may end up with duplicate
  1033. // (clsid, clsctx) pairs, and the PACKAGEDETAIL format requires that
  1034. // we have unique (clsid, clsctx) pairs. Another way to get this would
  1035. // be if COM introduced new clsctx types which we did not support -- these
  1036. // would map to zero, and again we could have duplicates
  1037. //
  1038. if ( ERROR_SUCCESS == Status )
  1039. {
  1040. CLASSDETAIL* pClassDetail;
  1041. pClassDetail = NULL;
  1042. Status = FindClass(
  1043. GuidString.GetStringValue(),
  1044. &pClassDetail);
  1045. //
  1046. // If we already have an entry for this clsid, check to see if
  1047. // it has the same clsctx bits -- if so it is a duplicate entry
  1048. // and we will cease processing it
  1049. //
  1050. if ( ( ERROR_SUCCESS == Status ) && pClassDetail )
  1051. {
  1052. *pbIgnoreClsid = ( dwClsCtx & pClassDetail->dwComClassContext );
  1053. if ( *pbIgnoreClsid )
  1054. {
  1055. return ERROR_SUCCESS;
  1056. }
  1057. }
  1058. }
  1059. //
  1060. // Convert the clsid string to a guid as mandated by the
  1061. // CLASSDETAIL structure
  1062. //
  1063. if ( ERROR_SUCCESS == Status )
  1064. {
  1065. HRESULT hr;
  1066. hr = CLSIDFromString(
  1067. GuidString.GetStringValue(),
  1068. &(pClsid->Clsid));
  1069. if ( FAILED(hr) )
  1070. {
  1071. Status = ERROR_GEN_FAILURE;
  1072. }
  1073. }
  1074. //
  1075. // Set the clsctx we computed above.
  1076. //
  1077. if ( ERROR_SUCCESS == Status )
  1078. {
  1079. pClsid->dwComClassContext = dwClsCtx;
  1080. }
  1081. return Status;
  1082. }
  1083. LONG
  1084. CClassCollection::ProcessProgId(
  1085. CMsiRecord* pRecord,
  1086. DataDestination* pDataDestination,
  1087. WCHAR** ppwszProgId)
  1088. {
  1089. LONG Status;
  1090. CMsiValue ProgIdString;
  1091. CMsiValue ClsidString;
  1092. CLASSDETAIL* pClassDetail;
  1093. //
  1094. // We attempt to map a progid record to a
  1095. // clsid that we've already processed, since
  1096. // the progid will eventually need to go
  1097. // inside the clsid's structure.
  1098. //
  1099. *ppwszProgId = NULL;
  1100. pClassDetail = NULL;
  1101. //
  1102. // Retrieve the value for the progid itself
  1103. //
  1104. Status = pRecord->GetValue(
  1105. CMsiValue::TYPE_STRING,
  1106. PROGID_COLUMN_PROGID,
  1107. &ProgIdString);
  1108. //
  1109. // Retrieve the value of the clsid associated with
  1110. // the progid
  1111. //
  1112. if ( ERROR_SUCCESS == Status )
  1113. {
  1114. Status = pRecord->GetValue(
  1115. CMsiValue::TYPE_STRING,
  1116. PROGID_COLUMN_CLSID,
  1117. &ClsidString);
  1118. }
  1119. //
  1120. // We must find the existing CLASSDETAIL structure
  1121. // that we are maintaining for the progid since the
  1122. // progid must eventually be referenced in that structure.
  1123. //
  1124. if ( ERROR_SUCCESS == Status )
  1125. {
  1126. Status = FindClass(
  1127. ClsidString.GetStringValue(),
  1128. &pClassDetail);
  1129. }
  1130. if ( ERROR_SUCCESS == Status )
  1131. {
  1132. //
  1133. // If we have successfully found the class,
  1134. //
  1135. if ( pClassDetail )
  1136. {
  1137. //
  1138. // Give the caller the progid string since
  1139. // we know that we have a class in which
  1140. // to place it
  1141. //
  1142. *ppwszProgId = ProgIdString.DuplicateString();
  1143. if ( ! *ppwszProgId )
  1144. {
  1145. Status = ERROR_NOT_ENOUGH_MEMORY;
  1146. }
  1147. else
  1148. {
  1149. //
  1150. // Set the caller's data destination to that of the
  1151. // progid vector within the clsid associated with this progid
  1152. //
  1153. pDataDestination->_ppvData = (void**) &( pClassDetail->prgProgId );
  1154. pDataDestination->_pcCurrent = (UINT*) &( pClassDetail->cProgId );
  1155. pDataDestination->_pcMax = (UINT*) &( pClassDetail->cMaxProgId );
  1156. }
  1157. }
  1158. }
  1159. //
  1160. // On failure, free any resources we've allocated
  1161. //
  1162. if ( ( ERROR_SUCCESS != Status ) &&
  1163. *ppwszProgId )
  1164. {
  1165. LocalFree( *ppwszProgId );
  1166. }
  1167. return Status;
  1168. }
  1169. LONG
  1170. CClassCollection::FindClass(
  1171. WCHAR* wszClsid,
  1172. CLASSDETAIL** ppClass)
  1173. {
  1174. CLSID Clsid;
  1175. HRESULT hr;
  1176. //
  1177. // Attempt to find a CLASSDETAIL structure in the PACKAGEDETAIL structure
  1178. // for the clsid given in string form in wszClsid
  1179. //
  1180. *ppClass = NULL;
  1181. //
  1182. // The PACKAGEDETAIL structure stores the clsid in guid form,
  1183. // so we must convert the string to that form before searching
  1184. //
  1185. hr = CLSIDFromString(
  1186. wszClsid,
  1187. &Clsid);
  1188. if ( FAILED(hr) )
  1189. {
  1190. return ERROR_GEN_FAILURE;
  1191. }
  1192. UINT iClsid;
  1193. //
  1194. // We now perform a simple linear search for the clsid
  1195. //
  1196. for (
  1197. iClsid = 0;
  1198. iClsid < _pPackageDetail->pActInfo->cClasses;
  1199. iClsid++)
  1200. {
  1201. if ( IsEqualGUID(
  1202. _pPackageDetail->pActInfo->pClasses[iClsid].Clsid,
  1203. Clsid) )
  1204. {
  1205. *ppClass = &(_pPackageDetail->pActInfo->pClasses[iClsid]);
  1206. return ERROR_SUCCESS;
  1207. }
  1208. }
  1209. return ERROR_SUCCESS;
  1210. }
  1211. void
  1212. CClassCollection::FreeClassDetail( CLASSDETAIL* pClass )
  1213. {
  1214. DWORD iProgId;
  1215. //
  1216. // Free each individual progid string
  1217. //
  1218. for ( iProgId = 0; iProgId < pClass->cProgId; iProgId++ )
  1219. {
  1220. LocalFree( pClass->prgProgId[ iProgId ] );
  1221. }
  1222. //
  1223. // Free the array of progid strings
  1224. //
  1225. LocalFree( pClass->prgProgId );
  1226. }
  1227. DataDestination::DataDestination(
  1228. DWORD dwType,
  1229. void** prgpvDestination,
  1230. UINT* pcCurrent,
  1231. UINT* pcMax ) :
  1232. _pcCurrent( pcCurrent ),
  1233. _ppvData( prgpvDestination ),
  1234. _pcMax ( pcMax )
  1235. {
  1236. //
  1237. // The size of the elements stored by
  1238. // the vector referenced from this class
  1239. // depend on the type of element --
  1240. // clsid, file extension, or progid
  1241. //
  1242. switch ( dwType )
  1243. {
  1244. case TYPE_EXTENSION:
  1245. _cbElementSize = sizeof( WCHAR* );
  1246. break;
  1247. case TYPE_CLSID:
  1248. _cbElementSize = sizeof( CLASSDETAIL );
  1249. break;
  1250. case TYPE_PROGID:
  1251. _cbElementSize = sizeof( WCHAR* );
  1252. break;
  1253. default:
  1254. ASSERT(FALSE);
  1255. }
  1256. }