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.

5992 lines
139 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. hwcomp.c
  5. Abstract:
  6. Win95 to NT hardware device comparison routines.
  7. Author:
  8. Jim Schmidt (jimschm) 8-Jul-1996
  9. Revision History:
  10. marcw 28-Jun-1999 Added HwComp_MakeLocalSourceDeviceExists
  11. jimschm 04-Dec-1998 Fixed checksum problem caused by date rounding
  12. jimschm 29-Sep-1998 Fixed incompatible hardware message to use proper roots
  13. jimschm 28-Apr-1998 Support for description-less hardware
  14. jimschm 01-Apr-1998 Added %1 to (not currently present) message
  15. jimschm 27-Feb-1998 Added suppression of (not currently present) devices
  16. marcw 11-Nov-1997 Minor change to ensure that we can find the dial-up adapter.
  17. jimschm 03-Nov-1997 Revised to use GROWBUFs and project's reg api
  18. jimschm 08-Oct-1997 Added legacy keyboard support
  19. marcw 18-Sep-1997 Added some fields to netcard enumerator
  20. jimschm 24-Jun-1997 Added net card enumerator
  21. marcw 05-May-1997 Fixed problem with looking for usable hdd. Need to
  22. look for "diskdrive" class instead of "hdc" class.
  23. marcw 18-Apr-1997 Added ability to determine if a CdRom and Hdd
  24. compatible with Windows Nt is online.
  25. marcw 14-Apr-1997 Retrofitted new progress bar handling code in.
  26. jimschm 2-Jan-1997 Added INF verification to hwcomp.dat
  27. to automatically detect when an OEM
  28. changes one or more INFs
  29. --*/
  30. #include "pch.h"
  31. #include "hwcompp.h"
  32. #include <cfgmgr32.h>
  33. #define DBG_HWCOMP "HwComp"
  34. #ifdef UNICODE
  35. #error "hwcomp.c cannot be compiled as UNICODE"
  36. #endif
  37. #define S_HWCOMP_DAT TEXT("hwcomp.dat")
  38. #define S_HKLM_ENUM TEXT("HKLM\\Enum")
  39. #define S_HARDWAREID TEXT("HardwareID")
  40. #define S_COMPATIBLEIDS TEXT("CompatibleIDs")
  41. #define S_CLASS TEXT("Class")
  42. #define S_MANUFACTURER TEXT("Manufacturer")
  43. #define S_IGNORE_THIS_FILE TEXT("*")
  44. #define PNPID_FIELD 2
  45. #define DECLARE(varname,text) text,
  46. PCTSTR g_DeviceFields[] = {
  47. DEVICE_FIELDS /* , */
  48. NULL
  49. };
  50. #undef DECLARE
  51. GROWBUFFER g_FileNames = GROWBUF_INIT;
  52. GROWBUFFER g_DecompFileNames = GROWBUF_INIT;
  53. HASHTABLE g_PnpIdTable;
  54. HASHTABLE g_UnsupPnpIdTable;
  55. HASHTABLE g_ForceBadIdTable;
  56. HASHTABLE g_InfFileTable;
  57. HASHTABLE g_NeededHardwareIds;
  58. HASHTABLE g_UiSuppliedIds;
  59. BOOL g_ValidWinDir;
  60. BOOL g_ValidSysDrive;
  61. BOOL g_IncompatibleScsiDevice = FALSE;
  62. BOOL g_ValidCdRom;
  63. BOOL g_FoundPnp8387;
  64. PPARSEDPATTERN g_PatternCompatibleIDsTable;
  65. typedef enum {
  66. HW_INCOMPATIBLE,
  67. HW_REINSTALL,
  68. HW_UNSUPPORTED
  69. } HWTYPES;
  70. static PCTSTR g_ExcludeTable[] = {
  71. TEXT("wkstamig.inf"),
  72. TEXT("desktop.inf"),
  73. TEXT("usermig.inf"),
  74. TEXT("dosnet.inf"),
  75. TEXT("pad.inf"),
  76. TEXT("msmail.inf"),
  77. TEXT("wordpad.inf"),
  78. TEXT("syssetup.inf"),
  79. TEXT("pinball.inf"),
  80. TEXT("perms.inf"),
  81. TEXT("optional.inf"),
  82. TEXT("multimed.inf"),
  83. TEXT("mmopt.inf"),
  84. TEXT("layout.inf"),
  85. TEXT("kbd.inf"),
  86. TEXT("iexplore.inf"),
  87. TEXT("intl.inf"),
  88. TEXT("imagevue.inf"),
  89. TEXT("games.inf"),
  90. TEXT("font.inf"),
  91. TEXT("communic.inf"),
  92. TEXT("apps.inf"),
  93. TEXT("accessor.inf"),
  94. TEXT("mailnews.inf"),
  95. TEXT("cchat.inf"),
  96. TEXT("iermv2.inf"),
  97. TEXT("default.inf"),
  98. TEXT("setup16.inf"),
  99. TEXT("")
  100. };
  101. BOOL
  102. GetFileNames (
  103. IN PCTSTR *InfDirs,
  104. IN UINT InfDirCount,
  105. IN BOOL QueryFlag,
  106. IN OUT PGROWBUFFER FileNames,
  107. IN OUT PGROWBUFFER DecompFileNames
  108. );
  109. VOID
  110. FreeFileNames (
  111. IN PGROWBUFFER FileNames,
  112. IN PGROWBUFFER DecompFileNames,
  113. IN BOOL QueryFlag
  114. );
  115. VOID
  116. pFreeHwCompDatName (
  117. PCTSTR Name
  118. );
  119. BOOL
  120. pIsInfFileExcluded (
  121. PCTSTR FileNamePtr
  122. );
  123. BOOL
  124. pGetFileNamesWorker (
  125. IN OUT PGROWBUFFER FileNames,
  126. IN OUT PGROWBUFFER DecompFileNames,
  127. IN PCTSTR InfDir,
  128. IN BOOL QueryFlag
  129. );
  130. BOOL
  131. pIsDeviceConsideredCompatible (
  132. PCTSTR DevIds
  133. );
  134. BOOL
  135. pFindForcedBadHardwareId (
  136. IN PCTSTR PnpIdList,
  137. OUT PTSTR InfFileName OPTIONAL
  138. );
  139. //
  140. // Implementation
  141. //
  142. BOOL
  143. WINAPI
  144. HwComp_Entry (
  145. IN HINSTANCE hinstDLL,
  146. IN DWORD dwReason,
  147. IN LPVOID lpv
  148. )
  149. /*++
  150. Routine Description:
  151. HwComp_Entry initializes the hwcomp library. It does what would normally
  152. happen if this were a standalone dll, as opposed to a library.
  153. At process detach, the device buffer is freed if necessary.
  154. Arguments:
  155. hinstDLL - (OS-supplied) instance handle for the DLL
  156. dwReason - (OS-supplied) indicates attach or detatch from process or
  157. thread
  158. lpv - unused
  159. Return Value:
  160. Return value is always TRUE (indicating successful init).
  161. --*/
  162. {
  163. switch (dwReason)
  164. {
  165. case DLL_PROCESS_ATTACH:
  166. g_UiSuppliedIds = HtAlloc();
  167. if (!g_UiSuppliedIds) {
  168. DEBUGMSG ((DBG_ERROR, "HwComp_Entry: Can't create g_UiSuppliedIds"));
  169. return FALSE;
  170. }
  171. break;
  172. case DLL_PROCESS_DETACH:
  173. DEBUGMSG_IF ((
  174. g_EnumsActive,
  175. DBG_ERROR,
  176. "%u hardware enumerations still active",
  177. g_EnumsActive
  178. ));
  179. DEBUGMSG_IF ((
  180. g_NetEnumsActive,
  181. DBG_ERROR,
  182. "%u network hardware enumerations still active",
  183. g_NetEnumsActive
  184. ));
  185. FreeNtHardwareList();
  186. if (g_NeededHardwareIds) {
  187. HtFree (g_NeededHardwareIds);
  188. g_NeededHardwareIds = NULL;
  189. }
  190. if (g_UiSuppliedIds) {
  191. HtFree (g_UiSuppliedIds);
  192. g_UiSuppliedIds = NULL;
  193. }
  194. if (g_PatternCompatibleIDsTable) {
  195. DestroyParsedPattern (g_PatternCompatibleIDsTable);
  196. g_PatternCompatibleIDsTable = NULL;
  197. }
  198. break;
  199. }
  200. return TRUE;
  201. }
  202. PCTSTR
  203. pGetHwCompDat (
  204. IN PCTSTR SourceDir,
  205. IN BOOL MustExist
  206. )
  207. /*++
  208. Routine Description:
  209. GetHwCompDat builds e:\i386\hwcomp.dat, where e:\i386 is specified in
  210. SourceDir. The caller must call pFreeHwCompDatName to clean up the
  211. memory allocation.
  212. Arguments:
  213. SourceDir - The directory holding hwcomp.dat
  214. MustExist - Specifies TRUE if hwcomp.dat must exist as specified,
  215. or FALSE if hwcomp.dat does not necessarily exist.
  216. Return Value:
  217. MustExist = TRUE:
  218. A pointer to a string, or NULL hwcomp.dat does not exist as
  219. specified, or if allocation failed.
  220. MustExist = FALSE:
  221. A pointer to a string, or NULL if a memory allocation failed.
  222. Caller must free the string via pFreeHwCompDatName.
  223. --*/
  224. {
  225. PTSTR FileName;
  226. if (SourceDir) {
  227. FileName = JoinPaths (SourceDir, S_HWCOMP_DAT);
  228. } else {
  229. FileName = DuplicatePathString (S_HWCOMP_DAT, 0);
  230. }
  231. if (MustExist && GetFileAttributes (FileName) == 0xffffffff) {
  232. pFreeHwCompDatName (FileName);
  233. return NULL;
  234. }
  235. return FileName;
  236. }
  237. VOID
  238. pFreeHwCompDatName (
  239. PCTSTR Name
  240. )
  241. {
  242. if (Name) {
  243. FreePathString (Name);
  244. }
  245. }
  246. //
  247. // Routines for accessing the registry
  248. //
  249. PVOID
  250. pPrivateRegValAllocator (
  251. DWORD Size
  252. )
  253. {
  254. return AllocText (Size);
  255. }
  256. VOID
  257. pPrivateRegValDeallocator (
  258. PCVOID Mem
  259. )
  260. {
  261. FreeText (Mem);
  262. }
  263. PCTSTR
  264. pGetAltDeviceDesc (
  265. PCTSTR DriverSubKey
  266. )
  267. {
  268. TCHAR DriverKey[MAX_REGISTRY_KEY];
  269. HKEY Key;
  270. PCTSTR Data;
  271. PTSTR ReturnText = NULL;
  272. if (!DriverSubKey) {
  273. return NULL;
  274. }
  275. //
  276. // Get driver key
  277. //
  278. wsprintf (DriverKey, TEXT("HKLM\\System\\CurrentControlSet\\Services\\Class\\%s"), DriverSubKey);
  279. Key = OpenRegKeyStr (DriverKey);
  280. if (!Key) {
  281. return NULL;
  282. }
  283. Data = GetRegValueString (Key, TEXT("DriverDesc"));
  284. CloseRegKey (Key);
  285. if (Data) {
  286. ReturnText = pPrivateRegValAllocator (SizeOfString (Data));
  287. if (ReturnText) {
  288. StringCopy (ReturnText, Data);
  289. }
  290. MemFree (g_hHeap, 0, Data);
  291. }
  292. return ReturnText;
  293. }
  294. VOID
  295. pGetRegValText (
  296. HKEY Key,
  297. PCTSTR VarText,
  298. PCTSTR *RetPtr
  299. )
  300. {
  301. MYASSERT (!(*RetPtr));
  302. *RetPtr = (PCTSTR) GetRegValueDataOfType2 (
  303. Key,
  304. VarText,
  305. REG_SZ,
  306. pPrivateRegValAllocator,
  307. pPrivateRegValDeallocator
  308. );
  309. }
  310. VOID
  311. pFreeRegValText (
  312. PHARDWARE_ENUM EnumPtr
  313. )
  314. {
  315. //
  316. // Free all device field text
  317. //
  318. #define DECLARE(varname,text) pPrivateRegValDeallocator((PVOID) EnumPtr->varname); EnumPtr->varname = NULL;
  319. DEVICE_FIELDS
  320. #undef DECLARE
  321. }
  322. VOID
  323. pGetAllRegVals (
  324. PHARDWARE_ENUM EnumPtr
  325. )
  326. {
  327. PCTSTR AltDesc;
  328. INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
  329. PTSTR BetterDesc = NULL;
  330. TCHAR PnpId[MAX_PNP_ID];
  331. PCTSTR PnpIdList;
  332. PCTSTR OldDesc;
  333. PCTSTR p;
  334. PCTSTR end;
  335. PCTSTR start;
  336. PTSTR newBuf;
  337. PTSTR ptr;
  338. CHARTYPE ch;
  339. #define DECLARE(varname,text) if(!EnumPtr->varname) { \
  340. pGetRegValText ( \
  341. EnumPtr->ek.CurrentKey->KeyHandle, \
  342. text, \
  343. &EnumPtr->varname \
  344. ); \
  345. } \
  346. DEVICE_FIELDS
  347. #undef DECLARE
  348. //
  349. // If there is a better name for this device, use it
  350. //
  351. PnpIdList = EnumPtr->HardwareID;
  352. while (PnpIdList && *PnpIdList) {
  353. PnpIdList = ExtractPnpId (PnpIdList, PnpId);
  354. if (*PnpId) {
  355. if (InfFindFirstLine (g_Win95UpgInf, S_PNP_DESCRIPTIONS, PnpId, &is)) {
  356. AltDesc = InfGetStringField (&is, 1);
  357. if (AltDesc) {
  358. OldDesc = EnumPtr->DeviceDesc;
  359. BetterDesc = (PTSTR) pPrivateRegValAllocator (SizeOfString (AltDesc));
  360. StringCopy (BetterDesc, AltDesc);
  361. EnumPtr->DeviceDesc = BetterDesc;
  362. DEBUGMSG ((
  363. DBG_HWCOMP,
  364. "Using %s for description (instead of %s)",
  365. BetterDesc,
  366. OldDesc
  367. ));
  368. pPrivateRegValDeallocator ((PVOID) OldDesc);
  369. break;
  370. }
  371. ELSE_DEBUGMSG ((DBG_WHOOPS, "Better description could not be retrieved"));
  372. }
  373. }
  374. }
  375. //
  376. // Workaround: if the device description is bad, use the driver
  377. // description if it is available
  378. //
  379. if (!BetterDesc) {
  380. if (!EnumPtr->DeviceDesc || CharCount (EnumPtr->DeviceDesc) < 5) {
  381. AltDesc = pGetAltDeviceDesc (EnumPtr->Driver);
  382. if (AltDesc) {
  383. pPrivateRegValDeallocator ((PVOID) EnumPtr->DeviceDesc);
  384. EnumPtr->DeviceDesc = AltDesc;
  385. }
  386. }
  387. //
  388. // Fix leading/trail space problems
  389. //
  390. if (EnumPtr->DeviceDesc) {
  391. start = SkipSpace (EnumPtr->DeviceDesc);
  392. end = GetEndOfString (start);
  393. p = SkipSpaceR (start, end);
  394. if (p && (p != end || start != EnumPtr->DeviceDesc)) {
  395. p = _tcsinc (p);
  396. newBuf = pPrivateRegValAllocator (
  397. (PBYTE) (p + 1) - (PBYTE) start
  398. );
  399. StringCopyAB (newBuf, start, p);
  400. pPrivateRegValDeallocator ((PVOID) EnumPtr->DeviceDesc);
  401. EnumPtr->DeviceDesc = newBuf;
  402. }
  403. }
  404. //
  405. // Eliminate newline chars inside description; replace \r or \n with a space
  406. // the replacement occurs inplace, so chars will be replaced inplace
  407. //
  408. if (EnumPtr->DeviceDesc) {
  409. for (ptr = (PTSTR)EnumPtr->DeviceDesc; *ptr; ptr = _tcsinc (ptr)) {
  410. ch = _tcsnextc (ptr);
  411. if (!_istprint (ch)) {
  412. //
  413. // in this case it's a single TCHAR, just replace it inplace
  414. //
  415. MYASSERT (*ptr == (TCHAR)ch);
  416. *ptr = TEXT(' ');
  417. }
  418. }
  419. }
  420. }
  421. InfCleanUpInfStruct (&is);
  422. }
  423. BOOL
  424. pGetPnpIdList (
  425. IN PINFSTRUCT is,
  426. OUT PTSTR Buffer,
  427. IN UINT Size
  428. )
  429. /*++
  430. Routine Description:
  431. pGetPnpIdList is similar to SetupGetMultiSzField, except it supports
  432. skipping of blank fields.
  433. Arguments:
  434. is - Specifies the INFSTRUCT that indicates the line being processed.
  435. Buffer - Receives the multi-sz ID list.
  436. Size - Specifies the size of Buffer.
  437. Return Value:
  438. TRUE if one or more PNP ID fields exist, or FALSE if none exist.
  439. --*/
  440. {
  441. UINT FieldCount;
  442. UINT Field;
  443. PTSTR p;
  444. PTSTR End;
  445. PCTSTR FieldStr;
  446. p = Buffer;
  447. End = (PTSTR) ((PBYTE) Buffer + Size);
  448. End--;
  449. FieldCount = InfGetFieldCount (is);
  450. if (FieldCount < PNPID_FIELD) {
  451. return FALSE;
  452. }
  453. for (Field = PNPID_FIELD ; Field <= FieldCount ; Field++) {
  454. FieldStr = InfGetStringField (is, Field);
  455. if (FieldStr && *FieldStr) {
  456. if (SizeOfString (FieldStr) > (UINT) (End - p)) {
  457. DEBUGMSG ((DBG_WHOOPS, "PNP ID list is bigger than %u bytes", Size));
  458. break;
  459. }
  460. StringCopy (p, FieldStr);
  461. p = GetEndOfString (p) + 1;
  462. }
  463. }
  464. *p = 0;
  465. return TRUE;
  466. }
  467. /*++
  468. Routine Description:
  469. BeginHardwareEnum initializes a structure for enumeration of all hardware
  470. configuration registry values. Call BeginHardwareEnum, followed by
  471. either NextHardwareEnum or AbortHardwareEnum.
  472. Arguments:
  473. EnumPtr - Receives the next enumerated item
  474. Return Value:
  475. TRUE if the supplied enumeration structure was filled, or FALSE if
  476. there are no hardware items (unlikely) or an error occurred.
  477. GetLastError() will provide the failure reason or ERROR_SUCCESS.
  478. --*/
  479. BOOL
  480. RealEnumFirstHardware (
  481. OUT PHARDWARE_ENUM EnumPtr,
  482. IN TYPE_OF_ENUM TypeOfEnum,
  483. IN DWORD EnumFlags
  484. )
  485. {
  486. //
  487. // If string tables have not been created, create them
  488. // before enumerating.
  489. //
  490. if (TypeOfEnum != ENUM_ALL_DEVICES) {
  491. if (!g_PnpIdTable || !g_UnsupPnpIdTable || !g_InfFileTable || !g_ForceBadIdTable) {
  492. if (!CreateNtHardwareList (
  493. SOURCEDIRECTORYARRAY(),
  494. SOURCEDIRECTORYCOUNT(),
  495. NULL,
  496. REGULAR_OUTPUT
  497. )) {
  498. LOG ((
  499. LOG_ERROR,
  500. "Unable to create NT hardware list "
  501. "(required for hardware enumeration)"
  502. ));
  503. return FALSE;
  504. }
  505. }
  506. }
  507. START_ENUM;
  508. //
  509. // Init enum struct
  510. //
  511. ZeroMemory (EnumPtr, sizeof (HARDWARE_ENUM));
  512. EnumPtr->State = STATE_ENUM_FIRST_KEY;
  513. EnumPtr->TypeOfEnum = TypeOfEnum;
  514. EnumPtr->EnumFlags = EnumFlags;
  515. //
  516. // Call NextHardwareEnum to fill in rest of struct
  517. //
  518. return RealEnumNextHardware (EnumPtr);
  519. }
  520. VOID
  521. pGenerateTapeIds (
  522. IN OUT PGROWBUFFER HackBuf,
  523. IN PCTSTR PnpIdList
  524. )
  525. /*++
  526. Routine Description:
  527. pGenerateTapeIds creates two IDs based on the IDs given by the caller. The
  528. first created ID is the caller's ID prefixed with Sequential. The second
  529. created ID is the caller's ID prefixed with Sequential and without the
  530. revision character. These new IDs match the tape IDs that NT supports.
  531. Arguments:
  532. HackBuf - Specifies a buffer that holds the new IDs, in a multi-sz. It
  533. may have some initial IDs in it. Receives additional IDs.
  534. PnpIdList - Specifies the ID list (either a hardware ID list or compatible
  535. ID list).
  536. Return Value:
  537. None.
  538. --*/
  539. {
  540. TCHAR PnpId[MAX_PNP_ID];
  541. TCHAR HackedPnpIdBuf[MAX_PNP_ID + 32];
  542. PTSTR HackedPnpId;
  543. PTSTR p;
  544. if (PnpIdList) {
  545. while (*PnpIdList) {
  546. PnpIdList = ExtractPnpId (PnpIdList, PnpId);
  547. //
  548. // Ignore PNP IDs that specify the root enumerator, or that
  549. // begin with Gen and don't have an underscore (such as GenDisk)
  550. //
  551. *HackedPnpIdBuf = 0;
  552. if (StringIMatchCharCount (TEXT("SCSI\\"), PnpId, 5)) {
  553. MoveMemory (PnpId, PnpId + 5, SizeOfString (PnpId + 5));
  554. HackedPnpId = _tcsappend (HackedPnpIdBuf, TEXT("SCSI\\"));
  555. } else {
  556. HackedPnpId = HackedPnpIdBuf;
  557. }
  558. if (_tcschr (PnpId, TEXT('\\'))) {
  559. continue;
  560. }
  561. if (StringIMatchCharCount (PnpId, TEXT("Gen"), 3) &&
  562. !_tcschr (PnpId, TEXT('_'))
  563. ) {
  564. continue;
  565. }
  566. //
  567. // Add another ID with Sequential
  568. //
  569. wsprintf (HackedPnpId, TEXT("Sequential%s"), PnpId);
  570. MultiSzAppend (HackBuf, HackedPnpIdBuf);
  571. //
  572. // Add another ID with Sequential and without the single
  573. // character revision
  574. //
  575. p = GetEndOfString (HackedPnpId);
  576. p = _tcsdec (HackedPnpId, p);
  577. *p = 0;
  578. MultiSzAppend (HackBuf, HackedPnpIdBuf);
  579. }
  580. }
  581. }
  582. BOOL
  583. pIsMultiFunctionDevice (
  584. IN PCTSTR PnpIdList OPTIONAL
  585. )
  586. /*++
  587. Routine Description:
  588. pIsMultiFunctionDevice scans the caller-supplied list of PNP IDs for one
  589. that starts with MF\. This prefix indicates the multi-function enumerator
  590. root.
  591. Arguments:
  592. PnpIdList - Specifies the comma-separated list of PNP IDs
  593. Return Value:
  594. TRUE if a multi-function ID is in the list, FALSE otherwise.
  595. --*/
  596. {
  597. TCHAR PnpId[MAX_PNP_ID];
  598. BOOL b = FALSE;
  599. if (PnpIdList) {
  600. while (*PnpIdList) {
  601. PnpIdList = ExtractPnpId (PnpIdList, PnpId);
  602. if (StringIMatchCharCount (TEXT("MF\\"), PnpId, 3)) {
  603. b = TRUE;
  604. break;
  605. }
  606. }
  607. }
  608. return b;
  609. }
  610. BOOL
  611. pGenerateMultiFunctionIDs (
  612. IN OUT PGROWBUFFER IdList,
  613. IN PCTSTR EncodedDevicePath
  614. )
  615. /*++
  616. Routine Description:
  617. pGenerateMultiFunctionIDs locates the device node for the device related to
  618. the multi-function node. If a device node is found, all of its IDs (both
  619. hardware IDs and compatible IDs) are added to the multi-function device as
  620. compatible IDs.
  621. Arguments:
  622. IdList - Specifies an initialized grow buffer that has zero or
  623. more multi-sz strings. Receives additional multi-sz
  624. entries.
  625. EncodedDevicePath - Specifies a device path in the form of
  626. Root&Device&Instance, as obtained from the
  627. multi-function dev node key name.
  628. Return Value:
  629. TRUE if the multi-function device has a master device, FALSE otherwise.
  630. --*/
  631. {
  632. HKEY Parent;
  633. HKEY Key;
  634. BOOL b = FALSE;
  635. TCHAR DevicePathCopy[MAX_REGISTRY_KEY];
  636. PCTSTR Start;
  637. PTSTR End;
  638. TCHAR c;
  639. PCTSTR Data;
  640. PTSTR q;
  641. //
  642. // Multifunction devices have IDs in the form of:
  643. //
  644. // MF\CHILDxxxx\Root&Device&Instance
  645. //
  646. // Find the original device by parsing this string.
  647. //
  648. Parent = OpenRegKeyStr (S_HKLM_ENUM);
  649. if (!Parent) {
  650. return FALSE;
  651. }
  652. StringCopy (DevicePathCopy, EncodedDevicePath);
  653. Start = DevicePathCopy;
  654. End = _tcschr (Start, TEXT('&'));
  655. for (;;) {
  656. if (!End) {
  657. c = 0;
  658. } else {
  659. c = *End;
  660. *End = 0;
  661. }
  662. Key = OpenRegKey (Parent, Start);
  663. if (Key) {
  664. //
  665. // Key exists. Close the parent, and begin
  666. // using the current key as the parent. Then
  667. // continue parsing if necessary.
  668. //
  669. CloseRegKey (Parent);
  670. Parent = Key;
  671. if (!c) {
  672. b = TRUE;
  673. break;
  674. }
  675. *End = TEXT('\\'); // turns DevicePathCopy into a subkey path
  676. Start = End + 1;
  677. End = _tcschr (Start, TEXT('&'));
  678. } else if (c) {
  679. //
  680. // Key does not exist, try breaking at the
  681. // next ampersand.
  682. //
  683. *End = c;
  684. End = _tcschr (End + 1, TEXT('&'));
  685. } else {
  686. //
  687. // Nothing left, key was not found
  688. //
  689. MYASSERT (!End);
  690. break;
  691. }
  692. }
  693. if (b) {
  694. DEBUGMSG ((DBG_HWCOMP, "Parsed MF device node is %s", DevicePathCopy));
  695. //
  696. // Now get all the IDs for this device
  697. //
  698. q = (PTSTR) IdList->Buf;
  699. Data = GetRegValueString (Parent, S_HARDWAREID);
  700. if (Data) {
  701. MultiSzAppend (IdList, Data);
  702. MemFree (g_hHeap, 0, Data);
  703. }
  704. Data = GetRegValueString (Parent, S_COMPATIBLEIDS);
  705. if (Data) {
  706. MultiSzAppend (IdList, Data);
  707. MemFree (g_hHeap, 0, Data);
  708. }
  709. //
  710. // Convert commas into nuls, because we are passing
  711. // back a multi-sz.
  712. //
  713. if (!q) {
  714. q = (PTSTR) IdList->Buf;
  715. }
  716. End = (PTSTR) (IdList->Buf + IdList->End);
  717. while (q < End) {
  718. if (_tcsnextc (q) == TEXT(',')) {
  719. *q = 0;
  720. }
  721. q = _tcsinc (q);
  722. }
  723. //
  724. // Do not double-terminate the multi-sz yet. The caller
  725. // may want to append more IDs to the list.
  726. //
  727. }
  728. CloseRegKey (Parent);
  729. return b;
  730. }
  731. /*++
  732. Routine Description:
  733. NextHardwareEnum returns the next registry value related to hardware
  734. configuration.
  735. Arguments:
  736. EnumPtr - Specifies the current enumeration structure
  737. Return Value:
  738. TRUE if the supplied enumeration structure was filled, or FALSE if
  739. there are no hardware items (unlikely) or an error occurred.
  740. GetLastError() will provide the failure reason or ERROR_SUCCESS.
  741. --*/
  742. BOOL
  743. RealEnumNextHardware (
  744. IN OUT PHARDWARE_ENUM EnumPtr
  745. )
  746. {
  747. TCHAR InstanceBuf[MAX_REGISTRY_KEY];
  748. PTSTR p;
  749. GROWBUFFER HackBuf = GROWBUF_INIT;
  750. MULTISZ_ENUM e;
  751. PTSTR NewBuf;
  752. BOOL TapeDevice;
  753. INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
  754. PCTSTR pattern;
  755. for (;;) {
  756. switch (EnumPtr->State) {
  757. case STATE_ENUM_FIRST_KEY:
  758. EnumPtr->State = STATE_ENUM_CHECK_KEY;
  759. if (!EnumFirstRegKeyInTree (&EnumPtr->ek, S_HKLM_ENUM)) {
  760. END_ENUM;
  761. return FALSE;
  762. }
  763. break;
  764. case STATE_ENUM_NEXT_KEY:
  765. EnumPtr->State = STATE_ENUM_CHECK_KEY;
  766. if (!EnumNextRegKeyInTree (&EnumPtr->ek)) {
  767. END_ENUM;
  768. return FALSE;
  769. }
  770. break;
  771. case STATE_ENUM_CHECK_KEY:
  772. EnumPtr->State = STATE_ENUM_FIRST_VALUE;
  773. if (InfFindFirstLine (g_Win95UpgInf, S_IGNORE_REG_KEY, NULL, &is)) {
  774. do {
  775. pattern = InfGetStringField (&is, 1);
  776. if (pattern && IsPatternMatch (pattern, EnumPtr->ek.FullKeyName)) {
  777. DEBUGMSG ((DBG_WARNING, "Hardware key %s is excluded", EnumPtr->ek.FullKeyName));
  778. EnumPtr->State = STATE_ENUM_NEXT_KEY;
  779. break;
  780. }
  781. } while (InfFindNextLine (&is));
  782. InfCleanUpInfStruct (&is);
  783. }
  784. break;
  785. case STATE_ENUM_FIRST_VALUE:
  786. if (!EnumFirstRegValue (
  787. &EnumPtr->ev,
  788. EnumPtr->ek.CurrentKey->KeyHandle
  789. )) {
  790. EnumPtr->State = STATE_ENUM_NEXT_KEY;
  791. } else {
  792. EnumPtr->State = STATE_EVALUATE_VALUE;
  793. }
  794. break;
  795. case STATE_ENUM_NEXT_VALUE:
  796. if (!EnumNextRegValue (&EnumPtr->ev)) {
  797. EnumPtr->State = STATE_ENUM_NEXT_KEY;
  798. } else {
  799. EnumPtr->State = STATE_EVALUATE_VALUE;
  800. }
  801. break;
  802. case STATE_EVALUATE_VALUE:
  803. if (StringIMatch (EnumPtr->ev.ValueName, S_CLASS)) {
  804. //
  805. // Match found: fill struct with data
  806. //
  807. EnumPtr->State = STATE_VALUE_CLEANUP;
  808. //
  809. // Get HardwareID and CompatibleIDs
  810. //
  811. pGetRegValText (
  812. EnumPtr->ek.CurrentKey->KeyHandle,
  813. S_HARDWAREID,
  814. &EnumPtr->HardwareID
  815. );
  816. pGetRegValText (
  817. EnumPtr->ek.CurrentKey->KeyHandle,
  818. S_COMPATIBLEIDS,
  819. &EnumPtr->CompatibleIDs
  820. );
  821. //
  822. // Special case: flip hardware ID and compatible IDs
  823. // if we don't have a hardware ID but we do have a
  824. // compatible ID
  825. //
  826. if (!EnumPtr->HardwareID && EnumPtr->CompatibleIDs) {
  827. DEBUGMSG ((
  828. DBG_WARNING,
  829. "Reversing hardware and compatible IDs for %s",
  830. EnumPtr->CompatibleIDs
  831. ));
  832. EnumPtr->HardwareID = EnumPtr->CompatibleIDs;
  833. EnumPtr->CompatibleIDs = NULL;
  834. }
  835. //
  836. // Multifunction device special case
  837. //
  838. if (pIsMultiFunctionDevice (EnumPtr->HardwareID)) {
  839. //
  840. // Multifunction devices have IDs in the form of:
  841. //
  842. // MF\CHILDxxxx\Root&Device&Instance
  843. //
  844. // Find the original device by parsing this string.
  845. //
  846. pGenerateMultiFunctionIDs (
  847. &HackBuf,
  848. EnumPtr->ek.CurrentKey->KeyName
  849. );
  850. }
  851. //
  852. // Tape device special case
  853. //
  854. else if (_tcsistr (EnumPtr->ek.FullKeyName, TEXT("SCSI"))) {
  855. pGetRegValText (
  856. EnumPtr->ek.CurrentKey->KeyHandle,
  857. S_CLASS,
  858. &EnumPtr->Class
  859. );
  860. TapeDevice = FALSE;
  861. if (!EnumPtr->Class) {
  862. TapeDevice = TRUE;
  863. } else if (_tcsistr (EnumPtr->Class, TEXT("tape"))) {
  864. TapeDevice = TRUE;
  865. }
  866. ELSE_DEBUGMSG ((
  867. DBG_VERBOSE,
  868. "SCSI device class %s is not a tape device",
  869. EnumPtr->Class
  870. ));
  871. if (TapeDevice) {
  872. //
  873. // For tape devices in the SCSI enumerator, we must create
  874. // extra compatible IDs. For each ID, we add two more,
  875. // both prefixed with Sequential, and one with its revision
  876. // number stripped off.
  877. //
  878. pGenerateTapeIds (&HackBuf, EnumPtr->HardwareID);
  879. pGenerateTapeIds (&HackBuf, EnumPtr->CompatibleIDs);
  880. DEBUGMSG_IF ((
  881. HackBuf.End,
  882. DBG_HWCOMP,
  883. "Tape Device detected, fixing PNP IDs."
  884. ));
  885. DEBUGMSG_IF ((
  886. !HackBuf.End,
  887. DBG_VERBOSE,
  888. "Tape Device detected, but no IDs to fix.\n"
  889. "Hardware ID: %s\n"
  890. "Compatible IDs: %s",
  891. EnumPtr->HardwareID,
  892. EnumPtr->CompatibleIDs
  893. ));
  894. }
  895. }
  896. //
  897. // Add all IDs in HackBuf (a multi-sz) to the compatible
  898. // ID list.
  899. //
  900. if (HackBuf.End) {
  901. MultiSzAppend (&HackBuf, S_EMPTY);
  902. if (EnumPtr->CompatibleIDs) {
  903. NewBuf = pPrivateRegValAllocator (
  904. ByteCount (EnumPtr->CompatibleIDs) +
  905. HackBuf.End
  906. );
  907. StringCopy (NewBuf, EnumPtr->CompatibleIDs);
  908. } else {
  909. NewBuf = pPrivateRegValAllocator (HackBuf.End);
  910. *NewBuf = 0;
  911. }
  912. p = GetEndOfString (NewBuf);
  913. if (EnumFirstMultiSz (&e, (PCTSTR) HackBuf.Buf)) {
  914. do {
  915. if (p != NewBuf) {
  916. p = _tcsappend (p, TEXT(","));
  917. }
  918. p = _tcsappend (p, e.CurrentString);
  919. } while (EnumNextMultiSz (&e));
  920. }
  921. DEBUGMSG ((
  922. DBG_HWCOMP,
  923. "Hardware ID: %s\n"
  924. "Old compatible ID list: %s\n"
  925. "New compatible ID list: %s",
  926. EnumPtr->HardwareID,
  927. EnumPtr->CompatibleIDs,
  928. NewBuf
  929. ));
  930. if (EnumPtr->CompatibleIDs) {
  931. pPrivateRegValDeallocator ((PVOID) EnumPtr->CompatibleIDs);
  932. }
  933. EnumPtr->CompatibleIDs = NewBuf;
  934. FreeGrowBuffer (&HackBuf);
  935. }
  936. //
  937. // Unless the user specified that the hardware ID was not required, break if it does not
  938. // exist.
  939. //
  940. if (!EnumPtr->HardwareID && !(EnumPtr->EnumFlags & ENUM_DONT_REQUIRE_HARDWAREID)) {
  941. break;
  942. }
  943. //
  944. // Process enumeration filter
  945. //
  946. if ((EnumPtr->EnumFlags & ENUM_WANT_COMPATIBLE_FLAG) ||
  947. (EnumPtr->TypeOfEnum != ENUM_ALL_DEVICES)
  948. ) {
  949. EnumPtr->HardwareIdCompatible = FindHardwareId (EnumPtr->HardwareID, NULL);
  950. EnumPtr->CompatibleIdCompatible = FindHardwareId (EnumPtr->CompatibleIDs, NULL);
  951. EnumPtr->HardwareIdUnsupported = FindUnsupportedHardwareId (EnumPtr->HardwareID, NULL);
  952. EnumPtr->CompatibleIdUnsupported = FindUnsupportedHardwareId (EnumPtr->CompatibleIDs, NULL);
  953. //
  954. // Process UI-based IDs and unsupported IDs
  955. //
  956. if (EnumPtr->EnumFlags & ENUM_USER_SUPPLIED_FLAG_NEEDED) {
  957. EnumPtr->SuppliedByUi = FindUserSuppliedDriver (EnumPtr->HardwareID, EnumPtr->CompatibleIDs);
  958. }
  959. if (EnumPtr->EnumFlags & ENUM_DONT_WANT_USER_SUPPLIED) {
  960. if (EnumPtr->SuppliedByUi) {
  961. break;
  962. }
  963. }
  964. if (EnumPtr->EnumFlags & ENUM_WANT_USER_SUPPLIED_ONLY) {
  965. if (!EnumPtr->SuppliedByUi) {
  966. break;
  967. }
  968. }
  969. EnumPtr->Compatible = EnumPtr->HardwareIdCompatible ||
  970. EnumPtr->CompatibleIdCompatible;
  971. EnumPtr->Unsupported = EnumPtr->HardwareIdUnsupported ||
  972. EnumPtr->CompatibleIdUnsupported;
  973. //
  974. // This logic is broken for a USB device that has both
  975. // unsupported and compatible IDs in its hardware ID list.
  976. //
  977. // Removing this if statement causes that device to be
  978. // reported as unsupported.
  979. //
  980. //if (EnumPtr->HardwareIdCompatible) {
  981. // EnumPtr->Unsupported = FALSE;
  982. //}
  983. if (EnumPtr->Unsupported) {
  984. EnumPtr->Compatible = FALSE;
  985. }
  986. //
  987. // Special case: force incompatible? If so, we indicate
  988. // this only by modifying the abstract Compatible flag.
  989. //
  990. if (pFindForcedBadHardwareId (EnumPtr->HardwareID, NULL) ||
  991. pFindForcedBadHardwareId (EnumPtr->CompatibleIDs, NULL)
  992. ) {
  993. EnumPtr->Compatible = FALSE;
  994. }
  995. //
  996. // Continue enumerating if this device does not fit the
  997. // caller's request.
  998. //
  999. if (EnumPtr->TypeOfEnum == ENUM_COMPATIBLE_DEVICES) {
  1000. if (!EnumPtr->Compatible) {
  1001. break;
  1002. }
  1003. } else if (EnumPtr->TypeOfEnum == ENUM_INCOMPATIBLE_DEVICES) {
  1004. if (EnumPtr->Compatible || EnumPtr->Unsupported) {
  1005. break;
  1006. }
  1007. } else if (EnumPtr->TypeOfEnum == ENUM_UNSUPPORTED_DEVICES) {
  1008. if (!EnumPtr->Unsupported) {
  1009. break;
  1010. }
  1011. } else if (EnumPtr->TypeOfEnum == ENUM_NON_FUNCTIONAL_DEVICES) {
  1012. if (EnumPtr->Compatible) {
  1013. break;
  1014. }
  1015. }
  1016. }
  1017. //
  1018. // Copy reg key to struct
  1019. //
  1020. StringCopy (InstanceBuf, EnumPtr->ek.FullKeyName);
  1021. p = _tcschr (InstanceBuf, TEXT('\\'));
  1022. MYASSERT(p);
  1023. if (p) {
  1024. p = _tcschr (_tcsinc (p), TEXT('\\'));
  1025. MYASSERT(p);
  1026. }
  1027. if (p) {
  1028. p = _tcschr (_tcsinc (p), TEXT('\\'));
  1029. MYASSERT(p);
  1030. }
  1031. if (p) {
  1032. p = _tcsinc (p);
  1033. }
  1034. EnumPtr->InstanceId = DuplicateText (p);
  1035. EnumPtr->FullKey = EnumPtr->ek.FullKeyName;
  1036. EnumPtr->KeyHandle = EnumPtr->ek.CurrentKey->KeyHandle;
  1037. //
  1038. // Get all fields; require Class field
  1039. //
  1040. if (!(EnumPtr->EnumFlags & ENUM_DONT_WANT_DEV_FIELDS)) {
  1041. pGetAllRegVals (EnumPtr);
  1042. if (!EnumPtr->Class) {
  1043. DEBUGMSG ((
  1044. DBG_HWCOMP,
  1045. "Device %s does not have a Class field",
  1046. EnumPtr->InstanceId
  1047. ));
  1048. break;
  1049. }
  1050. }
  1051. //
  1052. // Determine if device is online
  1053. //
  1054. if (EnumPtr->EnumFlags & ENUM_WANT_ONLINE_FLAG) {
  1055. EnumPtr->Online = IsPnpIdOnline (EnumPtr->InstanceId, EnumPtr->Class);
  1056. }
  1057. return TRUE;
  1058. } else {
  1059. EnumPtr->State = STATE_ENUM_NEXT_VALUE;
  1060. }
  1061. break;
  1062. case STATE_VALUE_CLEANUP:
  1063. //
  1064. // Free all device field text
  1065. //
  1066. pFreeRegValText (EnumPtr);
  1067. FreeText (EnumPtr->InstanceId);
  1068. EnumPtr->InstanceId = NULL;
  1069. EnumPtr->State = STATE_ENUM_NEXT_VALUE;
  1070. break;
  1071. default:
  1072. MYASSERT(FALSE);
  1073. END_ENUM;
  1074. return FALSE;
  1075. }
  1076. }
  1077. }
  1078. /*++
  1079. Routine Description:
  1080. AbortHardwareEnum cleans up all resources in use by an enumeration. Call
  1081. this function with the EnumPtr value of BeginHardwareEnum or NextHardwareEnum.
  1082. Arguments:
  1083. EnumPtr - Specifies the enumeration to abort.
  1084. Return Value:
  1085. none
  1086. --*/
  1087. VOID
  1088. AbortHardwareEnum (
  1089. IN OUT PHARDWARE_ENUM EnumPtr
  1090. )
  1091. {
  1092. PushError();
  1093. END_ENUM;
  1094. if (EnumPtr->State == STATE_VALUE_CLEANUP) {
  1095. pFreeRegValText (EnumPtr);
  1096. FreeText (EnumPtr->InstanceId);
  1097. }
  1098. AbortRegKeyTreeEnum (&EnumPtr->ek);
  1099. ZeroMemory (EnumPtr, sizeof (HARDWARE_ENUM));
  1100. PopError();
  1101. }
  1102. //
  1103. // NT5 INF database
  1104. //
  1105. BOOL
  1106. FindHardwareId (
  1107. IN PCTSTR PnpIdList,
  1108. OUT PTSTR InfFileName OPTIONAL
  1109. )
  1110. /*++
  1111. Routine Description:
  1112. FindHardwareId parses an ID string that may contain zero or more
  1113. plug and play device IDs, separated by commas. The function then
  1114. searches for each ID in the device ID table, copying the INF file
  1115. name to a supplied buffer when a match is found.
  1116. Arguments:
  1117. PnpIdList - An ID string that contains zero or more plug and play
  1118. device IDs, separated by commas.
  1119. InfFileName - A buffer (big enough to hold MAX_PATH characters)
  1120. that receives the INF file name upon successful
  1121. match. If a match is not found, InfFileName is
  1122. set to an empty string.
  1123. Return Value:
  1124. TRUE if a match was found, or FALSE if a match was not found.
  1125. --*/
  1126. {
  1127. return FindHardwareIdInHashTable (PnpIdList, InfFileName, g_PnpIdTable, TRUE);
  1128. }
  1129. BOOL
  1130. FindUnsupportedHardwareId (
  1131. IN PCTSTR PnpIdList,
  1132. OUT PTSTR InfFileName OPTIONAL
  1133. )
  1134. /*++
  1135. Routine Description:
  1136. FindUnsupportedHardwareId parses an ID string that may contain zero
  1137. or more plug and play device IDs, separated by commas. The function
  1138. then searches for each ID in the device ID table, copying the INF file
  1139. name to a supplied buffer when a match is found.
  1140. Arguments:
  1141. PnpIdList - An ID string that contains zero or more plug and play
  1142. device IDs, separated by commas.
  1143. InfFileName - A buffer (big enough to hold MAX_PATH characters)
  1144. that receives the INF file name upon successful
  1145. match. If a match is not found, InfFileName is
  1146. set to an empty string.
  1147. Return Value:
  1148. TRUE if a match was found, or FALSE if a match was not found.
  1149. --*/
  1150. {
  1151. return FindHardwareIdInHashTable (PnpIdList, InfFileName, g_UnsupPnpIdTable, FALSE);
  1152. }
  1153. BOOL
  1154. pFindForcedBadHardwareId (
  1155. IN PCTSTR PnpIdList,
  1156. OUT PTSTR InfFileName OPTIONAL
  1157. )
  1158. /*++
  1159. Routine Description:
  1160. pFindForcedBadHardwareId parses an ID string that may contain zero or more
  1161. plug and play device IDs, separated by commas. The function then searches
  1162. for each ID in the force bad device ID table, copying the INF file name to a
  1163. supplied buffer when a match is found.
  1164. Arguments:
  1165. PnpIdList - An ID string that contains zero or more plug and play
  1166. device IDs, separated by commas.
  1167. InfFileName - A buffer (big enough to hold MAX_PATH characters)
  1168. that receives the INF file name upon successful
  1169. match. If a match is not found, InfFileName is
  1170. set to an empty string.
  1171. Return Value:
  1172. TRUE if a match was found, or FALSE if a match was not found.
  1173. --*/
  1174. {
  1175. return FindHardwareIdInHashTable (PnpIdList, InfFileName, g_ForceBadIdTable, FALSE);
  1176. }
  1177. BOOL
  1178. FindUserSuppliedDriver (
  1179. IN PCTSTR HardwareIdList, OPTIONAL
  1180. IN PCTSTR CompatibleIdList OPTIONAL
  1181. )
  1182. /*++
  1183. Routine Description:
  1184. FindUserSuppliedDriver parses hardware and compatible hardware ID
  1185. strings that may contain zero or more plug and play device IDs,
  1186. separated by commas. The function then searches for each ID in
  1187. g_UiSuppliedIds table.
  1188. Arguments:
  1189. HardwareIdList - An ID string that contains zero or more plug and play
  1190. device IDs, separated by commas.
  1191. CompatibleIdList - An ID string that contains zero or more plug and play
  1192. device IDs, separated by commas.
  1193. Return Value:
  1194. TRUE if a match was found, or FALSE if a match was not found.
  1195. --*/
  1196. {
  1197. BOOL b = FALSE;
  1198. if (HardwareIdList) {
  1199. b = FindHardwareIdInHashTable (HardwareIdList, NULL, g_UiSuppliedIds, FALSE);
  1200. }
  1201. if (!b && CompatibleIdList) {
  1202. b = FindHardwareIdInHashTable (CompatibleIdList, NULL, g_UiSuppliedIds, FALSE);
  1203. }
  1204. return b;
  1205. }
  1206. BOOL
  1207. FindHardwareIdInHashTable (
  1208. IN PCTSTR PnpIdList,
  1209. OUT PTSTR InfFileName, OPTIONAL
  1210. IN HASHTABLE StrTable,
  1211. IN BOOL UseOverrideList
  1212. )
  1213. /*++
  1214. Routine Description:
  1215. FindHardwareIdInHashTable queries a string table for each PNP ID in
  1216. the specified list. If one is found, the routine optionally copies the
  1217. INF file it was found in. The caller can also choose to scan the win95upg.inf
  1218. override list.
  1219. Arguments:
  1220. PnpIdList - Specifies zero or more PNP IDs, separated by commas.
  1221. InfFileName - Receives the file name of the INF containing the PNP IDs.
  1222. StrTable - Specifies the string table to query. If InfFileName is not NULL,
  1223. the string table must have an extra data value of the offset in
  1224. g_InfFileTable.
  1225. UseOverrideList - Specifies TRUE if the win95upg.inf file is to be queried for
  1226. the PNP ID. This query is performed after it has been
  1227. determined that all IDs in PnpIdList are not in StrTable.
  1228. Return Value:
  1229. TRUE if at least one PNP ID in PnpIdList was found, or FALSE if none of the
  1230. IDs were found.
  1231. --*/
  1232. {
  1233. HASHITEM InfName;
  1234. TCHAR PnpId[MAX_PNP_ID];
  1235. PCSTR p;
  1236. TCHAR FixedEisaId[MAX_PNP_ID];
  1237. //
  1238. // Extract a PNP ID from PnpIdList, then look for it in string table
  1239. //
  1240. if (!PnpIdList) {
  1241. return FALSE;
  1242. }
  1243. MYASSERT (StrTable);
  1244. if (!StrTable) {
  1245. return FALSE;
  1246. }
  1247. p = PnpIdList;
  1248. while (*p) {
  1249. p = ExtractPnpId (p, PnpId);
  1250. if (*PnpId == 0) {
  1251. continue;
  1252. }
  1253. //
  1254. // Locate ID in PNP ID table
  1255. //
  1256. if (HtFindStringAndData (StrTable, PnpId, (PVOID) &InfName)) {
  1257. //
  1258. // Found PNP ID. Get INF file and return.
  1259. //
  1260. if (InfFileName) {
  1261. if (StrTable != g_PnpIdTable && StrTable != g_UnsupPnpIdTable && StrTable != g_ForceBadIdTable) {
  1262. DEBUGMSG ((DBG_WHOOPS, "Caller wants InfFileName from private string table"));
  1263. } else {
  1264. _tcssafecpy (
  1265. InfFileName,
  1266. HtGetStringFromItem (InfName),
  1267. MAX_TCHAR_PATH
  1268. );
  1269. }
  1270. }
  1271. return TRUE;
  1272. }
  1273. //
  1274. // This is a fix for the EISA roots. On Win9x, we have an EISA
  1275. // enumerator, but on NT, the ISA enumerator handles EISA too.
  1276. //
  1277. if (StringIMatchCharCount (TEXT("EISA\\"), PnpId, 5)) {
  1278. StringCopy (FixedEisaId, TEXT("EISA&"));
  1279. StringCat (FixedEisaId, PnpId + 5);
  1280. if (HtFindStringAndData (StrTable, FixedEisaId, (PVOID) &InfName)) {
  1281. //
  1282. // Found PNP ID. Get INF file and return.
  1283. //
  1284. if (InfFileName) {
  1285. if (StrTable != g_PnpIdTable && StrTable != g_UnsupPnpIdTable && StrTable != g_ForceBadIdTable) {
  1286. DEBUGMSG ((DBG_WHOOPS, "Caller wants InfFileName from private string table (2)"));
  1287. } else {
  1288. _tcssafecpy (
  1289. InfFileName,
  1290. HtGetStringFromItem (InfName),
  1291. MAX_TCHAR_PATH
  1292. );
  1293. }
  1294. }
  1295. return TRUE;
  1296. }
  1297. }
  1298. }
  1299. //
  1300. // Locate ID in override table
  1301. //
  1302. if (UseOverrideList) {
  1303. if (pIsDeviceConsideredCompatible (PnpIdList)) {
  1304. DEBUGMSG ((
  1305. DBG_WARNING,
  1306. "%s is considered compatible but actually does not have PNP support in NT.",
  1307. PnpIdList
  1308. ));
  1309. return TRUE;
  1310. }
  1311. }
  1312. return FALSE;
  1313. }
  1314. BOOL
  1315. pProcessNtInfFile (
  1316. IN PCTSTR InfFile,
  1317. IN INT UiMode,
  1318. IN OUT HASHTABLE InfFileTable,
  1319. IN OUT HASHTABLE PnpIdTable,
  1320. IN OUT HASHTABLE UnsupPnpIdTable
  1321. )
  1322. /*++
  1323. Routine Description:
  1324. pProcessNtInfFile scans an NT INF and places all hardware device
  1325. IDs in the PNP string table. All entries of the string table
  1326. have extra data that points to the INF file (added to the INF
  1327. file name string table).
  1328. Arguments:
  1329. InfFile - The path to an INF file to be examined
  1330. UiMode - Specifies VERBOSE_OUTPUT or PNPREPT_OUTPUT when the PNP
  1331. IDs are to be dumped tvia the progress bar output
  1332. routines. If REGULAR_OUTPUT, no output is generated.
  1333. Return Value:
  1334. TRUE if the function completes successfully, or FALSE if it fails.
  1335. Call GetLastError for additional failure information.
  1336. --*/
  1337. {
  1338. HINF hInf;
  1339. INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
  1340. INFSTRUCT isMfg = INITINFSTRUCT_GROWBUFFER;
  1341. INFSTRUCT isDev = INITINFSTRUCT_GROWBUFFER;
  1342. BOOL UnsupportedDevice;
  1343. PCTSTR DevSection;
  1344. PCTSTR Manufacturer;
  1345. TCHAR PnpId[MAX_PNPID_LENGTH * 4];
  1346. PTSTR CurrentDev;
  1347. TCHAR TrimmedId[MAX_PNP_ID];
  1348. PCTSTR FileName;
  1349. PCTSTR p;
  1350. CHARTYPE ch;
  1351. HASHITEM InfOffset = NULL;
  1352. LONG rc;
  1353. LONG DontCare = 0;
  1354. BOOL Result = FALSE;
  1355. PCTSTR RealDevSection = NULL;
  1356. BOOL b;
  1357. PCTSTR TempStr;
  1358. HASHITEM hashItem;
  1359. //
  1360. // Get a pointer to the inf file excluding the path
  1361. //
  1362. FileName = NULL;
  1363. for (p = InfFile ; *p ; p = _tcsinc (p)) {
  1364. ch = _tcsnextc (p);
  1365. if (ch == TEXT('\\')) {
  1366. FileName = _tcsinc (p);
  1367. } else if (!FileName && ch == TEXT(':')) {
  1368. FileName = _tcsinc (p);
  1369. }
  1370. }
  1371. MYASSERT (*FileName);
  1372. //
  1373. // Open INF file with Setup APIs
  1374. //
  1375. hInf = InfOpenInfFile (InfFile);
  1376. if (hInf == INVALID_HANDLE_VALUE) {
  1377. LOG ((LOG_ERROR, "Failed to open %s while processing hardware INFs.", InfFile));
  1378. return FALSE;
  1379. }
  1380. __try {
  1381. //
  1382. // Enumerate [Manufacturer] section
  1383. //
  1384. if (!InfFindFirstLine (hInf, S_MANUFACTURER, NULL, &is)) {
  1385. rc = GetLastError();
  1386. // If section not found, return success
  1387. if (rc == ERROR_SECTION_NOT_FOUND || rc == ERROR_LINE_NOT_FOUND) {
  1388. SetLastError (ERROR_SUCCESS);
  1389. Result = TRUE;
  1390. __leave;
  1391. }
  1392. SetLastError (rc);
  1393. LOG ((LOG_ERROR, "Error trying to find %s in %s", S_MANUFACTURER, InfFile));
  1394. __leave;
  1395. }
  1396. do {
  1397. //
  1398. // Get the manufacturer name
  1399. //
  1400. Manufacturer = InfGetLineText (&is);
  1401. if (!Manufacturer) {
  1402. LOG ((LOG_ERROR, "Error getting line text of enumerated line"));
  1403. __leave;
  1404. }
  1405. //
  1406. // Enumerate the devices listed in the manufacturer's section,
  1407. // looking for PnpId
  1408. //
  1409. if (!InfFindFirstLine (hInf, Manufacturer, NULL, &isMfg)) {
  1410. rc = GetLastError();
  1411. // if section not found, move on to next manufacturer
  1412. if (rc == ERROR_SECTION_NOT_FOUND || rc == ERROR_LINE_NOT_FOUND) {
  1413. DEBUGMSG ((
  1414. DBG_HWCOMP,
  1415. "Manufacturer %s section does not exist in %s",
  1416. Manufacturer,
  1417. InfFile
  1418. ));
  1419. continue;
  1420. }
  1421. LOG((LOG_ERROR, "Error while searching for %s in %s.", Manufacturer, InfFile));
  1422. __leave;
  1423. }
  1424. do {
  1425. //
  1426. // Is this an unsupported device?
  1427. //
  1428. DevSection = InfGetStringField (&isMfg, 1);
  1429. if (!DevSection) {
  1430. // There is no field 1
  1431. continue;
  1432. }
  1433. UnsupportedDevice = FALSE;
  1434. //
  1435. // Try section.NTx86 first, then section.NT, then section
  1436. //
  1437. RealDevSection = JoinText (DevSection, TEXT(".NTx86"));
  1438. b = InfFindFirstLine (hInf, RealDevSection, NULL, &isDev);
  1439. if (!b) {
  1440. FreeText (RealDevSection);
  1441. RealDevSection = JoinText (DevSection, TEXT(".NT"));
  1442. b = InfFindFirstLine (hInf, RealDevSection, NULL, &isDev);
  1443. }
  1444. if (!b) {
  1445. FreeText (RealDevSection);
  1446. RealDevSection = DuplicateText (DevSection);
  1447. b = InfFindFirstLine (hInf, RealDevSection, NULL, &isDev);
  1448. }
  1449. if (!b) {
  1450. DEBUGMSG ((
  1451. DBG_HWCOMP,
  1452. "Device section for %s does not exist in %s of %s",
  1453. RealDevSection,
  1454. Manufacturer,
  1455. InfFile
  1456. ));
  1457. } else {
  1458. if (InfFindFirstLine (hInf, RealDevSection, TEXT("DeviceUpgradeUnsupported"), &isDev)) {
  1459. TempStr = InfGetStringField (&isDev, 1);
  1460. if (TempStr && _ttoi (TempStr)) {
  1461. UnsupportedDevice = TRUE;
  1462. }
  1463. }
  1464. }
  1465. FreeText (RealDevSection);
  1466. //
  1467. // Get the device id
  1468. //
  1469. if (!pGetPnpIdList (&isMfg, PnpId, sizeof (PnpId))) {
  1470. // There is no field 2
  1471. continue;
  1472. }
  1473. //
  1474. // Add each device id to the id tree
  1475. //
  1476. CurrentDev = PnpId;
  1477. while (*CurrentDev) {
  1478. //
  1479. // First time through add the INF file name to string table
  1480. //
  1481. if (!InfOffset) {
  1482. if (InfFileTable) {
  1483. InfOffset = HtAddString (InfFileTable, FileName);
  1484. if (!InfOffset) {
  1485. LOG ((LOG_ERROR, "Cannot add %s to table of INFs.", FileName));
  1486. __leave;
  1487. }
  1488. }
  1489. }
  1490. //
  1491. // Add PNP ID to string table
  1492. //
  1493. StringCopy (TrimmedId, SkipSpace (CurrentDev));
  1494. TruncateTrailingSpace (TrimmedId);
  1495. if (UnsupportedDevice) {
  1496. hashItem = HtAddStringAndData (UnsupPnpIdTable, TrimmedId, &InfOffset);
  1497. } else {
  1498. hashItem = HtAddStringAndData (PnpIdTable, TrimmedId, &InfOffset);
  1499. }
  1500. if (!hashItem) {
  1501. LOG ((LOG_ERROR, "Cannot add %s to table of PNP IDs.", CurrentDev));
  1502. __leave;
  1503. }
  1504. MYASSERT (
  1505. UnsupportedDevice ?
  1506. hashItem == HtFindString (UnsupPnpIdTable, TrimmedId) :
  1507. hashItem == HtFindString (PnpIdTable, TrimmedId)
  1508. );
  1509. //
  1510. // UI options
  1511. //
  1512. if (UiMode == VERBOSE_OUTPUT || UiMode == PNPREPT_OUTPUT) {
  1513. TCHAR Msg[MAX_ENCODED_PNPID_LENGTH + MAX_INF_DESCRIPTION + 16];
  1514. TCHAR Desc[MAX_INF_DESCRIPTION];
  1515. TCHAR EncPnpId[MAX_ENCODED_PNPID_LENGTH * 4];
  1516. TCHAR EncDesc[MAX_INF_DESCRIPTION * 2];
  1517. if (SetupGetStringField (
  1518. &isMfg.Context,
  1519. 0,
  1520. Desc,
  1521. MAX_INF_DESCRIPTION,
  1522. NULL
  1523. )) {
  1524. if (UiMode == VERBOSE_OUTPUT) {
  1525. wsprintf (Msg, TEXT(" PNP ID: %s, Desc: %s"), PnpId, Desc);
  1526. } else {
  1527. StringCopy (EncPnpId, PnpId);
  1528. StringCopy (EncDesc, Desc);
  1529. EncodePnpId (EncPnpId);
  1530. EncodePnpId (EncDesc);
  1531. wsprintf (Msg, TEXT("%s\\%s\\%s"), EncPnpId, EncDesc, FileName);
  1532. }
  1533. ProgressBar_SetSubComponent (Msg);
  1534. }
  1535. }
  1536. CurrentDev = GetEndOfString (CurrentDev) + 1;
  1537. }
  1538. } while (InfFindNextLine (&isMfg));
  1539. } while (InfFindNextLine (&is));
  1540. InfCloseInfFile (hInf);
  1541. SetLastError (ERROR_SUCCESS);
  1542. Result = TRUE;
  1543. }
  1544. __finally {
  1545. PushError();
  1546. InfCleanUpInfStruct (&is);
  1547. InfCleanUpInfStruct (&isMfg);
  1548. InfCleanUpInfStruct (&isDev);
  1549. InfCloseInfFile (hInf);
  1550. PopError();
  1551. }
  1552. return Result;
  1553. }
  1554. PCTSTR
  1555. ExtractPnpId (
  1556. IN PCTSTR PnpIdList,
  1557. OUT PTSTR PnpIdBuf
  1558. )
  1559. /*++
  1560. Routine Description:
  1561. ExtractPnpId removes the next PNP ID from a list of zero or more
  1562. PNP IDs (separated by commas). Upon return, PnpIdBuf contains the
  1563. PNP ID (or empty string if none found), and the return value points
  1564. to the next PNP ID in the list.
  1565. This routine is designed to be called in a loop until the return
  1566. value points to the nul terminated of PnpIdList.
  1567. Arguments:
  1568. PnpIdList - Specifies a pointer to the next string in the PNP ID list.
  1569. PnpIdBuf - Receives the PNP ID with spaces trimmed on both sides of the
  1570. ID.
  1571. Return Value:
  1572. A pointer to the next item in the list, or a pointer to the nul at the
  1573. end of the list. If the pointer points to a non-nul character, call
  1574. ExtractPnpId again, using the return value for the PnpIdList param.
  1575. --*/
  1576. {
  1577. PCTSTR p, q;
  1578. PnpIdList = SkipSpace (PnpIdList);
  1579. q = _tcschr (PnpIdList, TEXT(','));
  1580. if (!q) {
  1581. q = GetEndOfString (PnpIdList);
  1582. }
  1583. p = q;
  1584. if (p > (PnpIdList + MAX_PNP_ID - 1)) {
  1585. p = PnpIdList + MAX_PNP_ID - 1;
  1586. }
  1587. StringCopyAB (PnpIdBuf, PnpIdList, p);
  1588. TruncateTrailingSpace (PnpIdBuf);
  1589. if (*q) {
  1590. q = _tcsinc (q);
  1591. }
  1592. return q;
  1593. }
  1594. BOOL
  1595. AddPnpIdsToHashTable (
  1596. IN OUT HASHTABLE Table,
  1597. IN PCTSTR PnpIdList
  1598. )
  1599. /*++
  1600. Routine Description:
  1601. AddPnpIdsToHashTable extracts all PNP IDs from a comma-separated
  1602. list of PNP IDs and places each one in the specified string table.
  1603. PNP IDs are added to the string table as case-insensitive.
  1604. Arguments:
  1605. Table - Specifies the table to add each PNP ID to
  1606. PnpIdList - Specifies a comma-separated list of zero or more PNP
  1607. IDs to add to Table.
  1608. Return Value:
  1609. TRUE if all IDs were processed successfully, or FALSE if an error
  1610. occurred adding to the string table.
  1611. --*/
  1612. {
  1613. TCHAR PnpId[MAX_PNP_ID];
  1614. PCTSTR p;
  1615. p = PnpIdList;
  1616. if (!p) {
  1617. return TRUE;
  1618. }
  1619. while (*p) {
  1620. p = ExtractPnpId (p, PnpId);
  1621. if (*PnpId) {
  1622. if (!HtAddString (Table, PnpId)) {
  1623. LOG ((LOG_ERROR, "Can't add %s to table of PNP ids.", PnpId));
  1624. return FALSE;
  1625. }
  1626. }
  1627. }
  1628. return TRUE;
  1629. }
  1630. BOOL
  1631. AddPnpIdsToGrowList (
  1632. IN OUT PGROWLIST GrowList,
  1633. IN PCTSTR PnpIdList
  1634. )
  1635. /*++
  1636. Routine Description:
  1637. AddPnpIdsToHashTable extracts all PNP IDs from a comma-separated
  1638. list of PNP IDs and places each one in the specified grow list.
  1639. Arguments:
  1640. GrowList - Specifies the list to add each PNP ID to
  1641. PnpIdList - Specifies a comma-separated list of zero or more PNP
  1642. IDs to add to GrowList.
  1643. Return Value:
  1644. TRUE if all IDs were processed successfully, or FALSE if an error
  1645. occurred adding to the grow list.
  1646. --*/
  1647. {
  1648. TCHAR PnpId[MAX_PNP_ID];
  1649. PCTSTR p;
  1650. p = PnpIdList;
  1651. while (*p) {
  1652. p = ExtractPnpId (p, PnpId);
  1653. if (*PnpId) {
  1654. if (!GrowListAppendString (GrowList, PnpId)) {
  1655. DEBUGMSG ((DBG_ERROR, "AddPnpIdsToGrowList: Can't add %s", PnpId));
  1656. return FALSE;
  1657. }
  1658. }
  1659. }
  1660. return TRUE;
  1661. }
  1662. PCTSTR
  1663. AddPnpIdsToGrowBuf (
  1664. IN OUT PGROWBUFFER GrowBuffer,
  1665. IN PCTSTR PnpIdList
  1666. )
  1667. /*++
  1668. Routine Description:
  1669. AddPnpIdsToGrowBuf extracts all PNP IDs from a comma-separated
  1670. list of PNP IDs and places each one in the specified grow buffer.
  1671. Arguments:
  1672. GrowBuffer - Specifies the buffer to add each PNP ID to
  1673. PnpIdList - Specifies a comma-separated list of zero or more PNP
  1674. IDs to add to GrowBuffer.
  1675. Return Value:
  1676. A pointer to the beginning of the multisz buffer
  1677. --*/
  1678. {
  1679. TCHAR PnpId[MAX_PNP_ID];
  1680. PCTSTR p;
  1681. p = PnpIdList;
  1682. while (*p) {
  1683. p = ExtractPnpId (p, PnpId);
  1684. if (*PnpId) {
  1685. if (!MultiSzAppend (GrowBuffer, PnpId)) {
  1686. DEBUGMSG ((DBG_ERROR, "AddPnpIdsToGrowBuf: Can't add %s", PnpId));
  1687. return FALSE;
  1688. }
  1689. }
  1690. }
  1691. return GrowBuffer->Buf;
  1692. }
  1693. BOOL
  1694. pIsFileOnCD (
  1695. PCTSTR File
  1696. )
  1697. /*++
  1698. Routine Description:
  1699. pIsFileOnCd checks the drive letter at the head of File to see if
  1700. it is a CD-ROM.
  1701. This function also emulates the CD-ROM behavior for the report tool.
  1702. Arguments:
  1703. File - Specifies the full path of the file to compare
  1704. Return Value:
  1705. TRUE if the file is on a CD-ROM, or FALSE if it is not.
  1706. --*/
  1707. {
  1708. TCHAR RootDir[4];
  1709. //
  1710. // If report tool, or private stress option, always return TRUE.
  1711. //
  1712. if (REPORTONLY()) {
  1713. return TRUE;
  1714. }
  1715. #ifdef PRERELEASE
  1716. if (g_Stress) {
  1717. return TRUE;
  1718. }
  1719. #endif
  1720. //
  1721. // A CD drive cannot be a UNC path
  1722. //
  1723. if (File[0] && File[1] != TEXT(':')) {
  1724. return FALSE;
  1725. }
  1726. RootDir[0] = File[0];
  1727. RootDir[1] = File[1];
  1728. RootDir[2] = TEXT('\\');
  1729. RootDir[3] = 0;
  1730. return DRIVE_CDROM == GetDriveType (RootDir);
  1731. }
  1732. DWORD
  1733. pComputeInfChecksum (
  1734. IN PCTSTR HwCompDat, OPTIONAL
  1735. OUT PBOOL Rebuild OPTIONAL
  1736. )
  1737. /*++
  1738. Routine Description:
  1739. pComputeInfChecksum calculates a checksum for all INFs in the
  1740. source directories. This routine scans all directories in the
  1741. SOURCEDIRECTORYARRAY() global string array.
  1742. Arguments:
  1743. HwCompDat - Specifies path to hwcomp.dat, required if Rebuild is
  1744. specified.
  1745. Rebuild - Receives TRUE if an INF file was found with a greater
  1746. date than hwcomp.dat.
  1747. Return Value:
  1748. The checksum.
  1749. --*/
  1750. {
  1751. HANDLE hFind;
  1752. WIN32_FIND_DATA fd;
  1753. DWORD Checksum = 0;
  1754. PTSTR p;
  1755. TCHAR InfPattern[MAX_TCHAR_PATH];
  1756. UINT u, v;
  1757. FILETIME HwCompDatTime;
  1758. MYASSERT ((!HwCompDat && !Rebuild) || (HwCompDat && Rebuild));
  1759. if (Rebuild) {
  1760. if (DoesFileExistEx (HwCompDat, &fd)) {
  1761. *Rebuild = FALSE;
  1762. HwCompDatTime = fd.ftLastWriteTime;
  1763. } else {
  1764. *Rebuild = TRUE;
  1765. }
  1766. }
  1767. //
  1768. // NTRAID#NTBUG9-379084-2001/04/27-jimschm disable this until a better solution is found
  1769. //
  1770. #if 0
  1771. for (u = 0 ; u < SOURCEDIRECTORYCOUNT() ; u++) {
  1772. //
  1773. // Have we already processed this source dir?
  1774. //
  1775. for (v = 0 ; v < u ; v++) {
  1776. if (StringIMatch (SOURCEDIRECTORY(u),SOURCEDIRECTORY(v))) {
  1777. break;
  1778. }
  1779. }
  1780. if (v != u) {
  1781. continue;
  1782. }
  1783. //
  1784. // Process this directory
  1785. //
  1786. StringCopy (InfPattern, SOURCEDIRECTORY(u));
  1787. AppendWack (InfPattern);
  1788. StringCat (InfPattern, TEXT("*.in?"));
  1789. hFind = FindFirstFile (InfPattern, &fd);
  1790. if (hFind != INVALID_HANDLE_VALUE) {
  1791. do {
  1792. //
  1793. // Make sure file name ends in underscore or f
  1794. //
  1795. // We cheat... because we know that if the file was DBCS,
  1796. // it couldn't end in .INF
  1797. //
  1798. p = GetEndOfString (fd.cFileName);
  1799. MYASSERT (p != fd.cFileName);
  1800. p = _tcsdec2 (fd.cFileName, p);
  1801. if (*p == TEXT('_')) {
  1802. if (_istlower (*(p -1)))
  1803. *p = TEXT('f');
  1804. else
  1805. *p = TEXT('F');
  1806. } else if (tolower (*p) != TEXT('f')) {
  1807. continue;
  1808. }
  1809. // Make sure the file is not excluded
  1810. if (pIsInfFileExcluded (fd.cFileName)) {
  1811. continue;
  1812. }
  1813. if (Rebuild) {
  1814. // Check file times
  1815. if (CompareFileTime (&fd.ftLastWriteTime, &HwCompDatTime) > 0) {
  1816. *Rebuild = TRUE;
  1817. // abandon computation
  1818. break;
  1819. }
  1820. }
  1821. // Add the file size to the checksum
  1822. Checksum = _rotl (Checksum, 1) ^ fd.nFileSizeLow;
  1823. // Add file name
  1824. for (p = fd.cFileName ; *p ; p++) {
  1825. // preserve character and order
  1826. Checksum += (DWORD) (*p) * (DWORD) (1 + fd.cFileName - p);
  1827. }
  1828. } while (FindNextFile (hFind, &fd));
  1829. FindClose (hFind);
  1830. }
  1831. }
  1832. #endif
  1833. return Checksum;
  1834. }
  1835. BOOL
  1836. LoadDeviceList (
  1837. IN LOADOP Operation,
  1838. IN PCTSTR HwCompDatPath
  1839. )
  1840. /*++
  1841. Routine Description:
  1842. LoadDeviceList attempts to load hwcomp.dat from the path specified
  1843. in the HwCompDat parameter. If it is able to load this file, all
  1844. PNP IDs for all INFs are valid. If it is not able to load this file,
  1845. the file does not exist, or the file does not match the INFs.
  1846. Arguments:
  1847. Operation - QUERY: the validity of hwcomp.dat is to be checked
  1848. LOAD: load the data into memory.
  1849. DUMP: dump the file to stdout
  1850. HwCompDatPath - The path of hwcomp.dat, the data file holding a
  1851. pre-compiled compatible PNP ID list.
  1852. Return Value:
  1853. TRUE if the function completes successfully, or FALSE if it fails.
  1854. Call GetLastError for additional failure information.
  1855. --*/
  1856. {
  1857. DWORD StoredChecksum;
  1858. BOOL b = FALSE;
  1859. HASHTABLE InfFileTable = NULL;
  1860. HASHTABLE PnpIdTable = NULL;
  1861. BOOL Rebuild;
  1862. DWORD CurrentChecksum;
  1863. DWORD HwCompDatId = 0;
  1864. //
  1865. // !!! IMPORTANT !!!
  1866. //
  1867. // hwcomp.dat is used by other parts of NT. *DO NOT* change it without first e-mailing
  1868. // the NT group. Also, be sure to keep code in lib.c in sync with changes.
  1869. //
  1870. if (Operation == DUMP) {
  1871. DumpHwCompDat (HwCompDatPath, TRUE);
  1872. return TRUE;
  1873. }
  1874. __try {
  1875. //
  1876. // Open the hardware compatibility database
  1877. //
  1878. HwCompDatId = OpenHwCompDat (HwCompDatPath);
  1879. if (!HwCompDatId) {
  1880. __leave;
  1881. }
  1882. #if 0
  1883. //
  1884. // Get the checksum
  1885. //
  1886. StoredChecksum = GetHwCompDatChecksum (HwCompDatId);
  1887. //
  1888. // Verify the checksum
  1889. //
  1890. CurrentChecksum = pComputeInfChecksum (HwCompDatPath, &Rebuild);
  1891. if (CurrentChecksum != StoredChecksum || Rebuild) {
  1892. if (!pIsFileOnCD (HwCompDatPath)) {
  1893. DEBUGMSG ((DBG_WARNING, "PNP dat file's internal checksum does not match"));
  1894. __leave;
  1895. }
  1896. DEBUGMSG ((
  1897. DBG_WARNING,
  1898. "PNP dat file's internal checksum does not match. Error "
  1899. "ignored because %s is on a CD.",
  1900. HwCompDatPath
  1901. ));
  1902. }
  1903. #endif
  1904. //
  1905. // Load the rest of hwcomp.dat
  1906. //
  1907. if (!LoadHwCompDat (HwCompDatId)) {
  1908. DEBUGMSG ((DBG_ERROR, "Can't load hwcomp.dat"));
  1909. __leave;
  1910. }
  1911. //
  1912. // If a load operation, put the hash tables into globals for use by
  1913. // the rest of hwcomp.c.
  1914. //
  1915. if (Operation == LOAD) {
  1916. //
  1917. // Take ownership of the hash tables
  1918. //
  1919. if (g_InfFileTable) {
  1920. HtFree (g_InfFileTable);
  1921. }
  1922. if (g_PnpIdTable) {
  1923. HtFree (g_PnpIdTable);
  1924. }
  1925. if (g_UnsupPnpIdTable) {
  1926. HtFree (g_UnsupPnpIdTable);
  1927. }
  1928. TakeHwCompHashTables (
  1929. HwCompDatId,
  1930. (PVOID *) (&g_PnpIdTable),
  1931. (PVOID *) (&g_UnsupPnpIdTable),
  1932. (PVOID *) (&g_InfFileTable)
  1933. );
  1934. }
  1935. b = TRUE;
  1936. }
  1937. __finally {
  1938. CloseHwCompDat (HwCompDatId);
  1939. }
  1940. return b;
  1941. }
  1942. BOOL
  1943. pWriteDword (
  1944. IN HANDLE File,
  1945. IN DWORD Val
  1946. )
  1947. /*++
  1948. Routine Description:
  1949. pWriteDword writes the specified DWORD value to File.
  1950. Arguments:
  1951. File - Specifies file to write to
  1952. Val - Specifies value to write
  1953. Return Value:
  1954. TRUE if the function completes successfully, or FALSE if it fails.
  1955. Call GetLastError for additional failure information.
  1956. --*/
  1957. {
  1958. DWORD BytesWritten;
  1959. return WriteFile (File, &Val, sizeof (Val), &BytesWritten, NULL);
  1960. }
  1961. BOOL
  1962. pWriteWord (
  1963. IN HANDLE File,
  1964. IN WORD Val
  1965. )
  1966. /*++
  1967. Routine Description:
  1968. pWriteWord writes the specified WORD vlue to File.
  1969. Arguments:
  1970. File - Specifies file to write to
  1971. Val - Specifies value to write
  1972. Return Value:
  1973. TRUE if the function completes successfully, or FALSE if it fails.
  1974. Call GetLastError for additional failure information.
  1975. --*/
  1976. {
  1977. DWORD BytesWritten;
  1978. return WriteFile (File, &Val, sizeof (Val), &BytesWritten, NULL);
  1979. }
  1980. BOOL
  1981. pWriteStringWithLength (
  1982. IN HANDLE File,
  1983. IN PCTSTR String
  1984. )
  1985. /*++
  1986. Routine Description:
  1987. pWriteStringWithLength writes the length of String as a WORD,
  1988. and then writes String (excluding the nul terminator).
  1989. Arguments:
  1990. File - Specifies file to write to
  1991. String - Specifies string to write
  1992. Return Value:
  1993. TRUE if the function completes successfully, or FALSE if it fails.
  1994. Call GetLastError for additional failure information.
  1995. --*/
  1996. {
  1997. DWORD BytesWritten;
  1998. WORD Length;
  1999. Length = (WORD) ByteCount (String);
  2000. if (!pWriteWord (File, Length)) {
  2001. DEBUGMSG ((DBG_ERROR, "pWriteStringWithLength: Can't write word"));
  2002. return FALSE;
  2003. }
  2004. if (Length) {
  2005. if (!WriteFile (File, String, Length, &BytesWritten, NULL)) {
  2006. DEBUGMSG ((DBG_ERROR, "pWriteStringWithLength: Can't write %s", String));
  2007. return FALSE;
  2008. }
  2009. }
  2010. return TRUE;
  2011. }
  2012. BOOL
  2013. pPnpIdEnum (
  2014. IN HASHTABLE Table,
  2015. IN HASHITEM StringId,
  2016. IN PCTSTR String,
  2017. IN PVOID ExtraData,
  2018. IN UINT ExtraDataSize,
  2019. IN LPARAM lParam
  2020. )
  2021. /*++
  2022. Routine Description:
  2023. pPnpIdEnum is a string table callback function that writes a PNP
  2024. ID to the file indicated in the Params struct (the lParam member).
  2025. This function only writes PNP IDs for a specific INF file (indicated
  2026. by the ExtraData arg).
  2027. Arguments:
  2028. Table - Specifies table being enumerated
  2029. StringId - Specifies offset of string in Table
  2030. String - Specifies string being enumerated
  2031. ExtraData - Specifies a pointer to a LONG that holds the INF ID
  2032. to enumerate. The PNP ID's INF ID must match this
  2033. parameter.
  2034. lParam - Specifies a pointer to a SAVE_ENUM_PARAMS struct
  2035. Return Value:
  2036. TRUE if the function completes successfully, or FALSE if it fails.
  2037. --*/
  2038. {
  2039. PSAVE_ENUM_PARAMS Params;
  2040. PCSTR BangString;
  2041. BOOL b = TRUE;
  2042. Params = (PSAVE_ENUM_PARAMS) lParam;
  2043. if (*((HASHITEM *) ExtraData) == Params->InfFileOffset) {
  2044. //
  2045. // Write this PNP ID to the file
  2046. //
  2047. if (Params->UnsupportedDevice) {
  2048. BangString = JoinTextExA (NULL, "!", String, NULL, 0, NULL);
  2049. b = pWriteStringWithLength (Params->File, BangString);
  2050. FreeTextA (BangString);
  2051. } else {
  2052. b = pWriteStringWithLength (Params->File, String);
  2053. }
  2054. }
  2055. return b;
  2056. }
  2057. BOOL
  2058. pInfFileEnum (
  2059. IN HASHTABLE Table,
  2060. IN HASHITEM StringId,
  2061. IN PCTSTR String,
  2062. IN HASHTABLE ExtraData,
  2063. IN UINT ExtraDataSize,
  2064. IN LPARAM lParam
  2065. )
  2066. /*++
  2067. Routine Description:
  2068. pInfFileEnum is a string table callback function and is called for
  2069. each INF in g_InfFileTable.
  2070. This routine writes the name of the INF to disk, and then enumerates
  2071. the PNP IDs for the INF, writing them to disk.
  2072. The PNP ID list is terminated with an empty string.
  2073. Arguments:
  2074. Table - Specifies g_InfFileTable
  2075. StringId - Specifies offset of String in g_InfFileTable
  2076. String - Specifies current INF file being enumerated
  2077. ExtraData - unused
  2078. ExtraDataSize - unused
  2079. lParam - Specifies a pointer to SAVE_ENUM_PARAMS struct.
  2080. Return Value:
  2081. TRUE if the function completes successfully, or FALSE if it fails.
  2082. --*/
  2083. {
  2084. PSAVE_ENUM_PARAMS Params;
  2085. Params = (PSAVE_ENUM_PARAMS) lParam;
  2086. Params->InfFileOffset = StringId;
  2087. //
  2088. // Save the file name
  2089. //
  2090. if (!pWriteStringWithLength (Params->File, String)) {
  2091. return FALSE;
  2092. }
  2093. //
  2094. // Enumerate all PNP IDs
  2095. //
  2096. Params->UnsupportedDevice = FALSE;
  2097. if (!EnumHashTableWithCallback (g_PnpIdTable, pPnpIdEnum, lParam)) {
  2098. LOG ((LOG_ERROR, "Error while saving device list."));
  2099. return FALSE;
  2100. }
  2101. Params->UnsupportedDevice = TRUE;
  2102. if (!EnumHashTableWithCallback (g_UnsupPnpIdTable, pPnpIdEnum, lParam)) {
  2103. LOG ((LOG_ERROR, "Error while saving device list. (2)"));
  2104. return FALSE;
  2105. }
  2106. //
  2107. // Terminate the PNP ID list
  2108. //
  2109. if (!pWriteStringWithLength (Params->File, S_EMPTY)) {
  2110. return FALSE;
  2111. }
  2112. return TRUE;
  2113. }
  2114. BOOL
  2115. SaveDeviceList (
  2116. PCTSTR HwCompDatPath
  2117. )
  2118. /*++
  2119. Routine Description:
  2120. SaveDeviceList writes all data stored in g_InfFileTable and g_PnpIdTable
  2121. to the file specified by HwCompDat. This file will therefore contain
  2122. all PNP IDs in all INFs of Windows NT.
  2123. Arguments:
  2124. HwCompDatPath - Specifies path of file to write
  2125. Return Value:
  2126. TRUE if the function completes successfully, or FALSE if it fails.
  2127. Call GetLastError for additional failure information.
  2128. --*/
  2129. {
  2130. HANDLE File;
  2131. DWORD BytesWritten;
  2132. BOOL b = FALSE;
  2133. SAVE_ENUM_PARAMS Params;
  2134. DWORD ChecksumToStore;
  2135. //
  2136. // !!! IMPORTANT !!!
  2137. //
  2138. // hwcomp.dat is used by other parts of NT. *DO NOT* change it without first e-mailing
  2139. // the NT group. Also, be sure to keep code in lib.c in sync with changes.
  2140. //
  2141. ChecksumToStore = pComputeInfChecksum (NULL, NULL);
  2142. File = CreateFile (
  2143. HwCompDatPath,
  2144. GENERIC_WRITE,
  2145. 0, // open for exclusive access
  2146. NULL, // no security attribs
  2147. CREATE_ALWAYS,
  2148. FILE_ATTRIBUTE_NORMAL,
  2149. NULL // no template
  2150. );
  2151. if (File == INVALID_HANDLE_VALUE) {
  2152. LOG ((LOG_ERROR, "Cannot open %s for writing", HwCompDatPath));
  2153. return FALSE;
  2154. }
  2155. __try {
  2156. //
  2157. // Write version stamp
  2158. //
  2159. if (!WriteFile (File, HWCOMPDAT_SIGNATURE, ByteCount (HWCOMPDAT_SIGNATURE), &BytesWritten, NULL)) {
  2160. LOG ((LOG_ERROR, "Can't write signature file."));
  2161. __leave;
  2162. }
  2163. //
  2164. // Write checksum
  2165. //
  2166. if (!pWriteDword (File, ChecksumToStore)) {
  2167. LOG ((LOG_ERROR, "Can't write checksum"));
  2168. __leave;
  2169. }
  2170. //
  2171. // Enumerate the INF table, writing the INF file name and all PNP IDs
  2172. //
  2173. Params.File = File;
  2174. if (!EnumHashTableWithCallback (
  2175. g_InfFileTable,
  2176. pInfFileEnum,
  2177. (LPARAM) (&Params)
  2178. )) {
  2179. DEBUGMSG ((DBG_WARNING, "SaveDeviceList: EnumHashTableWithCallback returned FALSE"));
  2180. __leave;
  2181. }
  2182. //
  2183. // Terminate the INF file list
  2184. //
  2185. if (!pWriteStringWithLength (File, S_EMPTY)) {
  2186. DEBUGMSG ((DBG_WARNING, "SaveDeviceList: Can't write INF terminator"));
  2187. __leave;
  2188. }
  2189. b = TRUE;
  2190. }
  2191. __finally {
  2192. CloseHandle (File);
  2193. if (!b) {
  2194. DeleteFile (HwCompDatPath);
  2195. }
  2196. }
  2197. return b;
  2198. }
  2199. BOOL
  2200. pIsInfFileExcluded (
  2201. PCTSTR FileNamePtr
  2202. )
  2203. /*++
  2204. Routine Description:
  2205. IsInfFileExcluded returns TRUE when the specified file name does
  2206. not contain PNP IDs.
  2207. Arguments:
  2208. FileNamePtr - The name of the uncompressed INF file, without any path info.
  2209. Return Value:
  2210. TRUE if the file should be ignored by the PNP parser, or FALSE if
  2211. the file may contain PNP IDs.
  2212. --*/
  2213. {
  2214. PCTSTR *p;
  2215. // Check for OEMN (old network INFs)
  2216. if (StringIMatchCharCount (FileNamePtr, TEXT("OEMN"), 4)) {
  2217. return TRUE;
  2218. }
  2219. // Make sure extension has INF
  2220. if (!StringIMatch (FileNamePtr + TcharCount (FileNamePtr) - 3 * sizeof (TCHAR), TEXT("INF"))) {
  2221. return TRUE;
  2222. }
  2223. // Check list of excluded files
  2224. for (p = g_ExcludeTable ; **p ; p++) {
  2225. if (StringIMatch (FileNamePtr, *p)) {
  2226. return TRUE;
  2227. }
  2228. }
  2229. return FALSE;
  2230. }
  2231. VOID
  2232. pGetNonExistingFile (
  2233. IN PCTSTR Path,
  2234. OUT PTSTR EndOfPath,
  2235. IN PCTSTR DefaultName
  2236. )
  2237. /*++
  2238. Routine Description:
  2239. pGetNonExistingFile generates a file name of a file that does
  2240. not exist. It creates an empty file with that name, to reserve it.
  2241. Arguments:
  2242. Path - Specifies the path where the file will exist. Path must
  2243. end in a backslash.
  2244. EndOfPath - Points to the nul at the end of Path and is used to
  2245. write the new file name.
  2246. DefaultName - Specifies the default file name to try to use. If such
  2247. a file already exists, numbers are appended to
  2248. DefaultName until a unique name is found.
  2249. Return Value:
  2250. none
  2251. --*/
  2252. {
  2253. UINT Count = 0;
  2254. StringCopy (EndOfPath, DefaultName);
  2255. while (GetFileAttributes (Path) != 0xffffffff) {
  2256. Count++;
  2257. wsprintf (EndOfPath, TEXT("%s.%03u"), DefaultName, Count);
  2258. }
  2259. }
  2260. BOOL
  2261. GetFileNames (
  2262. IN PCTSTR *InfDirs,
  2263. IN UINT InfDirCount,
  2264. IN BOOL QueryFlag,
  2265. IN OUT PGROWBUFFER FileNames,
  2266. IN OUT PGROWBUFFER DecompFileNames
  2267. )
  2268. /*++
  2269. Routine Description:
  2270. GetFileNames searches InfDirs for any file that ends with .INF or .IN_.
  2271. It builds a MULTI_SZ list of file names that may contain PNP IDs. All
  2272. compressed INFs are decompressed into a temporary directory.
  2273. If the QueryFlag is set, the file name list is prepared but no files
  2274. are decompressed.
  2275. Arguments:
  2276. InfDirs - A list of paths to the directory containing INFs, either
  2277. compressed or non-compressed.
  2278. InfDirCount - Specifies the number of dirs in the InfDirs array.
  2279. QueryFlag - TRUE if the function should build the file list but
  2280. should not decompress; FALSE if the function
  2281. should build the file list and decompress as needed.
  2282. FileNames - Specifies an empty GROWBUFFER struct that is used to build
  2283. a multi-sz list of full paths to the INF files.
  2284. Return Value:
  2285. A pointer to the MUTLI_SZ list. The caller is responsible for freeing
  2286. this buffer via FreeFileNames.
  2287. The return value is NULL if an error occurred. Call GetLastError for
  2288. an error code.
  2289. --*/
  2290. {
  2291. UINT u;
  2292. //
  2293. // Add list of files for each directory
  2294. //
  2295. for (u = 0 ; u < InfDirCount ; u++) {
  2296. if (!pGetFileNamesWorker (FileNames, DecompFileNames, InfDirs[u], QueryFlag)) {
  2297. FreeFileNames (FileNames, DecompFileNames, QueryFlag);
  2298. return FALSE;
  2299. }
  2300. }
  2301. MultiSzAppend (FileNames, S_EMPTY);
  2302. MultiSzAppend (DecompFileNames, S_EMPTY);
  2303. return TRUE;
  2304. }
  2305. BOOL
  2306. pGetFileNamesWorker (
  2307. IN OUT PGROWBUFFER FileNames,
  2308. IN OUT PGROWBUFFER DecompFileNames,
  2309. IN PCTSTR InfDir,
  2310. IN BOOL QueryFlag
  2311. )
  2312. /*++
  2313. Routine Description:
  2314. pGetFileNamesWorker gets the file names for a single directory.
  2315. See GetFileNames for more details.
  2316. Arguments:
  2317. FileNames - Specifies GROWBUFFER of file names. This routine
  2318. appends file names using MultiSzAppend but does not
  2319. append the final empty string.
  2320. InfDir - Specifies directory holding zero or more INFs (either
  2321. compressed or non-compressed).
  2322. QueryFlag - Specifies TRUE if INF list is to be queried, or
  2323. FALSE if the list is to be fully processed. When
  2324. QueryFlag is TRUE, files are not decompressed or
  2325. opened.
  2326. Return Value:
  2327. TRUE if the function completes successfully, or FALSE if it fails.
  2328. Call GetLastError for additional failure information.
  2329. --*/
  2330. {
  2331. PTSTR p;
  2332. TCHAR ActualFile[MAX_TCHAR_PATH];
  2333. CHAR AnsiFileName[MAX_MBCHAR_PATH];
  2334. PTSTR FileNameOnDisk;
  2335. HANDLE hFile;
  2336. DWORD BytesRead;
  2337. HANDLE hFind;
  2338. WIN32_FIND_DATA fd;
  2339. TCHAR Pattern[MAX_TCHAR_PATH];
  2340. TCHAR UncompressedFile[MAX_TCHAR_PATH];
  2341. TCHAR CompressedFile[MAX_TCHAR_PATH];
  2342. PTSTR FileNamePtr;
  2343. BOOL DecompressFlag;
  2344. DWORD rc;
  2345. BYTE BufForSp[2048];
  2346. PSP_INF_INFORMATION psp;
  2347. psp = (PSP_INF_INFORMATION) BufForSp;
  2348. //
  2349. // Get file names
  2350. //
  2351. StringCopy (Pattern, InfDir);
  2352. StringCopy (AppendWack (Pattern), TEXT("*.in?"));
  2353. hFind = FindFirstFile (Pattern, &fd);
  2354. if (hFind == INVALID_HANDLE_VALUE) {
  2355. if (GetLastError() == ERROR_NO_MORE_FILES) {
  2356. return TRUE;
  2357. }
  2358. LOG ((LOG_ERROR, "FindFirstFile failed for %s", Pattern));
  2359. return FALSE;
  2360. }
  2361. //
  2362. // Determine if each matching file is actually an INF, and if so
  2363. // add it to the FileNames growbuf.
  2364. //
  2365. rc = ERROR_SUCCESS;
  2366. do {
  2367. if (*g_CancelFlagPtr) {
  2368. rc = ERROR_CANCELLED;
  2369. break;
  2370. }
  2371. //
  2372. // Make sure file has _ or f at the end.
  2373. //
  2374. p = GetEndOfString (fd.cFileName);
  2375. MYASSERT (p != fd.cFileName);
  2376. p = _tcsdec2 (fd.cFileName, p);
  2377. MYASSERT (p);
  2378. if (!p) {
  2379. continue;
  2380. }
  2381. if (*p != TEXT('_') && _totlower (*p) != TEXT('f')) {
  2382. continue;
  2383. }
  2384. //
  2385. // Default actual file to uncompressed name
  2386. //
  2387. StringCopy (ActualFile, fd.cFileName);
  2388. //
  2389. // Build source file (CompressedFile)
  2390. //
  2391. StringCopy (CompressedFile, InfDir);
  2392. StringCopy (AppendWack (CompressedFile), ActualFile);
  2393. //
  2394. // Build destination file (UncompressedFile) and detect collisions
  2395. //
  2396. /*
  2397. StringCopy (UncompressedFile, g_TempDir);
  2398. FileNamePtr = AppendWack (UncompressedFile);
  2399. pGetNonExistingFile (UncompressedFile, FileNamePtr, ActualFile);
  2400. */
  2401. DecompressFlag = FALSE;
  2402. if (!GetTempFileName (g_TempDir, TEXT("inf"), 0, UncompressedFile)) {
  2403. rc = GetLastError ();
  2404. break;
  2405. }
  2406. //
  2407. // Create uncompressed file path
  2408. //
  2409. if (*p == TEXT('_')) {
  2410. //
  2411. // Extract real name from INF file at offset 0x3c
  2412. //
  2413. ActualFile[0] = 0;
  2414. hFile = CreateFile (
  2415. CompressedFile,
  2416. GENERIC_READ,
  2417. FILE_SHARE_READ,
  2418. NULL,
  2419. OPEN_EXISTING,
  2420. FILE_ATTRIBUTE_NORMAL,
  2421. NULL
  2422. );
  2423. if (hFile != INVALID_HANDLE_VALUE) {
  2424. if (0xffffffff != SetFilePointer (hFile, 0x3c, NULL, FILE_BEGIN)) {
  2425. if (ReadFile (
  2426. hFile,
  2427. AnsiFileName,
  2428. sizeof (AnsiFileName),
  2429. &BytesRead,
  2430. NULL
  2431. )) {
  2432. if (BytesRead >= SizeOfString (fd.cFileName)) {
  2433. FileNameOnDisk = ConvertAtoT (AnsiFileName);
  2434. if (StringIMatchCharCount (
  2435. fd.cFileName,
  2436. FileNameOnDisk,
  2437. CharCount (fd.cFileName) - 1
  2438. )) {
  2439. //
  2440. // Real name found -- use it as ActualFile
  2441. //
  2442. StringCopy (ActualFile, FileNameOnDisk);
  2443. //
  2444. // Also use real file name for decompression, but
  2445. // append numbers if collision.
  2446. //
  2447. /*
  2448. pGetNonExistingFile (
  2449. UncompressedFile,
  2450. FileNamePtr,
  2451. FileNameOnDisk
  2452. );
  2453. */
  2454. }
  2455. FreeAtoT (FileNameOnDisk);
  2456. }
  2457. }
  2458. }
  2459. CloseHandle (hFile);
  2460. }
  2461. //
  2462. // If file name could not be found, discard this file
  2463. //
  2464. if (!ActualFile[0]) {
  2465. DEBUGMSG ((DBG_HWCOMP, "%s is not an INF file", fd.cFileName));
  2466. continue;
  2467. }
  2468. DecompressFlag = TRUE;
  2469. } else {
  2470. StringCopy (UncompressedFile, CompressedFile);
  2471. }
  2472. //
  2473. // Skip excluded files
  2474. //
  2475. if (pIsInfFileExcluded (ActualFile)) {
  2476. continue;
  2477. }
  2478. if (!QueryFlag) {
  2479. //
  2480. // Uncompress file if necessary
  2481. //
  2482. if (DecompressFlag) {
  2483. /*
  2484. SetFileAttributes (UncompressedFile, FILE_ATTRIBUTE_NORMAL);
  2485. DeleteFile (UncompressedFile);
  2486. */
  2487. rc = SetupDecompressOrCopyFile (CompressedFile, UncompressedFile, 0);
  2488. if (rc != ERROR_SUCCESS) {
  2489. LOG ((LOG_ERROR, "Could not decompress %s to %s", CompressedFile, UncompressedFile));
  2490. break;
  2491. }
  2492. }
  2493. //
  2494. // Determine if this is an NT 4 INF
  2495. //
  2496. if (!SetupGetInfInformation (
  2497. UncompressedFile,
  2498. INFINFO_INF_NAME_IS_ABSOLUTE,
  2499. psp,
  2500. sizeof (BufForSp),
  2501. NULL) ||
  2502. psp->InfStyle != INF_STYLE_WIN4
  2503. ) {
  2504. DEBUGMSG ((DBG_HWCOMP, "%s is not a WIN4 INF file", UncompressedFile));
  2505. /*
  2506. if (DecompressFlag && !QueryFlag) {
  2507. DeleteFile (UncompressedFile);
  2508. }
  2509. */
  2510. StringCopy (UncompressedFile, S_IGNORE_THIS_FILE);
  2511. }
  2512. TickProgressBar();
  2513. }
  2514. //
  2515. // Add file to grow buffer
  2516. //
  2517. MultiSzAppend (DecompressFlag ? DecompFileNames : FileNames, UncompressedFile);
  2518. } while (rc == ERROR_SUCCESS && FindNextFile (hFind, &fd));
  2519. FindClose (hFind);
  2520. if (rc != ERROR_SUCCESS) {
  2521. SetLastError (rc);
  2522. DEBUGMSG ((DBG_ERROR, "pGetFileNamesWorker: Error encountered in loop"));
  2523. return FALSE;
  2524. }
  2525. return TRUE;
  2526. }
  2527. VOID
  2528. FreeFileNames (
  2529. IN PGROWBUFFER FileNames,
  2530. IN OUT PGROWBUFFER DecompFileNames,
  2531. IN BOOL QueryFlag
  2532. )
  2533. /*++
  2534. Routine Description:
  2535. FreeFileNames cleans up the list generated by GetFileNames. If
  2536. QueryFlag is set to FALSE, all temporary decompressed
  2537. files are deleted.
  2538. Arguments:
  2539. FileNames - The same grow buffer passed to GetFileNames
  2540. QueryFlag - The same flag passed to GetFileNames
  2541. Return Value:
  2542. none
  2543. --*/
  2544. {
  2545. PTSTR p;
  2546. p = (PTSTR) DecompFileNames->Buf;
  2547. if (!p) {
  2548. return;
  2549. }
  2550. //
  2551. // Remove all files in temp dir (we created them when performing decompression)
  2552. //
  2553. if (!QueryFlag) {
  2554. while (*p) {
  2555. if (StringIMatchCharCount (p, g_TempDirWack, g_TempDirWackChars)) {
  2556. SetFileAttributes (p, FILE_ATTRIBUTE_NORMAL);
  2557. DeleteFile (p);
  2558. }
  2559. p = GetEndOfString (p) + 1;
  2560. }
  2561. }
  2562. //
  2563. // Deallocate FileNames
  2564. //
  2565. FreeGrowBuffer (DecompFileNames);
  2566. FreeGrowBuffer (FileNames);
  2567. }
  2568. VOID
  2569. pBuildPatternCompatibleIDsTable (
  2570. VOID
  2571. )
  2572. {
  2573. INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
  2574. PCTSTR p;
  2575. GROWBUFFER joinedPattern = GROWBUF_INIT;
  2576. PPARSEDPATTERN test;
  2577. MYASSERT (g_Win95UpgInf != INVALID_HANDLE_VALUE);
  2578. if (InfFindFirstLine (g_Win95UpgInf, S_COMPATIBLE_PNP_IDS, NULL, &is)) {
  2579. do {
  2580. p = InfGetStringField (&is, 0);
  2581. if (*p) {
  2582. //
  2583. // first check if the pattern is correct
  2584. // if it isn't, we skip it
  2585. //
  2586. test = CreateParsedPattern (p);
  2587. if (test) {
  2588. DestroyParsedPattern (test);
  2589. GrowBufAppendString (&joinedPattern, TEXT("<"));
  2590. GrowBufAppendString (&joinedPattern, p);
  2591. GrowBufAppendString (&joinedPattern, TEXT(">"));
  2592. }
  2593. ELSE_DEBUGMSG ((DBG_WHOOPS, "Unable to parse pattern %s in [%s]", p, S_COMPATIBLE_PNP_IDS));
  2594. }
  2595. } while (InfFindNextLine (&is));
  2596. }
  2597. InfCleanUpInfStruct (&is);
  2598. if (joinedPattern.Buf) {
  2599. g_PatternCompatibleIDsTable = CreateParsedPattern (joinedPattern.Buf);
  2600. FreeGrowBuffer (&joinedPattern);
  2601. }
  2602. }
  2603. BOOL
  2604. CreateNtHardwareList (
  2605. IN PCTSTR * NtInfPaths,
  2606. IN UINT NtInfPathCount,
  2607. IN PCTSTR HwCompDatPath, OPTIONAL
  2608. IN INT UiMode
  2609. )
  2610. /*++
  2611. Routine Description:
  2612. CreateNtHardwareList gets a list of all INF files and calls pProcessNtInfFile
  2613. to build the NT device list. This routine is called at initialization.
  2614. The resulting list is saved to disk as hwcomp.dat. If hwcomp.dat already
  2615. exists, the device list is read from disk.
  2616. Arguments:
  2617. NtInfPaths - Specifies an array of full paths to the NT INF files.
  2618. NtInfPathCount - Specifies the number of elements in NtInfPaths. Cannot
  2619. be zero.
  2620. HwCompDatPath - Specifies a full path spec where the new HWCOMP.DAT file
  2621. should be loaded from. This is used by the hwdatgen tool.
  2622. UiMode - Specifies the type of output (if any) to produce while building
  2623. the device lists. Values are zero, PNPREPT_OUTPUT, or
  2624. VERBOSE_OUTPUT.
  2625. Return Value:
  2626. Returns TRUE if successful, or FALSE if not. Call GetLastError for
  2627. failure code.
  2628. --*/
  2629. {
  2630. PCTSTR SourceFile;
  2631. PCTSTR DestFile;
  2632. BOOL FreeSourceAndDest;
  2633. UINT u;
  2634. PTSTR File;
  2635. DWORD rc;
  2636. BOOL bSaved = FALSE;
  2637. INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
  2638. PCTSTR p;
  2639. MYASSERT (NtInfPathCount > 0);
  2640. //
  2641. // If string tables already exist, then we do not have to build this list
  2642. // a second time.
  2643. //
  2644. if (!g_PatternCompatibleIDsTable) {
  2645. //
  2646. // table not set up; build it now
  2647. //
  2648. pBuildPatternCompatibleIDsTable ();
  2649. }
  2650. if (g_PnpIdTable && g_UnsupPnpIdTable && g_InfFileTable && g_ForceBadIdTable) {
  2651. return TRUE;
  2652. }
  2653. DEBUGMSG ((DBG_VERBOSE, "CreateNtHardwareList: building hardware list"));
  2654. MYASSERT (!g_PnpIdTable);
  2655. MYASSERT (!g_UnsupPnpIdTable);
  2656. MYASSERT (!g_InfFileTable);
  2657. MYASSERT (!g_ForceBadIdTable);
  2658. //
  2659. // Prepare file names. If HwCompDatPath is provided, use it only.
  2660. //
  2661. if (HwCompDatPath) {
  2662. //
  2663. // Use caller-supplied path; the caller is not necessarily NT Setup.
  2664. //
  2665. SourceFile = HwCompDatPath;
  2666. DestFile = HwCompDatPath;
  2667. FreeSourceAndDest = FALSE;
  2668. } else {
  2669. //
  2670. // Locate the source hwcomp.dat. If one does not exist,
  2671. // use the first source directory as Source.
  2672. //
  2673. SourceFile = NULL;
  2674. for (u = 0 ; !SourceFile && u < NtInfPathCount ; u++) {
  2675. SourceFile = pGetHwCompDat (NtInfPaths[u], TRUE);
  2676. }
  2677. if (!SourceFile) {
  2678. SourceFile = pGetHwCompDat (NtInfPaths[0], FALSE);
  2679. }
  2680. DestFile = pGetHwCompDat (g_TempDir, FALSE);
  2681. FreeSourceAndDest = TRUE;
  2682. }
  2683. //
  2684. // Build force table
  2685. //
  2686. if (g_ForceBadIdTable) {
  2687. HtFree (g_ForceBadIdTable);
  2688. }
  2689. g_ForceBadIdTable = HtAlloc();
  2690. if (InfFindFirstLine (g_Win95UpgInf, TEXT("Forced Incompatible IDs"), NULL, &is)) {
  2691. do {
  2692. p = InfGetStringField (&is, 0);
  2693. if (*p) {
  2694. HtAddString (g_ForceBadIdTable, p);
  2695. }
  2696. } while (InfFindNextLine (&is));
  2697. }
  2698. InfCleanUpInfStruct (&is);
  2699. __try {
  2700. //
  2701. // Try loading state from CD
  2702. //
  2703. if (UiMode != PNPREPT_OUTPUT) {
  2704. if (!LoadDeviceList (LOAD, SourceFile)) {
  2705. //
  2706. // Could not load from CD -- try loading from temporary storage
  2707. // location
  2708. //
  2709. if (!HwCompDatPath && LoadDeviceList (LOAD, DestFile)) {
  2710. return TRUE;
  2711. }
  2712. DEBUGMSG ((DBG_HWCOMP, "%s does not exist or needs to be rebuilt", SourceFile));
  2713. } else {
  2714. return TRUE;
  2715. }
  2716. }
  2717. //
  2718. // Load the INF file names
  2719. //
  2720. ProgressBar_SetComponentById (MSG_DECOMPRESSING);
  2721. // Get file names
  2722. if (!g_FileNames.Buf && !g_DecompFileNames.Buf) {
  2723. if (!GetFileNames (NtInfPaths, NtInfPathCount, FALSE, &g_FileNames, &g_DecompFileNames)) {
  2724. DEBUGMSG ((DBG_WARNING, "HWCOMP: Can't get INF file names"));
  2725. return FALSE;
  2726. }
  2727. }
  2728. __try {
  2729. ProgressBar_SetComponentById (MSG_HWCOMP);
  2730. //
  2731. // Initialize string tables
  2732. //
  2733. g_PnpIdTable = HtAllocWithData (sizeof (HASHITEM));
  2734. g_UnsupPnpIdTable = HtAllocWithData (sizeof (HASHITEM));
  2735. g_InfFileTable = HtAlloc();
  2736. if (!g_PnpIdTable || !g_UnsupPnpIdTable || !g_InfFileTable) {
  2737. LOG ((LOG_ERROR, "HWCOMP: Can't allocate string tables"));
  2738. return FALSE;
  2739. }
  2740. //
  2741. // Walk through list of INF files, and locate device names inside
  2742. // manufacturer sections. Add each name to the string table.
  2743. //
  2744. File = (PTSTR) g_FileNames.Buf;
  2745. while (*File) {
  2746. //
  2747. // Skip non-WIN4 INF files
  2748. //
  2749. if (StringMatch (File, S_IGNORE_THIS_FILE)) {
  2750. File = GetEndOfString (File) + 1;
  2751. if (!TickProgressBar()) {
  2752. break;
  2753. }
  2754. continue;
  2755. }
  2756. //
  2757. // Process all WIN4 INF files
  2758. //
  2759. if (UiMode != PNPREPT_OUTPUT) {
  2760. ProgressBar_SetSubComponent (File);
  2761. }
  2762. if (!pProcessNtInfFile (File, UiMode, g_InfFileTable, g_PnpIdTable, g_UnsupPnpIdTable)) {
  2763. if ((GetLastError() & 0xe0000000) == 0xe0000000) {
  2764. DEBUGMSG ((DBG_WARNING, "pProcessNtInfFile failed to parse %s.", File));
  2765. } else {
  2766. break;
  2767. }
  2768. }
  2769. if (!TickProgressBar()) {
  2770. break;
  2771. }
  2772. File = GetEndOfString (File) + 1;
  2773. }
  2774. rc = GetLastError();
  2775. if (rc == ERROR_SUCCESS) {
  2776. File = (PTSTR) g_DecompFileNames.Buf;
  2777. while (*File) {
  2778. //
  2779. // Skip non-WIN4 INF files
  2780. //
  2781. if (StringMatch (File, S_IGNORE_THIS_FILE)) {
  2782. File = GetEndOfString (File) + 1;
  2783. if (!TickProgressBar()) {
  2784. break;
  2785. }
  2786. continue;
  2787. }
  2788. //
  2789. // Process all WIN4 INF files
  2790. //
  2791. if (UiMode != PNPREPT_OUTPUT) {
  2792. ProgressBar_SetSubComponent (File);
  2793. }
  2794. if (!pProcessNtInfFile (File, UiMode, g_InfFileTable, g_PnpIdTable, g_UnsupPnpIdTable)) {
  2795. if ((GetLastError() & 0xe0000000) == 0xe0000000) {
  2796. DEBUGMSG ((DBG_WARNING, "pProcessNtInfFile failed to parse %s.", File));
  2797. } else {
  2798. break;
  2799. }
  2800. }
  2801. if (!TickProgressBar()) {
  2802. break;
  2803. }
  2804. File = GetEndOfString (File) + 1;
  2805. }
  2806. rc = GetLastError();
  2807. }
  2808. //
  2809. // Clean up UI
  2810. //
  2811. ProgressBar_SetComponent (NULL);
  2812. ProgressBar_SetSubComponent (NULL);
  2813. //
  2814. // Save string tables to hwcomp.dat
  2815. //
  2816. if (UiMode == PNPREPT_OUTPUT) {
  2817. bSaved = TRUE;
  2818. } else if (rc == ERROR_SUCCESS) {
  2819. bSaved = SaveDeviceList (DestFile);
  2820. //
  2821. // Try copying this file to the right place for future installs
  2822. //
  2823. if (bSaved && !HwCompDatPath) {
  2824. if (!StringIMatch (DestFile, SourceFile)) {
  2825. CopyFile (DestFile, SourceFile, FALSE);
  2826. }
  2827. }
  2828. if (!bSaved) {
  2829. rc = GetLastError();
  2830. }
  2831. }
  2832. }
  2833. __finally {
  2834. FreeFileNames (&g_FileNames, &g_DecompFileNames, FALSE);
  2835. }
  2836. }
  2837. __finally {
  2838. if (FreeSourceAndDest) {
  2839. pFreeHwCompDatName (SourceFile);
  2840. pFreeHwCompDatName (DestFile);
  2841. }
  2842. }
  2843. return bSaved;
  2844. }
  2845. VOID
  2846. FreeNtHardwareList (
  2847. VOID
  2848. )
  2849. /*++
  2850. Routine Description:
  2851. FreeNtHardwareList cleans up the string tables. This function is called by
  2852. DllMain when a process detaches.
  2853. Arguments:
  2854. none
  2855. Return Value:
  2856. none
  2857. --*/
  2858. {
  2859. if (g_InfFileTable) {
  2860. HtFree (g_InfFileTable);
  2861. g_InfFileTable = NULL;
  2862. }
  2863. if (g_PnpIdTable) {
  2864. HtFree (g_PnpIdTable);
  2865. g_PnpIdTable = NULL;
  2866. }
  2867. if (g_UnsupPnpIdTable) {
  2868. HtFree (g_UnsupPnpIdTable);
  2869. g_UnsupPnpIdTable = NULL;
  2870. }
  2871. if (g_ForceBadIdTable) {
  2872. HtFree (g_ForceBadIdTable);
  2873. g_ForceBadIdTable = NULL;
  2874. }
  2875. }
  2876. //
  2877. // Routines that use the enumerators
  2878. //
  2879. BOOL
  2880. HwComp_ScanForCriticalDevices (
  2881. VOID
  2882. )
  2883. /*++
  2884. Routine Description:
  2885. HwComp_ScanForCriticalDevices is one of the first functions called in
  2886. the upgrade module. It enumerates the hardware and determines if
  2887. certain required devices are compatible.
  2888. Arguments:
  2889. none
  2890. Return Value:
  2891. TRUE if processing was successful, or FALSE if an error occurred.
  2892. Call GetLastError() for failure code.
  2893. --*/
  2894. {
  2895. HARDWARE_ENUM e;
  2896. //
  2897. // Reset flags for reentrancy
  2898. //
  2899. g_ValidWinDir = FALSE;
  2900. g_ValidSysDrive = FALSE;
  2901. g_ValidCdRom = FALSE;
  2902. g_FoundPnp8387 = FALSE;
  2903. if (g_NeededHardwareIds) {
  2904. HtFree (g_NeededHardwareIds);
  2905. g_NeededHardwareIds = NULL;
  2906. }
  2907. g_NeededHardwareIds = HtAlloc();
  2908. MYASSERT (g_NeededHardwareIds);
  2909. //
  2910. // Make sure hardware list is valid
  2911. //
  2912. if (!CreateNtHardwareList (
  2913. SOURCEDIRECTORYARRAY(),
  2914. SOURCEDIRECTORYCOUNT(),
  2915. NULL,
  2916. REGULAR_OUTPUT
  2917. )) {
  2918. DEBUGMSG_IF ((
  2919. GetLastError() != ERROR_CANCELLED,
  2920. DBG_ERROR,
  2921. "HwComp_ScanForCriticalDevices: CreateNtHardwareList failed!"
  2922. ));
  2923. return FALSE;
  2924. }
  2925. //
  2926. // Scan all hardware
  2927. //
  2928. if (EnumFirstHardware (&e, ENUM_ALL_DEVICES, ENUM_WANT_COMPATIBLE_FLAG | ENUM_DONT_REQUIRE_HARDWAREID)) {
  2929. do {
  2930. //
  2931. // Fill g_NeededHardwareIDs with all PNP IDs of incompatible devices.
  2932. // Skip the devices that are deliberately unsupported.
  2933. //
  2934. if (!e.Compatible && !e.Unsupported) {
  2935. if (e.HardwareID) {
  2936. AddPnpIdsToHashTable (g_NeededHardwareIds, e.HardwareID);
  2937. }
  2938. if (e.CompatibleIDs) {
  2939. AddPnpIdsToHashTable (g_NeededHardwareIds, e.CompatibleIDs);
  2940. }
  2941. }
  2942. //
  2943. // Test 1: Check to see if (A) g_WinDir is on supported device, and
  2944. // (B) g_BootDriveLetter is on a supported device.
  2945. //
  2946. if (e.Compatible && e.CurrentDriveLetter) {
  2947. if (_tcschr (e.CurrentDriveLetter, _tcsnextc (g_WinDir))) {
  2948. g_ValidWinDir = TRUE;
  2949. }
  2950. if (_tcschr (e.CurrentDriveLetter, g_BootDriveLetter)) {
  2951. g_ValidSysDrive = TRUE;
  2952. }
  2953. }
  2954. //
  2955. // Test 2: Check to see if the class is CDROM
  2956. //
  2957. if (e.Compatible && e.Class) {
  2958. if (StringIMatch (e.Class, TEXT("CDROM"))) {
  2959. g_ValidCdRom = TRUE;
  2960. }
  2961. }
  2962. //
  2963. // Test 3: Check to see if HardwareID or CompatibleIDs contains
  2964. // *PNP8387 (Dial-Up Adapter)
  2965. //
  2966. if (e.CompatibleIDs && _tcsistr (e.CompatibleIDs, TEXT("*PNP8387"))) {
  2967. g_FoundPnp8387 = TRUE;
  2968. }
  2969. if (e.HardwareID && _tcsistr (e.HardwareID, TEXT("*PNP8387"))) {
  2970. g_FoundPnp8387 = TRUE;
  2971. }
  2972. //
  2973. // Test 4: Test for an incompatible SCSI adapter
  2974. //
  2975. if (e.HardwareID && !e.Compatible && _tcsistr (e.Class, TEXT("SCSI"))) {
  2976. g_IncompatibleScsiDevice = TRUE;
  2977. }
  2978. } while (EnumNextHardware (&e));
  2979. }
  2980. return TRUE;
  2981. }
  2982. BOOL
  2983. HwComp_DialUpAdapterFound (
  2984. VOID
  2985. )
  2986. /*++
  2987. Routine Description:
  2988. HwComp_DialUpAdapterFound returns TRUE if *PNP8387 was found
  2989. during the HwComp_ScanForCriticalDevices routine.
  2990. Arguments:
  2991. none
  2992. Return Value:
  2993. TRUE if the Microsoft Dial-Up Adapter exists, or FALSE if it
  2994. does not.
  2995. --*/
  2996. {
  2997. return g_FoundPnp8387;
  2998. }
  2999. BOOL
  3000. HwComp_NtUsableHardDriveExists (
  3001. VOID
  3002. )
  3003. /*++
  3004. Routine Description:
  3005. HwComp_NtUsableHardDriveExists returns TRUE if a compatible
  3006. hard disk exists for the Windows directory and the boot
  3007. drive.
  3008. Arguments:
  3009. none
  3010. Return Value:
  3011. TRUE if a compatible hard disk exists, or FALSE if one does
  3012. not exist.
  3013. --*/
  3014. {
  3015. return g_ValidSysDrive && g_ValidWinDir;
  3016. }
  3017. BOOL
  3018. HwComp_ReportIncompatibleController (
  3019. VOID
  3020. )
  3021. /*++
  3022. Routine Description:
  3023. HwComp_ReportIncompatibleController adds a message when an incompatible
  3024. hard disk controller is found. If the boot drive or windir drive is
  3025. incompatible, then a strong warning is given. Otherwise, the message
  3026. is informational.
  3027. Arguments:
  3028. none
  3029. Return Value:
  3030. TRUE if an incompatible controller message was added, FALSE otherwise.
  3031. --*/
  3032. {
  3033. HARDWARE_ENUM e;
  3034. BOOL MsgAdded = FALSE;
  3035. PCTSTR Group;
  3036. PCTSTR Message;
  3037. BOOL BadMainDev;
  3038. //
  3039. // Do this only if a bad controller exists
  3040. //
  3041. BadMainDev = HwComp_NtUsableHardDriveExists();
  3042. if (!BadMainDev && !g_IncompatibleScsiDevice) {
  3043. return FALSE;
  3044. }
  3045. //
  3046. // Scan incompatible hardware
  3047. //
  3048. if (EnumFirstHardware (
  3049. &e,
  3050. ENUM_NON_FUNCTIONAL_DEVICES,
  3051. ENUM_WANT_COMPATIBLE_FLAG | ENUM_DONT_REQUIRE_HARDWAREID
  3052. )) {
  3053. do {
  3054. //
  3055. // this test is not reliable
  3056. // there are CDROMs that will falll into this category but they are not HD controllers
  3057. // and there are also real SCSI controllers that have an "Unknown" class because
  3058. // Win9x doesn't have (or need) a driver for them
  3059. //
  3060. #if 0
  3061. if (_tcsistr (e.Class, TEXT("SCSI"))) {
  3062. if (!MsgAdded) {
  3063. MsgAdded = TRUE;
  3064. Group = BuildMessageGroup (MSG_INCOMPATIBLE_HARDWARE_ROOT, MSG_INCOMPATIBLE_HARD_DISK_SUBGROUP, NULL);
  3065. Message = GetStringResource (
  3066. BadMainDev ? MSG_INCOMPATIBLE_HARD_DISK_WARNING :
  3067. MSG_INCOMPATIBLE_HARD_DISK_NOTIFICATION
  3068. );
  3069. MsgMgr_ContextMsg_Add (TEXT("*BadController"), Group, Message);
  3070. FreeText (Group);
  3071. FreeStringResource (Message);
  3072. }
  3073. MsgMgr_LinkObjectWithContext (TEXT("*BadController"), e.FullKey);
  3074. DEBUGMSG ((DBG_HWCOMP, "Bad controller key: %s", e.FullKey));
  3075. }
  3076. #endif
  3077. } while (EnumNextHardware (&e));
  3078. }
  3079. return MsgAdded;
  3080. }
  3081. BOOL
  3082. HwComp_NtUsableCdRomDriveExists (
  3083. VOID
  3084. )
  3085. /*++
  3086. Routine Description:
  3087. HwComp_NtUsableCdRomDriveExists returns TRUE if a compatible
  3088. CD-ROM drive exists.
  3089. Arguments:
  3090. none
  3091. Return Value:
  3092. TRUE if a compatible CD-ROM drive exists, or FALSE if it does not.
  3093. --*/
  3094. {
  3095. return g_ValidCdRom;
  3096. }
  3097. BOOL
  3098. HwComp_MakeLocalSourceDeviceExists (
  3099. VOID
  3100. )
  3101. /*++
  3102. Routine Description:
  3103. MakeLocalSourceDevice scans the IDs found on the system for a match in a
  3104. special section in win95upg.inf. If one of these devices is found, the
  3105. function returns TRUE. This is used to catch cases where the user's CDrom
  3106. drive may be attached to a device that will not be available during
  3107. textmode.
  3108. Arguments:
  3109. None.
  3110. Return Value:
  3111. TRUE if such a device exists, FALSE otherwise.
  3112. --*/
  3113. {
  3114. INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
  3115. HASHTABLE table;
  3116. HARDWARE_ENUM e;
  3117. BOOL rDeviceExists = FALSE;
  3118. PTSTR p = NULL;
  3119. __try {
  3120. if (InfFindFirstLine (g_Win95UpgInf, S_MAKELOCALSOURCEDEVICES, NULL, &is)) {
  3121. table = HtAlloc ();
  3122. //
  3123. // Add all of the "bad" pnpids listed in win95upg.inf.
  3124. //
  3125. do {
  3126. p = InfGetStringField (&is, 0);
  3127. HtAddString (table, p);
  3128. } while (InfFindNextLine (&is));
  3129. //
  3130. // Now, enumerate the devices on the system and see if we have any matches.
  3131. //
  3132. if (EnumFirstHardware (&e, ENUM_ALL_DEVICES, ENUM_WANT_ONLINE_FLAG)) {
  3133. do {
  3134. if (HtFindString (table, e.HardwareID)) {
  3135. rDeviceExists = TRUE;
  3136. AbortHardwareEnum (&e);
  3137. DEBUGMSG ((DBG_WARNING, "Device %s requires us to turn on the local source flag.", e.HardwareID));
  3138. break;
  3139. }
  3140. } while (EnumNextHardware (&e));
  3141. }
  3142. HtFree (table);
  3143. }
  3144. }
  3145. __except (1) {
  3146. return FALSE;
  3147. }
  3148. return rDeviceExists;
  3149. }
  3150. BOOL
  3151. HwComp_DoesDatFileNeedRebuilding (
  3152. VOID
  3153. )
  3154. /*++
  3155. Routine Description:
  3156. HwComp_DoesDatFileNeedRebuilding locates hwcomp.dat in the source
  3157. directories and determines if it needs to be rebuilt by obtaining
  3158. the checksum in the file and comparing it against the one from
  3159. the current INF files.
  3160. Arguments:
  3161. none
  3162. Return Value:
  3163. TRUE if the hwcomp.dat file needs to be rebuilt, or FALSE if it
  3164. does not.
  3165. --*/
  3166. {
  3167. PCTSTR SourceFile = NULL;
  3168. UINT u;
  3169. BOOL b = FALSE;
  3170. for (u = 0 ; u < SOURCEDIRECTORYCOUNT() ; u++) {
  3171. SourceFile = pGetHwCompDat(SOURCEDIRECTORY(u), TRUE);
  3172. if (SourceFile) {
  3173. break;
  3174. }
  3175. }
  3176. if (SourceFile) {
  3177. TurnOnWaitCursor();
  3178. b = LoadDeviceList (QUERY, SourceFile);
  3179. TurnOffWaitCursor();
  3180. pFreeHwCompDatName (SourceFile);
  3181. }
  3182. // b is TRUE when hwcomp.dat is valid, so return opposite
  3183. return !b;
  3184. }
  3185. INT
  3186. HwComp_GetProgressMax (
  3187. VOID
  3188. )
  3189. /*++
  3190. Routine Description:
  3191. HwComp_GetProgressMax calculates the number of INF files that would
  3192. need to be scanned if hwcomp.dat needs to be rebuilt. If it is
  3193. determined that hwcomp.dat does not need to be rebuilt, the function
  3194. returns zero.
  3195. Arguments:
  3196. none
  3197. Return Value:
  3198. The number of INF files that need to be processed, times two. (One
  3199. pass for decompression, another pass for parsing the INFs.)
  3200. --*/
  3201. {
  3202. INT FileCount = 0;
  3203. PCTSTR File;
  3204. PCTSTR SourceFile = NULL;
  3205. BOOL b = FALSE;
  3206. //
  3207. // Query validity of hwcomp.dat
  3208. //
  3209. if (!HwComp_DoesDatFileNeedRebuilding()) {
  3210. return 0;
  3211. }
  3212. //
  3213. // hwcomp.dat needs to be rebuilt, so return the number of files
  3214. // that need to be scanned times two.
  3215. //
  3216. // Count files
  3217. if (!g_FileNames.Buf && !g_DecompFileNames.Buf) {
  3218. if (!GetFileNames (
  3219. SOURCEDIRECTORYARRAY(),
  3220. SOURCEDIRECTORYCOUNT(),
  3221. TRUE,
  3222. &g_FileNames,
  3223. &g_DecompFileNames
  3224. )) {
  3225. LOG ((LOG_ERROR, "HWCOMP: Can't estimate number of INF files"));
  3226. return 850; // estimation, so there is some progress bar activity
  3227. }
  3228. }
  3229. for (File = (PCTSTR) g_FileNames.Buf ; *File ; File = GetEndOfString (File) + 1) {
  3230. FileCount++;
  3231. }
  3232. for (File = (PCTSTR) g_DecompFileNames.Buf ; *File ; File = GetEndOfString (File) + 1) {
  3233. FileCount++;
  3234. }
  3235. FreeFileNames (&g_FileNames, &g_DecompFileNames, TRUE);
  3236. MYASSERT (!g_FileNames.Buf);
  3237. MYASSERT (!g_DecompFileNames.Buf);
  3238. return FileCount*2;
  3239. }
  3240. BOOL
  3241. pIsDeviceConsideredCompatible (
  3242. PCTSTR DevIds
  3243. )
  3244. /*++
  3245. Routine Description:
  3246. pIsDeviceConsideredCompatible scans a list of comma-separated
  3247. PNP IDs against the list in win95upg.inf. If at least one
  3248. ID matches, TRUE is returned.
  3249. This function also implements a hack for the VIRTUAL root.
  3250. Arguments:
  3251. DevIds - Specifies a list of zero or more PNP IDs, separated
  3252. by commas.
  3253. Return Value:
  3254. TRUE if a PNP ID was found to be overridden as compatible, or
  3255. FALSE if none of the IDs are in win95upg.inf.
  3256. --*/
  3257. {
  3258. TCHAR Id[MAX_PNP_ID];
  3259. INFCONTEXT ic;
  3260. while (*DevIds) {
  3261. //
  3262. // Create Id string from comma-separated PNP ID list
  3263. //
  3264. DevIds = ExtractPnpId (DevIds, Id);
  3265. if (*Id == 0) {
  3266. continue;
  3267. }
  3268. //
  3269. // Search win95upg.inf for the PNP ID
  3270. //
  3271. if (SetupFindFirstLine (g_Win95UpgInf, S_STANDARD_PNP_IDS, Id, &ic)) {
  3272. DEBUGMSG ((DBG_HWCOMP, "%s is incompatible, but suppressed in win95upg.inf", Id));
  3273. return TRUE;
  3274. }
  3275. //
  3276. // This is a hack for the VIRTUAL enumerator, used by Turtle Beach.
  3277. //
  3278. if (StringIMatchCharCount (TEXT("VIRTUAL\\"), Id, 8)) {
  3279. return TRUE;
  3280. }
  3281. //
  3282. // Test for pattern PNPIDs
  3283. //
  3284. if (g_PatternCompatibleIDsTable && TestParsedPattern (g_PatternCompatibleIDsTable, Id)) {
  3285. DEBUGMSG ((DBG_HWCOMP, "%s is incompatible, but suppressed in win95upg.inf", Id));
  3286. return TRUE;
  3287. }
  3288. }
  3289. return FALSE;
  3290. }
  3291. BOOL
  3292. pIsDeviceInstalled (
  3293. IN PCTSTR DeviceDesc
  3294. )
  3295. /*++
  3296. Routine Description:
  3297. pIsDeviceInstalled scans the registry looking for a device that has the
  3298. specified description and is online.
  3299. Arguments:
  3300. DeviceDesc - Specifies the description of the duplicate device to find
  3301. (i.e., Dial-Up Adapter)
  3302. Return Value:
  3303. TRUE if an identical device was found and is online, or FALSE if not.
  3304. --*/
  3305. {
  3306. HARDWARE_ENUM e;
  3307. if (EnumFirstHardware (
  3308. &e,
  3309. ENUM_NON_FUNCTIONAL_DEVICES,
  3310. ENUM_WANT_ONLINE_FLAG|ENUM_DONT_WANT_USER_SUPPLIED
  3311. )) {
  3312. do {
  3313. if (e.Online) {
  3314. if (e.DeviceDesc) {
  3315. if (StringIMatch (e.DeviceDesc, DeviceDesc)) {
  3316. AbortHardwareEnum (&e);
  3317. return TRUE;
  3318. }
  3319. }
  3320. }
  3321. } while (EnumNextHardware (&e));
  3322. }
  3323. return FALSE;
  3324. }
  3325. VOID
  3326. pGetFriendlyClassName (
  3327. IN HKEY ClassKey,
  3328. IN PCTSTR Class,
  3329. OUT PTSTR Buffer
  3330. )
  3331. {
  3332. PCTSTR Data = NULL;
  3333. HKEY SubKey;
  3334. SubKey = OpenRegKey (ClassKey, Class);
  3335. if (SubKey) {
  3336. Data = GetRegValueString (SubKey, S_EMPTY);
  3337. CloseRegKey (SubKey);
  3338. if (!Data || !*Data) {
  3339. SubKey = NULL;
  3340. }
  3341. }
  3342. if (!SubKey) {
  3343. Data = GetStringResource (MSG_UNKNOWN_DEVICE_CLASS);
  3344. if (Data) {
  3345. _tcssafecpy (Buffer, Data, MAX_TCHAR_PATH);
  3346. FreeStringResource (Data);
  3347. }
  3348. ELSE_DEBUGMSG ((DBG_ERROR, "Unable to load string resource MSG_UNKNOWN_DEVICE_CLASS. Check localization."));
  3349. return;
  3350. }
  3351. _tcssafecpy (Buffer, Data, MAX_TCHAR_PATH);
  3352. MemFree (g_hHeap, 0, Data);
  3353. return;
  3354. }
  3355. BOOL
  3356. pStuffDeviceInReport (
  3357. PHARDWARE_ENUM e,
  3358. HKEY Key,
  3359. HWTYPES SupportedType
  3360. )
  3361. /*++
  3362. Routine Description:
  3363. pStuffDeviceInReport adds a device to the upgrade report. The device is
  3364. either incompatible or unsupported.
  3365. Arguments:
  3366. e - Specifies the current hardware enumerator struct.
  3367. Key - Specifies a handle to the Class key in HKLM\System\Services
  3368. SupportedType - Specifies one of the HWTYPES constants -- HW_INCOMPATIBLE,
  3369. HW_REINSTALL or HW_UNINSTALL, to indicate which category
  3370. to stuff the message into.
  3371. Return Value:
  3372. None.
  3373. --*/
  3374. {
  3375. PCTSTR ModifiedDescription = NULL;
  3376. PCTSTR Group = NULL;
  3377. PCTSTR Message = NULL;
  3378. PCTSTR DeviceDesc = NULL;
  3379. PCTSTR Array[6];
  3380. BOOL UnknownClass = FALSE;
  3381. PCTSTR Mfg;
  3382. PCTSTR Class;
  3383. PCTSTR HardwareID;
  3384. PCTSTR CompatibleID;
  3385. PCTSTR ClassAndName;
  3386. TCHAR FriendlyClass[MAX_TCHAR_PATH];
  3387. UINT SubGroup;
  3388. PCTSTR SubGroupText; // for log output only, hard-coded text
  3389. BOOL b = FALSE;
  3390. //
  3391. // Determine which group this message belongs in. The order is determined
  3392. // by the alphanumeric order of SubGroup.
  3393. //
  3394. if (SupportedType == HW_INCOMPATIBLE) {
  3395. SubGroup = MSG_INCOMPATIBLE_HARDWARE_PNP_SUBGROUP;
  3396. SubGroupText = TEXT("Incompatible");
  3397. } else if (SupportedType == HW_REINSTALL) {
  3398. SubGroup = MSG_REINSTALL_HARDWARE_PNP_SUBGROUP;
  3399. SubGroupText = TEXT("Reinstall");
  3400. } else {
  3401. SubGroup = MSG_UNSUPPORTED_HARDWARE_PNP_SUBGROUP;
  3402. SubGroupText = TEXT("Unsupported");
  3403. }
  3404. //
  3405. // Is device suppressed?
  3406. //
  3407. if (IsReportObjectHandled (e->FullKey)) {
  3408. return FALSE;
  3409. }
  3410. //
  3411. // Sometimes blank entries are found!!
  3412. //
  3413. __try {
  3414. DeviceDesc = e->DeviceDesc;
  3415. if (!DeviceDesc || (DeviceDesc && *DeviceDesc == 0)) {
  3416. LOG ((
  3417. LOG_ERROR,
  3418. "Skipping device because it lacks DriverDesc (%s,%s,%s)",
  3419. e->Mfg,
  3420. e->Class,
  3421. e->HardwareID
  3422. ));
  3423. __leave;
  3424. }
  3425. Mfg = e->Mfg;
  3426. if (!Mfg) {
  3427. DEBUGMSG ((
  3428. DBG_WARNING,
  3429. "Device lacking manufacturer (%s,%s,%s)",
  3430. e->DeviceDesc,
  3431. e->Class,
  3432. e->HardwareID
  3433. ));
  3434. Mfg = S_EMPTY;
  3435. }
  3436. Class = e->Class;
  3437. if (!Class) {
  3438. DEBUGMSG ((
  3439. DBG_WARNING,
  3440. "Device lacking class (%s,%s,%s)",
  3441. e->DeviceDesc,
  3442. e->Mfg,
  3443. e->HardwareID
  3444. ));
  3445. Class = GetStringResource (MSG_UNKNOWN_DEVICE_CLASS);
  3446. MYASSERT (Class);
  3447. UnknownClass = TRUE;
  3448. }
  3449. HardwareID = e->HardwareID;
  3450. if (!HardwareID) {
  3451. DEBUGMSG ((
  3452. DBG_WARNING,
  3453. "Device lacking hardware ID (%s,%s,%s)",
  3454. e->DeviceDesc,
  3455. e->Mfg,
  3456. e->Class
  3457. ));
  3458. HardwareID = S_EMPTY;
  3459. }
  3460. CompatibleID = e->CompatibleIDs;
  3461. if (!CompatibleID) {
  3462. CompatibleID = S_EMPTY;
  3463. }
  3464. //
  3465. // Add "(not currently present)" to offline devices
  3466. //
  3467. if (!e->Online) {
  3468. //
  3469. // Verify identical online device doesn't exist
  3470. //
  3471. if (pIsDeviceInstalled (DeviceDesc)) {
  3472. __leave;
  3473. }
  3474. Array[0] = DeviceDesc;
  3475. ModifiedDescription = ParseMessageID (MSG_OFFLINE_DEVICE, Array);
  3476. }
  3477. //
  3478. // Add hardware message to the incompatibility table
  3479. //
  3480. if (UnknownClass) {
  3481. StringCopy (FriendlyClass, Class);
  3482. } else {
  3483. pGetFriendlyClassName (Key, Class, FriendlyClass);
  3484. }
  3485. DEBUGMSG ((
  3486. DBG_HWCOMP,
  3487. "%s Device:\n"
  3488. " %s (%s)\n"
  3489. " %s\n"
  3490. " %s (%s)\n"
  3491. " %s\n",
  3492. SubGroupText,
  3493. HardwareID,
  3494. CompatibleID,
  3495. ModifiedDescription ? ModifiedDescription : DeviceDesc,
  3496. Class,
  3497. FriendlyClass,
  3498. Mfg
  3499. ));
  3500. //
  3501. // Add the message via message manager
  3502. //
  3503. Array[0] = ModifiedDescription ? ModifiedDescription : DeviceDesc;
  3504. Array[1] = S_EMPTY; // formerly Enumerator Text
  3505. Array[2] = Class;
  3506. Array[3] = Mfg;
  3507. Array[4] = HardwareID;
  3508. Array[5] = FriendlyClass;
  3509. ClassAndName = JoinPaths (Array[5], Array[0]);
  3510. Group = BuildMessageGroup (
  3511. MSG_INCOMPATIBLE_HARDWARE_ROOT,
  3512. SubGroup,
  3513. ClassAndName
  3514. );
  3515. MYASSERT (Group);
  3516. FreePathString (ClassAndName);
  3517. Message = ParseMessageID (MSG_HARDWARE_MESSAGE, Array);
  3518. MYASSERT (Message);
  3519. MsgMgr_ObjectMsg_Add (e->FullKey, Group, Message);
  3520. LOG ((
  3521. LOG_INFORMATION,
  3522. "%s Device:\n"
  3523. " %s (%s)\n"
  3524. " %s\n"
  3525. " %s\n"
  3526. " %s\n",
  3527. SubGroupText,
  3528. HardwareID,
  3529. CompatibleID,
  3530. ModifiedDescription ? ModifiedDescription : DeviceDesc,
  3531. Class,
  3532. Mfg
  3533. ));
  3534. b = TRUE;
  3535. }
  3536. __finally {
  3537. //
  3538. // Cleanup
  3539. //
  3540. FreeStringResource (ModifiedDescription);
  3541. FreeText (Group);
  3542. FreeStringResource (Message);
  3543. if (UnknownClass) {
  3544. FreeStringResource (Class);
  3545. }
  3546. }
  3547. return b;
  3548. }
  3549. LONG
  3550. HwComp_PrepareReport (
  3551. VOID
  3552. )
  3553. /*++
  3554. Routine Description:
  3555. HwComp_PrepareReport is called after the progress bar on the
  3556. Win9x side of the upgrade. It enumerates the hardware and adds
  3557. incompatibility messages for all incompatible hardware.
  3558. Arguments:
  3559. None.
  3560. Return Value:
  3561. A Win32 status code.
  3562. --*/
  3563. {
  3564. LONG rc;
  3565. HARDWARE_ENUM e;
  3566. HKEY Key;
  3567. HWTYPES msgType;
  3568. INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
  3569. PTSTR pnpIdList;
  3570. PTSTR p, q;
  3571. TCHAR ch;
  3572. Key = OpenRegKeyStr (TEXT("HKLM\\System\\CurrentControlSet\\Services\\Class"));
  3573. __try {
  3574. if (!CreateNtHardwareList (
  3575. SOURCEDIRECTORYARRAY(),
  3576. SOURCEDIRECTORYCOUNT(),
  3577. NULL,
  3578. REGULAR_OUTPUT
  3579. )) {
  3580. rc = GetLastError();
  3581. if (rc != ERROR_CANCELLED) {
  3582. LOG ((LOG_ERROR, "Could not create list of NT hardware."));
  3583. }
  3584. __leave;
  3585. }
  3586. if (EnumFirstHardware (
  3587. &e,
  3588. ENUM_INCOMPATIBLE_DEVICES,
  3589. ENUM_WANT_ONLINE_FLAG|ENUM_DONT_WANT_USER_SUPPLIED
  3590. )) {
  3591. do {
  3592. msgType = HW_INCOMPATIBLE;
  3593. if (e.HardwareID) {
  3594. pnpIdList = DuplicateText (e.HardwareID);
  3595. p = pnpIdList;
  3596. do {
  3597. q = _tcschr (p, TEXT(','));
  3598. if (!q) {
  3599. q = GetEndOfString (p);
  3600. }
  3601. ch = *q;
  3602. *q = 0;
  3603. if (InfFindFirstLine (g_Win95UpgInf, S_REINSTALL_PNP_IDS, p, &is)) {
  3604. msgType = HW_REINSTALL;
  3605. DEBUGMSG ((DBG_HWCOMP, "Found reinstall hardware ID %s", p));
  3606. break;
  3607. }
  3608. *q = ch;
  3609. p = q + 1;
  3610. } while (ch);
  3611. FreeText (pnpIdList);
  3612. }
  3613. if (msgType == HW_INCOMPATIBLE && e.CompatibleIDs) {
  3614. pnpIdList = DuplicateText (e.CompatibleIDs);
  3615. p = pnpIdList;
  3616. do {
  3617. q = _tcschr (p, TEXT(','));
  3618. if (!q) {
  3619. q = GetEndOfString (p);
  3620. }
  3621. ch = *q;
  3622. *q = 0;
  3623. if (InfFindFirstLine (g_Win95UpgInf, S_REINSTALL_PNP_IDS, p, &is)) {
  3624. msgType = HW_REINSTALL;
  3625. DEBUGMSG ((DBG_HWCOMP, "Found reinstall compatible ID %s", p));
  3626. break;
  3627. }
  3628. *q = ch;
  3629. p = q + 1;
  3630. } while (ch);
  3631. FreeText (pnpIdList);
  3632. }
  3633. if (pStuffDeviceInReport (&e, Key, msgType)) {
  3634. DEBUGMSG ((DBG_HWCOMP, "Found incompatible hardware %s", e.DeviceDesc));
  3635. }
  3636. } while (EnumNextHardware (&e));
  3637. }
  3638. if (EnumFirstHardware (
  3639. &e,
  3640. ENUM_UNSUPPORTED_DEVICES,
  3641. ENUM_WANT_ONLINE_FLAG|ENUM_DONT_WANT_USER_SUPPLIED
  3642. )) {
  3643. do {
  3644. if (pStuffDeviceInReport (&e, Key, HW_UNSUPPORTED)) {
  3645. DEBUGMSG ((DBG_HWCOMP, "Found incompatible hardware %s", e.DeviceDesc));
  3646. }
  3647. } while (EnumNextHardware (&e));
  3648. }
  3649. rc = ERROR_SUCCESS;
  3650. }
  3651. __finally {
  3652. if (Key) {
  3653. CloseRegKey (Key);
  3654. }
  3655. InfCleanUpInfStruct (&is);
  3656. }
  3657. return rc;
  3658. }
  3659. BUSTYPE
  3660. pGetBusType (
  3661. IN PHARDWARE_ENUM EnumPtr
  3662. )
  3663. /*++
  3664. Routine Description:
  3665. pGetBusType returns the type of bus that the current hardware
  3666. devices belongs to.
  3667. Arguments:
  3668. EnumPtr - Specifies hardware device to process as returned from
  3669. the hardware enum functions.
  3670. Return Value:
  3671. The enumerated bus type.
  3672. --*/
  3673. {
  3674. PCTSTR p, q;
  3675. TCHAR Bus[MAX_HARDWARE_STRING];
  3676. if (EnumPtr->BusType) {
  3677. StringCopy (Bus, EnumPtr->BusType);
  3678. } else {
  3679. p = EnumPtr->FullKey;
  3680. p = _tcschr (p, TEXT('\\'));
  3681. if (p) {
  3682. p = _tcschr (_tcsinc (p), TEXT('\\'));
  3683. }
  3684. if (!p) {
  3685. return BUSTYPE_UNKNOWN;
  3686. }
  3687. p++;
  3688. q = _tcschr (p, TEXT('\\'));
  3689. if (!q) {
  3690. q = GetEndOfString (p);
  3691. }
  3692. StringCopyAB (Bus, p, q);
  3693. }
  3694. if (StringIMatch (Bus, S_ISA)) {
  3695. return BUSTYPE_ISA;
  3696. }
  3697. if (StringIMatch (Bus, S_EISA)) {
  3698. return BUSTYPE_EISA;
  3699. }
  3700. if (StringIMatch (Bus, S_MCA)) {
  3701. return BUSTYPE_MCA;
  3702. }
  3703. if (StringIMatch (Bus, S_PCI)) {
  3704. return BUSTYPE_PCI;
  3705. }
  3706. if (StringIMatch (Bus, S_PNPISA)) {
  3707. return BUSTYPE_PNPISA;
  3708. }
  3709. if (StringIMatch (Bus, S_PCMCIA)) {
  3710. return BUSTYPE_PCMCIA;
  3711. }
  3712. if (StringIMatch (Bus, S_ROOT)) {
  3713. return BUSTYPE_ROOT;
  3714. }
  3715. if (ISPC98()) {
  3716. if (StringIMatch (Bus, S_C98PNP)) {
  3717. return BUSTYPE_PNPISA;
  3718. }
  3719. }
  3720. return BUSTYPE_UNKNOWN;
  3721. }
  3722. VOID
  3723. pGetIoAddrs (
  3724. IN PHARDWARE_ENUM EnumPtr,
  3725. OUT PTSTR Buf
  3726. )
  3727. /*++
  3728. Routine Description:
  3729. pGetIoAddrs returns a list of comma-separated IO address ranges.
  3730. For example:
  3731. 0x310-0x31F,0x388-0x38F
  3732. Arguments:
  3733. EnumPtr - Specifies device to process
  3734. Buf - Receives zero or more comma-separated address ranges.
  3735. Return Value:
  3736. none
  3737. --*/
  3738. {
  3739. DEVNODERESOURCE_ENUM e;
  3740. PIO_RESOURCE_9X IoRes;
  3741. PTSTR p;
  3742. *Buf = 0;
  3743. p = Buf;
  3744. if (EnumFirstDevNodeResource (&e, EnumPtr->FullKey)) {
  3745. do {
  3746. if (e.Type == ResType_IO) {
  3747. if (p > Buf) {
  3748. p = _tcsappend (p, TEXT(","));
  3749. }
  3750. IoRes = (PIO_RESOURCE_9X) e.ResourceData;
  3751. wsprintf (
  3752. p,
  3753. TEXT("0x%X-0x%X"),
  3754. IoRes->IO_Header.IOD_Alloc_Base,
  3755. IoRes->IO_Header.IOD_Alloc_End
  3756. );
  3757. p = GetEndOfString (p);
  3758. }
  3759. } while (EnumNextDevNodeResource (&e));
  3760. }
  3761. }
  3762. VOID
  3763. pGetIrqs (
  3764. IN PHARDWARE_ENUM EnumPtr,
  3765. OUT PTSTR Buf
  3766. )
  3767. /*++
  3768. Routine Description:
  3769. pGetIrqs returns a list of comma-separated IRQs used by the device.
  3770. For example:
  3771. 0x07,0x0F
  3772. Arguments:
  3773. EnumPtr - Specifies the device to process
  3774. Buf - Receives zero or more comma-separated IRQs
  3775. Return Value:
  3776. none
  3777. --*/
  3778. {
  3779. DEVNODERESOURCE_ENUM e;
  3780. PIRQ_RESOURCE_9X IrqRes;
  3781. PTSTR p;
  3782. *Buf = 0;
  3783. p = Buf;
  3784. if (EnumFirstDevNodeResource (&e, EnumPtr->FullKey)) {
  3785. do {
  3786. if (e.Type == ResType_IRQ) {
  3787. if (p > Buf) {
  3788. p = _tcsappend (p, TEXT(","));
  3789. }
  3790. IrqRes = (PIRQ_RESOURCE_9X) e.ResourceData;
  3791. wsprintf (
  3792. p,
  3793. TEXT("0x%02X"),
  3794. IrqRes->AllocNum
  3795. );
  3796. p = GetEndOfString (p);
  3797. }
  3798. } while (EnumNextDevNodeResource (&e));
  3799. }
  3800. }
  3801. VOID
  3802. pGetDma (
  3803. IN PHARDWARE_ENUM EnumPtr,
  3804. OUT PTSTR Buf
  3805. )
  3806. /*++
  3807. Routine Description:
  3808. pGetDma returns a list of comma-separated DMA channels used
  3809. by the device. For example:
  3810. 1,4
  3811. An empty string means "auto"
  3812. Arguments:
  3813. EnumPtr - Specifies hardware device to process
  3814. Buf - Receives zero or more comma-separated DMA channel numbers
  3815. Return Value:
  3816. none
  3817. --*/
  3818. {
  3819. DEVNODERESOURCE_ENUM e;
  3820. PDMA_RESOURCE_9X DmaRes;
  3821. DWORD Bit, Channel;
  3822. PTSTR p;
  3823. *Buf = 0;
  3824. p = Buf;
  3825. if (EnumFirstDevNodeResource (&e, EnumPtr->FullKey)) {
  3826. do {
  3827. if (e.Type == ResType_DMA) {
  3828. if (p > Buf) {
  3829. p = _tcsappend (p, TEXT(","));
  3830. }
  3831. DmaRes = (PDMA_RESOURCE_9X) e.ResourceData;
  3832. Channel = 0;
  3833. for (Bit = 1 ; Bit ; Bit <<= 1) {
  3834. if (DmaRes->DMA_Bits & Bit) {
  3835. wsprintf (p, TEXT("%u"), Channel);
  3836. p = GetEndOfString (p);
  3837. }
  3838. Channel++;
  3839. }
  3840. p = GetEndOfString (p);
  3841. }
  3842. } while (EnumNextDevNodeResource (&e));
  3843. }
  3844. }
  3845. VOID
  3846. pGetMemRanges (
  3847. IN PHARDWARE_ENUM EnumPtr,
  3848. OUT PTSTR Buf
  3849. )
  3850. /*++
  3851. Routine Description:
  3852. pGetMemRanges returns a list of comma-separated memory addresses
  3853. used by the device. For example:
  3854. 0x0000D800-0x0000D9FF,0x0000F000-0x0000FFFF
  3855. Arguments:
  3856. EnumPtr - Specifies hardware to process
  3857. Buf - Receives zero or more comma-separated address ranges
  3858. Return Value:
  3859. none
  3860. --*/
  3861. {
  3862. DEVNODERESOURCE_ENUM e;
  3863. PMEM_RESOURCE_9X MemRes;
  3864. PTSTR p;
  3865. *Buf = 0;
  3866. p = Buf;
  3867. if (EnumFirstDevNodeResource (&e, EnumPtr->FullKey)) {
  3868. do {
  3869. if (e.Type == ResType_Mem) {
  3870. if (p > Buf) {
  3871. p = _tcsappend (p, TEXT(","));
  3872. }
  3873. MemRes = (PMEM_RESOURCE_9X) e.ResourceData;
  3874. wsprintf (
  3875. p,
  3876. TEXT("0x%08X-0x%08X"),
  3877. MemRes->MEM_Header.MD_Alloc_Base,
  3878. MemRes->MEM_Header.MD_Alloc_End
  3879. );
  3880. p = GetEndOfString (p);
  3881. }
  3882. } while (EnumNextDevNodeResource (&e));
  3883. }
  3884. }
  3885. TRANSCIEVERTYPE
  3886. pGetTranscieverType (
  3887. IN PHARDWARE_ENUM EnumPtr
  3888. )
  3889. /*++
  3890. Routine Description:
  3891. pGetTranscieverType returns the transciever type for the specified
  3892. device (i.e. net card).
  3893. Arguments:
  3894. EnumPtr - Specifies hardware device to process
  3895. Return Value:
  3896. The devices's transciever type
  3897. --*/
  3898. {
  3899. return TRANSCIEVERTYPE_AUTO;
  3900. }
  3901. IOCHANNELREADY
  3902. pGetIoChannelReady (
  3903. IN PHARDWARE_ENUM EnumPtr
  3904. )
  3905. /*++
  3906. Routine Description:
  3907. pGetIoChannelReady returns the setting of the IoChannelReady
  3908. mode for a device.
  3909. Arguments:
  3910. EnumPtr - Specifies hardware device to process
  3911. Return Value:
  3912. The device's IO Channel Ready mode
  3913. --*/
  3914. {
  3915. return IOCHANNELREADY_AUTODETECT;
  3916. }
  3917. /*++
  3918. Routine Description:
  3919. EnumFirstNetCard/EnumNextNetCard enumerate all installed network adapters
  3920. on a machine.
  3921. Arguments:
  3922. EnumPtr - An uninitiailzed structure that receives the state of enumeration.
  3923. Return Value:
  3924. TRUE if a net card was enumerated, or FALSE if no more net cards exist.
  3925. --*/
  3926. BOOL
  3927. EnumFirstNetCard (
  3928. OUT PNETCARD_ENUM EnumPtr
  3929. )
  3930. {
  3931. START_NET_ENUM;
  3932. ZeroMemory (EnumPtr, sizeof (NETCARD_ENUM));
  3933. EnumPtr->State = STATE_ENUM_FIRST_HARDWARE;
  3934. return EnumNextNetCard (EnumPtr);
  3935. }
  3936. BOOL
  3937. EnumNextNetCard (
  3938. IN OUT PNETCARD_ENUM EnumPtr
  3939. )
  3940. {
  3941. PHARDWARE_ENUM ep;
  3942. for (;;) {
  3943. switch (EnumPtr->State) {
  3944. case STATE_ENUM_FIRST_HARDWARE:
  3945. if (!EnumFirstHardware (&EnumPtr->HardwareEnum, ENUM_ALL_DEVICES,ENUM_DONT_REQUIRE_HARDWAREID)) {
  3946. END_NET_ENUM;
  3947. return FALSE;
  3948. }
  3949. EnumPtr->State = STATE_EVALUATE_HARDWARE;
  3950. break;
  3951. case STATE_ENUM_NEXT_HARDWARE:
  3952. if (!EnumNextHardware (&EnumPtr->HardwareEnum)) {
  3953. END_NET_ENUM;
  3954. return FALSE;
  3955. }
  3956. EnumPtr->State = STATE_EVALUATE_HARDWARE;
  3957. break;
  3958. case STATE_EVALUATE_HARDWARE:
  3959. ep = &EnumPtr->HardwareEnum;
  3960. EnumPtr->State = STATE_ENUM_NEXT_HARDWARE;
  3961. if (!StringIMatch (ep->Class, TEXT("Net"))) {
  3962. break;
  3963. }
  3964. if (ep -> HardwareID) {
  3965. _tcssafecpy (EnumPtr->HardwareId, ep->HardwareID, MAX_HARDWARE_STRING);
  3966. }
  3967. if (ep -> CompatibleIDs) {
  3968. _tcssafecpy (EnumPtr -> CompatibleIDs, ep -> CompatibleIDs, MAX_HARDWARE_STRING);
  3969. }
  3970. if (ep->DeviceDesc) {
  3971. _tcssafecpy (EnumPtr->Description, ep->DeviceDesc, MAX_HARDWARE_STRING);
  3972. } else {
  3973. EnumPtr->Description[0] = 0;
  3974. }
  3975. _tcssafecpy (EnumPtr->CurrentKey, ep->ek.FullKeyName, MAX_HARDWARE_STRING);
  3976. EnumPtr->BusType = pGetBusType (ep);
  3977. EnumPtr->TranscieverType = pGetTranscieverType (ep);
  3978. EnumPtr->IoChannelReady = pGetIoChannelReady (ep);
  3979. pGetIoAddrs (ep, EnumPtr->IoAddrs);
  3980. pGetIrqs (ep, EnumPtr->Irqs);
  3981. pGetDma (ep, EnumPtr->Dma);
  3982. pGetMemRanges (ep, EnumPtr->MemRanges);
  3983. return TRUE;
  3984. }
  3985. }
  3986. }
  3987. VOID
  3988. EnumNetCardAbort (
  3989. IN PNETCARD_ENUM EnumPtr
  3990. )
  3991. {
  3992. PushError();
  3993. END_NET_ENUM;
  3994. AbortHardwareEnum (&EnumPtr->HardwareEnum);
  3995. PopError();
  3996. }
  3997. VOID
  3998. EncodePnpId (
  3999. IN OUT LPSTR Id
  4000. )
  4001. /*++
  4002. Routine Description:
  4003. This routine is used for the pnprept tool to encode a PNP ID so it does not
  4004. have any backslashes.
  4005. Arguments:
  4006. Id - Specifies ID that may have backslashes. The buffer must be big enough
  4007. to hold strlen(Id) * 2 characters.
  4008. Return Value:
  4009. none
  4010. --*/
  4011. {
  4012. CHAR TempBuf[MAX_MBCHAR_PATH];
  4013. LPSTR s, d;
  4014. s = Id;
  4015. d = TempBuf;
  4016. while (*s) {
  4017. if (*s == '\\') {
  4018. *d = '~';
  4019. d++;
  4020. *d = '1';
  4021. } else if (*s == '*') {
  4022. *d = '~';
  4023. d++;
  4024. *d = '2';
  4025. } else if (*s == '~') {
  4026. *d = '~';
  4027. d++;
  4028. *d = '~';
  4029. }
  4030. else {
  4031. *d = *s;
  4032. }
  4033. d++;
  4034. s++;
  4035. }
  4036. *d = 0;
  4037. lstrcpy (Id, TempBuf);
  4038. }
  4039. VOID
  4040. DecodePnpId (
  4041. IN OUT LPSTR Id
  4042. )
  4043. /*++
  4044. Routine Description:
  4045. This routine is used by pnprept to decode a PNP ID that was encoded by
  4046. EncodePnpId.
  4047. Arguments:
  4048. Id - Specifies the encoded string to process. The string is truncated if
  4049. any encoded characters are found.
  4050. Return Value:
  4051. none
  4052. --*/
  4053. {
  4054. LPSTR s, d;
  4055. s = Id;
  4056. d = Id;
  4057. while (*s) {
  4058. if (*s == '~') {
  4059. s++;
  4060. if (*s == '1') {
  4061. *d = '\\';
  4062. } else if (*s == '2') {
  4063. *d = '*';
  4064. } else {
  4065. *d = *s;
  4066. }
  4067. } else if (d < s) {
  4068. *d = *s;
  4069. }
  4070. d++;
  4071. s++;
  4072. }
  4073. *d = 0;
  4074. }
  4075. BOOL
  4076. pFindFileInAnySourceDir (
  4077. IN PCTSTR File,
  4078. OUT PTSTR MatchPath
  4079. )
  4080. {
  4081. UINT Count;
  4082. UINT i;
  4083. PCTSTR Path;
  4084. Count = SOURCEDIRECTORYCOUNT();
  4085. for (i = 0 ; i < Count ; i++) {
  4086. Path = JoinPaths (SOURCEDIRECTORY(i), File);
  4087. __try {
  4088. if (DoesFileExist (Path)) {
  4089. _tcssafecpy (MatchPath, Path, MAX_TCHAR_PATH);
  4090. return TRUE;
  4091. }
  4092. }
  4093. __finally {
  4094. FreePathString (Path);
  4095. }
  4096. }
  4097. return FALSE;
  4098. }
  4099. BOOL
  4100. GetLegacyKeyboardId (
  4101. OUT PTSTR Buffer,
  4102. IN UINT BufferSize
  4103. )
  4104. /*++
  4105. Routine Description:
  4106. GetLegacyKeyboardId looks in NT's keyboard.inf for a legacy mapping.
  4107. If one is found, the legacy ID is returned to the caller so they
  4108. can write it to the answer file.
  4109. Arguments:
  4110. Buffer - Receives the legacy text corresponding to the installed
  4111. keyboard device.
  4112. BufferSize - Specifies the size of Buffer in bytes
  4113. Return Value:
  4114. TRUE if the legacy ID was found and copied, or FALSE if an error
  4115. occurred. If FALSE is returned, the caller should add an incompatibility
  4116. message and let NT decide which keyboard to use.
  4117. --*/
  4118. {
  4119. HINF Inf;
  4120. TCHAR KeyboardInfPath[MAX_TCHAR_PATH];
  4121. TCHAR TempDir[MAX_TCHAR_PATH];
  4122. PCTSTR TempKeyboardInfPath;
  4123. INFSTRUCT is = INITINFSTRUCT_POOLHANDLE;
  4124. PCTSTR PnpId, LegacyId;
  4125. LONG rc;
  4126. UINT keyboardType;
  4127. UINT keyboardSubtype;
  4128. PTSTR id = NULL;
  4129. HARDWARE_ENUM e;
  4130. TCHAR curId[MAX_PNP_ID];
  4131. PCTSTR devIds = NULL;
  4132. keyboardType = GetKeyboardType (0);
  4133. keyboardSubtype = GetKeyboardType (1);
  4134. //
  4135. // keyboard type : 7 - Japanese keyboard
  4136. //
  4137. if (keyboardType == 7) {
  4138. //
  4139. // sub type : 2 - 106 keyboard
  4140. //
  4141. if (keyboardSubtype == 2) {
  4142. id = TEXT("PCAT_106KEY");
  4143. //
  4144. // Ok, we have a japanese keyboard. Still, it may be USB which
  4145. // requires a different entry. Lets check.
  4146. //
  4147. if (EnumFirstHardware (&e, ENUM_COMPATIBLE_DEVICES, ENUM_WANT_DEV_FIELDS)) {
  4148. do {
  4149. devIds = e.HardwareID;
  4150. while (*devIds) {
  4151. devIds = ExtractPnpId (devIds, curId);
  4152. if (*curId == 0) {
  4153. continue;
  4154. }
  4155. if (InfFindFirstLine (g_Win95UpgInf, S_JPN_USB_KEYBOARDS, curId, &is)) {
  4156. id = TEXT("kbdhid");
  4157. AbortHardwareEnum (&e);
  4158. break;
  4159. }
  4160. }
  4161. } while (EnumNextHardware (&e) && !StringIMatch (id, TEXT("kbdhid")));
  4162. }
  4163. _tcssafecpy (Buffer, id, BufferSize / sizeof (TCHAR));
  4164. return TRUE;
  4165. }
  4166. }
  4167. //
  4168. // keyboard type : 8 - Korean keyboard
  4169. //
  4170. if (keyboardType == 8) {
  4171. switch (keyboardSubtype) {
  4172. case 3:
  4173. //
  4174. // 101a keyboard
  4175. //
  4176. id = TEXT("STANDARD");
  4177. break;
  4178. case 4:
  4179. //
  4180. // 101b keyboard
  4181. //
  4182. id = TEXT("101B TYPE");
  4183. break;
  4184. case 5:
  4185. //
  4186. // 101c keyboard
  4187. //
  4188. id = TEXT("101C TYPE");
  4189. break;
  4190. case 6:
  4191. //
  4192. // 103 keyboard
  4193. //
  4194. id = TEXT("103 TYPE");
  4195. break;
  4196. }
  4197. if (id) {
  4198. _tcssafecpy (Buffer, id, BufferSize);
  4199. return TRUE;
  4200. }
  4201. }
  4202. //
  4203. // Move keyboard.in_ (or keyboard.inf) from NT sources into
  4204. // temp dir.
  4205. //
  4206. if (!pFindFileInAnySourceDir (S_KEYBOARD_IN_, KeyboardInfPath)) {
  4207. if (!pFindFileInAnySourceDir (S_KEYBOARD_INF, KeyboardInfPath)) {
  4208. LOG ((LOG_ERROR,"GetLegacyKeyboardId: keyboard.inf not found in sources"));
  4209. return FALSE;
  4210. }
  4211. }
  4212. GetTempPath (sizeof (TempDir), TempDir);
  4213. TempKeyboardInfPath = JoinPaths (TempDir, S_KEYBOARD_INF);
  4214. __try {
  4215. rc = SetupDecompressOrCopyFile (
  4216. KeyboardInfPath,
  4217. TempKeyboardInfPath,
  4218. 0
  4219. );
  4220. if (rc != ERROR_SUCCESS) {
  4221. LOG ((LOG_ERROR,"GetLegacyKeyboardId: keyboard.inf could not be copied to temp dir"));
  4222. return FALSE;
  4223. }
  4224. __try {
  4225. Inf = InfOpenInfFile (TempKeyboardInfPath);
  4226. if (Inf == INVALID_HANDLE_VALUE) {
  4227. LOG ((LOG_ERROR,"GetLegacyKeyboardId: %s could not be opened",TempKeyboardInfPath));
  4228. return FALSE;
  4229. }
  4230. __try {
  4231. //
  4232. // We now have keyboard.inf open. Let's enumerate each PNP
  4233. // ID in the [LegacyXlate.DevId] section and check if the
  4234. // hardware is available.
  4235. //
  4236. if (InfFindFirstLine (Inf, S_WIN9XUPGRADE, NULL, &is)) {
  4237. do {
  4238. PnpId = InfGetStringField (&is, 0);
  4239. if (PnpId) {
  4240. //
  4241. // Is PnpId online?
  4242. //
  4243. DEBUGMSG ((DBG_HWCOMP, "Searching for keyboard ID %s", PnpId));
  4244. if (IsPnpIdOnline (PnpId, S_KEYBOARD_CLASS)) {
  4245. //
  4246. // Yes - obtain PNP, copy to caller's buffer
  4247. // and get out
  4248. //
  4249. LegacyId = InfGetStringField (&is, 1);
  4250. if (LegacyId) {
  4251. DEBUGMSG ((DBG_HWCOMP, "Found match: %s = %s", LegacyId, PnpId));
  4252. _tcssafecpy (Buffer, LegacyId, BufferSize / sizeof (TCHAR));
  4253. return TRUE;
  4254. }
  4255. }
  4256. }
  4257. InfResetInfStruct (&is);
  4258. } while (InfFindNextLine (&is));
  4259. }
  4260. }
  4261. __finally {
  4262. InfCleanUpInfStruct (&is);
  4263. InfCloseInfFile (Inf);
  4264. }
  4265. }
  4266. __finally {
  4267. DeleteFile (TempKeyboardInfPath);
  4268. }
  4269. }
  4270. __finally {
  4271. FreePathString (TempKeyboardInfPath);
  4272. }
  4273. return FALSE;
  4274. }
  4275. BOOL
  4276. IsComputerOffline (
  4277. VOID
  4278. )
  4279. /*++
  4280. Routine Description:
  4281. IsComputerOffline checks if a network card exists on the machine and is
  4282. currently present.
  4283. Arguments:
  4284. None.
  4285. Return Value:
  4286. TRUE if the machine is known to be offline, or FALSE if the online state is
  4287. not known.
  4288. --*/
  4289. {
  4290. HARDWARE_ENUM e;
  4291. BOOL possiblyOnline = FALSE;
  4292. if (EnumFirstHardware (&e, ENUM_ALL_DEVICES, ENUM_WANT_ONLINE_FLAG|ENUM_WANT_COMPATIBLE_FLAG)) {
  4293. do {
  4294. //
  4295. // Enumerate all PNP devices of class Net
  4296. // or Modem
  4297. //
  4298. if (e.Class) {
  4299. if (StringIMatch (e.Class, TEXT("net")) ||
  4300. StringIMatch (e.Class, TEXT("modem"))
  4301. ) {
  4302. //
  4303. // Determine if this is not a RAS adapter
  4304. //
  4305. if (e.CompatibleIDs && !_tcsistr (e.CompatibleIDs, TEXT("*PNP8387"))) {
  4306. possiblyOnline = TRUE;
  4307. }
  4308. if (e.HardwareID && !_tcsistr (e.HardwareID, TEXT("*PNP8387"))) {
  4309. possiblyOnline = TRUE;
  4310. }
  4311. //
  4312. // If this is not a RAS adapter, we assume it may be a LAN
  4313. // adapter. If the LAN adapter is present, then we assume
  4314. // that it might be online.
  4315. //
  4316. if (possiblyOnline) {
  4317. possiblyOnline = e.Online;
  4318. }
  4319. }
  4320. }
  4321. //
  4322. // Other tests for online go here
  4323. //
  4324. if (possiblyOnline) {
  4325. AbortHardwareEnum (&e);
  4326. return FALSE;
  4327. }
  4328. } while (EnumNextHardware (&e));
  4329. }
  4330. //
  4331. // We have one of the following cases:
  4332. //
  4333. // - No device with the class of "Net"
  4334. // - Only the RAS adapter is installed
  4335. // - All "Net" class devices are not present
  4336. //
  4337. // That makes us say "this computer is offline"
  4338. //
  4339. return TRUE;
  4340. }
  4341. BOOL
  4342. HwComp_AnyNeededDrivers (
  4343. VOID
  4344. )
  4345. {
  4346. return !HtIsEmpty (g_NeededHardwareIds);
  4347. }
  4348. BOOL
  4349. AppendDynamicSuppliedDrivers (
  4350. IN PCTSTR DriversPath
  4351. )
  4352. /*++
  4353. Routine Description:
  4354. AppendDynamicSuppliedDrivers scans a path and its subdirs for new drivers and places
  4355. all hardware device IDs in the global PNP string tables.
  4356. Arguments:
  4357. DriversPath - The root path to new drivers
  4358. Return Value:
  4359. TRUE if the function completes successfully, or FALSE if it fails for at least one driver.
  4360. --*/
  4361. {
  4362. TREE_ENUM te;
  4363. DWORD HwCompDatId;
  4364. BOOL b = TRUE;
  4365. if (EnumFirstFileInTree (&te, DriversPath, TEXT("hwcomp.dat"), TRUE)) {
  4366. do {
  4367. if (te.Directory) {
  4368. continue;
  4369. }
  4370. //
  4371. // Open the hardware compatibility database
  4372. //
  4373. HwCompDatId = OpenAndLoadHwCompDatEx (
  4374. te.FullPath,
  4375. (PVOID)g_PnpIdTable,
  4376. (PVOID)g_UnsupPnpIdTable,
  4377. (PVOID)g_InfFileTable
  4378. );
  4379. if (HwCompDatId) {
  4380. SetWorkingTables (HwCompDatId, NULL, NULL, NULL);
  4381. CloseHwCompDat (HwCompDatId);
  4382. } else {
  4383. LOG ((
  4384. LOG_WARNING,
  4385. "AppendDynamicSuppliedDrivers: OpenAndLoadHwCompDat failed for %s (rc=0x%x)",
  4386. te.FullPath,
  4387. GetLastError ()
  4388. ));
  4389. //
  4390. // report failure, but keep going
  4391. //
  4392. b = FALSE;
  4393. }
  4394. } while (EnumNextFileInTree (&te));
  4395. }
  4396. return b;
  4397. }