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.

815 lines
18 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1995 - 1999
  3. All rights reserved.
  4. Module Name:
  5. detect.cxx
  6. Abstract:
  7. PnP printer autodetection.
  8. Author:
  9. Lazar Ivanov (LazarI) May-06-1999
  10. Revision History:
  11. May-06-1999 - Created.
  12. Larry Zhu (LZhu) Mar-12-2000 - Rewrote PnP detection code.
  13. --*/
  14. #include "precomp.hxx"
  15. #pragma hdrstop
  16. #include "detect.hxx"
  17. #include <initguid.h>
  18. #include <ntddpar.h> // for GUID_PARALLEL_DEVICE
  19. #include <regstr.h>
  20. #if DBG
  21. #define DBGCHKMSG(bShowMessage, MsgAndArgs) \
  22. { \
  23. if (bShowMessage) \
  24. { \
  25. DBGMSG(DBG_ERROR, MsgAndArgs); \
  26. } \
  27. } \
  28. #else // DBG
  29. #define DBGCHKMSG(bShowMessage, MsgAndArgs)
  30. #endif // DBG
  31. /********************************************************************
  32. PnP private stuff
  33. ********************************************************************/
  34. //
  35. // current 1394 printers are enumerated under LPTENUM
  36. //
  37. #define szParallelClassEnumerator TEXT("LPTENUM")
  38. #define szParallelDot4PrintClassEnumerator TEXT("DOT4PRT")
  39. #define szUsbPrintClassEnumerator TEXT("USBPRINT")
  40. #define szInfraRedPrintClassEnumerator TEXT("IRENUM")
  41. // This timeout is per recommendation of the PnP guys
  42. // 1 second should be enough
  43. #define DELAY_KICKOFF_TIMEOUT 1000
  44. extern "C" {
  45. //
  46. // config mgr privates
  47. //
  48. DWORD
  49. CMP_WaitNoPendingInstallEvents(
  50. IN DWORD dwTimeout
  51. );
  52. }
  53. /********************************************************************
  54. Global functions
  55. ********************************************************************/
  56. #if FALSE // comment out the old enum code
  57. #define szParalelPortDevNodePath TEXT("Root\\ParallelClass\\0000")
  58. BOOL
  59. PnP_Enum_KickOff(
  60. VOID
  61. )
  62. /*++
  63. Routine Description:
  64. Kicks off the PNP enumeration event over the
  65. LPT ports only.
  66. Arguments:
  67. None
  68. Return Value:
  69. TRUE - on success
  70. FALSE - if a failure occurs
  71. Notes:
  72. --*/
  73. {
  74. BOOL bReturn;
  75. DEVINST hLPTDevInst;
  76. CONFIGRET result;
  77. //
  78. // Find the root dev node
  79. //
  80. result = CM_Locate_DevNode_Ex( &hLPTDevInst,
  81. szParalelPortDevNodePath, CM_LOCATE_DEVNODE_NORMAL, NULL );
  82. if( result == CR_SUCCESS )
  83. {
  84. //
  85. // Reenumerate from the root of the devnode tree
  86. //
  87. result = CM_Reenumerate_DevNode_Ex( hLPTDevInst, CM_REENUMERATE_NORMAL, NULL );
  88. if( result == CR_SUCCESS )
  89. {
  90. bReturn = TRUE;
  91. }
  92. }
  93. return bReturn;
  94. }
  95. #endif // comment out the old enum code
  96. BOOL
  97. PnP_Enum_KickOff(
  98. VOID
  99. )
  100. /*++
  101. Routine Description:
  102. Kicks off the PNP enumeration event.
  103. Arguments:
  104. None
  105. Return Value:
  106. TRUE - on success
  107. FALSE - if a failure occurs
  108. Notes:
  109. --*/
  110. {
  111. DWORD crStatus = CR_DEFAULT;
  112. DEVINST devRoot = {0};
  113. crStatus = CM_Locate_DevNode(&devRoot, NULL, CM_LOCATE_DEVNODE_NORMAL);
  114. DBGCHKMSG((CR_SUCCESS != crStatus), ("CM_Locate_Devnode failed with 0x%x\n", crStatus));
  115. if (CR_SUCCESS == crStatus)
  116. {
  117. crStatus = CM_Reenumerate_DevNode(devRoot, CM_REENUMERATE_RETRY_INSTALLATION);
  118. DBGCHKMSG((CR_SUCCESS != crStatus), ("CM_Reenumerate_DevNode failed with 0x%x\n", crStatus));
  119. }
  120. //
  121. // There is no point to return CR_XXX code
  122. //
  123. SetLastError(CR_SUCCESS == crStatus ? ERROR_SUCCESS : ERROR_INVALID_DATA);
  124. return CR_SUCCESS == crStatus;
  125. }
  126. VOID
  127. PnP_Enumerate_Sync(
  128. VOID
  129. )
  130. /*++
  131. Routine Description:
  132. Enumerates the LPT devices sync.
  133. Arguments:
  134. None
  135. Return Value:
  136. None
  137. Notes:
  138. --*/
  139. {
  140. //
  141. // Kick off PnP enumeration
  142. //
  143. if( PnP_Enum_KickOff( ) )
  144. {
  145. for( ;; )
  146. {
  147. //
  148. // We must wait about 1 sec (DELAY_KICKOFF_TIMEOUT) to make sure the enumeration
  149. // event has been kicked off completely, so we can wait successfully use the
  150. // CMP_WaitNoPendingInstallEvents() private API. This is by LonnyM recommendation.
  151. // This delay also solves the problem with multiple installs like DOT4 printers.
  152. //
  153. Sleep( DELAY_KICKOFF_TIMEOUT );
  154. //
  155. // Check to see if there are pending install events
  156. //
  157. if( WAIT_TIMEOUT != CMP_WaitNoPendingInstallEvents( 0 ) )
  158. break;
  159. //
  160. // Wait untill no more pending install events
  161. //
  162. CMP_WaitNoPendingInstallEvents( INFINITE );
  163. }
  164. }
  165. }
  166. /********************************************************************
  167. TPnPDetect - PnP printer detector class
  168. ********************************************************************/
  169. TPnPDetect::
  170. TPnPDetect(
  171. VOID
  172. )
  173. :
  174. _bDetectionInProgress( FALSE ),
  175. _pInfo4Before( NULL ),
  176. _cInfo4Before( 0 ),
  177. _hEventDone( NULL )
  178. /*++
  179. Routine Description:
  180. TPnPDetect constructor
  181. Arguments:
  182. None
  183. Return Value:
  184. None
  185. Notes:
  186. --*/
  187. {
  188. }
  189. TPnPDetect::
  190. ~TPnPDetect(
  191. VOID
  192. )
  193. /*++
  194. Routine Description:
  195. TPnPDetect destructor
  196. Arguments:
  197. None
  198. Return Value:
  199. None
  200. Notes:
  201. --*/
  202. {
  203. //
  204. // Check to free memory
  205. //
  206. Reset( );
  207. }
  208. typedef bool PI4_less_type(const PRINTER_INFO_4 p1, const PRINTER_INFO_4 p2);
  209. static bool PI4_less(const PRINTER_INFO_4 p1, const PRINTER_INFO_4 p2)
  210. {
  211. return (lstrcmp(p1.pPrinterName, p2.pPrinterName) < 0);
  212. }
  213. BOOL
  214. TPnPDetect::
  215. bKickOffPnPEnumeration(
  216. VOID
  217. )
  218. /*++
  219. Routine Description:
  220. Kicks off PnP enumeration event on the LPT ports.
  221. Arguments:
  222. None
  223. Return Value:
  224. TRUE - on success
  225. FALSE - if a failure occurs
  226. Notes:
  227. --*/
  228. {
  229. DWORD cbInfo4Before = 0;
  230. TStatusB bStatus;
  231. bStatus DBGNOCHK = FALSE;
  232. //
  233. // Check to free memory
  234. //
  235. Reset( );
  236. //
  237. // Enumerate the printers before PnP enumeration
  238. //
  239. bStatus DBGCHK = VDataRefresh::bEnumPrinters(
  240. PRINTER_ENUM_LOCAL, NULL, 4, reinterpret_cast<PVOID *>( &_pInfo4Before ),
  241. &cbInfo4Before, &_cInfo4Before );
  242. if( bStatus )
  243. {
  244. //
  245. // Sort out the PRINTER_INFO_4 structures here
  246. //
  247. std::sort<PRINTER_INFO_4*, PI4_less_type*>(
  248. _pInfo4Before,
  249. _pInfo4Before + _cInfo4Before,
  250. PI4_less);
  251. //
  252. // Kick off the PnP enumeration
  253. //
  254. _hEventDone = CreateEvent( NULL, TRUE, FALSE, NULL );
  255. if( _hEventDone )
  256. {
  257. HANDLE hEventOut = NULL;
  258. const HANDLE hCurrentProcess = GetCurrentProcess();
  259. bStatus DBGCHK = DuplicateHandle( hCurrentProcess,
  260. _hEventDone,
  261. hCurrentProcess,
  262. &hEventOut,
  263. 0,
  264. TRUE,
  265. DUPLICATE_SAME_ACCESS );
  266. if( bStatus )
  267. {
  268. //
  269. // Kick off the enumeration thread.
  270. //
  271. DWORD dwThreadId;
  272. HANDLE hThread = TSafeThread::Create( NULL,
  273. 0,
  274. (LPTHREAD_START_ROUTINE)TPnPDetect::EnumThreadProc,
  275. hEventOut,
  276. 0,
  277. &dwThreadId );
  278. if( hThread )
  279. {
  280. //
  281. // We don't need the thread handle any more.
  282. //
  283. CloseHandle( hThread );
  284. //
  285. // Detection initiated successfully.
  286. //
  287. _bDetectionInProgress = TRUE;
  288. }
  289. else
  290. {
  291. //
  292. // Failed to create the thread. Close the event.
  293. //
  294. CloseHandle( hEventOut );
  295. }
  296. }
  297. }
  298. //
  299. // Check to free the allocated resources if something has failed.
  300. //
  301. if( !_bDetectionInProgress )
  302. {
  303. Reset();
  304. }
  305. }
  306. return bStatus;
  307. }
  308. BOOL
  309. TPnPDetect::
  310. bDetectionInProgress(
  311. VOID
  312. )
  313. /*++
  314. Routine Description:
  315. Checks whether there is pending detection/installation
  316. process.
  317. Arguments:
  318. None
  319. Return Value:
  320. TRUE - yes there is pending detect/install process.
  321. FALSE - otherwise.
  322. Notes:
  323. --*/
  324. {
  325. return _bDetectionInProgress;
  326. }
  327. BOOL
  328. TPnPDetect::
  329. bFinished(
  330. DWORD dwTimeout
  331. )
  332. /*++
  333. Routine Description:
  334. Checks whether there is no more pending detect/install events
  335. after the last PnP enumeration event.
  336. Arguments:
  337. dwTimeout - Time to wait. By default is zero (no wait - just ping)
  338. Return Value:
  339. TRUE - on success
  340. FALSE - if a failure occurs
  341. Notes:
  342. --*/
  343. {
  344. //
  345. // Check to see if the enum process has been kicked off at all.
  346. //
  347. if( _bDetectionInProgress && _pInfo4Before && _hEventDone )
  348. {
  349. //
  350. // Ping to see whether the PnP detect/install process has finished
  351. //
  352. if( WAIT_OBJECT_0 == WaitForSingleObject( _hEventDone, 0 ) )
  353. {
  354. _bDetectionInProgress = FALSE;
  355. }
  356. }
  357. //
  358. // We must check for all the three conditions below as _bDetectionInProgress
  359. // may be FALSE, but the enum process has never kicked off. We want to make
  360. // sure we are in the case _bDetectionInProgress is set to FALSE after the enum
  361. // process is finished.
  362. //
  363. return ( !_bDetectionInProgress && _pInfo4Before && _hEventDone );
  364. }
  365. BOOL
  366. TPnPDetect::
  367. bGetDetectedPrinterName(
  368. TString *pstrPrinterName
  369. )
  370. /*++
  371. Routine Description:
  372. Enum the printers after the PnP enumeration has finished to
  373. check whether new local (LPT) printers have been detected.
  374. Arguments:
  375. None
  376. Return Value:
  377. TRUE - on success
  378. FALSE - if a failure occurs
  379. Notes:
  380. --*/
  381. {
  382. TStatusB bStatus;
  383. bStatus DBGNOCHK = FALSE;
  384. if( pstrPrinterName && !_bDetectionInProgress && _pInfo4Before )
  385. {
  386. //
  387. // The detection is done here. Enum the printers after the PnP
  388. // detect/install to see whether new printer has been detected.
  389. //
  390. PRINTER_INFO_4 *pInfo4After = NULL;
  391. DWORD cInfo4After = 0;
  392. DWORD cbInfo4After = 0;
  393. bStatus DBGCHK = VDataRefresh::bEnumPrinters(
  394. PRINTER_ENUM_LOCAL, NULL, 4, reinterpret_cast<PVOID *>( &pInfo4After ),
  395. &cbInfo4After, &cInfo4After );
  396. if( bStatus && cInfo4After > _cInfo4Before )
  397. {
  398. for( UINT uAfter = 0; uAfter < cInfo4After; uAfter++ )
  399. {
  400. if( !std::binary_search<PRINTER_INFO_4*, PRINTER_INFO_4, PI4_less_type*>(
  401. _pInfo4Before,
  402. _pInfo4Before + _cInfo4Before,
  403. pInfo4After[uAfter],
  404. PI4_less) )
  405. {
  406. //
  407. // This printer hasn't been found in the before list
  408. // so it must be the new printer detected. I know this is partial
  409. // solution because we are not considering the case we detect more
  410. // than one local PnP printer, but it is not our intention for now.
  411. //
  412. bStatus DBGCHK = pstrPrinterName->bUpdate( pInfo4After[uAfter].pPrinterName );
  413. break;
  414. }
  415. }
  416. }
  417. else
  418. {
  419. bStatus DBGNOCHK = FALSE;
  420. }
  421. FreeMem( pInfo4After );
  422. }
  423. return bStatus;
  424. }
  425. DWORD WINAPI
  426. TPnPDetect::
  427. EnumThreadProc(
  428. LPVOID lpParameter
  429. )
  430. /*++
  431. Routine Description:
  432. Invokes the PnP enumeration routine
  433. Arguments:
  434. lpParameter - Standard (see MSDN)
  435. Return Value:
  436. Standard (see MSDN)
  437. Notes:
  438. --*/
  439. {
  440. DWORD dwStatus = EXIT_FAILURE;
  441. HANDLE hEventDone = (HANDLE )lpParameter;
  442. dwStatus = hEventDone ? ERROR_SUCCESS : EXIT_FAILURE;
  443. //
  444. // If there is a NULL driver (meaning no driver) associated with a devnode,
  445. // we want to set CONFIGFLAG_FINISH_INSTALL flag so that PnP manager will
  446. // try to reinstall a driver for this device.
  447. //
  448. if (ERROR_SUCCESS == dwStatus)
  449. {
  450. dwStatus = ProcessDevNodesWithNullDriversAll();
  451. }
  452. //
  453. // Invokes the enumeration routine. It executes syncroniously.
  454. //
  455. if (ERROR_SUCCESS == dwStatus)
  456. {
  457. (void)PnP_Enumerate_Sync();
  458. }
  459. //
  460. // Notify the client we are done.
  461. //
  462. if (hEventDone)
  463. {
  464. SetEvent( hEventDone );
  465. //
  466. // We own the event handle, so we must close it now.
  467. //
  468. CloseHandle( hEventDone );
  469. }
  470. return dwStatus;
  471. }
  472. DWORD WINAPI
  473. TPnPDetect::
  474. ProcessDevNodesWithNullDriversForOneEnumerator(
  475. IN PCTSTR pszEnumerator
  476. )
  477. /*++
  478. Routine Description:
  479. This routine enumerates devnodes of one enumerator and it sets
  480. CONFIGFLAG_FINISH_INSTALL for devnode that has no driver (or NULL driver)
  481. so that this device will be re-detected by PnP manager.
  482. Arguments:
  483. pszEnumerator - The enumerator for a particular bus, this can be
  484. "LPTENUM", "USBPRINT", "DOT4PRT" etc.
  485. Return Value:
  486. Standard (see MSDN)
  487. Notes:
  488. */
  489. {
  490. DWORD dwStatus = ERROR_SUCCESS;
  491. HDEVINFO hDevInfoSet = INVALID_HANDLE_VALUE;
  492. TCHAR buffer[MAX_PATH] = {0};
  493. DWORD dwDataType = REG_NONE;
  494. SP_DEVINFO_DATA devInfoData = {0};
  495. DWORD cbRequiredSize = 0;
  496. DWORD dwConfigFlags = 0;
  497. hDevInfoSet = SetupDiGetClassDevs(NULL, pszEnumerator, NULL, DIGCF_ALLCLASSES);
  498. dwStatus = (INVALID_HANDLE_VALUE != hDevInfoSet) ? ERROR_SUCCESS : GetLastError();
  499. if (ERROR_SUCCESS == dwStatus)
  500. {
  501. devInfoData.cbSize = sizeof(devInfoData);
  502. for (DWORD cDevIndex = 0; ERROR_SUCCESS == dwStatus; cDevIndex++)
  503. {
  504. dwStatus = SetupDiEnumDeviceInfo(hDevInfoSet, cDevIndex, &devInfoData) ? ERROR_SUCCESS : GetLastError();
  505. //
  506. // When SPDRP_DRIVER is not present, this devnode is associated
  507. // with no driver or, in the other word, NULL driver
  508. //
  509. // For devnodes with NULL drivers, we will set CONFIGFLAG_FINISH_INSTALL
  510. // so that the device will be re-detected
  511. //
  512. // Notes on the error code: internally SetupDiGetDeviceRegistryProperty
  513. // first returns CR_NO_SUCH_VALUE, but this error code is remapped
  514. // to ERROR_INVALID_DATA by setupapi
  515. //
  516. if (ERROR_SUCCESS == dwStatus)
  517. {
  518. dwStatus = SetupDiGetDeviceRegistryProperty(hDevInfoSet,
  519. &devInfoData,
  520. SPDRP_DRIVER,
  521. &dwDataType,
  522. reinterpret_cast<PBYTE>(buffer),
  523. sizeof(buffer), &cbRequiredSize) ? ERROR_SUCCESS : GetLastError();
  524. if (ERROR_INVALID_DATA == dwStatus)
  525. {
  526. dwConfigFlags = 0;
  527. dwStatus = SetupDiGetDeviceRegistryProperty(hDevInfoSet,
  528. &devInfoData,
  529. SPDRP_CONFIGFLAGS,
  530. &dwDataType,
  531. reinterpret_cast<PBYTE>(&dwConfigFlags),
  532. sizeof(dwConfigFlags),
  533. &cbRequiredSize) ? (REG_DWORD == dwDataType ? ERROR_SUCCESS : ERROR_INVALID_PARAMETER) : GetLastError();
  534. if ((ERROR_SUCCESS == dwStatus) && (!(dwConfigFlags & CONFIGFLAG_FINISH_INSTALL)))
  535. {
  536. dwConfigFlags |= CONFIGFLAG_FINISH_INSTALL;
  537. dwStatus = SetupDiSetDeviceRegistryProperty(hDevInfoSet,
  538. &devInfoData,
  539. SPDRP_CONFIGFLAGS,
  540. reinterpret_cast<PBYTE>(&dwConfigFlags),
  541. sizeof(dwConfigFlags)) ? ERROR_SUCCESS : GetLastError();
  542. }
  543. }
  544. }
  545. }
  546. dwStatus = ERROR_NO_MORE_ITEMS == dwStatus ? ERROR_SUCCESS : dwStatus;
  547. }
  548. else
  549. {
  550. dwStatus = ERROR_INVALID_DATA == dwStatus ? ERROR_SUCCESS : dwStatus;
  551. }
  552. if (INVALID_HANDLE_VALUE != hDevInfoSet)
  553. {
  554. SetupDiDestroyDeviceInfoList(hDevInfoSet);
  555. }
  556. return dwStatus;
  557. }
  558. DWORD WINAPI
  559. TPnPDetect::
  560. ProcessDevNodesWithNullDriversAll(
  561. VOID
  562. )
  563. /*++
  564. Routine Description:
  565. This routine enumerates a subset of devnodes and it sets
  566. CONFIGFLAG_FINISH_INSTALL for devnodes that have no driver (or NULL driver)
  567. so that these devices will be re-detected by PnP manager.
  568. Arguments:
  569. None
  570. Return Value:
  571. Standard (see MSDN)
  572. Notes:
  573. */
  574. {
  575. DWORD dwStatus = ERROR_SUCCESS;
  576. //
  577. // The following enumerators can have printers attached
  578. //
  579. static PCTSTR acszEnumerators[] =
  580. {
  581. szParallelClassEnumerator,
  582. szParallelDot4PrintClassEnumerator,
  583. szUsbPrintClassEnumerator,
  584. szInfraRedPrintClassEnumerator,
  585. };
  586. for (UINT i = 0; (ERROR_SUCCESS == dwStatus) && (i < COUNTOF(acszEnumerators)); i++)
  587. {
  588. dwStatus = ProcessDevNodesWithNullDriversForOneEnumerator(acszEnumerators[i]);
  589. }
  590. return dwStatus;
  591. }
  592. VOID
  593. TPnPDetect::
  594. Reset(
  595. VOID
  596. )
  597. /*++
  598. Routine Description:
  599. Resets the PnP detector class, so you can kick off the
  600. PnP enumeration event again. Release the memory allocated
  601. from calling EnumPrinters() before kicking off PnP enum.
  602. Arguments:
  603. None
  604. Return Value:
  605. None
  606. Notes:
  607. --*/
  608. {
  609. if( _hEventDone )
  610. {
  611. CloseHandle( _hEventDone );
  612. _hEventDone = NULL;
  613. }
  614. if( _pInfo4Before )
  615. {
  616. FreeMem( _pInfo4Before );
  617. _pInfo4Before = NULL;
  618. }
  619. _bDetectionInProgress = FALSE;
  620. }