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.

692 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. ULONG ulNoChars = lstrlen ( _wszValue ) + 1;
  111. wszResult = (WCHAR*) LocalAlloc(
  112. 0,
  113. sizeof(WCHAR*) * ulNoChars );
  114. //
  115. // If we successfully obtained room for the string,
  116. // copy it
  117. //
  118. if ( wszResult )
  119. {
  120. HRESULT hr;
  121. hr = StringCchCopy ( wszResult, ulNoChars, _wszValue);
  122. ASSERT(SUCCEEDED(hr));
  123. }
  124. return wszResult;
  125. }
  126. void
  127. CMsiValue::SetDWORDValue( DWORD dwValue )
  128. {
  129. //
  130. // This operation will implicitly set the type
  131. // of this value to DWORD
  132. //
  133. SetType( TYPE_DWORD );
  134. //
  135. // Now we can safely set the value
  136. //
  137. _dwValue = dwValue;
  138. }
  139. LONG
  140. CMsiValue::SetStringValue( WCHAR* wszValue )
  141. {
  142. DWORD cchSize;
  143. LONG Status;
  144. Status = ERROR_SUCCESS;
  145. //
  146. // This operation will implicitly set the
  147. // type of this value to string
  148. //
  149. SetType( TYPE_STRING );
  150. //
  151. // We need to determine the size of this string,
  152. // in chars, without the null terminator, in order to
  153. // allow this value to represent it
  154. //
  155. cchSize = lstrlen( wszValue );
  156. if ( cchSize > _cchSize )
  157. {
  158. //
  159. // Attempt to get space for this string
  160. // by setting its size -- if this fails,
  161. // our type will be implicitly set to none
  162. // Here, allocating one extra byte than required.
  163. //
  164. Status = SetStringSize( cchSize );
  165. if ( ERROR_SUCCESS != Status )
  166. {
  167. return Status;
  168. }
  169. //
  170. // We have room for the string, so copy it
  171. // into its newly allocated space
  172. //
  173. HRESULT hr;
  174. hr = StringCchCopy( _wszValue, cchSize+1, wszValue );
  175. ASSERT(SUCCEEDED(hr));
  176. }
  177. return Status;
  178. }
  179. DWORD
  180. CMsiValue::GetStringSize()
  181. {
  182. ASSERT( TYPE_STRING == _dwDiscriminant );
  183. //
  184. // Retrieve the size of this string in chars,
  185. // WITHOUT the null terminator
  186. //
  187. return _cchSize;
  188. }
  189. LONG
  190. CMsiValue::SetStringSize( DWORD cchSize )
  191. {
  192. ASSERT( TYPE_STRING == _dwDiscriminant );
  193. //
  194. // This method only makes sense if the
  195. // type of this object is already string
  196. //
  197. //
  198. // If the requested size is less than or
  199. // equal to our current size, we already have
  200. // enough space -- we can exit now. We do
  201. // not "shrink" space, only expand as necessary
  202. //
  203. if ( cchSize <= _cchSize )
  204. {
  205. return ERROR_SUCCESS;
  206. }
  207. //
  208. // At this point, we know we don't have enough
  209. // space, so we'll have to allocate it. Before we
  210. // do so, reset our type to none so that if we fail
  211. // to get space, we can indicate the indeterminate
  212. // state.
  213. //
  214. SetType( TYPE_NOT_SET );
  215. //
  216. // Allocate space, and include the zero terminator
  217. //
  218. _wszValue = new WCHAR [ cchSize + 1 ];
  219. if ( ! _wszValue )
  220. {
  221. return ERROR_NOT_ENOUGH_MEMORY;
  222. }
  223. //
  224. // We are successful, remember the current size
  225. //
  226. _cchSize = cchSize;
  227. //
  228. // Change the type back to string since we can
  229. // safely represent a string of this size
  230. //
  231. SetType( TYPE_STRING );
  232. return ERROR_SUCCESS;
  233. }
  234. void
  235. CMsiValue::SetType( DWORD dwType )
  236. {
  237. //
  238. // Setting the type to a new type implicitly clears
  239. // state associated with the new type
  240. //
  241. //
  242. // If the current type and requested type are the same
  243. // this is a no op and we are done.
  244. //
  245. if ( dwType == _dwDiscriminant )
  246. {
  247. return;
  248. }
  249. //
  250. // If the requested type is string, we need to
  251. // set this object to have appropriate state
  252. //
  253. if ( TYPE_STRING == dwType )
  254. {
  255. //
  256. // If we have no space for a string
  257. //
  258. if ( ! _wszValue )
  259. {
  260. //
  261. // Use the default buffer...
  262. //
  263. _wszValue = _wszDefaultBuf;
  264. //
  265. // ... and set the size accordingly
  266. //
  267. _cchSize = sizeof( _wszDefaultBuf ) / sizeof( *_wszDefaultBuf );
  268. }
  269. //
  270. // We are done -- this object can now represent a string, though
  271. // at this point it must be a string of size _cchSize -- the size
  272. // will have to be increased through SetStringSize if there's
  273. // a need to represent a larger string
  274. //
  275. return;
  276. }
  277. //
  278. // If the current type is string, we use the fact that the requested
  279. // type is not string as a hint to free the state associated with
  280. // the string. This is a heuristic designed to ensure that we
  281. // do not continue to hold memory of which we are not actively making
  282. // use.
  283. //
  284. if ( TYPE_STRING == _dwDiscriminant )
  285. {
  286. //
  287. // If the string's current storage is not that of our default
  288. // buffer (which is part of the object itself), we
  289. // release that storage as it was allocated on the heap.
  290. //
  291. if ( _wszValue != _wszDefaultBuf )
  292. {
  293. delete [] _wszValue;
  294. _wszValue = NULL;
  295. }
  296. }
  297. //
  298. // We may now set the type to that requested by the caller
  299. //
  300. _dwDiscriminant = dwType;
  301. }
  302. LONG
  303. CMsiRecord::GetValue(
  304. DWORD dwType,
  305. DWORD dwValue,
  306. CMsiValue* pMsiValue)
  307. {
  308. LONG Status = ERROR_SUCCESS;
  309. //
  310. // Values are the properties of the column of an
  311. // msi record -- we are retrieving members of the
  312. // record
  313. //
  314. //
  315. // The value is our out parameter -- set it
  316. // to the type desired by the caller
  317. //
  318. pMsiValue->SetType( dwType );
  319. switch ( dwType )
  320. {
  321. case CMsiValue::TYPE_STRING:
  322. DWORD cchSize;
  323. //
  324. // We must determine the maximum size of the
  325. // string that can be represented by the value
  326. // so we can pass it to the msi api
  327. //
  328. cchSize = pMsiValue->GetStringSize();
  329. //
  330. // Attempt to retrieve the string by storing
  331. // it in the buffer of the value
  332. //
  333. Status = MsiRecordGetString(
  334. GetState(),
  335. dwValue,
  336. pMsiValue->GetStringValue(),
  337. &cchSize);
  338. //
  339. // Our attempt to retrieve the string data will
  340. // fail if the value's string buffer is not sufficiently
  341. // large.
  342. //
  343. if ( ERROR_MORE_DATA == Status )
  344. {
  345. //
  346. // In the case where the value's buffer is not large enough,
  347. // we explicitly set the size of the value to that of the
  348. // size returned by the msi api PLUS a zero terminator --
  349. // this is because the size returned by MSI does NOT
  350. // include the zero terminator.
  351. //
  352. cchSize++;
  353. Status = pMsiValue->SetStringSize( cchSize );
  354. //
  355. // We now retry the string retrieval since we have the
  356. // correct size now.
  357. //
  358. if ( ERROR_SUCCESS == Status )
  359. {
  360. Status = MsiRecordGetString(
  361. GetState(),
  362. dwValue,
  363. pMsiValue->GetStringValue(),
  364. &cchSize);
  365. }
  366. }
  367. break;
  368. case CMsiValue::TYPE_DWORD:
  369. Status = ERROR_INVALID_PARAMETER;
  370. int IntegerValue;
  371. //
  372. // Retrieve an integer by calling the msi api
  373. //
  374. IntegerValue = MsiRecordGetInteger(
  375. GetState(),
  376. dwValue);
  377. if ( MSI_NULL_INTEGER != IntegerValue )
  378. {
  379. //
  380. // We now set the value to that retrieved by the api
  381. //
  382. pMsiValue->SetDWORDValue( (DWORD) IntegerValue );
  383. Status = ERROR_SUCCESS;
  384. }
  385. break;
  386. default:
  387. ASSERT( FALSE );
  388. break;
  389. }
  390. return Status;
  391. }
  392. LONG
  393. CMsiQuery::GetNextRecord( CMsiRecord* pMsiRecord)
  394. {
  395. LONG Status;
  396. MSIHANDLE MsiHandle;
  397. //
  398. // The MsiViewFetch api will retrieve a record from a query --
  399. // it does this in an enumeration style, so we are retrieving
  400. // the next record in the query
  401. //
  402. Status = MsiViewFetch(
  403. GetState(),
  404. &MsiHandle);
  405. if ( ERROR_SUCCESS == Status )
  406. {
  407. //
  408. // We successfully obtained an MSIHANDLE corresponding to the
  409. // retrieved record, so we use this to set the state of our
  410. // abstraction of the record
  411. //
  412. pMsiRecord->SetState( MsiHandle );
  413. }
  414. return Status;
  415. }
  416. LONG
  417. CMsiQuery::UpdateQueryFromFilter( CMsiRecord* pFilterRecord )
  418. {
  419. LONG Status;
  420. //
  421. // The MsiViewExecute api causes the results of the query to
  422. // be computed. The filter record passed in allows us to
  423. // specify a filter for the query results
  424. //
  425. Status = MsiViewExecute(
  426. GetState(),
  427. pFilterRecord ? pFilterRecord->GetState() : NULL );
  428. return Status;
  429. }
  430. LONG
  431. CMsiDatabase::Open(
  432. WCHAR* wszPath,
  433. DWORD cTransforms,
  434. WCHAR** rgwszTransforms)
  435. {
  436. MSIHANDLE DatabaseHandle;
  437. LONG Status;
  438. //
  439. // The MsiOpenDatabase api abstracts an .msi package
  440. //
  441. Status = MsiOpenDatabase(
  442. wszPath,
  443. MSIDBOPEN_READONLY,
  444. &DatabaseHandle);
  445. if ( ERROR_SUCCESS == Status )
  446. {
  447. DWORD iTransform;
  448. //
  449. // The successful open above does not include transforms --
  450. // we need to add each transform to generate a resultant
  451. // database that includes the changes of each transform
  452. //
  453. //
  454. // We apply the transforms in the order in which they are
  455. // stored in the vector -- this order conforms to that
  456. // specified by the administrator, and since order affects
  457. // the result, we must honor the administrator's ordering
  458. //
  459. for ( iTransform = 0; iTransform < cTransforms; iTransform++ )
  460. {
  461. if ( ERROR_SUCCESS == Status )
  462. {
  463. //
  464. // This api adds the effects of the transform to the
  465. // database.
  466. //
  467. Status = MsiDatabaseApplyTransform(
  468. DatabaseHandle,
  469. rgwszTransforms[iTransform],
  470. 0);
  471. }
  472. if ( ERROR_SUCCESS != Status )
  473. {
  474. //
  475. // If we failed to apply a transform, we bail
  476. //
  477. break;
  478. }
  479. }
  480. if ( ERROR_SUCCESS == Status )
  481. {
  482. //
  483. // We have successfully created an database of the
  484. // package + transforms, so we allow the lifetime of its state
  485. // to be controlled by this object
  486. //
  487. SetState( DatabaseHandle );
  488. }
  489. else
  490. {
  491. //
  492. // If we failed to apply a transform, the database
  493. // resource is useless, so we free it
  494. //
  495. MsiCloseHandle( DatabaseHandle );
  496. }
  497. }
  498. return Status;
  499. }
  500. LONG
  501. CMsiDatabase::OpenQuery(
  502. WCHAR* wszQuery,
  503. CMsiQuery* pQuery )
  504. {
  505. LONG Status;
  506. MSIHANDLE MsiHandle;
  507. //
  508. // This api will initialize a query without comoputing its
  509. // results. This will allow the caller finer control over result
  510. // computation later, which distinguishes this method from GetQueryResults
  511. //
  512. Status = MsiDatabaseOpenView(
  513. GetState(),
  514. wszQuery,
  515. &MsiHandle);
  516. if ( ERROR_SUCCESS == Status )
  517. {
  518. //
  519. // Give the caller's query object the state for the query
  520. // so that it can control its lifetime
  521. //
  522. pQuery->SetState( MsiHandle );
  523. }
  524. return Status;
  525. }
  526. LONG
  527. CMsiDatabase::GetQueryResults(
  528. WCHAR* wszQuery,
  529. CMsiQuery* pQuery )
  530. {
  531. LONG Status;
  532. MSIHANDLE MsiHandle;
  533. //
  534. // This api will initialize a query without computing the results
  535. //
  536. Status = MsiDatabaseOpenView(
  537. GetState(),
  538. wszQuery,
  539. &MsiHandle);
  540. if ( ERROR_SUCCESS == Status )
  541. {
  542. //
  543. // The semantics of this method are that the caller may also
  544. // enumerate results after calling the method, so we must
  545. // now computer the results so that the caller may enumerate --
  546. // the api below will do this
  547. //
  548. Status = MsiViewExecute(
  549. MsiHandle,
  550. NULL);
  551. if ( ERROR_SUCCESS == Status )
  552. {
  553. //
  554. // In the success case, we give the lifetime of the msi
  555. // state to the query object
  556. //
  557. pQuery->SetState( MsiHandle );
  558. }
  559. else
  560. {
  561. //
  562. // On failure, we must clear the msi query state
  563. // since it is useless now.
  564. //
  565. MsiCloseHandle( MsiHandle );
  566. }
  567. }
  568. return Status;
  569. }
  570. LONG
  571. CMsiDatabase::TableExists(
  572. WCHAR* wszTableName,
  573. BOOL* pbTableExists )
  574. {
  575. MSICONDITION TableState;
  576. TableState = MsiDatabaseIsTablePersistent( GetState(), wszTableName );
  577. if ( MSICONDITION_ERROR == TableState )
  578. {
  579. return ERROR_INVALID_PARAMETER;
  580. }
  581. *pbTableExists = MSICONDITION_TRUE == TableState;
  582. return ERROR_SUCCESS;
  583. }