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.

865 lines
22 KiB

  1. // devnode.cpp0
  2. #include "stdafx.h"
  3. #include "devnode.h"
  4. // globals from alcclass
  5. AutoListClass *pALCHead = NULL;
  6. AutoListClass *pALCTail = NULL;
  7. int AutoListClassCount = NULL;
  8. /*******************************************************************
  9. Constructiors
  10. *******************************************************************/
  11. DevnodeClass::DevnodeClass(DEVNODE hDevice, DEVNODE l_hParent)
  12. {
  13. pDeviceID = NULL;
  14. hDevnode = hDevice;
  15. hParent = l_hParent;
  16. pszDescription = NULL;
  17. ulProblemCode = (ULONG) -1;
  18. ulStatus = 0;
  19. pszClass = NULL;
  20. bDNHasMark = (BOOL)-1;
  21. bCanDisable = FALSE;
  22. bCanTest = FALSE;
  23. bDidPass = FALSE;
  24. pHardwareID = NULL;
  25. pCompatID = NULL;
  26. pszGUID = NULL;
  27. pszLocation = NULL;
  28. pszPDO = NULL;
  29. pszFriendlyName = NULL;
  30. pszMFG = NULL;
  31. InterfaceBusType = InterfaceTypeUndefined;
  32. GetDeviceInformation();
  33. }
  34. DevnodeClass::DevnodeClass(void)
  35. {
  36. pDeviceID = NULL;
  37. hDevnode = 0;
  38. hParent = 0;
  39. pszDescription = NULL;
  40. ulProblemCode = (ULONG) -1;
  41. ulStatus = 0;
  42. pszClass = NULL;
  43. bDNHasMark = (BOOL)-1;
  44. bCanDisable = FALSE;
  45. bCanTest = FALSE;
  46. bDidPass = FALSE;
  47. pHardwareID = NULL;
  48. pCompatID = NULL;
  49. pszGUID = NULL;
  50. pszLocation = NULL;
  51. pszPDO = NULL;
  52. pszFriendlyName = NULL;
  53. pszMFG = NULL;
  54. InterfaceBusType = InterfaceTypeUndefined;
  55. }
  56. DevnodeClass::~DevnodeClass()
  57. {
  58. hDevnode = -1;
  59. ulProblemCode = (ULONG) -1;
  60. ulStatus = (ULONG)-1;
  61. bDNHasMark = (BOOL)-1;
  62. InterfaceBusType = InterfaceTypeUndefined;
  63. bCanDisable = FALSE;
  64. bCanTest = FALSE;
  65. bDidPass = FALSE;
  66. if ( pDeviceID )
  67. {
  68. delete pDeviceID;
  69. pDeviceID = NULL;
  70. }
  71. if ( pszClass )
  72. {
  73. delete pszClass;
  74. pszClass = NULL;
  75. }
  76. if ( pszDescription )
  77. {
  78. delete pszDescription;
  79. pszDescription = NULL;
  80. }
  81. if ( pszFriendlyName )
  82. {
  83. delete pszFriendlyName;
  84. pszFriendlyName = NULL;
  85. }
  86. if ( pszGUID )
  87. {
  88. delete pszGUID;
  89. pszGUID = NULL;
  90. }
  91. if ( pszLocation )
  92. {
  93. delete pszLocation;
  94. pszLocation = NULL;
  95. }
  96. if ( pszPDO )
  97. {
  98. delete pszPDO;
  99. pszPDO = NULL;
  100. }
  101. if ( pszMFG )
  102. {
  103. delete pszMFG;
  104. pszMFG = NULL;
  105. }
  106. if ( pHardwareID )
  107. {
  108. delete pHardwareID;
  109. pHardwareID = NULL;
  110. }
  111. if ( pCompatID )
  112. {
  113. delete pCompatID;
  114. pCompatID = NULL;
  115. }
  116. }
  117. /*******************************************************************
  118. Member Functions
  119. *******************************************************************/
  120. BOOL DevnodeClass::SetHandle(DEVNODE Devnode, DEVNODE Parent)
  121. {
  122. hDevnode = -1;
  123. ulProblemCode = (ULONG) -1;
  124. ulStatus = (ULONG)-1;
  125. bDNHasMark = (BOOL)-1;
  126. InterfaceBusType = InterfaceTypeUndefined;
  127. bCanDisable = FALSE;
  128. bCanTest = FALSE;
  129. bDidPass = FALSE;
  130. if ( pDeviceID )
  131. {
  132. delete pDeviceID;
  133. pDeviceID = NULL;
  134. }
  135. if ( pszClass )
  136. {
  137. delete pszClass;
  138. pszClass = NULL;
  139. }
  140. if ( pszDescription )
  141. {
  142. delete pszDescription;
  143. pszDescription = NULL;
  144. }
  145. if ( pszFriendlyName )
  146. {
  147. delete pszFriendlyName;
  148. pszFriendlyName = NULL;
  149. }
  150. if ( pszGUID )
  151. {
  152. delete pszGUID;
  153. pszGUID = NULL;
  154. }
  155. if ( pszLocation )
  156. {
  157. delete pszLocation;
  158. pszLocation = NULL;
  159. }
  160. if ( pszPDO )
  161. {
  162. delete pszPDO;
  163. pszPDO = NULL;
  164. }
  165. if ( pszMFG )
  166. {
  167. delete pszMFG;
  168. pszMFG = NULL;
  169. }
  170. if ( pHardwareID )
  171. {
  172. delete pHardwareID;
  173. pHardwareID = NULL;
  174. }
  175. if ( pCompatID )
  176. {
  177. delete pCompatID;
  178. pCompatID = NULL;
  179. }
  180. hDevnode = Devnode;
  181. hParent = Parent;
  182. return (GetDeviceInformation());
  183. }
  184. /****************************************************************************
  185. GetDeviceInformation
  186. find information about devnode
  187. modifies this, members in DevnodeClass
  188. *****************************************************************************/
  189. CONFIGRET DevnodeClass::GetDeviceInformation (void)
  190. {
  191. CONFIGRET retval;
  192. ULONG ulSize;
  193. // check to see that we have a devnode handle
  194. if ( !hDevnode )
  195. {
  196. return (CR_NO_SUCH_DEVNODE);
  197. }
  198. if ( !hParent )
  199. {
  200. DEVNODE hParentDevnode;
  201. if ( !GetParent(&hParentDevnode) )
  202. {
  203. hParent = hParentDevnode;
  204. }
  205. }
  206. /**********
  207. Get Device ID
  208. ***********/
  209. retval = CM_Get_Device_ID_Size (&ulSize, hDevnode, 0L);
  210. if ( retval ) return(retval);
  211. pDeviceID = new TCHAR [++ulSize]; // add one for terminating NULL
  212. if ( !pDeviceID ) return(CR_OUT_OF_MEMORY);
  213. retval = CM_Get_Device_ID (hDevnode,
  214. pDeviceID,
  215. ulSize, //is null terminated?!?
  216. 0L);
  217. if ( retval ) return(retval);
  218. /**********
  219. Get device description/friendly name
  220. ***********/
  221. ulSize = 0;
  222. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  223. CM_DRP_DEVICEDESC,
  224. NULL,
  225. NULL,
  226. &ulSize,
  227. 0);
  228. if ( retval )
  229. if ( (retval == CR_BUFFER_SMALL) )
  230. {
  231. //if ( bVerbose )
  232. // logprintf ("Still Having trouble with CM_Get_DevNode_Registry_Property returning CR_BUFFER_TOO_SMALL\r\n"
  233. // "When trying to get the size of the Device description\r\n");
  234. ulSize = 511;
  235. }
  236. else
  237. return(retval);
  238. pszDescription = new TCHAR [ulSize+1];
  239. if ( !pszDescription ) return(CR_OUT_OF_MEMORY);
  240. //Now get value
  241. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  242. CM_DRP_DEVICEDESC,
  243. NULL,
  244. pszDescription,
  245. &ulSize,
  246. 0);
  247. if ( retval )
  248. return(retval);
  249. /**********
  250. Get device description/friendly name
  251. ***********/
  252. ulSize = 0;
  253. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  254. CM_DRP_FRIENDLYNAME,
  255. NULL,
  256. NULL,
  257. &ulSize,
  258. 0);
  259. if ( ulSize )
  260. {
  261. pszFriendlyName = new TCHAR [ulSize+1];
  262. if ( !pszFriendlyName ) return(CR_OUT_OF_MEMORY);
  263. //Now get value
  264. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  265. CM_DRP_FRIENDLYNAME,
  266. NULL,
  267. pszFriendlyName,
  268. &ulSize,
  269. 0);
  270. if ( retval )
  271. return(retval);
  272. }
  273. /**********
  274. Get device class
  275. ***********/
  276. ulSize = 0;
  277. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  278. CM_DRP_CLASS,
  279. NULL,
  280. NULL,
  281. &ulSize,
  282. 0);
  283. if ( retval )
  284. {
  285. if ( (retval == CR_BUFFER_SMALL) )
  286. {
  287. //if ( bVerbose )
  288. // logprintf ("Still Having trouble with CM_Get_DevNode_Registry_Property returning CR_BUFFER_TOO_SMALL\r\n"
  289. // "When trying to get the size of the class\r\n");
  290. ulSize = 511;
  291. }
  292. else if ( retval == CR_NO_SUCH_VALUE )
  293. {
  294. //if ( bVerbose )
  295. //{
  296. // logprintf("This device does not have a class associated with it\r\n");
  297. //}
  298. ulSize = 511;
  299. }
  300. else
  301. ulSize = 0;
  302. }
  303. if (ulSize)
  304. {
  305. pszClass = new TCHAR [ulSize+1];
  306. if ( !pszClass )
  307. return(CR_OUT_OF_MEMORY);
  308. //Now get value
  309. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  310. CM_DRP_CLASS,
  311. NULL,
  312. pszClass,
  313. &ulSize,
  314. 0);
  315. if ( retval )
  316. {
  317. if (pszClass)
  318. delete pszClass;
  319. pszClass = NULL;
  320. }
  321. else
  322. _strupr(pszClass);
  323. }
  324. /**********
  325. Get Hardware ID information
  326. ***********/
  327. ulSize = 0;
  328. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  329. CM_DRP_HARDWAREID,
  330. NULL,
  331. NULL,
  332. &ulSize,
  333. 0);
  334. if ( retval && !ulSize )
  335. if ( (retval == CR_BUFFER_SMALL) )
  336. {
  337. //if ( bVerbose )
  338. // logprintf ("Still Having trouble with CM_Get_DevNode_Registry_Property returning CR_BUFFER_TOO_SMALL\r\n"
  339. // "When trying to get the size of the Device description\r\n");
  340. ulSize = 511;
  341. }
  342. else
  343. return(retval);
  344. pHardwareID = new TCHAR [++ulSize+1];
  345. if ( !pHardwareID ) return(CR_OUT_OF_MEMORY);
  346. //Now get value
  347. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  348. CM_DRP_HARDWAREID,
  349. NULL,
  350. pHardwareID,
  351. &ulSize,
  352. 0);
  353. if ( retval )
  354. return(retval);
  355. /**********
  356. Get Compat ID information
  357. ***********/
  358. ulSize = 0;
  359. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  360. CM_DRP_COMPATIBLEIDS,
  361. NULL,
  362. NULL,
  363. &ulSize,
  364. 0);
  365. if ( retval && !ulSize )
  366. if ( (retval == CR_BUFFER_SMALL) )
  367. {
  368. //if ( bVerbose )
  369. // logprintf ("Still Having trouble with CM_Get_DevNode_Registry_Property returning CR_BUFFER_TOO_SMALL\r\n"
  370. // "When trying to get the size of the Device description\r\n");
  371. ulSize = 511;
  372. }
  373. else
  374. ulSize = 0;
  375. if (ulSize)
  376. {
  377. pCompatID = new TCHAR [++ulSize+1];
  378. if ( !pCompatID ) return(CR_OUT_OF_MEMORY);
  379. //Now get value
  380. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  381. CM_DRP_COMPATIBLEIDS,
  382. NULL,
  383. pCompatID,
  384. &ulSize,
  385. 0);
  386. if ( retval )
  387. return(retval);
  388. }
  389. /**********
  390. Get ClassGUID
  391. ***********/
  392. ulSize = 0;
  393. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  394. CM_DRP_CLASSGUID,
  395. NULL,
  396. NULL,
  397. &ulSize,
  398. 0);
  399. if ( ulSize )
  400. {
  401. pszGUID = new TCHAR [ulSize+1];
  402. if ( !pszGUID ) return(CR_OUT_OF_MEMORY);
  403. //Now get value
  404. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  405. CM_DRP_CLASSGUID,
  406. NULL,
  407. pszGUID,
  408. &ulSize,
  409. 0);
  410. }
  411. /**********
  412. Get PDO Name
  413. ***********/
  414. ulSize = 0;
  415. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  416. CM_DRP_PHYSICAL_DEVICE_OBJECT_NAME,
  417. NULL,
  418. NULL,
  419. &ulSize,
  420. 0);
  421. if ( ulSize )
  422. {
  423. pszPDO = new TCHAR [ulSize+1];
  424. if ( !pszPDO ) return(CR_OUT_OF_MEMORY);
  425. //Now get value
  426. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  427. CM_DRP_PHYSICAL_DEVICE_OBJECT_NAME,
  428. NULL,
  429. pszPDO,
  430. &ulSize,
  431. 0);
  432. }
  433. /**********
  434. Get MFG Name
  435. ***********/
  436. ulSize = 0;
  437. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  438. CM_DRP_MFG,
  439. NULL,
  440. NULL,
  441. &ulSize,
  442. 0);
  443. if ( ulSize )
  444. {
  445. pszMFG = new TCHAR [ulSize+1];
  446. if ( !pszMFG ) return(CR_OUT_OF_MEMORY);
  447. //Now get value
  448. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  449. CM_DRP_MFG,
  450. NULL,
  451. pszMFG,
  452. &ulSize,
  453. 0);
  454. }
  455. /**********
  456. Get LocationInformation
  457. ***********/
  458. ulSize = 0;
  459. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  460. CM_DRP_LOCATION_INFORMATION,
  461. NULL,
  462. NULL,
  463. &ulSize,
  464. 0);
  465. if ( ulSize )
  466. {
  467. pszLocation = new TCHAR [ulSize+1];
  468. if ( !pszLocation ) return(CR_OUT_OF_MEMORY);
  469. //Now get value
  470. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  471. CM_DRP_LOCATION_INFORMATION,
  472. NULL,
  473. pszLocation,
  474. &ulSize,
  475. 0);
  476. }
  477. /**********
  478. Get interface/bus type
  479. ***********/
  480. //Now get value
  481. ulSize = sizeof(INTERFACE_TYPE);
  482. retval = CM_Get_DevNode_Registry_Property (hDevnode,
  483. CM_DRP_LEGACYBUSTYPE,
  484. NULL,
  485. &InterfaceBusType,
  486. &ulSize,
  487. 0);
  488. // if (!retval)
  489. // {
  490. // InterfaceBusType= InterfaceTypeUndefined;
  491. // }
  492. /**********
  493. Get Problem and status code
  494. ***********/
  495. retval = CM_Get_DevNode_Status (&ulStatus,
  496. &ulProblemCode,
  497. hDevnode,
  498. 0L);
  499. if ( retval ) return(retval);
  500. /**********
  501. set bCanDisable
  502. ***********/
  503. // If we get here, let's assume that the device is testable, and filter from there
  504. bCanDisable = TRUE;
  505. bCanTest = TRUE;
  506. return(CR_SUCCESS);
  507. }
  508. /*****************************************************************************
  509. GetXxxx fuctions
  510. *****************************************************************************/
  511. CONFIGRET DevnodeClass::GetChild(DEVNODE *pChildDevnode)
  512. {
  513. return (CM_Get_Child(pChildDevnode, hDevnode, 0l));
  514. }
  515. CONFIGRET DevnodeClass::GetParent(DEVNODE *pParentDevnode)
  516. {
  517. return (CM_Get_Parent(pParentDevnode, hDevnode, 0l));
  518. }
  519. CONFIGRET DevnodeClass::GetSibling(DEVNODE *pSiblingDevnode)
  520. {
  521. return (CM_Get_Sibling(pSiblingDevnode, hDevnode, 0l));
  522. }
  523. /*****************************************************************************
  524. Disabler Funcitons
  525. *****************************************************************************/
  526. /*
  527. CONFIGRET DevnodeClass::Remove(ULONG uFlags)
  528. {
  529. //return (CM_Query_And_Remove_SubTree(hDevnode, NULL, NULL, 0, uFlags));
  530. return (CM_Remove_SubTree(hDevnode, uFlags));
  531. } */
  532. typedef CONFIGRET (WINAPI *pCM_Query_And_Remove_SubTree)(DEVNODE, PPNP_VETO_TYPE, LPSTR, ULONG, ULONG);
  533. CONFIGRET DevnodeClass::Remove(ULONG uFlags)
  534. {
  535. static pCM_Query_And_Remove_SubTree fpCM_Query_And_Remove_SubTree = NULL;
  536. if (!fpCM_Query_And_Remove_SubTree)
  537. {
  538. OSVERSIONINFO ver;
  539. memset(&ver, 0, sizeof(OSVERSIONINFO));
  540. ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  541. GetVersionEx(&ver);
  542. if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT)
  543. {
  544. // is windows NT
  545. HINSTANCE hinst;
  546. hinst = LoadLibrary(_T("cfgmgr32.dll"));
  547. fpCM_Query_And_Remove_SubTree = (pCM_Query_And_Remove_SubTree)GetProcAddress(hinst, "CM_Query_And_Remove_SubTreeA");
  548. //a-kjaw to fix prefix bug 259378.
  549. if(NULL == fpCM_Query_And_Remove_SubTree)
  550. return CR_FAILURE;
  551. }
  552. else
  553. {
  554. //else is not winnt
  555. fpCM_Query_And_Remove_SubTree = (pCM_Query_And_Remove_SubTree)-1;
  556. }
  557. }
  558. if (fpCM_Query_And_Remove_SubTree == (pCM_Query_And_Remove_SubTree)-1)
  559. {
  560. // is win9x
  561. return (CM_Remove_SubTree(hDevnode, uFlags));
  562. }
  563. else
  564. {
  565. return (fpCM_Query_And_Remove_SubTree(hDevnode, NULL, NULL, 0, uFlags));
  566. }
  567. }
  568. CONFIGRET DevnodeClass::Refresh(ULONG uFlags)
  569. {
  570. CONFIGRET retval;
  571. retval = CM_Reenumerate_DevNode(hParent, uFlags);
  572. if ( retval ) return (retval);
  573. retval = FindDevnode();
  574. GetProblemCode();
  575. return(retval);
  576. }
  577. CONFIGRET DevnodeClass::Disable(ULONG uFlags)
  578. {
  579. return (CM_Disable_DevNode(hDevnode, uFlags));
  580. }
  581. CONFIGRET DevnodeClass::Enable(ULONG uFlags)
  582. {
  583. CONFIGRET retval;
  584. retval = CM_Enable_DevNode(hDevnode, uFlags);
  585. if ( retval ) return (retval);
  586. return (FindDevnode());
  587. }
  588. CONFIGRET DevnodeClass::GetProblemCode(ULONG *Status, ULONG *Problem)
  589. {
  590. CONFIGRET retval;
  591. retval = CM_Get_DevNode_Status (&ulStatus,
  592. &ulProblemCode,
  593. hDevnode,
  594. 0L);
  595. if ( retval ) return(retval);
  596. if (Status) *Status = ulStatus;
  597. if (Problem) *Problem = ulProblemCode;
  598. return (retval);
  599. }
  600. /**************
  601. FindDevnode
  602. This function just updates the hDevnode, refreshed the device,
  603. and updates the status and problem code
  604. **************/
  605. CONFIGRET DevnodeClass::FindDevnode(void)
  606. {
  607. CONFIGRET retval;
  608. retval = CM_Locate_DevNode (&hDevnode, pDeviceID, CM_LOCATE_DEVNODE_NORMAL);
  609. if ( retval )
  610. {
  611. hDevnode = NULL;
  612. ulProblemCode = (ULONG)-1;
  613. ulStatus = (ULONG)-1;
  614. return (retval);
  615. }
  616. return (CM_Reenumerate_DevNode (hDevnode, CM_REENUMERATE_SYNCHRONOUS));
  617. }
  618. /***************
  619. operator==
  620. *************/
  621. int DevnodeClass::operator==(const DevnodeClass &OtherDevnode)
  622. {
  623. if ( strcmp(pDeviceID, OtherDevnode.pDeviceID) )
  624. return (FALSE);
  625. if ( ulProblemCode != OtherDevnode.ulProblemCode )
  626. {
  627. return (FALSE);
  628. }
  629. if ( (ulStatus ^ OtherDevnode.ulStatus) & ~IGNORE_ME_BITS )
  630. {
  631. return (FALSE);
  632. }
  633. return (TRUE);
  634. }
  635. ULONG ReadRegKeyInformationSZ (HKEY RootKey, TCHAR *KeyName, TCHAR **Value)
  636. {
  637. TCHAR * szBuffer;
  638. LONG retval;
  639. DWORD dwSize = 0;
  640. DWORD dwType = 0;
  641. // make sure the buffer is clear
  642. //assert (Value); // make sure that we actually got a value
  643. //assert (!*Value); // and also make sure that the buffer is already empty
  644. // for non debug versions
  645. *Value = NULL;
  646. //a-kjaw. Prefix bug no. 259379. if dwSize if not NULL &lpData is Null
  647. // func returns the size reqd to store the string which helps string allocation.
  648. retval = RegQueryValueEx(RootKey,
  649. KeyName,
  650. 0, // reserved
  651. &dwType,
  652. NULL,
  653. &dwSize);
  654. if ( retval != ERROR_SUCCESS )
  655. {
  656. return (retval); // cant continue
  657. }
  658. if ( (dwType != REG_SZ) || !dwSize )
  659. {
  660. return (ERROR_FILE_NOT_FOUND);
  661. }
  662. szBuffer = new TCHAR[++dwSize];
  663. if ( !szBuffer )
  664. {
  665. return (ERROR_NOT_ENOUGH_MEMORY);
  666. }
  667. retval = RegQueryValueEx(RootKey,
  668. KeyName,
  669. 0, // reserved
  670. &dwType,
  671. (UCHAR *)szBuffer,
  672. &dwSize);
  673. if ( retval )
  674. {
  675. delete szBuffer;
  676. return (retval);
  677. }
  678. *Value = szBuffer;
  679. return (ERROR_SUCCESS);
  680. }
  681. BOOL Enumerate_WalkTree_Devnode(DEVNODE hDevnode, DEVNODE hParent)
  682. {
  683. CONFIGRET retval;
  684. DevnodeClass *pNewDevice;
  685. DEVNODE hSib;
  686. pNewDevice = new DevnodeClass(hDevnode, hParent);
  687. retval = pNewDevice->GetChild(&hSib);
  688. if ( !retval )
  689. {
  690. Enumerate_WalkTree_Devnode(hSib, hDevnode);
  691. }
  692. retval = pNewDevice->GetSibling(&hSib);
  693. if ( !retval )
  694. {
  695. Enumerate_WalkTree_Devnode(hSib, hParent);
  696. }
  697. return (retval);
  698. }
  699. ULONG EnumerateTree_Devnode(void)
  700. {
  701. DEVNODE hDevnode;
  702. CM_Locate_DevNode(&hDevnode, NULL, CM_LOCATE_DEVNODE_NORMAL);
  703. Enumerate_WalkTree_Devnode(hDevnode, NULL);
  704. return (DevnodeClass::ALCCount());
  705. }