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.

1800 lines
47 KiB

  1. #include "headers.hxx"
  2. #include "global.hpp"
  3. #include "Analysis.hpp"
  4. #include "AnalysisResults.hpp"
  5. #include "CSVDSReader.hpp"
  6. #include "resourceDspecup.h"
  7. #include "AdsiHelpers.hpp"
  8. #include "constants.hpp"
  9. #include "dspecup.hpp"
  10. Analysis::Analysis
  11. (
  12. const GUID guid_,
  13. const CSVDSReader& csvReader409_,
  14. const CSVDSReader& csvReaderIntl_,
  15. const String& ldapPrefix_,
  16. const String& rootContainerDn_,
  17. AnalysisResults &res,
  18. const String &reportName_,//=L"",
  19. void *caleeStruct_,//=NULL,
  20. progressFunction stepIt_,//=NULL,
  21. progressFunction totalSteps_//=NULL,
  22. )
  23. :
  24. guid(guid_),
  25. csvReader409(csvReader409_),
  26. csvReaderIntl(csvReaderIntl_),
  27. ldapPrefix(ldapPrefix_),
  28. rootContainerDn(rootContainerDn_),
  29. results(res),
  30. reportName(reportName_),
  31. caleeStruct(caleeStruct_),
  32. stepIt(stepIt_),
  33. totalSteps(totalSteps_)
  34. {
  35. LOG_CTOR(Analysis);
  36. ASSERT(!ldapPrefix.empty());
  37. ASSERT(!rootContainerDn.empty());
  38. };
  39. // Analysis entry point
  40. HRESULT
  41. Analysis::run()
  42. {
  43. LOG_FUNCTION(Analysis::run);
  44. if(changes.size()==0)
  45. {
  46. setChanges();
  47. }
  48. HRESULT hr=S_OK;
  49. do
  50. {
  51. LongList locales;
  52. for(long t=0;LOCALEIDS[t]!=0;t++)
  53. {
  54. locales.push_back(LOCALEIDS[t]);
  55. }
  56. locales.push_back(LOCALE409[0]);
  57. if(totalSteps!=NULL)
  58. {
  59. // The cast bellow is for IA64 compilation since we know
  60. // that locales.size() will fit in a long.
  61. totalSteps(static_cast<long>(locales.size()),caleeStruct);
  62. }
  63. BREAK_ON_FAILED_HRESULT(hr);
  64. LongList::iterator begin=locales.begin();
  65. LongList::iterator end=locales.end();
  66. while(begin!=end)
  67. {
  68. long locale=*begin;
  69. bool isPresent;
  70. hr=dealWithContainer(locale,isPresent);
  71. BREAK_ON_FAILED_HRESULT(hr);
  72. if (isPresent)
  73. {
  74. hr=dealWithW2KObjects(locale);
  75. BREAK_ON_FAILED_HRESULT(hr);
  76. }
  77. if(stepIt!=NULL)
  78. {
  79. stepIt(1,caleeStruct);
  80. }
  81. begin++;
  82. }
  83. BREAK_ON_FAILED_HRESULT(hr);
  84. if(!reportName.empty())
  85. {
  86. hr=createReport(reportName);
  87. BREAK_ON_FAILED_HRESULT(hr);
  88. }
  89. }
  90. while (0);
  91. LOG_HRESULT(hr);
  92. return hr;
  93. }
  94. // add entry to result.createContainers if container is not present
  95. // also returns flag isPresent
  96. HRESULT
  97. Analysis::dealWithContainer(
  98. const long locale,
  99. bool &isPresent)
  100. {
  101. LOG_FUNCTION(Analysis::dealWithContainer);
  102. ASSERT(locale > 0);
  103. ASSERT(!rootContainerDn.empty());
  104. HRESULT hr = S_OK;
  105. do
  106. {
  107. String container = String::format(L"CN=%1!3x!,", locale);
  108. String childContainerDn =ldapPrefix + container + rootContainerDn;
  109. // Attempt to bind to the container.
  110. SmartInterface<IADs> iads(0);
  111. hr = AdsiOpenObject<IADs>(childContainerDn, iads);
  112. if (HRESULT_CODE(hr) == ERROR_DS_NO_SUCH_OBJECT)
  113. {
  114. // The container object does not exist. This is possible because
  115. // the user has manually removed the container, or because it
  116. // was never created due to an aboted post-dcpromo import of the
  117. // display specifiers when the forest root dc was first promoted.
  118. // NTRAID#NTBUG9-726839-2002/10/31-lucios
  119. // We are only recovering the 409 container since recovering
  120. // an international locale will overwrite possible 409 customizations
  121. if (locale == 0x409) results.createContainers.push_back(locale);
  122. isPresent=false;
  123. hr = S_OK;
  124. break;
  125. }
  126. else if (FAILED(hr))
  127. {
  128. error=String::format(IDS_ERROR_BINDING_TO_CONTAINER,
  129. childContainerDn.c_str());
  130. break;
  131. }
  132. // At this point, the bind succeeded, so the child container exists.
  133. // So now we want to examine objects in that container.
  134. isPresent=true;
  135. }
  136. while (0);
  137. LOG_HRESULT(hr);
  138. return hr;
  139. }
  140. // sets iDirObj with the Active Directory object
  141. // corresponding to the locale and object
  142. HRESULT
  143. Analysis::getADObj
  144. (
  145. const long locale,
  146. const String& object,
  147. SmartInterface<IDirectoryObject> &iDirObj
  148. )
  149. {
  150. HRESULT hr = S_OK;
  151. do
  152. {
  153. String objectPath =
  154. ldapPrefix + L"CN=" + object + L"," +
  155. String::format(L"CN=%1!3x!,", locale) + rootContainerDn;
  156. SmartInterface<IADs> iads(0);
  157. hr = AdsiOpenObject<IADs>(objectPath, iads);
  158. if (HRESULT_CODE(hr) == ERROR_DS_NO_SUCH_OBJECT)
  159. {
  160. // The object does not exist.
  161. hr = S_FALSE;
  162. break;
  163. }
  164. if (FAILED(hr))
  165. {
  166. // Unexpected error
  167. error=String::format
  168. (
  169. IDS_ERROR_BINDING_TO_OBJECT,
  170. object.c_str(),
  171. objectPath.c_str()
  172. );
  173. break;
  174. }
  175. // At this point, the display specifier object exists.
  176. hr=iDirObj.AcquireViaQueryInterface(iads);
  177. BREAK_ON_FAILED_HRESULT(hr);
  178. } while (0);
  179. LOG_HRESULT(hr);
  180. return hr;
  181. }
  182. // add entries to results.createW2KObjects
  183. // and results.objectActions as necessary
  184. HRESULT
  185. Analysis::dealWithW2KObjects(const long locale)
  186. {
  187. LOG_FUNCTION(Analysis::dealWithW2KObjects);
  188. ASSERT(locale > 0);
  189. HRESULT hr = S_OK;
  190. do
  191. {
  192. hr=checkChanges(locale,changes[guid][locale]);
  193. BREAK_ON_FAILED_HRESULT(hr);
  194. hr=checkChanges(locale,changes[guid][-1]);
  195. BREAK_ON_FAILED_HRESULT(hr);
  196. } while (0);
  197. LOG_HRESULT(hr);
  198. return hr;
  199. }
  200. HRESULT
  201. Analysis::checkChanges
  202. (
  203. const long locale,
  204. const changeList& changes
  205. )
  206. {
  207. LOG_FUNCTION(Analysis::checkChanges);
  208. HRESULT hr=S_OK;
  209. do
  210. {
  211. changeList::const_iterator curChange,endChange;
  212. for
  213. (
  214. curChange=changes.begin(),endChange=changes.end();
  215. curChange!=endChange;
  216. curChange++
  217. )
  218. {
  219. const String &object=curChange->object;
  220. const String &property=curChange->property;
  221. const String &firstArg=curChange->firstArg;
  222. const String &secondArg=curChange->secondArg;
  223. SmartInterface<IDirectoryObject> iDirObj;
  224. hr=getADObj(locale,object,iDirObj);
  225. BREAK_ON_FAILED_HRESULT(hr);
  226. if(hr==S_FALSE) // object doesn't exist
  227. {
  228. ObjectId tempObj(locale,String(object));
  229. if(curChange->type==ADD_OBJECT)
  230. {
  231. results.createWhistlerObjects.push_back(tempObj);
  232. }
  233. else
  234. {
  235. // NTRAID#NTBUG9-726839-2002/10/31-lucios
  236. // We are only recovering objects in the 409 container
  237. // since recovering an object in an international locale
  238. // will overwrite possible 409 customizations
  239. if (
  240. (locale == 0x409) &&
  241. find
  242. (
  243. results.createW2KObjects.begin(),
  244. results.createW2KObjects.end(),
  245. tempObj
  246. ) == results.createW2KObjects.end()
  247. )
  248. {
  249. results.createW2KObjects.push_back(tempObj);
  250. }
  251. }
  252. hr=S_OK;
  253. continue;
  254. }
  255. else
  256. {
  257. ObjectId tempObj(locale,String(object));
  258. if(curChange->type==ADD_OBJECT)
  259. {
  260. results.conflictingWhistlerObjects.push_back(tempObj);
  261. }
  262. }
  263. switch(curChange->type)
  264. {
  265. case ADD_ALL_CSV_VALUES:
  266. hr = addAllCsvValues
  267. (
  268. iDirObj,
  269. locale,
  270. object,
  271. property
  272. );
  273. break;
  274. case ADD_VALUE:
  275. hr = addValue
  276. (
  277. iDirObj,
  278. locale,
  279. object,
  280. property,
  281. firstArg
  282. );
  283. break;
  284. case REPLACE_W2K_MULTIPLE_VALUE:
  285. hr = replaceW2KMultipleValue
  286. (
  287. iDirObj,
  288. locale,
  289. object,
  290. property,
  291. firstArg,
  292. secondArg
  293. );
  294. break;
  295. case REPLACE_W2K_SINGLE_VALUE:
  296. hr = replaceW2KSingleValue
  297. (
  298. iDirObj,
  299. locale,
  300. object,
  301. property,
  302. firstArg,
  303. secondArg
  304. );
  305. break;
  306. case ADD_GUID:
  307. hr = addGuid
  308. (
  309. iDirObj,
  310. locale,
  311. object,
  312. property,
  313. firstArg
  314. );
  315. break;
  316. case REMOVE_GUID:
  317. hr = removeGuid
  318. (
  319. iDirObj,
  320. locale,
  321. object,
  322. property,
  323. firstArg
  324. );
  325. break;
  326. case REPLACE_GUID:
  327. hr = replaceGuid
  328. (
  329. iDirObj,
  330. locale,
  331. object,
  332. property,
  333. firstArg,
  334. secondArg
  335. );
  336. break;
  337. case ADD_OBJECT:
  338. break; // dealt with in the beginning of the function
  339. default:
  340. ASSERT(false);
  341. }
  342. BREAK_ON_FAILED_HRESULT(hr);
  343. }
  344. BREAK_ON_FAILED_HRESULT(hr);
  345. } while(0);
  346. LOG_HRESULT(hr);
  347. return hr;
  348. }
  349. // adds ordAndGuid to the property if Guid is not already there.
  350. HRESULT
  351. Analysis::addGuid
  352. (
  353. IDirectoryObject *iDirObj,
  354. const int locale,
  355. const String &object,
  356. const String &property,
  357. const String &ordAndGuid
  358. )
  359. {
  360. LOG_FUNCTION(Analysis::addGuid);
  361. HRESULT hr = S_OK;
  362. do
  363. {
  364. String guidFound;
  365. hr=getADGuid(
  366. iDirObj,
  367. property,
  368. ordAndGuid,
  369. guidFound
  370. );
  371. BREAK_ON_FAILED_HRESULT(hr);
  372. if (hr == S_FALSE)
  373. {
  374. ObjectId tempObj(locale,String(object));
  375. ValueActions &act=results.objectActions[tempObj][property];
  376. act.addValues.push_back(ordAndGuid);
  377. }
  378. }
  379. while (0);
  380. LOG_HRESULT(hr);
  381. return hr;
  382. }
  383. // replaces ordAndGuidWin2K for ordAndGuidWhistler.
  384. HRESULT
  385. Analysis::replaceGuid
  386. (
  387. IDirectoryObject *iDirObj,
  388. const int locale,
  389. const String &object,
  390. const String &property,
  391. const String &ordAndGuidWin2K,
  392. const String &ordAndGuidWhistler
  393. )
  394. {
  395. LOG_FUNCTION(Analysis::replaceGuid);
  396. HRESULT hr = S_OK;
  397. do
  398. {
  399. String guidFound;
  400. hr=getADGuid(
  401. iDirObj,
  402. property,
  403. ordAndGuidWhistler,
  404. guidFound
  405. );
  406. BREAK_ON_FAILED_HRESULT(hr);
  407. if (hr == S_OK) // The Whistler GUID was found
  408. {
  409. hr=removeExtraneousGUID
  410. (
  411. iDirObj,
  412. locale,
  413. object,
  414. property,
  415. guidFound,
  416. ordAndGuidWin2K,
  417. ordAndGuidWhistler
  418. );
  419. break;
  420. }
  421. // The Whistler GUID is not present
  422. hr=getADGuid(
  423. iDirObj,
  424. property,
  425. ordAndGuidWin2K,
  426. guidFound
  427. );
  428. BREAK_ON_FAILED_HRESULT(hr);
  429. if (hr == S_OK) // The Win2K GUID was found
  430. {
  431. size_t posFound=guidFound.find(L',');
  432. ASSERT(posFound != String::npos);
  433. size_t posWhistler=ordAndGuidWhistler.find(L',');
  434. ASSERT(posWhistler != String::npos);
  435. String guidToAdd = guidFound.substr(0,posFound) +
  436. ordAndGuidWhistler.substr(posWhistler);
  437. ObjectId tempObj(locale,String(object));
  438. ValueActions &act=results.objectActions[tempObj][property];
  439. act.delValues.push_back(guidFound);
  440. act.addValues.push_back(guidToAdd);
  441. hr=removeExtraneousGUID
  442. (
  443. iDirObj,
  444. locale,
  445. object,
  446. property,
  447. guidFound,
  448. ordAndGuidWin2K,
  449. ordAndGuidWhistler
  450. );
  451. break;
  452. }
  453. // Neither the Win2K nor the Whistler GUIDs were found
  454. // Since the customer did not wan't the Win2K GUID
  455. // he probably will not want the Whistler GUID either,
  456. // so we do nothing.
  457. } while(0);
  458. LOG_HRESULT(hr);
  459. return hr;
  460. }
  461. // removes ordAndGuid from the property if Guid is there.
  462. HRESULT
  463. Analysis::removeGuid
  464. (
  465. IDirectoryObject *iDirObj,
  466. const int locale,
  467. const String &object,
  468. const String &property,
  469. const String &ordAndGuid)
  470. {
  471. LOG_FUNCTION(Analysis::removeGuid);
  472. HRESULT hr = S_OK;
  473. do
  474. {
  475. String guidFound;
  476. hr=getADGuid(
  477. iDirObj,
  478. property,
  479. ordAndGuid,
  480. guidFound
  481. );
  482. BREAK_ON_FAILED_HRESULT(hr);
  483. if (hr == S_OK)
  484. {
  485. ObjectId tempObj(locale,String(object));
  486. ValueActions &act=results.objectActions[tempObj][property];
  487. act.delValues.push_back(guidFound);
  488. }
  489. }
  490. while (0);
  491. LOG_HRESULT(hr);
  492. return hr;
  493. }
  494. // adds all csv values still not on the property
  495. HRESULT
  496. Analysis::addAllCsvValues
  497. (
  498. IDirectoryObject *iDirObj,
  499. const long locale,
  500. const String &object,
  501. const String &property
  502. )
  503. {
  504. LOG_FUNCTION(Analysis::addAllCsvValues);
  505. HRESULT hr = S_OK;
  506. const CSVDSReader &csvReader=(locale==0x409)?csvReader409:csvReaderIntl;
  507. do
  508. {
  509. StringList values;
  510. hr=csvReader.getCsvValues(locale,object.c_str(),property.c_str(),values);
  511. BREAK_ON_FAILED_HRESULT(hr);
  512. if (values.size()==0)
  513. {
  514. error=String::format(IDS_NO_CSV_VALUE,locale,object.c_str());
  515. hr=E_FAIL;
  516. break;
  517. }
  518. StringList::iterator begin=values.begin();
  519. StringList::iterator end=values.end();
  520. while(begin!=end)
  521. {
  522. hr=addValue(iDirObj,locale,object,property,begin->c_str());
  523. BREAK_ON_FAILED_HRESULT(hr);
  524. begin++;
  525. }
  526. BREAK_ON_FAILED_HRESULT(hr);
  527. }
  528. while (0);
  529. LOG_HRESULT(hr);
  530. return hr;
  531. }
  532. // adds value to the property if it is not already there.
  533. HRESULT
  534. Analysis::addValue(
  535. IDirectoryObject *iDirObj,
  536. const long locale,
  537. const String &object,
  538. const String &property,
  539. const String &value)
  540. {
  541. LOG_FUNCTION(Analysis::addValue);
  542. HRESULT hr = S_OK;
  543. do
  544. {
  545. hr=isADValuePresent (
  546. iDirObj,
  547. property,
  548. value
  549. );
  550. BREAK_ON_FAILED_HRESULT(hr);
  551. if (hr == S_FALSE)
  552. {
  553. ObjectId tempObj(locale,String(object));
  554. ValueActions &act=results.objectActions[tempObj][property];
  555. act.addValues.push_back(value);
  556. }
  557. }
  558. while (0);
  559. LOG_HRESULT(hr);
  560. return hr;
  561. }
  562. // The idea of replaceW2KValue is replacing the W2K value
  563. // for the Whistler. We also make sure we don't extraneous values.
  564. HRESULT
  565. Analysis::replaceW2KSingleValue
  566. (
  567. IDirectoryObject *iDirObj,
  568. const int locale,
  569. const String &object,
  570. const String &property,
  571. const String &W2KCsvValue,
  572. const String &WhistlerCsvValue
  573. )
  574. {
  575. LOG_FUNCTION(Analysis::replaceW2KSingleValue);
  576. HRESULT hr = S_OK;
  577. do
  578. {
  579. hr=isADValuePresent(iDirObj,property,WhistlerCsvValue);
  580. BREAK_ON_FAILED_HRESULT(hr);
  581. if(hr == S_OK) // The Whistler value is already there
  582. {
  583. // We will remove any other value than the Whistler
  584. hr=removeExtraneous
  585. (
  586. iDirObj,
  587. locale,
  588. object,
  589. property,
  590. WhistlerCsvValue
  591. );
  592. break;
  593. }
  594. // Now we know that the Whistler value is not present
  595. // and therefore we will add it if the W2K value is present
  596. hr=isADValuePresent(iDirObj,property,W2KCsvValue);
  597. BREAK_ON_FAILED_HRESULT(hr);
  598. if(hr == S_OK) // The W2K value is there.
  599. {
  600. ObjectId tempObj(locale,String(object));
  601. ValueActions &act=results.objectActions[tempObj][property];
  602. act.addValues.push_back(WhistlerCsvValue);
  603. act.delValues.push_back(W2KCsvValue);
  604. // remove all but the W2K that we removed in the previous line
  605. hr=removeExtraneous
  606. (
  607. iDirObj,
  608. locale,
  609. object,
  610. property,
  611. W2KCsvValue
  612. );
  613. break;
  614. }
  615. // Now we know that neither Whistler nor W2K values are present
  616. // If we have a value we will log that it is a custom value
  617. String ADValue;
  618. hr=getADFirstValue(iDirObj,property,ADValue);
  619. BREAK_ON_FAILED_HRESULT(hr);
  620. if(hr == S_OK) // We have a value
  621. {
  622. SingleValue tmpCustom(locale,object,property,ADValue);
  623. results.customizedValues.push_back(tmpCustom);
  624. // We will remove any other value than the one we found
  625. hr=removeExtraneous(iDirObj,locale,object,property,ADValue);
  626. break;
  627. }
  628. // Now we know that we don't have any values at all.
  629. ObjectId tempObj(locale,String(object));
  630. ValueActions &act=results.objectActions[tempObj][property];
  631. act.addValues.push_back(WhistlerCsvValue);
  632. }
  633. while(0);
  634. LOG_HRESULT(hr);
  635. return hr;
  636. }
  637. // The idea of replaceW2KValue is replacing the W2K value
  638. // for the Whistler. We also make sure we don't ahve extraneous values.
  639. HRESULT
  640. Analysis::replaceW2KMultipleValue
  641. (
  642. IDirectoryObject *iDirObj,
  643. const int locale,
  644. const String &object,
  645. const String &property,
  646. const String &W2KCsvValue,
  647. const String &WhistlerCsvValue
  648. )
  649. {
  650. LOG_FUNCTION(Analysis::replaceW2KMultipleValue);
  651. // First we should get the beginning of the W2K
  652. // snd Whistler strings for use in removeExtraneous calls
  653. size_t pos=W2KCsvValue.find(L',');
  654. ASSERT(pos != String::npos); // W2KRepl ensures the comma
  655. String W2KStart=W2KCsvValue.substr(0,pos+1);
  656. pos=WhistlerCsvValue.find(L',');
  657. ASSERT(pos != String::npos); // W2KRepl ensures the comma
  658. String WhistlerStart=WhistlerCsvValue.substr(0,pos+1);
  659. HRESULT hr = S_OK;
  660. do
  661. {
  662. hr=isADValuePresent(iDirObj,property,WhistlerCsvValue);
  663. BREAK_ON_FAILED_HRESULT(hr);
  664. if(hr == S_OK) // The Whistler value is already there
  665. {
  666. hr=removeExtraneous(
  667. iDirObj,
  668. locale,
  669. object,
  670. property,
  671. WhistlerCsvValue,
  672. WhistlerStart,
  673. W2KStart
  674. );
  675. BREAK_ON_FAILED_HRESULT(hr);
  676. break;
  677. }
  678. // Now we know that the Whistler value is not present
  679. // and therefore we will add it if the W2K value is present
  680. hr=isADValuePresent(iDirObj,property,W2KCsvValue);
  681. BREAK_ON_FAILED_HRESULT(hr);
  682. if(hr == S_OK) // The W2K value is there.
  683. {
  684. ObjectId tempObj(locale,String(object));
  685. ValueActions &act=results.objectActions[tempObj][property];
  686. act.addValues.push_back(WhistlerCsvValue);
  687. act.delValues.push_back(W2KCsvValue);
  688. // remove all but the W2K that we removed in the previous line
  689. hr=removeExtraneous(
  690. iDirObj,
  691. locale,
  692. object,
  693. property,
  694. W2KCsvValue,
  695. WhistlerStart,
  696. W2KStart
  697. );
  698. break;
  699. }
  700. // Now we know that neither Whistler nor W2K values are present
  701. // If we have a value starting like the W2K we will log that it
  702. // is a custom value
  703. String ADValue;
  704. hr=isADStartValuePresent(iDirObj,property,W2KStart,ADValue);
  705. BREAK_ON_FAILED_HRESULT(hr);
  706. if(hr==S_OK) // Something starts like the W2K csv value
  707. {
  708. SingleValue tmpCustom(locale,object,property,ADValue);
  709. results.customizedValues.push_back(tmpCustom);
  710. // We will keep only the first custom value
  711. hr=removeExtraneous(
  712. iDirObj,
  713. locale,
  714. object,
  715. property,
  716. ADValue,
  717. WhistlerStart,
  718. W2KStart
  719. );
  720. break;
  721. }
  722. // Now neither Whistler, W2K or W2KStart are present
  723. if ( WhistlerStart == W2KStart )
  724. {
  725. // We have to check the WhistlerStart as well
  726. hr=isADStartValuePresent(iDirObj,property,WhistlerStart,ADValue);
  727. BREAK_ON_FAILED_HRESULT(hr);
  728. if(hr == S_OK) // Something starts like the Whistler csv value
  729. {
  730. SingleValue tmpCustom(locale,object,property,ADValue);
  731. results.customizedValues.push_back(tmpCustom);
  732. // We will keep only the first custom value
  733. hr=removeExtraneous(
  734. iDirObj,
  735. locale,
  736. object,
  737. property,
  738. ADValue,
  739. WhistlerStart,
  740. W2KStart
  741. );
  742. break;
  743. }
  744. }
  745. // Now we know that there are no values starting like
  746. // the Whistler or W2K csv values so we have to add
  747. // the Whistler value
  748. ObjectId tempObj(locale,String(object));
  749. ValueActions &act=results.objectActions[tempObj][property];
  750. act.addValues.push_back(WhistlerCsvValue);
  751. }
  752. while(0);
  753. LOG_HRESULT(hr);
  754. return hr;
  755. }
  756. //called from RwplaceW2KMultipleValue to remove all values
  757. // starting with start1 or start2 other than keeper
  758. HRESULT
  759. Analysis::removeExtraneous
  760. (
  761. IDirectoryObject *iDirObj,
  762. const int locale,
  763. const String &object,
  764. const String &property,
  765. const String &keeper,
  766. const String &start1,
  767. const String &start2
  768. )
  769. {
  770. LOG_FUNCTION(Analysis::removeExtraneous);
  771. HRESULT hr = S_OK;
  772. DWORD dwReturn=0;
  773. ADS_ATTR_INFO *pAttrInfo =NULL;
  774. // iDirObj->GetObjectAttributes swears that pAttrName is an IN argument.
  775. // It should have used a LPCWSTR but now we have to pay the
  776. // casting price
  777. LPWSTR pAttrName[] ={const_cast<LPWSTR>(property.c_str())};
  778. do
  779. {
  780. hr = iDirObj->GetObjectAttributes(
  781. pAttrName,
  782. 1,
  783. &pAttrInfo,
  784. &dwReturn
  785. );
  786. do
  787. {
  788. BREAK_ON_FAILED_HRESULT(hr);
  789. if(pAttrInfo==NULL)
  790. {
  791. hr = S_FALSE;
  792. break;
  793. }
  794. for (
  795. DWORD val=0;
  796. val < pAttrInfo->dwNumValues;
  797. val++
  798. )
  799. {
  800. ASSERT
  801. (
  802. pAttrInfo->pADsValues[val].dwType ==
  803. ADSTYPE_CASE_IGNORE_STRING
  804. );
  805. wchar_t *valueAD = pAttrInfo->pADsValues[val].CaseIgnoreString;
  806. if ( wcscmp(valueAD,keeper.c_str())!=0 &&
  807. (
  808. wcsncmp(valueAD,start1.c_str(),start1.size())==0 ||
  809. wcsncmp(valueAD,start2.c_str(),start2.size())==0
  810. )
  811. )
  812. {
  813. String value=valueAD;
  814. ObjectId tempObj(locale,String(object));
  815. ValueActions &act=results.extraneousValues[tempObj][property];
  816. act.delValues.push_back(value);
  817. }
  818. }
  819. } while(0);
  820. if (pAttrInfo!=NULL) FreeADsMem(pAttrInfo);
  821. }
  822. while (0);
  823. LOG_HRESULT(hr);
  824. return hr;
  825. }
  826. // called from RwplaceW2KSingleValue to remove all values
  827. // other than keeper
  828. HRESULT
  829. Analysis::removeExtraneous
  830. (
  831. IDirectoryObject *iDirObj,
  832. const int locale,
  833. const String &object,
  834. const String &property,
  835. const String &keeper
  836. )
  837. {
  838. LOG_FUNCTION(Analysis::removeExtraneous);
  839. HRESULT hr = S_OK;
  840. DWORD dwReturn=0;
  841. ADS_ATTR_INFO *pAttrInfo =NULL;
  842. // iDirObj->GetObjectAttributes swears that pAttrName is an IN argument.
  843. // It should have used a LPCWSTR but now we have to pay the
  844. // casting price
  845. LPWSTR pAttrName[] ={const_cast<LPWSTR>(property.c_str())};
  846. do
  847. {
  848. hr = iDirObj->GetObjectAttributes(
  849. pAttrName,
  850. 1,
  851. &pAttrInfo,
  852. &dwReturn
  853. );
  854. do
  855. {
  856. BREAK_ON_FAILED_HRESULT(hr);
  857. if(pAttrInfo==NULL)
  858. {
  859. hr = S_FALSE;
  860. break;
  861. }
  862. for (
  863. DWORD val=0;
  864. val < pAttrInfo->dwNumValues;
  865. val++
  866. )
  867. {
  868. ASSERT
  869. (
  870. pAttrInfo->pADsValues[val].dwType ==
  871. ADSTYPE_CASE_IGNORE_STRING
  872. );
  873. wchar_t *valueAD = pAttrInfo->pADsValues[val].CaseIgnoreString;
  874. if ( wcscmp(valueAD,keeper.c_str())!=0 )
  875. {
  876. String value=valueAD;
  877. ObjectId tempObj(locale,String(object));
  878. ValueActions &act=results.extraneousValues[tempObj][property];
  879. act.delValues.push_back(value);
  880. }
  881. }
  882. } while(0);
  883. if (pAttrInfo!=NULL) FreeADsMem(pAttrInfo);
  884. }
  885. while (0);
  886. LOG_HRESULT(hr);
  887. return hr;
  888. }
  889. // called from replaceGUID to remove all values
  890. // starting with the GUID in ordAndGuid1
  891. // or the GUID in ordAndGuid2 other than keeper
  892. HRESULT
  893. Analysis::removeExtraneousGUID
  894. (
  895. IDirectoryObject *iDirObj,
  896. const int locale,
  897. const String &object,
  898. const String &property,
  899. const String &keeper,
  900. const String &ordAndGuid1,
  901. const String &ordAndGuid2
  902. )
  903. {
  904. LOG_FUNCTION(Analysis::removeExtraneousGUID);
  905. HRESULT hr = S_OK;
  906. size_t pos=ordAndGuid1.find(L',');
  907. ASSERT(pos != String::npos);
  908. String guid1=ordAndGuid1.substr(pos+1);
  909. pos=ordAndGuid2.find(L',');
  910. ASSERT(pos != String::npos);
  911. String guid2=ordAndGuid2.substr(pos+1);
  912. DWORD dwReturn=0;
  913. ADS_ATTR_INFO *pAttrInfo =NULL;
  914. // iDirObj->GetObjectAttributes swears that pAttrName is an IN argument.
  915. // It should have used a LPCWSTR but now we have to pay the
  916. // casting price
  917. LPWSTR pAttrName[] ={const_cast<LPWSTR>(property.c_str())};
  918. do
  919. {
  920. hr = iDirObj->GetObjectAttributes(
  921. pAttrName,
  922. 1,
  923. &pAttrInfo,
  924. &dwReturn
  925. );
  926. do
  927. {
  928. BREAK_ON_FAILED_HRESULT(hr);
  929. if(pAttrInfo==NULL)
  930. {
  931. hr = S_FALSE;
  932. break;
  933. }
  934. for (
  935. DWORD val=0;
  936. val < pAttrInfo->dwNumValues;
  937. val++
  938. )
  939. {
  940. ASSERT
  941. (
  942. pAttrInfo->pADsValues[val].dwType ==
  943. ADSTYPE_CASE_IGNORE_STRING
  944. );
  945. wchar_t *valueAD = pAttrInfo->pADsValues[val].CaseIgnoreString;
  946. if (keeper.icompare(valueAD)!=0)
  947. {
  948. String valueStr=valueAD;
  949. pos=valueStr.find(L',');
  950. if (pos!=String::npos)
  951. {
  952. String guid=valueStr.substr(pos+1);
  953. if(guid1.icompare(guid)==0 || guid2.icompare(guid)==0)
  954. {
  955. ObjectId tempObj(locale,String(object));
  956. ValueActions &act=results.extraneousValues[tempObj][property];
  957. act.delValues.push_back(valueStr);
  958. }
  959. }
  960. }
  961. }
  962. } while(0);
  963. if (pAttrInfo!=NULL) FreeADsMem(pAttrInfo);
  964. }
  965. while (0);
  966. LOG_HRESULT(hr);
  967. return hr;
  968. }
  969. // if any value exists in the AD with the same guid as guidValue
  970. // it is returned in guidFound, otherwise S_FALSE is returned
  971. HRESULT
  972. Analysis::getADGuid
  973. (
  974. IDirectoryObject *iDirObj,
  975. const String &property,
  976. const String &guidValue,
  977. String &guidFound
  978. )
  979. {
  980. LOG_FUNCTION(Analysis::getADGuid);
  981. DWORD dwReturn=0;
  982. ADS_ATTR_INFO *pAttrInfo =NULL;
  983. // iDirObj->GetObjectAttributes swears that pAttrName is an IN argument.
  984. // It should have used a LPCWSTR but now we have to pay the
  985. // casting price
  986. LPWSTR pAttrName[] ={const_cast<LPWSTR>(property.c_str())};
  987. size_t pos=guidValue.find(L',');
  988. ASSERT(pos!=String::npos);
  989. String guid=guidValue.substr(pos+1);
  990. HRESULT hr = S_OK;
  991. do
  992. {
  993. hr = iDirObj->GetObjectAttributes(
  994. pAttrName,
  995. 1,
  996. &pAttrInfo,
  997. &dwReturn
  998. );
  999. do
  1000. {
  1001. BREAK_ON_FAILED_HRESULT(hr);
  1002. // If there are no values we finish the search
  1003. hr=S_FALSE;
  1004. if(pAttrInfo==NULL)
  1005. {
  1006. break;
  1007. }
  1008. for (
  1009. DWORD val=0;
  1010. val < pAttrInfo->dwNumValues;
  1011. val++
  1012. )
  1013. {
  1014. ASSERT
  1015. (
  1016. pAttrInfo->pADsValues[val].dwType ==
  1017. ADSTYPE_CASE_IGNORE_STRING
  1018. );
  1019. wchar_t *guidAD=wcschr(pAttrInfo->pADsValues[val].CaseIgnoreString,L',');
  1020. if(guidAD != NULL)
  1021. {
  1022. guidAD++;
  1023. if (guid.icompare(guidAD)==0)
  1024. {
  1025. guidFound=pAttrInfo->pADsValues[val].CaseIgnoreString;
  1026. hr=S_OK;
  1027. break;
  1028. }
  1029. }
  1030. }
  1031. } while(0);
  1032. if (pAttrInfo!=NULL) FreeADsMem(pAttrInfo);
  1033. }
  1034. while (0);
  1035. LOG_HRESULT(hr);
  1036. return hr;
  1037. }
  1038. // returns S_OK if value is present or S_FALSE otherwise
  1039. HRESULT
  1040. Analysis::isADValuePresent
  1041. (
  1042. IDirectoryObject *iDirObj,
  1043. const String &property,
  1044. const String &value
  1045. )
  1046. {
  1047. LOG_FUNCTION(Analysis::isADValuePresent);
  1048. DWORD dwReturn=0;
  1049. ADS_ATTR_INFO *pAttrInfo =NULL;
  1050. // iDirObj->GetObjectAttributes swears that pAttrName is an IN argument.
  1051. // It should have used a LPCWSTR but now we have to pay the
  1052. // casting price
  1053. LPWSTR pAttrName[] ={const_cast<LPWSTR>(property.c_str())};
  1054. HRESULT hr = S_OK;
  1055. do
  1056. {
  1057. hr = iDirObj->GetObjectAttributes(
  1058. pAttrName,
  1059. 1,
  1060. &pAttrInfo,
  1061. &dwReturn
  1062. );
  1063. do
  1064. {
  1065. BREAK_ON_FAILED_HRESULT(hr);
  1066. hr=S_FALSE;
  1067. // If there are no values we finish the search
  1068. if(pAttrInfo==NULL)
  1069. {
  1070. break;
  1071. }
  1072. for (
  1073. DWORD val=0;
  1074. val < pAttrInfo->dwNumValues;
  1075. val++
  1076. )
  1077. {
  1078. ASSERT
  1079. (
  1080. pAttrInfo->pADsValues[val].dwType ==
  1081. ADSTYPE_CASE_IGNORE_STRING
  1082. );
  1083. wchar_t *valueAD=pAttrInfo->pADsValues[val].CaseIgnoreString;
  1084. if (wcscmp(value.c_str(),valueAD)==0)
  1085. {
  1086. hr=S_OK;
  1087. break;
  1088. }
  1089. }
  1090. } while(0);
  1091. if (pAttrInfo!=NULL) FreeADsMem(pAttrInfo);
  1092. }
  1093. while (0);
  1094. LOG_HRESULT(hr);
  1095. return hr;
  1096. }
  1097. // retrieves the first value starting with valueStart
  1098. // from the Active Directory
  1099. // If no value is found S_FALSE is returned.
  1100. HRESULT
  1101. Analysis::isADStartValuePresent
  1102. (
  1103. IDirectoryObject *iDirObj,
  1104. const String &property,
  1105. const String &valueStart,
  1106. String &value
  1107. )
  1108. {
  1109. LOG_FUNCTION(Analysis::isADStartValuePresent);
  1110. DWORD dwReturn=0;
  1111. ADS_ATTR_INFO *pAttrInfo =NULL;
  1112. // iDirObj->GetObjectAttributes swears that pAttrName is an IN argument.
  1113. // It should have used a LPCWSTR but now we have to pay the
  1114. // casting price
  1115. LPWSTR pAttrName[] ={const_cast<LPWSTR>(property.c_str())};
  1116. HRESULT hr = S_OK;
  1117. do
  1118. {
  1119. hr = iDirObj->GetObjectAttributes(
  1120. pAttrName,
  1121. 1,
  1122. &pAttrInfo,
  1123. &dwReturn
  1124. );
  1125. do
  1126. {
  1127. BREAK_ON_FAILED_HRESULT(hr);
  1128. value.erase();
  1129. hr = S_FALSE;
  1130. // If there are no values we finish the search
  1131. if(pAttrInfo==NULL)
  1132. {
  1133. break;
  1134. }
  1135. for (
  1136. DWORD val=0;
  1137. (val < pAttrInfo->dwNumValues);
  1138. val++
  1139. )
  1140. {
  1141. ASSERT
  1142. (
  1143. pAttrInfo->pADsValues[val].dwType ==
  1144. ADSTYPE_CASE_IGNORE_STRING
  1145. );
  1146. wchar_t *valueAD=pAttrInfo->pADsValues[val].CaseIgnoreString;
  1147. if (wcsncmp(valueStart.c_str(),valueAD,valueStart.size())==0)
  1148. {
  1149. value=pAttrInfo->pADsValues[val].CaseIgnoreString;
  1150. hr=S_OK;
  1151. break;
  1152. }
  1153. }
  1154. } while(0);
  1155. if (pAttrInfo!=NULL) FreeADsMem(pAttrInfo);
  1156. }
  1157. while (0);
  1158. LOG_HRESULT(hr);
  1159. return hr;
  1160. }
  1161. // retrieves the first value
  1162. // from the Active Directory
  1163. // If no value is found S_FALSE is returned.
  1164. HRESULT
  1165. Analysis::getADFirstValue
  1166. (
  1167. IDirectoryObject *iDirObj,
  1168. const String &property,
  1169. String &value
  1170. )
  1171. {
  1172. LOG_FUNCTION(Analysis::getADFirstValue);
  1173. DWORD dwReturn=0;
  1174. ADS_ATTR_INFO *pAttrInfo =NULL;
  1175. // iDirObj->GetObjectAttributes swears that pAttrName is an IN argument.
  1176. // It should have used a LPCWSTR but now we have to pay the
  1177. // casting price
  1178. LPWSTR pAttrName[] ={const_cast<LPWSTR>(property.c_str())};
  1179. HRESULT hr = S_OK;
  1180. do
  1181. {
  1182. hr = iDirObj->GetObjectAttributes(
  1183. pAttrName,
  1184. 1,
  1185. &pAttrInfo,
  1186. &dwReturn
  1187. );
  1188. do
  1189. {
  1190. BREAK_ON_FAILED_HRESULT(hr);
  1191. // If there are no values we finish the search
  1192. if(pAttrInfo==NULL)
  1193. {
  1194. hr = S_FALSE;
  1195. break;
  1196. }
  1197. ASSERT(pAttrInfo->pADsValues->dwType==ADSTYPE_CASE_IGNORE_STRING);
  1198. value=pAttrInfo->pADsValues->CaseIgnoreString;
  1199. } while(0);
  1200. if (pAttrInfo!=NULL) FreeADsMem(pAttrInfo);
  1201. }
  1202. while (0);
  1203. LOG_HRESULT(hr);
  1204. return hr;
  1205. }
  1206. // auxiliary in the createReport to
  1207. // enumerate an ObjectIdList
  1208. HRESULT
  1209. Analysis::reportObjects
  1210. (
  1211. HANDLE file,
  1212. const ObjectIdList &list,
  1213. const String &header
  1214. )
  1215. {
  1216. LOG_FUNCTION(Analysis::reportObjects);
  1217. HRESULT hr=S_OK;
  1218. do
  1219. {
  1220. if(list.size()==0) break;
  1221. hr=FS::WriteLine(file,header);
  1222. BREAK_ON_FAILED_HRESULT(hr);
  1223. ObjectIdList::const_iterator begin,end;
  1224. begin=list.begin();
  1225. end=list.end();
  1226. while(begin!=end)
  1227. {
  1228. hr=FS::WriteLine(
  1229. file,
  1230. String::format
  1231. (
  1232. IDS_RPT_OBJECT_FORMAT,
  1233. begin->object.c_str(),
  1234. begin->locale
  1235. )
  1236. );
  1237. BREAK_ON_FAILED_HRESULT(hr);
  1238. begin++;
  1239. }
  1240. BREAK_ON_FAILED_HRESULT(hr);
  1241. }
  1242. while(0);
  1243. LOG_HRESULT(hr);
  1244. return hr;
  1245. }
  1246. // auxiliary in the createReport to
  1247. // enumerate a LongList
  1248. HRESULT
  1249. Analysis::reportContainers
  1250. (
  1251. HANDLE file,
  1252. const LongList &list,
  1253. const String &header
  1254. )
  1255. {
  1256. LOG_FUNCTION(Analysis::reportContainers);
  1257. HRESULT hr=S_OK;
  1258. do
  1259. {
  1260. if(list.size()==0) break;
  1261. hr=FS::WriteLine(file,header);
  1262. BREAK_ON_FAILED_HRESULT(hr);
  1263. LongList::const_iterator begin,end;
  1264. begin=list.begin();
  1265. end=list.end();
  1266. while(begin!=end)
  1267. {
  1268. hr=FS::WriteLine(
  1269. file,
  1270. String::format
  1271. (
  1272. IDS_RPT_CONTAINER_FORMAT,
  1273. *begin
  1274. )
  1275. );
  1276. BREAK_ON_FAILED_HRESULT(hr);
  1277. begin++;
  1278. }
  1279. BREAK_ON_FAILED_HRESULT(hr);
  1280. }
  1281. while(0);
  1282. LOG_HRESULT(hr);
  1283. return hr;
  1284. }
  1285. // auxiliary in the createReport to
  1286. // enumerate a SingleValueList
  1287. HRESULT
  1288. Analysis::reportValues
  1289. (
  1290. HANDLE file,
  1291. const SingleValueList &list,
  1292. const String &header
  1293. )
  1294. {
  1295. LOG_FUNCTION(Analysis::reportValues);
  1296. HRESULT hr=S_OK;
  1297. do
  1298. {
  1299. if(list.size()==0) break;
  1300. hr=FS::WriteLine(file,header);
  1301. BREAK_ON_FAILED_HRESULT(hr);
  1302. SingleValueList::const_iterator begin,end;
  1303. begin=list.begin();
  1304. end=list.end();
  1305. while(begin!=end)
  1306. {
  1307. hr=FS::WriteLine(
  1308. file,
  1309. String::format
  1310. (
  1311. IDS_RPT_VALUE_FORMAT,
  1312. begin->value.c_str(),
  1313. begin->locale,
  1314. begin->object.c_str(),
  1315. begin->property.c_str()
  1316. )
  1317. );
  1318. BREAK_ON_FAILED_HRESULT(hr);
  1319. begin++;
  1320. }
  1321. BREAK_ON_FAILED_HRESULT(hr);
  1322. }
  1323. while(0);
  1324. LOG_HRESULT(hr);
  1325. return hr;
  1326. }
  1327. // auxiliary in the createReport to
  1328. // enumerate ObjectActions
  1329. HRESULT
  1330. Analysis::reportActions
  1331. (
  1332. HANDLE file,
  1333. const ObjectActions &list,
  1334. const String &header
  1335. )
  1336. {
  1337. LOG_FUNCTION(Analysis::reportActions);
  1338. HRESULT hr=S_OK;
  1339. do
  1340. {
  1341. if(list.size()==0) break;
  1342. hr=FS::WriteLine(file,header);
  1343. BREAK_ON_FAILED_HRESULT(hr);
  1344. ObjectActions::const_iterator beginObj=list.begin();
  1345. ObjectActions::const_iterator endObj=list.end();
  1346. while(beginObj!=endObj)
  1347. {
  1348. hr=FS::WriteLine
  1349. (
  1350. file,
  1351. String::format
  1352. (
  1353. IDS_RPT_OBJECT_FORMAT,
  1354. beginObj->first.object.c_str(),
  1355. beginObj->first.locale
  1356. )
  1357. );
  1358. BREAK_ON_FAILED_HRESULT(hr);
  1359. PropertyActions::iterator beginAct=beginObj->second.begin();
  1360. PropertyActions::iterator endAct=beginObj->second.end();
  1361. while(beginAct!=endAct)
  1362. {
  1363. StringList::iterator
  1364. beginDel = beginAct->second.delValues.begin();
  1365. StringList::iterator
  1366. endDel = beginAct->second.delValues.end();
  1367. while(beginDel!=endDel)
  1368. {
  1369. hr=FS::WriteLine
  1370. (
  1371. file,
  1372. String::format
  1373. (
  1374. IDS_RPT_DEL_VALUE_FORMAT,
  1375. beginAct->first.c_str(),
  1376. beginDel->c_str()
  1377. )
  1378. );
  1379. BREAK_ON_FAILED_HRESULT(hr);
  1380. beginDel++;
  1381. }
  1382. BREAK_ON_FAILED_HRESULT(hr);
  1383. StringList::iterator
  1384. beginAdd = beginAct->second.addValues.begin();
  1385. StringList::iterator
  1386. endAdd = beginAct->second.addValues.end();
  1387. while(beginAdd!=endAdd)
  1388. {
  1389. hr=FS::WriteLine
  1390. (
  1391. file,
  1392. String::format
  1393. (
  1394. IDS_RPT_ADD_VALUE_FORMAT,
  1395. beginAct->first.c_str(),
  1396. beginAdd->c_str()
  1397. )
  1398. );
  1399. BREAK_ON_FAILED_HRESULT(hr);
  1400. beginAdd++;
  1401. }
  1402. BREAK_ON_FAILED_HRESULT(hr);
  1403. beginAct++;
  1404. } // while(beginAct!=endAct)
  1405. BREAK_ON_FAILED_HRESULT(hr);
  1406. beginObj++;
  1407. } // while(beginObj!=endObj)
  1408. BREAK_ON_FAILED_HRESULT(hr);
  1409. }
  1410. while(0);
  1411. LOG_HRESULT(hr);
  1412. return hr;
  1413. }
  1414. // Create the report from the AnalysisResults
  1415. HRESULT
  1416. Analysis::createReport(const String& reportName)
  1417. {
  1418. LOG_FUNCTION(Analysis::createReport);
  1419. HRESULT hr=S_OK;
  1420. do
  1421. {
  1422. HANDLE file;
  1423. hr=FS::CreateFile(reportName,
  1424. file,
  1425. GENERIC_WRITE);
  1426. if (FAILED(hr))
  1427. {
  1428. error=String::format(IDS_COULD_NOT_CREATE_FILE,reportName.c_str());
  1429. break;
  1430. }
  1431. do
  1432. {
  1433. hr=FS::WriteLine(file,String::load(IDS_RPT_HEADER));
  1434. BREAK_ON_FAILED_HRESULT(hr);
  1435. hr=reportActions (
  1436. file,
  1437. results.extraneousValues,
  1438. String::load(IDS_RPT_EXTRANEOUS)
  1439. );
  1440. BREAK_ON_FAILED_HRESULT(hr);
  1441. hr=reportValues (
  1442. file,
  1443. results.customizedValues,
  1444. String::load(IDS_RPT_CUSTOMIZED)
  1445. );
  1446. BREAK_ON_FAILED_HRESULT(hr);
  1447. hr=reportObjects
  1448. (
  1449. file,
  1450. results.conflictingWhistlerObjects,
  1451. String::load
  1452. (
  1453. IDS_RPT_CONFLICTING_WITH_NEW_WHISTLER_OBJECTS
  1454. )
  1455. );
  1456. BREAK_ON_FAILED_HRESULT(hr);
  1457. hr=reportActions (
  1458. file,
  1459. results.objectActions,
  1460. String::load(IDS_RPT_ACTIONS)
  1461. );
  1462. BREAK_ON_FAILED_HRESULT(hr);
  1463. hr=reportObjects (
  1464. file,
  1465. results.createW2KObjects,
  1466. String::load(IDS_RPT_CREATEW2K)
  1467. );
  1468. BREAK_ON_FAILED_HRESULT(hr);
  1469. hr=reportObjects (
  1470. file,
  1471. results.createWhistlerObjects,
  1472. String::load(IDS_RPT_CREATE_WHISTLER)
  1473. );
  1474. BREAK_ON_FAILED_HRESULT(hr);
  1475. hr=reportContainers(
  1476. file,
  1477. results.createContainers,
  1478. String::load(IDS_RPT_CONTAINERS)
  1479. );
  1480. BREAK_ON_FAILED_HRESULT(hr);
  1481. } while(0);
  1482. CloseHandle(file);
  1483. BREAK_ON_FAILED_HRESULT(hr);
  1484. } while(0);
  1485. LOG_HRESULT(hr);
  1486. return hr;
  1487. }