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.

681 lines
16 KiB

  1. //+--------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1994 - 2001.
  5. //
  6. // File: msibase.cpp
  7. //
  8. // Contents: msi database abstractions
  9. //
  10. // History: 4-14-2000 adamed Created
  11. //
  12. //---------------------------------------------------------------------------
  13. #include "precomp.hxx"
  14. CMsiState::CMsiState() :
  15. _MsiHandle( NULL )
  16. {
  17. //
  18. // The MSIHANDLE encapsulates the state for
  19. // all msi operations / data -- clearing this
  20. // member is akin to clearing the state.
  21. //
  22. }
  23. CMsiState::~CMsiState()
  24. {
  25. //
  26. // The lifetime of the object is the lifetime
  27. // of the underlying state -- be sure to release it
  28. //
  29. MsiCloseHandle( _MsiHandle );
  30. }
  31. void
  32. CMsiState::SetState( MSIHANDLE MsiHandle )
  33. {
  34. //
  35. // Set the state of this object based on
  36. // a handle retrieved from an MSI operation --
  37. // note that this should only be done if this
  38. // object has an empty state
  39. //
  40. ASSERT( ! _MsiHandle );
  41. _MsiHandle = MsiHandle;
  42. }
  43. MSIHANDLE
  44. CMsiState::GetState()
  45. {
  46. //
  47. // Allow callers that need to perform explicit MSI
  48. // operations to retrieve state compatible with MSI
  49. //
  50. return _MsiHandle;
  51. }
  52. CMsiValue::CMsiValue() :
  53. _dwDiscriminant( TYPE_NOT_SET ),
  54. _wszValue( NULL ),
  55. _cchSize( sizeof( _wszDefaultBuf ) / sizeof( *_wszDefaultBuf ) )
  56. {
  57. //
  58. // The goal of this initialization is to set this object to
  59. // an "empty" state -- consumers must explicitly invoke methods
  60. // on this object to alter this condition so that Get methods
  61. // will succeed.
  62. //
  63. }
  64. CMsiValue::~CMsiValue()
  65. {
  66. //
  67. // Setting the type to "none" implicitly clears our state
  68. // (e.g. allocated memory, any other resources)
  69. //
  70. SetType( TYPE_NOT_SET );
  71. }
  72. DWORD
  73. CMsiValue::GetDWORDValue()
  74. {
  75. ASSERT( TYPE_DWORD == _dwDiscriminant );
  76. //
  77. // Retrieve this value as a DWORD -- note that this
  78. // does not coerce non-DWORD values to DWORD -- the
  79. // value must already be a DWORD for this to have meaning
  80. //
  81. return _dwValue;
  82. }
  83. WCHAR*
  84. CMsiValue::GetStringValue()
  85. {
  86. ASSERT( TYPE_STRING == _dwDiscriminant );
  87. //
  88. // Retrieve this value as a string -- note that this
  89. // does not coerce non-string values to string -- the
  90. // value must already be a string for this to have meaning.
  91. // Note that the value is returned as a reference to the address
  92. // at which this value actually stores the string -- thus, this
  93. // may also be used to retrieve the value's buffer so that its
  94. // contents may be edited outside the strictures of this class.
  95. //
  96. return _wszValue;
  97. }
  98. WCHAR*
  99. CMsiValue::DuplicateString()
  100. {
  101. WCHAR* wszResult;
  102. ASSERT( TYPE_STRING == _dwDiscriminant );
  103. //
  104. // The caller requires ownership of a duplicate
  105. // of this string's data.
  106. //
  107. //
  108. // First, allocate memory for this
  109. //
  110. wszResult = (WCHAR*) LocalAlloc(
  111. 0,
  112. sizeof(WCHAR*) * (lstrlen ( _wszValue ) + 1 ) );
  113. //
  114. // If we successfully obtained room for the string,
  115. // copy it
  116. //
  117. if ( wszResult )
  118. {
  119. lstrcpy( wszResult, _wszValue);
  120. }
  121. return wszResult;
  122. }
  123. void
  124. CMsiValue::SetDWORDValue( DWORD dwValue )
  125. {
  126. //
  127. // This operation will implicitly set the type
  128. // of this value to DWORD
  129. //
  130. SetType( TYPE_DWORD );
  131. //
  132. // Now we can safely set the value
  133. //
  134. _dwValue = dwValue;
  135. }
  136. LONG
  137. CMsiValue::SetStringValue( WCHAR* wszValue )
  138. {
  139. DWORD cchSize;
  140. LONG Status;
  141. Status = ERROR_SUCCESS;
  142. //
  143. // This operation will implicitly set the
  144. // type of this value to string
  145. //
  146. SetType( TYPE_STRING );
  147. //
  148. // We need to determine the size of this string,
  149. // in chars, without the null terminator, in order to
  150. // allow this value to represent it
  151. //
  152. cchSize = lstrlen( wszValue );
  153. if ( cchSize > _cchSize )
  154. {
  155. //
  156. // Attempt to get space for this string
  157. // by setting its size -- if this fails,
  158. // our type will be implicitly set to none
  159. //
  160. Status = SetStringSize( cchSize );
  161. if ( ERROR_SUCCESS != Status )
  162. {
  163. return Status;
  164. }
  165. //
  166. // We have room for the string, so copy it
  167. // into its newly allocated space
  168. //
  169. lstrcpy( _wszValue, wszValue );
  170. }
  171. return Status;
  172. }
  173. DWORD
  174. CMsiValue::GetStringSize()
  175. {
  176. ASSERT( TYPE_STRING == _dwDiscriminant );
  177. //
  178. // Retrieve the size of this string in chars,
  179. // WITHOUT the null terminator
  180. //
  181. return _cchSize;
  182. }
  183. LONG
  184. CMsiValue::SetStringSize( DWORD cchSize )
  185. {
  186. ASSERT( TYPE_STRING == _dwDiscriminant );
  187. //
  188. // This method only makes sense if the
  189. // type of this object is already string
  190. //
  191. //
  192. // If the requested size is less than or
  193. // equal to our current size, we already have
  194. // enough space -- we can exit now. We do
  195. // not "shrink" space, only expand as necessary
  196. //
  197. if ( cchSize <= _cchSize )
  198. {
  199. return ERROR_SUCCESS;
  200. }
  201. //
  202. // At this point, we know we don't have enough
  203. // space, so we'll have to allocate it. Before we
  204. // do so, reset our type to none so that if we fail
  205. // to get space, we can indicate the indeterminate
  206. // state.
  207. //
  208. SetType( TYPE_NOT_SET );
  209. //
  210. // Allocate space, and include the zero terminator
  211. //
  212. _wszValue = new WCHAR [ cchSize + 1 ];
  213. if ( ! _wszValue )
  214. {
  215. return ERROR_NOT_ENOUGH_MEMORY;
  216. }
  217. //
  218. // We are successful, remember the current size
  219. //
  220. _cchSize = cchSize;
  221. //
  222. // Change the type back to string since we can
  223. // safely represent a string of this size
  224. //
  225. SetType( TYPE_STRING );
  226. return ERROR_SUCCESS;
  227. }
  228. void
  229. CMsiValue::SetType( DWORD dwType )
  230. {
  231. //
  232. // Setting the type to a new type implicitly clears
  233. // state associated with the new type
  234. //
  235. //
  236. // If the current type and requested type are the same
  237. // this is a no op and we are done.
  238. //
  239. if ( dwType == _dwDiscriminant )
  240. {
  241. return;
  242. }
  243. //
  244. // If the requested type is string, we need to
  245. // set this object to have appropriate state
  246. //
  247. if ( TYPE_STRING == dwType )
  248. {
  249. //
  250. // If we have no space for a string
  251. //
  252. if ( ! _wszValue )
  253. {
  254. //
  255. // Use the default buffer...
  256. //
  257. _wszValue = _wszDefaultBuf;
  258. //
  259. // ... and set the size accordingly
  260. //
  261. _cchSize = sizeof( _wszDefaultBuf ) / sizeof( *_wszDefaultBuf );
  262. }
  263. //
  264. // We are done -- this object can now represent a string, though
  265. // at this point it must be a string of size _cchSize -- the size
  266. // will have to be increased through SetStringSize if there's
  267. // a need to represent a larger string
  268. //
  269. return;
  270. }
  271. //
  272. // If the current type is string, we use the fact that the requested
  273. // type is not string as a hint to free the state associated with
  274. // the string. This is a heuristic designed to ensure that we
  275. // do not continue to hold memory of which we are not actively making
  276. // use.
  277. //
  278. if ( TYPE_STRING == _dwDiscriminant )
  279. {
  280. //
  281. // If the string's current storage is not that of our default
  282. // buffer (which is part of the object itself), we
  283. // release that storage as it was allocated on the heap.
  284. //
  285. if ( _wszValue != _wszDefaultBuf )
  286. {
  287. delete [] _wszValue;
  288. _wszValue = NULL;
  289. }
  290. }
  291. //
  292. // We may now set the type to that requested by the caller
  293. //
  294. _dwDiscriminant = dwType;
  295. }
  296. LONG
  297. CMsiRecord::GetValue(
  298. DWORD dwType,
  299. DWORD dwValue,
  300. CMsiValue* pMsiValue)
  301. {
  302. LONG Status = ERROR_SUCCESS;
  303. //
  304. // Values are the properties of the column of an
  305. // msi record -- we are retrieving members of the
  306. // record
  307. //
  308. //
  309. // The value is our out parameter -- set it
  310. // to the type desired by the caller
  311. //
  312. pMsiValue->SetType( dwType );
  313. switch ( dwType )
  314. {
  315. case CMsiValue::TYPE_STRING:
  316. DWORD cchSize;
  317. //
  318. // We must determine the maximum size of the
  319. // string that can be represented by the value
  320. // so we can pass it to the msi api
  321. //
  322. cchSize = pMsiValue->GetStringSize();
  323. //
  324. // Attempt to retrieve the string by storing
  325. // it in the buffer of the value
  326. //
  327. Status = MsiRecordGetString(
  328. GetState(),
  329. dwValue,
  330. pMsiValue->GetStringValue(),
  331. &cchSize);
  332. //
  333. // Our attempt to retrieve the string data will
  334. // fail if the value's string buffer is not sufficiently
  335. // large.
  336. //
  337. if ( ERROR_MORE_DATA == Status )
  338. {
  339. //
  340. // In the case where the value's buffer is not large enough,
  341. // we explicitly set the size of the value to that of the
  342. // size returned by the msi api PLUS a zero terminator --
  343. // this is because the size returned by MSI does NOT
  344. // include the zero terminator.
  345. //
  346. cchSize++;
  347. Status = pMsiValue->SetStringSize( cchSize );
  348. //
  349. // We now retry the string retrieval since we have the
  350. // correct size now.
  351. //
  352. if ( ERROR_SUCCESS == Status )
  353. {
  354. Status = MsiRecordGetString(
  355. GetState(),
  356. dwValue,
  357. pMsiValue->GetStringValue(),
  358. &cchSize);
  359. }
  360. }
  361. break;
  362. case CMsiValue::TYPE_DWORD:
  363. Status = ERROR_INVALID_PARAMETER;
  364. int IntegerValue;
  365. //
  366. // Retrieve an integer by calling the msi api
  367. //
  368. IntegerValue = MsiRecordGetInteger(
  369. GetState(),
  370. dwValue);
  371. if ( MSI_NULL_INTEGER != IntegerValue )
  372. {
  373. //
  374. // We now set the value to that retrieved by the api
  375. //
  376. pMsiValue->SetDWORDValue( (DWORD) IntegerValue );
  377. Status = ERROR_SUCCESS;
  378. }
  379. break;
  380. default:
  381. ASSERT( FALSE );
  382. break;
  383. }
  384. return Status;
  385. }
  386. LONG
  387. CMsiQuery::GetNextRecord( CMsiRecord* pMsiRecord)
  388. {
  389. LONG Status;
  390. MSIHANDLE MsiHandle;
  391. //
  392. // The MsiViewFetch api will retrieve a record from a query --
  393. // it does this in an enumeration style, so we are retrieving
  394. // the next record in the query
  395. //
  396. Status = MsiViewFetch(
  397. GetState(),
  398. &MsiHandle);
  399. if ( ERROR_SUCCESS == Status )
  400. {
  401. //
  402. // We successfully obtained an MSIHANDLE corresponding to the
  403. // retrieved record, so we use this to set the state of our
  404. // abstraction of the record
  405. //
  406. pMsiRecord->SetState( MsiHandle );
  407. }
  408. return Status;
  409. }
  410. LONG
  411. CMsiQuery::UpdateQueryFromFilter( CMsiRecord* pFilterRecord )
  412. {
  413. LONG Status;
  414. //
  415. // The MsiViewExecute api causes the results of the query to
  416. // be computed. The filter record passed in allows us to
  417. // specify a filter for the query results
  418. //
  419. Status = MsiViewExecute(
  420. GetState(),
  421. pFilterRecord ? pFilterRecord->GetState() : NULL );
  422. return Status;
  423. }
  424. LONG
  425. CMsiDatabase::Open(
  426. WCHAR* wszPath,
  427. DWORD cTransforms,
  428. WCHAR** rgwszTransforms)
  429. {
  430. MSIHANDLE DatabaseHandle;
  431. LONG Status;
  432. //
  433. // The MsiOpenDatabase api abstracts an .msi package
  434. //
  435. Status = MsiOpenDatabase(
  436. wszPath,
  437. MSIDBOPEN_READONLY,
  438. &DatabaseHandle);
  439. if ( ERROR_SUCCESS == Status )
  440. {
  441. DWORD iTransform;
  442. //
  443. // The successful open above does not include transforms --
  444. // we need to add each transform to generate a resultant
  445. // database that includes the changes of each transform
  446. //
  447. //
  448. // We apply the transforms in the order in which they are
  449. // stored in the vector -- this order conforms to that
  450. // specified by the administrator, and since order affects
  451. // the result, we must honor the administrator's ordering
  452. //
  453. for ( iTransform = 0; iTransform < cTransforms; iTransform++ )
  454. {
  455. if ( ERROR_SUCCESS == Status )
  456. {
  457. //
  458. // This api adds the effects of the transform to the
  459. // database.
  460. //
  461. Status = MsiDatabaseApplyTransform(
  462. DatabaseHandle,
  463. rgwszTransforms[iTransform],
  464. 0);
  465. }
  466. if ( ERROR_SUCCESS != Status )
  467. {
  468. //
  469. // If we failed to apply a transform, we bail
  470. //
  471. break;
  472. }
  473. }
  474. if ( ERROR_SUCCESS == Status )
  475. {
  476. //
  477. // We have successfully created an database of the
  478. // package + transforms, so we allow the lifetime of its state
  479. // to be controlled by this object
  480. //
  481. SetState( DatabaseHandle );
  482. }
  483. else
  484. {
  485. //
  486. // If we failed to apply a transform, the database
  487. // resource is useless, so we free it
  488. //
  489. MsiCloseHandle( DatabaseHandle );
  490. }
  491. }
  492. return Status;
  493. }
  494. LONG
  495. CMsiDatabase::OpenQuery(
  496. WCHAR* wszQuery,
  497. CMsiQuery* pQuery )
  498. {
  499. LONG Status;
  500. MSIHANDLE MsiHandle;
  501. //
  502. // This api will initialize a query without comoputing its
  503. // results. This will allow the caller finer control over result
  504. // computation later, which distinguishes this method from GetQueryResults
  505. //
  506. Status = MsiDatabaseOpenView(
  507. GetState(),
  508. wszQuery,
  509. &MsiHandle);
  510. if ( ERROR_SUCCESS == Status )
  511. {
  512. //
  513. // Give the caller's query object the state for the query
  514. // so that it can control its lifetime
  515. //
  516. pQuery->SetState( MsiHandle );
  517. }
  518. return Status;
  519. }
  520. LONG
  521. CMsiDatabase::GetQueryResults(
  522. WCHAR* wszQuery,
  523. CMsiQuery* pQuery )
  524. {
  525. LONG Status;
  526. MSIHANDLE MsiHandle;
  527. //
  528. // This api will initialize a query without computing the results
  529. //
  530. Status = MsiDatabaseOpenView(
  531. GetState(),
  532. wszQuery,
  533. &MsiHandle);
  534. if ( ERROR_SUCCESS == Status )
  535. {
  536. //
  537. // The semantics of this method are that the caller may also
  538. // enumerate results after calling the method, so we must
  539. // now computer the results so that the caller may enumerate --
  540. // the api below will do this
  541. //
  542. Status = MsiViewExecute(
  543. MsiHandle,
  544. NULL);
  545. if ( ERROR_SUCCESS == Status )
  546. {
  547. //
  548. // In the success case, we give the lifetime of the msi
  549. // state to the query object
  550. //
  551. pQuery->SetState( MsiHandle );
  552. }
  553. else
  554. {
  555. //
  556. // On failure, we must clear the msi query state
  557. // since it is useless now.
  558. //
  559. MsiCloseHandle( MsiHandle );
  560. }
  561. }
  562. return Status;
  563. }
  564. LONG
  565. CMsiDatabase::TableExists(
  566. WCHAR* wszTableName,
  567. BOOL* pbTableExists )
  568. {
  569. MSICONDITION TableState;
  570. TableState = MsiDatabaseIsTablePersistent( GetState(), wszTableName );
  571. if ( MSICONDITION_ERROR == TableState )
  572. {
  573. return ERROR_INVALID_PARAMETER;
  574. }
  575. *pbTableExists = MSICONDITION_TRUE == TableState;
  576. return ERROR_SUCCESS;
  577. }