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.

915 lines
24 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. For Internal use only!
  4. Module Name:
  5. INFSCAN
  6. installscan.cpp
  7. Abstract:
  8. Individual install section scanner class
  9. main entry point InfScan::ScanInstallSection
  10. WARNING! WARNING!
  11. All of this implementation relies on intimate knowledge of
  12. SetupAPI's SETUPAPI!SetupInstallFromInfSection
  13. It is re-implemented here for speed due to having to process
  14. 700+ INF's in a single go at the cost of having to maintain
  15. this.
  16. DO NOT (I repeat) DO NOT re-implement the code here without
  17. consultation with SetupAPI owner.
  18. History:
  19. Created July 2001 - JamieHun
  20. --*/
  21. #include "precomp.h"
  22. #pragma hdrstop
  23. InstallScan::InstallScan()
  24. /*++
  25. Routine Description:
  26. Initialization
  27. --*/
  28. {
  29. pGlobalScan = NULL;
  30. pInfScan = NULL;
  31. PlatformMask = 0;
  32. pTargetDirectory = NULL;
  33. NotDeviceInstall = false;
  34. HasDependentFileChanged = false;
  35. }
  36. InstallScan::~InstallScan()
  37. /*++
  38. Routine Description:
  39. Cleanup allocated data/handles
  40. --*/
  41. {
  42. }
  43. void InstallScan::AddHWIDs(const StringSet & hwids)
  44. /*++
  45. Routine Description:
  46. Add to list of HWIDs effected by this section
  47. --*/
  48. {
  49. StringSet::iterator i;
  50. for(i = hwids.begin(); i != hwids.end(); i++) {
  51. HWIDs.insert(*i);
  52. }
  53. }
  54. void InstallScan::GetHWIDs(StringSet & hwids)
  55. /*++
  56. Routine Description:
  57. Retrieve list of HWIDs effected by this section
  58. --*/
  59. {
  60. StringSet::iterator i;
  61. for(i = HWIDs.begin(); i != HWIDs.end(); i++) {
  62. hwids.insert(*i);
  63. }
  64. }
  65. int
  66. InstallScan::ScanInstallSection()
  67. /*++
  68. Routine Description:
  69. Main entry point for processing an install section
  70. WARNING! tightly coupled with SetupAPI implementation that could change
  71. MAINTAINANCE: keep this in step with supported Needs/nesting syntax
  72. MAINTAINANCE: keep in step with install processing
  73. Arguments:
  74. NONE
  75. Return Value:
  76. 0 on success
  77. --*/
  78. {
  79. int res;
  80. //
  81. // prime initial list of search INF's
  82. //
  83. Layouts();
  84. int count = 1; // no more than 1
  85. res = RecurseKeyword(Section,TEXT("Include"),IncludeCallback,count);
  86. if(res != 0) {
  87. return res;
  88. }
  89. if(count<0) {
  90. Fail(MSG_MULTIPLE_INCLUDE,Section);
  91. } else if(Pedantic() && (count<1)) {
  92. Fail(MSG_INCLUDE,Section);
  93. }
  94. count = 1; // no more than 1
  95. res = RecurseKeyword(Section,TEXT("Needs"),NeedsCallback,count);
  96. if(res != 0) {
  97. return res;
  98. }
  99. if(count<0) {
  100. Fail(MSG_MULTIPLE_NEEDS,Section);
  101. } else if(Pedantic() && (count<1)) {
  102. Fail(MSG_NEEDS,Section);
  103. }
  104. return CheckInstallSubSection(Section);
  105. }
  106. int
  107. InstallScan::RecurseKeyword(const SafeString & sect,const SafeString & keyword,RecurseKeywordCallback callback,int & count)
  108. /*++
  109. Routine Description:
  110. Section parsing workhorse
  111. WARNING! Uses knowledge about how SetupAPI searches INF lists, could change
  112. MAINTAINANCE: Keep search algorithm in step with SetupAPI
  113. see aslo QuerySourceFile, GetLineCount and DoesSectionExist
  114. given keyword = val[,val...]
  115. each value is converted to lower-case and callback is called with information
  116. about what inf/section the value is in.
  117. Arguments:
  118. sect - name of section to search
  119. keyword - name of keyword
  120. callback - callback function for each hit
  121. count - must be initialized to max # of callbacks to invoke
  122. - returns -1 if there are more entries than wanted
  123. Return Value:
  124. 0 on success
  125. --*/
  126. {
  127. INFCONTEXT context;
  128. int res;
  129. int i;
  130. SafeString value;
  131. //
  132. // we didn't call SetupOpenAppendInf for any INF's, so instead
  133. // enumerate all "included inf's" using our view of the world
  134. // this allows us to reset our view for each install section
  135. // to validate include/needs processing
  136. //
  137. ParseInfContextList::iterator AnInf;
  138. for(AnInf = InfSearchList.begin(); AnInf != InfSearchList.end(); AnInf++) {
  139. ParseInfContextBlob & TheInf = *AnInf;
  140. if(TheInf->InfHandle == INVALID_HANDLE_VALUE) {
  141. //
  142. // a fail-loaded inf
  143. //
  144. continue;
  145. }
  146. if(!SetupFindFirstLine(TheInf->InfHandle,sect.c_str(),keyword.c_str(),&context)) {
  147. //
  148. // can't find (keyword in) section in this inf
  149. //
  150. continue;
  151. }
  152. do {
  153. if(count>=0) {
  154. count--;
  155. if(count<0) {
  156. return 0;
  157. }
  158. }
  159. for(i=1;MyGetStringField(&context,i,value);i++) {
  160. res = (this->*callback)(TheInf,sect,keyword,value);
  161. if(res != 0) {
  162. return res;
  163. }
  164. }
  165. } while (SetupFindNextMatchLine(&context,keyword.c_str(),&context));
  166. }
  167. return 0;
  168. }
  169. int
  170. InstallScan::IncludeCallback(ParseInfContextBlob & TheInf,const SafeString & sect,const SafeString & keyword,const SafeString & val)
  171. /*++
  172. Routine Description:
  173. Callback for "INCLUDE" processing
  174. Add INF to the search-list
  175. MAINTAINANCE: keep this in step with supported Include/nesting syntax
  176. Arguments:
  177. TheInf - INF that the include was found in
  178. sect - section that the include was found in
  179. keyword - "INCLUDE"
  180. val - name of INF to include
  181. Return Value:
  182. 0 on success
  183. --*/
  184. {
  185. if(!val.length()) {
  186. return 0;
  187. }
  188. //
  189. // simply 'append' the INF
  190. //
  191. return Include(val);
  192. }
  193. int
  194. InstallScan::NeedsCallback(ParseInfContextBlob & TheInf,const SafeString & sect,const SafeString & keyword,const SafeString & val)
  195. /*++
  196. Routine Description:
  197. Callback for "NEEDS" processing
  198. process the specified needed section
  199. MAINTAINANCE: keep this in step with supported Needs/nesting syntax
  200. Arguments:
  201. TheInf - INF that the needs was found in
  202. sect - section that the needs was found in
  203. keyword - "NEEDS"
  204. val - name of section to process
  205. Return Value:
  206. 0 on success
  207. --*/
  208. {
  209. if(!val.length()) {
  210. return 0;
  211. }
  212. INFCONTEXT context;
  213. //
  214. // concerning if section doesn't exist
  215. //
  216. if(!DoesSectionExist(val)) {
  217. Fail(MSG_NEEDS_NOSECT,sect,val);
  218. return 0;
  219. }
  220. //
  221. // catch recursive includes/needs
  222. // set want-count to zero so we don't actually recurse
  223. // (we need to use RecurseKeyword to ensure correct searching)
  224. //
  225. int res;
  226. int count = 0; // no more than zero (query)
  227. res = RecurseKeyword(val,TEXT("Include"),IncludeCallback,count);
  228. if(res != 0) {
  229. return res;
  230. }
  231. if(count<0) {
  232. Fail(MSG_RECURSIVE_INCLUDE,sect,val);
  233. }
  234. count = 0; // no more than zero (query)
  235. res = RecurseKeyword(val,TEXT("Needs"),NeedsCallback,count);
  236. if(res != 0) {
  237. return res;
  238. }
  239. if(count<0) {
  240. Fail(MSG_RECURSIVE_NEEDS,sect,val);
  241. }
  242. return CheckInstallSubSection(val);
  243. }
  244. int InstallScan::CheckInstallSubSection(const SafeString & section)
  245. /*++
  246. Routine Description:
  247. Effectively SetupInstallInfSection
  248. Processed for primary and each needed section
  249. WARNING! tightly coupled with SetupAPI implementation that could change
  250. MAINTAINANCE: keep in step with install processing
  251. Arguments:
  252. NONE
  253. Return Value:
  254. 0 on success
  255. --*/
  256. {
  257. int res;
  258. res = CheckCopyFiles(section);
  259. if(res != 0) {
  260. return res;
  261. }
  262. return 0;
  263. }
  264. int InstallScan::CheckCopyFiles(const SafeString & section)
  265. /*++
  266. Routine Description:
  267. Process CopyFiles entries in install section
  268. WARNING! tightly coupled with SetupAPI implementation that could change
  269. MAINTAINANCE: keep in step with CopyFiles processing
  270. Arguments:
  271. section - section to check for CopyFiles= entries
  272. Return Value:
  273. 0 on success
  274. --*/
  275. {
  276. //
  277. // enumerate all CopyFiles entries in this section
  278. //
  279. int count = -1; // there can be any # of CopyFiles keywords in a section
  280. return RecurseKeyword(section,TEXT("CopyFiles"),CopyFilesCallback,count);
  281. }
  282. int
  283. InstallScan::CopyFilesCallback(ParseInfContextBlob & TheInf,const SafeString & section,const SafeString & keyword,const SafeString & val)
  284. /*++
  285. Routine Description:
  286. Process CopyFiles entries in install section (callback)
  287. WARNING! tightly coupled with SetupAPI implementation that could change
  288. MAINTAINANCE: keep in step with CopyFiles processing
  289. MAINTAINANCE: keep in step with CopyFiles section syntax
  290. MAINTAINANCE: keep in step with DestinationDirs section syntax
  291. Arguments:
  292. TheInf - INF that the needs was found in
  293. sect - section that the needs was found in
  294. keyword - "CopyFiles"
  295. val - section or @file
  296. Return Value:
  297. 0 on success
  298. --*/
  299. {
  300. if(!val.length()) {
  301. return 0;
  302. }
  303. //
  304. // a single copy-files section entry
  305. //
  306. INFCONTEXT context;
  307. int res;
  308. bool FoundAny = false;
  309. if(val[0] == TEXT('@')) {
  310. //
  311. // immediate copy, use default target as specified in context inf
  312. //
  313. SafeString source = val.substr(1);
  314. return CheckSingleCopyFile(TheInf,TheInf->GetDefaultTargetDirectory(),source,source,section);
  315. }
  316. //
  317. // interpret as a section, we need to enumerate through all the INF's
  318. // that have this particular copy section
  319. //
  320. ParseInfContextList::iterator AnInf;
  321. for(AnInf = InfSearchList.begin(); AnInf != InfSearchList.end(); AnInf++) {
  322. ParseInfContextBlob & TheCopyInf = *AnInf;
  323. if(TheCopyInf->InfHandle == INVALID_HANDLE_VALUE) {
  324. //
  325. // a fail-loaded inf
  326. //
  327. continue;
  328. }
  329. DWORD flgs = TheCopyInf->DoingCopySection(val,PlatformMask);
  330. if(flgs!=0) {
  331. //
  332. // we have already done this copy section in this INF
  333. //
  334. if(flgs != (DWORD)(-1)) {
  335. FoundAny = true;
  336. if(flgs & PLATFORM_MASK_MODIFIEDFILES) {
  337. //
  338. // hint that one of the files in this copy section
  339. // was determined to be modified
  340. //
  341. HasDependentFileChanged = true;
  342. }
  343. }
  344. continue;
  345. }
  346. //
  347. // section contains multiple copy-files
  348. //
  349. if(!SetupFindFirstLine(TheCopyInf->InfHandle,val.c_str(),NULL,&context)) {
  350. if(SetupGetLineCount(TheCopyInf->InfHandle, val.c_str()) != 0) {
  351. //
  352. // make a note that this inf doesn't have this copy section
  353. //
  354. TheCopyInf->NoCopySection(val);
  355. } else {
  356. FoundAny = true;
  357. }
  358. continue;
  359. }
  360. //
  361. // we've found copy section inside TheCopyInf that we haven't
  362. // previously processed for this platform
  363. //
  364. FoundAny = true;
  365. //
  366. // determine target directory for this copy section
  367. // this must be in same inf as copy section
  368. //
  369. TargetDirectoryEntry *pTargDir = TheCopyInf->GetTargetDirectory(val);
  370. SafeString target;
  371. SafeString source;
  372. //
  373. // enumerate through each <desc> = <install>
  374. //
  375. do {
  376. //
  377. // each line consists of <target>[,<src>]
  378. //
  379. if(!MyGetStringField(&context,1,target) || !target.length()) {
  380. continue;
  381. }
  382. if(!MyGetStringField(&context,2,source) || !source.length()) {
  383. source = target;
  384. }
  385. //
  386. // source/target always in lower-case to aid comparisons
  387. //
  388. res = CheckSingleCopyFile(TheCopyInf,pTargDir,target,source,val);
  389. if(res != 0) {
  390. return res;
  391. }
  392. } while(SetupFindNextLine(&context,&context));
  393. }
  394. if(!FoundAny) {
  395. Fail(MSG_MISSING_COPY_SECTION,val,section);
  396. }
  397. return 0;
  398. }
  399. int InstallScan::CheckSingleCopyFile(ParseInfContextBlob & TheInf,TargetDirectoryEntry *pTargDir,const SafeString & target,const SafeString & source,const SafeString & section)
  400. /*++
  401. Routine Description:
  402. Process a single CopyFile entry (either immediate or not)
  403. WARNING! tightly coupled with SetupAPI implementation that could change
  404. MAINTAINANCE: keep in step with CopyFiles processing
  405. MAINTAINANCE: keep in step with CopyFiles section syntax
  406. Arguments:
  407. TheInf - Inf that the copy section is in
  408. pTargDir - relevent target directory entry for this copy
  409. target - name of target file
  410. source - name of source file
  411. section - name of copyfiles section, or of install section
  412. Return Value:
  413. 0 on success
  414. --*/
  415. {
  416. if(!target.length()) {
  417. return 0;
  418. }
  419. if(!source.length()) {
  420. return 0;
  421. }
  422. //
  423. // this is a single copy target/source entry
  424. //
  425. // need to lookup file in SourceDisksFiles
  426. // for matching platform
  427. //
  428. // for each file, need to
  429. // (1) queue source name
  430. // (2) textmode & guimode validate target
  431. //
  432. SourceDisksFilesList sources;
  433. int res = QuerySourceFile(TheInf,section,source,sources);
  434. if(res != 0) {
  435. return res;
  436. }
  437. if(!sources.size()) {
  438. return 0;
  439. }
  440. SafeString destdir;
  441. if(pGlobalScan->TargetsToo) {
  442. int IsDriverFile = NotDeviceInstall ? 0 : 1;
  443. if(IsDriverFile) {
  444. //
  445. // make a special note that we determined at least once that this is a driver file
  446. // wrt this primary INF
  447. // (this saves us reporting that file is and is not a driver file for a given inf)
  448. //
  449. pInfScan->DriverSourceCheck.insert(source);
  450. } else if(pInfScan->DriverSourceCheck.find(source) != pInfScan->DriverSourceCheck.end()) {
  451. IsDriverFile = 1; // we found this to be a driver fill during driver pass
  452. }
  453. //
  454. // we want a more detailed output for further analysis
  455. // or import into database
  456. //
  457. basic_ostringstream<TCHAR> line;
  458. //
  459. // source name
  460. //
  461. line << QuoteIt(source);
  462. //
  463. // target location
  464. //
  465. if(pTargDir) {
  466. line << TEXT(",") << pTargDir->DirId
  467. << TEXT(",") << QuoteIt(pTargDir->SubDir);
  468. } else {
  469. line << TEXT(",,");
  470. }
  471. //
  472. // final name and if this appears to be a driver file or not
  473. //
  474. line << TEXT(",") << QuoteIt(target)
  475. << TEXT(",") << (NotDeviceInstall ? TEXT("0") : TEXT("1"));
  476. //
  477. // report primary INF
  478. //
  479. line << TEXT(",") << QuoteIt(GetFileNamePart(pInfScan->PrimaryInf->InfName));
  480. if(!IsDriverFile) {
  481. //
  482. // if this doesn't appear to be a driver file, report install section
  483. //
  484. line << TEXT(",") << QuoteIt(Section);
  485. }
  486. pInfScan->SourceFiles.push_back(line.str());
  487. } else {
  488. //
  489. // just source will do
  490. //
  491. pInfScan->SourceFiles.push_back(source);
  492. }
  493. if((pGlobalScan->BuildChangedDevices & BUILD_CHANGED_DEVICES_DEPCHANGED)
  494. && !pInfScan->HasDependentFileChanged
  495. && pGlobalScan->IsFileChanged(source)) {
  496. HasDependentFileChanged = true;
  497. //
  498. // since we only do a copy section once, we need to make the copy section
  499. // 'dirty' so next time we reference section out of here
  500. //
  501. TheInf->DoingCopySection(section,PLATFORM_MASK_MODIFIEDFILES);
  502. }
  503. if(pGlobalScan->IgnoreErrors) {
  504. //
  505. // if not interested in checking errors
  506. // we're done
  507. //
  508. return 0;
  509. }
  510. SourceDisksFilesList::iterator i;
  511. for(i = sources.begin(); i != sources.end(); i++) {
  512. i->Used = true;
  513. if((i->UpgradeDisposition != 3) || (i->TextModeDisposition != 3)) {
  514. //
  515. // need to do a consistency check
  516. //
  517. if(!pTargDir) {
  518. //
  519. // error already reported (?)
  520. //
  521. continue;
  522. }
  523. if(pTargDir->DirId != 10) {
  524. //
  525. // DirId should be 10 or normalized off 10
  526. //
  527. Fail(MSG_TEXTMODE_TARGET_MISMATCH,section,source);
  528. continue;
  529. }
  530. if(!i->TargetDirectory) {
  531. //
  532. // error already reported
  533. //
  534. continue;
  535. }
  536. IntToString::iterator gd = pGlobalScan->GlobalDirectories.find(i->TargetDirectory);
  537. if(gd == pGlobalScan->GlobalDirectories.end()) {
  538. //
  539. // no textmode subdir for specified dir
  540. //
  541. Fail(MSG_TEXTMODE_TARGET_UNKNOWN,section,source);
  542. continue;
  543. }
  544. SafeString gm_target = PathConcat(pTargDir->SubDir,target);
  545. SafeString tm_target = PathConcat(gd->second,i->TargetName.length() ? i->TargetName : source);
  546. if(gm_target.compare(tm_target)!=0) {
  547. Fail(MSG_TEXTMODE_TARGET_MISMATCH,section,source);
  548. continue;
  549. }
  550. //
  551. // get here, user-mode and text-mode match
  552. //
  553. }
  554. }
  555. return 0;
  556. }
  557. int InstallScan::Include(const SafeString & name)
  558. /*++
  559. Routine Description:
  560. Process a single Include entry
  561. WARNING! tightly coupled with SetupAPI implementation that could change
  562. MAINTAINANCE: keep in step with INCLUDE processing
  563. Arguments:
  564. name - argument passed into INCLUDE
  565. Return Value:
  566. 0 on success
  567. --*/
  568. {
  569. //
  570. // nested include
  571. //
  572. if(Included.find(name) == Included.end()) {
  573. //
  574. // first time this install section has come across this file
  575. //
  576. Included.insert(name); // so we don't try to re-load
  577. ParseInfContextBlob & ThisInf = pInfScan->Include(name,true);
  578. InfSearchList.push_back(ThisInf);
  579. }
  580. return 0;
  581. }
  582. int InstallScan::Layouts()
  583. /*++
  584. Routine Description:
  585. Prime search list with primary inf and list of layout files
  586. WARNING! tightly coupled with SetupAPI implementation that could change
  587. MAINTAINANCE: keep in step with LAYOUT processing
  588. DISCREPENCY: layout files list currently limited to layout.inf
  589. Arguments:
  590. NONE
  591. Return Value:
  592. 0 on success
  593. --*/
  594. {
  595. //
  596. // start the search list with the default inf
  597. //
  598. Included.insert(pInfScan->PrimaryInf->InfName);
  599. InfSearchList.push_back(pInfScan->PrimaryInf);
  600. //
  601. // start the search list with layout inf's
  602. //
  603. // we can do this with pGlobalScan as the data is readable but not modified
  604. // there is an interlocked dependency on multi-processors
  605. //
  606. ParseInfContextList::iterator i;
  607. for(i = pGlobalScan->LayoutInfs.begin(); i != pGlobalScan->LayoutInfs.end(); i++) {
  608. Included.insert((*i)->InfName); // so we don't try to re-load
  609. InfSearchList.push_back(*i); // interlocked dependency
  610. }
  611. return 0;
  612. }
  613. int InstallScan::QuerySourceFile(ParseInfContextBlob & TheInf,const SafeString & section,const SafeString & source,SourceDisksFilesList & Target)
  614. /*++
  615. Routine Description:
  616. Determine list of possible source entries for given source file
  617. This can be zero (bad layout)
  618. 1 (typical)
  619. >1 (multiple targets)
  620. Arguments:
  621. TheInf - INF of the copy section, or install section
  622. section - name of the copy section or install section (for error reporting only)
  623. source - name of source file
  624. Target - returned list of source media information
  625. Return Value:
  626. 0 on success
  627. --*/
  628. {
  629. int res =0;
  630. Target.clear();
  631. //
  632. // return list of entries matching source file
  633. //
  634. DWORD platforms = pGlobalScan->Version.PlatformMask & PlatformMask & (PLATFORM_MASK_NT|PLATFORM_MASK_WIN);
  635. //
  636. // attempt to search within context of specified inf
  637. //
  638. res = TheInf->QuerySourceFile(platforms,section,source,Target);
  639. if(res != 0) {
  640. if(res<0) {
  641. //
  642. // non-fatal
  643. //
  644. res = 0;
  645. }
  646. return res;
  647. }
  648. if(Target.empty()) {
  649. //
  650. // now attempt to search within context of any inf's
  651. //
  652. ParseInfContextList::iterator AnInf;
  653. for(AnInf = InfSearchList.begin(); AnInf != InfSearchList.end(); AnInf++) {
  654. ParseInfContextBlob & TheLayoutInf = *AnInf;
  655. if(TheLayoutInf->InfHandle == INVALID_HANDLE_VALUE) {
  656. //
  657. // a fail-loaded inf
  658. //
  659. continue;
  660. }
  661. res = TheLayoutInf->QuerySourceFile(platforms,section,source,Target);
  662. if(res != 0) {
  663. if(res<0) {
  664. //
  665. // non-fatal
  666. //
  667. res = 0;
  668. }
  669. return res;
  670. }
  671. if(!Target.empty()) {
  672. //
  673. // found a hit
  674. //
  675. break;
  676. }
  677. }
  678. }
  679. if(Target.empty()) {
  680. //
  681. // not found
  682. //
  683. Fail(MSG_SOURCE_NOT_LISTED,section,source);
  684. return 0;
  685. }
  686. return 0;
  687. }
  688. LONG InstallScan::GetLineCount(const SafeString & section)
  689. /*++
  690. Routine Description:
  691. Simulates SetupGetLineCount
  692. WARNING! Uses knowledge about how SetupAPI searches INF lists, could change
  693. MAINTAINANCE: Keep search algorithm in step with SetupAPI
  694. reference RecurseKeyword
  695. Arguments:
  696. section - name of section to count
  697. Return Value:
  698. # of lines or -1 if no sections found
  699. --*/
  700. {
  701. INFCONTEXT context;
  702. int res;
  703. int i;
  704. SafeString value;
  705. LONG count = -1;
  706. ParseInfContextList::iterator AnInf;
  707. for(AnInf = InfSearchList.begin(); AnInf != InfSearchList.end(); AnInf++) {
  708. ParseInfContextBlob & TheInf = *AnInf;
  709. if(TheInf->InfHandle == INVALID_HANDLE_VALUE) {
  710. //
  711. // a fail-loaded inf
  712. //
  713. continue;
  714. }
  715. LONG actcount = SetupGetLineCount(TheInf->InfHandle,section.c_str());
  716. if(actcount >= 0) {
  717. if(count<=0) {
  718. count = actcount;
  719. } else {
  720. count += actcount;
  721. }
  722. }
  723. }
  724. return 0;
  725. }
  726. bool InstallScan::DoesSectionExist(const SafeString & section)
  727. /*++
  728. Routine Description:
  729. Optimally simulates "SetupGetLineCount()>=0"
  730. WARNING! Uses knowledge about how SetupAPI searches INF lists, could change
  731. MAINTAINANCE: Keep search algorithm in step with SetupAPI
  732. reference SetupGetLineCount and RecurseKeyword
  733. Arguments:
  734. section - name of section to count
  735. Return Value:
  736. true if named section exists
  737. --*/
  738. {
  739. //
  740. // optimized version of (GetLineCount(section)>=0)
  741. //
  742. INFCONTEXT context;
  743. int res;
  744. int i;
  745. SafeString value;
  746. ParseInfContextList::iterator AnInf;
  747. for(AnInf = InfSearchList.begin(); AnInf != InfSearchList.end(); AnInf++) {
  748. ParseInfContextBlob & TheInf = *AnInf;
  749. if(TheInf->InfHandle == INVALID_HANDLE_VALUE) {
  750. //
  751. // a fail-loaded inf
  752. //
  753. continue;
  754. }
  755. LONG actcount = SetupGetLineCount(TheInf->InfHandle,section.c_str());
  756. if(actcount >= 0) {
  757. return true;
  758. }
  759. }
  760. return false;
  761. }