Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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