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.

1218 lines
35 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. All Rights Reserved
  4. Module Name:
  5. EnumUtil.cpp
  6. Abstract:
  7. Utiltiy functions used by the EnumPorts function.
  8. Author: M. Fenelon
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. TCHAR sComma = TEXT(',');
  13. TCHAR sNull = TEXT('\0');
  14. DWORD
  15. SpinUpdateThread( void )
  16. {
  17. HANDLE hThread = NULL;
  18. DWORD dwStatus = ERROR_SUCCESS;
  19. hThread = CreateThread (
  20. NULL,
  21. 0,
  22. (LPTHREAD_START_ROUTINE)UpdateThread,
  23. &gDynaMonInfo,
  24. 0,
  25. NULL
  26. );
  27. if (hThread)
  28. {
  29. CloseHandle(hThread);
  30. }
  31. else
  32. {
  33. dwStatus = GetLastError ();
  34. }
  35. return dwStatus;
  36. }
  37. VOID
  38. UpdateThread(
  39. PDYNAMON_MONITOR_INFO pMonInfo
  40. )
  41. {
  42. PPORT_UPDATE pUpdateList = NULL,
  43. pNext;
  44. DWORD dwPrinters;
  45. LPPRINTER_INFO_5 pPrinterInfo5List = NULL;
  46. BOOL bCheck;
  47. // Loop indefinitely
  48. while ( 1 )
  49. {
  50. // Wait for the Event to be signaled
  51. WaitForSingleObject( pMonInfo->hUpdateEvent, INFINITE );
  52. // Get access to the Update List Pointer
  53. ECS( pMonInfo->UpdateListCS );
  54. // Get the current list
  55. pUpdateList = pMonInfo->pUpdateList;
  56. pMonInfo->pUpdateList = NULL;
  57. // Release acces to the list pointer
  58. LCS( pMonInfo->UpdateListCS );
  59. dwPrinters = 0;
  60. pPrinterInfo5List = NULL;
  61. bCheck = GetPrinterInfo( &pPrinterInfo5List, &dwPrinters );
  62. // If there is anything in the list process it....
  63. while ( pUpdateList )
  64. {
  65. // First get a pointer to the next update
  66. pNext = pUpdateList->pNext;
  67. if ( bCheck &&
  68. !PortNameNeededBySpooler( pUpdateList->szPortName,
  69. pPrinterInfo5List,
  70. dwPrinters,
  71. pUpdateList->bActive ) &&
  72. !pUpdateList->bActive )
  73. {
  74. RegSetValueEx( pUpdateList->hKey, cszRecyclable, 0, REG_NONE, 0, 0);
  75. }
  76. // Close the Reg Key & Free the memory
  77. RegCloseKey( pUpdateList->hKey);
  78. FreeSplMem( pUpdateList );
  79. pUpdateList = pNext;
  80. }
  81. if ( pPrinterInfo5List )
  82. FreeSplMem( pPrinterInfo5List );
  83. }
  84. }
  85. BOOL
  86. GetPrinterInfo(
  87. OUT LPPRINTER_INFO_5 *ppPrinterInfo5,
  88. OUT LPDWORD pdwReturned
  89. )
  90. /*++
  91. Routine Description:
  92. Does an EnumPrinter and returns a list of PRINTER_INFO_5s of all local
  93. printers. Caller should free the pointer.
  94. Arguments:
  95. ppPrinterInfo5 : Points to PRINTER_INFO_5s on return
  96. pdwReturned : Tells how many PRINTER_INFO_5s are returned
  97. Return Value:
  98. TRUE on success, FALSE else
  99. --*/
  100. {
  101. BOOL bRet = FALSE;
  102. static DWORD dwNeeded = 0;
  103. LPBYTE pBuf = NULL;
  104. *pdwReturned = 0;
  105. if ( !(pBuf = (LPBYTE) AllocSplMem( dwNeeded ) ) )
  106. goto Cleanup;
  107. if ( !EnumPrinters(PRINTER_ENUM_LOCAL,
  108. NULL,
  109. 5,
  110. pBuf,
  111. dwNeeded,
  112. &dwNeeded,
  113. pdwReturned) )
  114. {
  115. if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
  116. goto Cleanup;
  117. FreeSplMem(pBuf);
  118. if ( !(pBuf = (LPBYTE) AllocSplMem( dwNeeded ) ) ||
  119. !EnumPrinters(PRINTER_ENUM_LOCAL,
  120. NULL,
  121. 5,
  122. pBuf,
  123. dwNeeded,
  124. &dwNeeded,
  125. pdwReturned) )
  126. {
  127. goto Cleanup;
  128. }
  129. }
  130. bRet = TRUE;
  131. Cleanup:
  132. if ( bRet && *pdwReturned )
  133. {
  134. *ppPrinterInfo5 = (LPPRINTER_INFO_5)pBuf;
  135. }
  136. else
  137. {
  138. FreeSplMem(pBuf);
  139. *ppPrinterInfo5 = NULL;
  140. *pdwReturned = 0;
  141. }
  142. return bRet;
  143. }
  144. BOOL
  145. PortNameNeededBySpooler(
  146. IN LPTSTR pszPortName,
  147. IN LPPRINTER_INFO_5 pPrinterInfo5,
  148. IN DWORD dwPrinters,
  149. IN BOOL bActive
  150. )
  151. /*++
  152. Routine Description:
  153. Tells if a port is needed by spooler. Any port to which spooler currently
  154. has a printer going is needed.
  155. Arguments:
  156. pszPortName : Port name in question
  157. pPrinterInfo5 : List of PrinterInfo5s
  158. dwPrinters : Count of the list of printers
  159. Return Value:
  160. TRUE if spooler currently has a printer which is using the port
  161. FALSE otherwise
  162. --*/
  163. {
  164. BOOL bPortUsedByAPrinter = FALSE,
  165. bPrinterUsesOnlyThisPort;
  166. DWORD dwIndex;
  167. LPTSTR pszStr1, pszStr2;
  168. for ( dwIndex = 0 ; dwIndex < dwPrinters ; ++dwIndex, ++pPrinterInfo5 )
  169. {
  170. bPrinterUsesOnlyThisPort = FALSE;
  171. //
  172. // Port names are returned comma separated by spooler,
  173. // and there are blanks
  174. //
  175. pszStr1 = pPrinterInfo5->pPortName;
  176. if ( _tcsicmp( (LPCTSTR) pszPortName, pszStr1 ) == 0 )
  177. bPortUsedByAPrinter = bPrinterUsesOnlyThisPort = TRUE;
  178. else
  179. {
  180. //
  181. // Look at each port in the list of ports printer uses
  182. //
  183. while ( pszStr2 = _tcschr( pszStr1, sComma ) )
  184. {
  185. *pszStr2 = sNull;
  186. if ( _tcsicmp( pszPortName, pszStr1 ) == 0 )
  187. bPortUsedByAPrinter = TRUE;
  188. *pszStr2 = sComma; // Put the comma back
  189. if ( bPortUsedByAPrinter )
  190. break;
  191. pszStr1 = pszStr2 + 1;
  192. // Skip spaces
  193. while ( *pszStr1 == TEXT(' ') )
  194. ++pszStr1;
  195. }
  196. if ( !bPortUsedByAPrinter )
  197. bPortUsedByAPrinter = _tcsicmp( pszPortName, pszStr1 ) == 0;
  198. }
  199. // We will change only status of printer for non-pooled printers only
  200. if ( bPrinterUsesOnlyThisPort )
  201. SetOnlineStaus( pPrinterInfo5, bActive );
  202. }
  203. return bPortUsedByAPrinter;
  204. }
  205. BOOL
  206. SetOnlineStaus(
  207. LPPRINTER_INFO_5 pPrinterInfo5,
  208. BOOL bOnline
  209. )
  210. {
  211. BOOL bRet = FALSE;
  212. HANDLE hPrinter;
  213. PRINTER_DEFAULTS PrinterDefault = {NULL, NULL, PRINTER_ALL_ACCESS};
  214. //
  215. // Don't change Online Status at all for TS ports
  216. //
  217. if ( _tcsnicmp( pPrinterInfo5->pPortName, cszTS, _tcslen(cszTS) ) == 0 )
  218. return TRUE;
  219. //
  220. // Force all DOT4 ports to remain online at all times.
  221. //
  222. if ( _tcsnicmp( pPrinterInfo5->pPortName, cszDOT4, _tcslen(cszDOT4) ) == 0 )
  223. bOnline = TRUE;
  224. //
  225. // Check if spooler already has the correct status
  226. // (can happen on spooler startup)
  227. //
  228. if ( bOnline )
  229. {
  230. if ( !(pPrinterInfo5->Attributes & PRINTER_ATTRIBUTE_WORK_OFFLINE) )
  231. return TRUE;
  232. }
  233. else
  234. if ( pPrinterInfo5->Attributes & PRINTER_ATTRIBUTE_WORK_OFFLINE )
  235. return TRUE;
  236. if ( !OpenPrinter( pPrinterInfo5->pPrinterName, &hPrinter, &PrinterDefault ) )
  237. return FALSE;
  238. if ( bOnline )
  239. pPrinterInfo5->Attributes &= ~PRINTER_ATTRIBUTE_WORK_OFFLINE;
  240. else
  241. pPrinterInfo5->Attributes |= PRINTER_ATTRIBUTE_WORK_OFFLINE;
  242. bRet = SetPrinter( hPrinter, 5, (LPBYTE)pPrinterInfo5, 0 );
  243. ClosePrinter( hPrinter );
  244. return bRet;
  245. }
  246. DWORD
  247. BuildPortList(
  248. PDYNAMON_MONITOR_INFO pMonitorInfo,
  249. PPORT_UPDATE* ppPortUpdateList
  250. )
  251. {
  252. DWORD dwLastError;
  253. SETUPAPI_INFO SetupApiInfo;
  254. if ( !LoadSetupApiDll( &SetupApiInfo ) )
  255. return GetLastError();
  256. ECS( pMonitorInfo->EnumPortsCS );
  257. dwLastError = ProcessGUID( &SetupApiInfo, pMonitorInfo,
  258. ppPortUpdateList, (LPGUID) &USB_PRINTER_GUID );
  259. LCS( pMonitorInfo->EnumPortsCS );
  260. if ( SetupApiInfo.hSetupApi )
  261. FreeLibrary(SetupApiInfo.hSetupApi);
  262. return dwLastError;
  263. }
  264. BOOL
  265. LoadSetupApiDll(
  266. PSETUPAPI_INFO pSetupInfo
  267. )
  268. {
  269. UINT uOldErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  270. pSetupInfo->hSetupApi = LoadLibrary(TEXT("setupapi"));
  271. SetErrorMode(uOldErrMode);
  272. if ( !pSetupInfo->hSetupApi )
  273. return FALSE;
  274. pSetupInfo->DestroyDeviceInfoList = (pfSetupDiDestroyDeviceInfoList) GetProcAddress(pSetupInfo->hSetupApi,
  275. "SetupDiDestroyDeviceInfoList");
  276. pSetupInfo->GetClassDevs = (pfSetupDiGetClassDevs) GetProcAddress(pSetupInfo->hSetupApi,
  277. "SetupDiGetClassDevsW");
  278. pSetupInfo->EnumDeviceInfo = (pfSetupDiEnumDeviceInfo) GetProcAddress(pSetupInfo->hSetupApi,
  279. "SetupDiEnumDeviceInfo");
  280. pSetupInfo->EnumDeviceInterfaces = (pfSetupDiEnumDeviceInterfaces) GetProcAddress(pSetupInfo->hSetupApi,
  281. "SetupDiEnumDeviceInterfaces");
  282. pSetupInfo->GetDeviceInterfaceDetail = (pfSetupDiGetDeviceInterfaceDetail) GetProcAddress(pSetupInfo->hSetupApi,
  283. "SetupDiGetDeviceInterfaceDetailW");
  284. pSetupInfo->OpenDeviceInterfaceRegKey = (pfSetupDiOpenDeviceInterfaceRegKey) GetProcAddress(pSetupInfo->hSetupApi,
  285. "SetupDiOpenDeviceInterfaceRegKey");
  286. if ( !pSetupInfo->DestroyDeviceInfoList ||
  287. !pSetupInfo->GetClassDevs ||
  288. !pSetupInfo->EnumDeviceInfo ||
  289. !pSetupInfo->EnumDeviceInterfaces ||
  290. !pSetupInfo->GetDeviceInterfaceDetail ||
  291. !pSetupInfo->OpenDeviceInterfaceRegKey )
  292. {
  293. SPLASSERT(FALSE);
  294. FreeLibrary(pSetupInfo->hSetupApi);
  295. pSetupInfo->hSetupApi = NULL;
  296. return FALSE;
  297. }
  298. return TRUE;
  299. }
  300. DWORD
  301. ProcessGUID(
  302. PSETUPAPI_INFO pSetupApiInfo,
  303. PDYNAMON_MONITOR_INFO pMonitorInfo,
  304. PPORT_UPDATE* ppPortUpdateList,
  305. LPGUID pGUID
  306. )
  307. {
  308. DWORD dwIndex, dwLastError, dwSize, dwNeeded;
  309. BOOL bIsPortActive;
  310. HANDLE hToken;
  311. HDEVINFO hDevList = INVALID_HANDLE_VALUE;
  312. PDYNAMON_PORT pPtr;
  313. PUSELESS_PORT pCur, pPrev;
  314. SP_DEVICE_INTERFACE_DATA DeviceInterface;
  315. PSP_DEVICE_INTERFACE_DETAIL_DATA pDeviceDetail = NULL;
  316. hToken = RevertToPrinterSelf();
  317. hDevList = pSetupApiInfo->GetClassDevs( pGUID,
  318. NULL,
  319. NULL,
  320. DIGCF_INTERFACEDEVICE);
  321. if ( hDevList == INVALID_HANDLE_VALUE )
  322. {
  323. dwLastError = GetLastError();
  324. goto Done;
  325. }
  326. dwSize = sizeof(PSP_DEVICE_INTERFACE_DETAIL_DATA)
  327. + MAX_DEVICE_PATH * sizeof(TCHAR);
  328. pDeviceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA) AllocSplMem(dwSize);
  329. if ( !pDeviceDetail )
  330. {
  331. dwLastError = GetLastError();
  332. goto Done;
  333. }
  334. dwLastError = ERROR_SUCCESS;
  335. dwIndex = 0;
  336. pDeviceDetail->cbSize = sizeof(*pDeviceDetail);
  337. DeviceInterface.cbSize = sizeof(DeviceInterface);
  338. do
  339. {
  340. if ( !pSetupApiInfo->EnumDeviceInterfaces( hDevList,
  341. NULL,
  342. pGUID,
  343. dwIndex,
  344. &DeviceInterface) )
  345. {
  346. dwLastError = GetLastError();
  347. if ( dwLastError == ERROR_NO_MORE_ITEMS )
  348. break; // Normal exit
  349. DBGMSG(DBG_WARNING,
  350. ("DynaMon: ProcessGUID: SetupDiEnumDeviceInterfaces failed with %d for inderx %d\n",
  351. dwLastError, dwIndex));
  352. goto Next;
  353. }
  354. if ( !pSetupApiInfo->GetDeviceInterfaceDetail( hDevList,
  355. &DeviceInterface,
  356. pDeviceDetail,
  357. dwSize,
  358. &dwNeeded,
  359. NULL) )
  360. {
  361. dwLastError = GetLastError();
  362. DBGMSG(DBG_ERROR,
  363. ("DynaMon: ProcessGUID: SetupDiGetDeviceInterfaceDetail failed with error %d size %d\n",
  364. dwLastError, dwNeeded));
  365. goto Next;
  366. }
  367. //
  368. // This is the only flag we care about
  369. //
  370. bIsPortActive = (DeviceInterface.Flags & SPINT_ACTIVE);
  371. //
  372. // For inactive port if it is already known as a useless port
  373. // no need to process further
  374. //
  375. if ( !bIsPortActive && FindUselessEntry( pMonitorInfo, pDeviceDetail->DevicePath, &pPrev) )
  376. {
  377. goto Next;
  378. }
  379. //
  380. // When port active status did not change we should have nothing
  381. // to update. By skipping the PortUpdateInfo we avoid registry access
  382. // and it is a performance improvement
  383. //
  384. if ( (pPtr = FindPortUsingDevicePath( pMonitorInfo,
  385. pDeviceDetail->DevicePath ) ) &&
  386. pPtr->pBasePort->compActiveState( bIsPortActive ) )
  387. {
  388. if (!bIsPortActive && pPtr-> pBasePort-> getPortType () == TSPORT)
  389. {
  390. //
  391. // Potential update
  392. //
  393. }
  394. else
  395. {
  396. goto Next;
  397. }
  398. }
  399. ProcessPortInfo( pSetupApiInfo, pMonitorInfo, hDevList, &DeviceInterface,
  400. pDeviceDetail, bIsPortActive, ppPortUpdateList);
  401. Next:
  402. dwLastError = ERROR_SUCCESS;
  403. ++dwIndex;
  404. pDeviceDetail->cbSize = sizeof(*pDeviceDetail);
  405. DeviceInterface.cbSize = sizeof(DeviceInterface);
  406. } while ( dwLastError == ERROR_SUCCESS );
  407. if ( dwLastError == ERROR_NO_MORE_ITEMS )
  408. dwLastError = ERROR_SUCCESS;
  409. Done:
  410. if ( hDevList != INVALID_HANDLE_VALUE )
  411. pSetupApiInfo->DestroyDeviceInfoList( hDevList );
  412. if ( !ImpersonatePrinterClient(hToken) )
  413. dwLastError = GetLastError();
  414. FreeSplMem(pDeviceDetail);
  415. return dwLastError;
  416. }
  417. PUSELESS_PORT
  418. FindUselessEntry(
  419. IN PDYNAMON_MONITOR_INFO pMonitorInfo,
  420. IN LPTSTR pszDevicePath,
  421. OUT PUSELESS_PORT* ppPrev
  422. )
  423. /*++
  424. Routine Description:
  425. Searches for a device path in the useless port list
  426. Arguments:
  427. Return Value:
  428. NULL if no entry found in the list
  429. Else a valid USELESS_PORT_INFO pointer
  430. Weather port is found or not *ppPrev will return the previous element
  431. --*/
  432. {
  433. INT iCmp;
  434. PUSELESS_PORT pHead;
  435. for ( pHead = pMonitorInfo->pJunkList, *ppPrev = NULL ;
  436. pHead && (iCmp = lstrcmp(pszDevicePath, pHead->szDevicePath)) < 0 ;
  437. *ppPrev = pHead, pHead = pHead->pNext )
  438. ;
  439. //
  440. // If useless port should go in the middle but not currently there
  441. //
  442. if ( pHead && iCmp != 0 )
  443. pHead = NULL;
  444. return pHead;
  445. }
  446. PDYNAMON_PORT
  447. FindPortUsingDevicePath(
  448. IN PDYNAMON_MONITOR_INFO pMonitorInfo,
  449. IN LPTSTR pszDevicePath
  450. )
  451. /*++
  452. Routine Description:
  453. Finds a port by device path.
  454. Arguments:
  455. pMonitorInfo : Pointer to MONITOR_INFO structure
  456. pszDevicePath : Device path name to search for
  457. Return Value:
  458. If NULL port is not in list, else pointer to the PORT_INFO entry for the
  459. given device path
  460. --*/
  461. {
  462. INT iCmp;
  463. PDYNAMON_PORT pHead;
  464. ECS( pMonitorInfo->EnumPortsCS );
  465. //
  466. // Port list is sorted on port name, so we have to scan the whole list
  467. //
  468. for ( pHead = pMonitorInfo->pPortList ; pHead ; pHead = pHead->pNext )
  469. if ( pHead->pBasePort->compDevicePath( pszDevicePath ) == 0 )
  470. break;
  471. LCS( pMonitorInfo->EnumPortsCS );
  472. return pHead;
  473. }
  474. VOID
  475. ProcessPortInfo(
  476. IN PSETUPAPI_INFO pSetupApiInfo,
  477. IN PDYNAMON_MONITOR_INFO pMonitorInfo,
  478. IN HDEVINFO hDevList,
  479. IN PSP_DEVICE_INTERFACE_DATA pDeviceInterface,
  480. IN PSP_DEVICE_INTERFACE_DETAIL_DATA pDeviceDetail,
  481. IN BOOL bIsPortActive,
  482. IN OUT PPORT_UPDATE* ppPortUpdateList
  483. )
  484. {
  485. HKEY hKey = INVALID_HANDLE_VALUE;
  486. TCHAR szPortName[MAX_PORT_LEN];
  487. PDYNAMON_PORT pCur, pPrev;
  488. PORTTYPE portType = USBPORT;
  489. hKey = GetPortNameAndRegKey( pSetupApiInfo, hDevList, pDeviceInterface,
  490. szPortName, COUNTOF (szPortName), &portType );
  491. if ( hKey == INVALID_HANDLE_VALUE )
  492. {
  493. //
  494. // If this port is inactive and is not in our known port list
  495. // add to useless list. Earlier we would have been opening the registry
  496. // every time and find that port number is missing because of KM drivers
  497. // not deleting the inactive device interfaces
  498. //
  499. if ( !bIsPortActive &&
  500. !FindPortUsingDevicePath(pMonitorInfo, pDeviceDetail->DevicePath) )
  501. AddUselessPortEntry(pMonitorInfo, pDeviceDetail->DevicePath);
  502. return;
  503. }
  504. pCur = FindPort(pMonitorInfo, szPortName, &pPrev);
  505. //
  506. // Port info is currently in our list?
  507. //
  508. if ( pCur )
  509. {
  510. //
  511. // Did the device path or flags change?
  512. //
  513. BOOL bActiveStateChanged = !pCur->pBasePort->
  514. compActiveState (
  515. bIsPortActive
  516. );
  517. BOOL bDevicePathChanged = pCur->pBasePort->
  518. compDevicePath (
  519. pDeviceDetail->DevicePath
  520. );
  521. if (bActiveStateChanged || bDevicePathChanged ||
  522. (!bIsPortActive && pCur-> pBasePort-> getPortType () == TSPORT)
  523. )
  524. {
  525. //
  526. // Even nothing has been changed, for INACTIVE TS ports
  527. // we need to call UpdatePortInfo to check if recyclable flag is set.
  528. //
  529. if (bDevicePathChanged)
  530. {
  531. for (
  532. PDYNAMON_PORT pDuplicate = pMonitorInfo-> pPortList;
  533. pDuplicate;
  534. pDuplicate = pDuplicate-> pNext
  535. )
  536. {
  537. if (pDuplicate == pCur || pDuplicate-> pBasePort-> isActive ())
  538. {
  539. continue;
  540. }
  541. //
  542. // Search for CBasePort with the same DevicePath
  543. // but with a different PortName...
  544. //
  545. if (pDuplicate-> pBasePort-> compDevicePath (pDeviceDetail-> DevicePath) == 0 &&
  546. pDuplicate-> pBasePort-> compPortName (szPortName) != 0
  547. )
  548. {
  549. //
  550. // Clear the port's device path and description...
  551. //
  552. pDuplicate-> pBasePort-> setDevicePath (L"");
  553. pDuplicate-> pBasePort-> setPortDesc (L"");
  554. }
  555. }//end for
  556. //
  557. // If pJunkList has the entry with the DeviceName equal to pCur,
  558. // this entry has to be deleted.
  559. //
  560. PUSELESS_PORT pPrevUseless = NULL;
  561. PUSELESS_PORT pUseless =
  562. FindUselessEntry (
  563. pMonitorInfo,
  564. pDeviceDetail-> DevicePath,
  565. &pPrevUseless
  566. );
  567. if (pUseless)
  568. {
  569. if (pPrevUseless)
  570. {
  571. pPrevUseless-> pNext = pUseless-> pNext;
  572. }
  573. else
  574. {
  575. pMonitorInfo-> pJunkList = pUseless-> pNext;
  576. }
  577. FreeSplMem (pUseless);
  578. }
  579. }//end if
  580. UpdatePortInfo (
  581. pCur,
  582. pDeviceDetail->DevicePath,
  583. bIsPortActive,
  584. &hKey,
  585. ppPortUpdateList
  586. );
  587. }//end if
  588. }
  589. else
  590. {
  591. AddPortToList( portType, szPortName, pDeviceDetail->DevicePath,
  592. bIsPortActive, &hKey, pMonitorInfo, pPrev,
  593. ppPortUpdateList);
  594. }
  595. if ( hKey != INVALID_HANDLE_VALUE )
  596. RegCloseKey(hKey);
  597. }
  598. HKEY
  599. GetPortNameAndRegKey(
  600. IN PSETUPAPI_INFO pSetupInfo,
  601. IN HDEVINFO hDevList,
  602. IN PSP_DEVICE_INTERFACE_DATA pDeviceInterface,
  603. OUT LPTSTR pszPortName,
  604. IN size_t cchPortName,
  605. OUT PORTTYPE* pPortType
  606. )
  607. /*++
  608. Routine Description:
  609. Find port name for a device interface and also return reg handle
  610. Arguments:
  611. hDevList : List of USB printer devices
  612. pDeviceInterface : pointer to device interface in question
  613. pszPortName : Port name on return.
  614. Return Value:
  615. INVALID_HANDLE_VALUE on some errors.
  616. Otherwize a valid registry handle with pszPortName giving port name
  617. --*/
  618. {
  619. HKEY hKey = INVALID_HANDLE_VALUE;
  620. DWORD dwPortNumber, dwNeeded, dwLastError;
  621. TCHAR szPortBaseName[MAX_PORT_LEN-3];
  622. hKey = pSetupInfo->OpenDeviceInterfaceRegKey(hDevList,
  623. pDeviceInterface,
  624. 0,
  625. KEY_ALL_ACCESS);
  626. if ( hKey == INVALID_HANDLE_VALUE )
  627. {
  628. dwLastError = GetLastError();
  629. DBGMSG(DBG_WARNING,
  630. ("DynaMon: GetPortNameAndRegKey: SetupDiOpenDeviceInterfaceRegKey failed with error %d\n",
  631. dwLastError));
  632. return INVALID_HANDLE_VALUE;
  633. }
  634. dwNeeded = sizeof(dwPortNumber);
  635. if ( ERROR_SUCCESS != RegQueryValueEx(hKey, cszPortNumber, 0, NULL,
  636. (LPBYTE)&dwPortNumber, &dwNeeded) )
  637. {
  638. dwLastError = GetLastError();
  639. DBGMSG(DBG_WARNING,
  640. ("DynaMon: GetPortNameAndRegKey: RegQueryValueEx failed for port number with error %d\n", dwLastError));
  641. goto Fail;
  642. }
  643. dwNeeded = sizeof(szPortBaseName);
  644. if ( ERROR_SUCCESS != (dwLastError = RegQueryValueEx(hKey, cszBaseName, 0, NULL,
  645. (LPBYTE)szPortBaseName, &dwNeeded) ) )
  646. {
  647. dwLastError = GetLastError();
  648. DBGMSG(DBG_WARNING,
  649. ("GetPortNameAndRegKey: RegQueryValueEx failed for Base Name with error %d\n", dwLastError));
  650. goto Fail;
  651. }
  652. *pPortType = USBPORT;
  653. if ( _tcsncmp( szPortBaseName, cszDOT4, _tcslen(cszDOT4) ) == 0 )
  654. *pPortType = DOT4PORT;
  655. else if ( _tcsncmp( szPortBaseName, csz1394, _tcslen(csz1394) ) == 0 )
  656. *pPortType = P1394PORT;
  657. else if ( _tcsncmp( szPortBaseName, cszTS, _tcslen(cszTS) ) == 0 )
  658. *pPortType = TSPORT;
  659. (VOID)StringCchPrintf (pszPortName, cchPortName, TEXT("%s%03u"), szPortBaseName, dwPortNumber);
  660. return hKey;
  661. Fail:
  662. RegCloseKey(hKey);
  663. return INVALID_HANDLE_VALUE;
  664. }
  665. VOID
  666. AddUselessPortEntry(
  667. IN PDYNAMON_MONITOR_INFO pMonitorInfo,
  668. IN LPTSTR pszDevicePath
  669. )
  670. /*++
  671. Routine Description:
  672. This adds a useless port entry to our list. So next time we see an inactive
  673. port that is already in our known usless port list we can skip the port
  674. entry
  675. Arguments:
  676. pMonitorInfo : Pointer to monitor inf
  677. pszDevicePath : Device path for the useless port
  678. Return Value:
  679. None. Under normal circumstances will add a useless entry to our list
  680. --*/
  681. {
  682. PUSELESS_PORT pTemp, pPrev;
  683. pTemp = FindUselessEntry( pMonitorInfo, pszDevicePath, &pPrev );
  684. //
  685. // Don't add an entry that is already there
  686. //
  687. SPLASSERT(pTemp == NULL);
  688. if ( pTemp = (PUSELESS_PORT) AllocSplMem(sizeof(*pTemp)) )
  689. {
  690. SafeCopy(MAX_PATH, pszDevicePath, pTemp->szDevicePath);
  691. if ( pPrev )
  692. {
  693. pTemp->pNext = pPrev->pNext;
  694. pPrev->pNext = pTemp;
  695. }
  696. else
  697. {
  698. pTemp->pNext = pMonitorInfo->pJunkList;
  699. pMonitorInfo->pJunkList = pTemp;
  700. }
  701. }
  702. }
  703. PDYNAMON_PORT
  704. FindPort(
  705. IN PDYNAMON_MONITOR_INFO pMonitorInfo,
  706. IN LPTSTR pszPortName,
  707. OUT PDYNAMON_PORT* ppPrev
  708. )
  709. /*++
  710. Routine Description:
  711. Finds a port by name. Ports are kept in singly linked list sorted by name.
  712. If found previous in the list is returned via *ppPrev.
  713. Arguments:
  714. pHead : Head pointer to port list
  715. pszPortName : Name of port to look
  716. ppPrev : On return will have pointer to previous element
  717. Return Value:
  718. If NULL port is not in list, else the found element
  719. Weather port is found or not *ppPrev will return the previous element
  720. --*/
  721. {
  722. INT iCmp;
  723. PDYNAMON_PORT pHead;
  724. ECS( pMonitorInfo->EnumPortsCS );
  725. pHead = pMonitorInfo->pPortList;
  726. for ( *ppPrev = NULL ;
  727. pHead && ( iCmp = pHead->pBasePort->compPortName( pszPortName) ) < 0 ;
  728. *ppPrev = pHead, pHead = pHead->pNext )
  729. ;
  730. //
  731. // If port should go in the middle but not currently there
  732. //
  733. if ( pHead && iCmp != 0 )
  734. pHead = NULL;
  735. LCS( pMonitorInfo->EnumPortsCS );
  736. return pHead;
  737. }
  738. VOID
  739. UpdatePortInfo(
  740. PDYNAMON_PORT pPort,
  741. LPTSTR pszDevicePath,
  742. BOOL bIsPortActive,
  743. HKEY* phKey,
  744. PPORT_UPDATE* ppPortUpdateList
  745. )
  746. {
  747. DWORD dwSize;
  748. CBasePort* pCurrentPort = pPort->pBasePort;
  749. if (bIsPortActive && !pCurrentPort-> isActive ())
  750. {
  751. //
  752. // This port goes active.
  753. // 'recyclable' flag has to be removed
  754. //
  755. DWORD dwRetVal =
  756. RegDeleteValue (
  757. *phKey,
  758. cszRecyclable
  759. );
  760. if (dwRetVal != ERROR_SUCCESS &&
  761. dwRetVal != ERROR_FILE_NOT_FOUND
  762. )
  763. {
  764. //
  765. // 'recyclable' flag removing failed.
  766. // This port cannot be activated
  767. //
  768. return;
  769. }
  770. }
  771. pCurrentPort->setDevicePath( pszDevicePath );
  772. TCHAR szPortDescription[MAX_PORT_DESC_LEN];
  773. dwSize = sizeof(szPortDescription);
  774. if ( ERROR_SUCCESS == RegQueryValueEx( *phKey,
  775. cszPortDescription,
  776. 0,
  777. NULL,
  778. (LPBYTE) szPortDescription,
  779. &dwSize) )
  780. {
  781. pCurrentPort->setPortDesc( szPortDescription );
  782. }
  783. if ( !pCurrentPort->compActiveState( bIsPortActive ) )
  784. {
  785. pCurrentPort->setActive( bIsPortActive );
  786. AddToPortUpdateList(ppPortUpdateList, pPort, phKey);
  787. }
  788. else
  789. {
  790. //
  791. // TS Ports only
  792. //
  793. if (!bIsPortActive && pCurrentPort-> getPortType () == TSPORT)
  794. {
  795. //
  796. // Is it already recyclable?
  797. //
  798. DWORD dwRetValue =
  799. RegQueryValueEx (
  800. *phKey,
  801. cszRecyclable,
  802. 0,
  803. NULL,
  804. NULL,
  805. NULL
  806. );
  807. if (dwRetValue == ERROR_FILE_NOT_FOUND)
  808. {
  809. //
  810. // The port is inactive and it is not recyclable.
  811. // I have to add this port in update list to check is it still needed
  812. // by the spooler. Only UpdateThread checks if the port name is in use.
  813. AddToPortUpdateList(ppPortUpdateList, pPort, phKey);
  814. }
  815. //
  816. // If dwRetValue is equal to ERROR_SUCCESS, then it is already recyclable.
  817. // If dwRetVal has some other value, some error occured but next EnumPort
  818. // will check this status again.
  819. //
  820. }
  821. }
  822. }
  823. BOOL
  824. AddPortToList(
  825. PORTTYPE portType,
  826. LPTSTR pszPortName,
  827. LPTSTR pszDevicePath,
  828. BOOL bIsPortActive,
  829. HKEY* phKey,
  830. PDYNAMON_MONITOR_INFO pMonitorInfo,
  831. PDYNAMON_PORT pPrevPort,
  832. PPORT_UPDATE* ppPortUpdateList
  833. )
  834. {
  835. DWORD dwSize, dwLastError;
  836. PDYNAMON_PORT pPort;
  837. PUSELESS_PORT pCur, pPrev;
  838. CBasePort* pNewPort;
  839. SPLASSERT(FindPortUsingDevicePath(pMonitorInfo, pszDevicePath) == NULL);
  840. if (bIsPortActive)
  841. {
  842. //
  843. // If the port goes active, recyclable flag should be removed
  844. //
  845. DWORD dwRetVal =
  846. RegDeleteValue (
  847. *phKey,
  848. cszRecyclable
  849. );
  850. if (dwRetVal != ERROR_SUCCESS &&
  851. dwRetVal != ERROR_FILE_NOT_FOUND
  852. )
  853. {
  854. //
  855. // Recyclable flag removing failed. Don't use this port.
  856. //
  857. return FALSE;
  858. }
  859. }
  860. pPort = (PDYNAMON_PORT) AllocSplMem(sizeof(DYNAMON_PORT));
  861. if ( !pPort )
  862. return FALSE;
  863. pPort->dwSignature = DYNAMON_SIGNATURE;
  864. // Now create the port based on Port Type.
  865. switch ( portType )
  866. {
  867. case DOT4PORT:
  868. pNewPort = new CDot4Port( bIsPortActive, pszPortName, pszDevicePath );
  869. break;
  870. case TSPORT:
  871. pNewPort = new CTSPort( bIsPortActive, pszPortName, pszDevicePath );
  872. break;
  873. case P1394PORT:
  874. pNewPort = new C1394Port( bIsPortActive, pszPortName, pszDevicePath );
  875. break;
  876. case USBPORT:
  877. default:
  878. pNewPort = new CUSBPort( bIsPortActive, pszPortName, pszDevicePath );
  879. break;
  880. }
  881. if ( !pNewPort )
  882. {
  883. dwLastError = GetLastError();
  884. FreeSplMem( pPort );
  885. SetLastError( dwLastError );
  886. return FALSE;
  887. }
  888. TCHAR szPortDescription[MAX_PORT_DESC_LEN];
  889. dwSize = sizeof(szPortDescription);
  890. if ( ERROR_SUCCESS == RegQueryValueEx(*phKey,
  891. cszPortDescription,
  892. 0,
  893. NULL,
  894. (LPBYTE) szPortDescription,
  895. &dwSize) )
  896. {
  897. pNewPort->setPortDesc( szPortDescription );
  898. }
  899. // See if the port has a max data size restriction
  900. DWORD dwMaxBufferSize;
  901. dwSize = sizeof(dwMaxBufferSize);
  902. if ( ERROR_SUCCESS == RegQueryValueEx(*phKey,
  903. cszMaxBufferSize,
  904. 0,
  905. NULL,
  906. (LPBYTE) &dwMaxBufferSize,
  907. &dwSize) )
  908. {
  909. pNewPort->setMaxBuffer( dwMaxBufferSize );
  910. }
  911. // Assign Object Pointer to port list entry
  912. pPort->pBasePort = pNewPort;
  913. if ( pPrevPort )
  914. {
  915. pPort->pNext = pPrevPort->pNext;
  916. pPrevPort->pNext = pPort;
  917. }
  918. else
  919. {
  920. pPort->pNext = pMonitorInfo->pPortList;
  921. pMonitorInfo->pPortList = pPort;
  922. }
  923. //
  924. // If this is a port that is getting recycled remove from useless list
  925. //
  926. if ( pCur = FindUselessEntry( pMonitorInfo, pszDevicePath, &pPrev) )
  927. {
  928. if ( pPrev )
  929. pPrev->pNext = pCur->pNext;
  930. else
  931. pMonitorInfo->pJunkList = pCur->pNext;
  932. FreeSplMem(pCur);
  933. }
  934. //
  935. // On spooler startup we always have to check if the online/offline status
  936. // has to be changed. This is because spooler will remember the last state
  937. // before previous spooler shutdown which may be incorrect
  938. //
  939. AddToPortUpdateList(ppPortUpdateList, pPort, phKey);
  940. return TRUE;
  941. }
  942. VOID
  943. AddToPortUpdateList(
  944. IN OUT PPORT_UPDATE* ppPortUpdateList,
  945. IN PDYNAMON_PORT pPort,
  946. IN OUT HKEY* phKey
  947. )
  948. /*++
  949. Routine Description:
  950. Adds a port to the list of ports that need to status updated.
  951. Arguments:
  952. ppPortUpdateList : Pointer to the head of port update list
  953. pPort : Gives the port for which we need to update
  954. port status
  955. phKey : Pointer to reg handle. If port update element created
  956. this will be passed to background thread for use and
  957. closing
  958. Return Value:
  959. None
  960. If the port update element is created then phKey is set to invalid hanlde
  961. since ownership is going to be passed to background thread.
  962. New port update element will be the first in the list
  963. --*/
  964. {
  965. PPORT_UPDATE pTemp;
  966. if ( pTemp = (PPORT_UPDATE) AllocSplMem( sizeof(PORT_UPDATE) ) )
  967. {
  968. SafeCopy( MAX_PORT_LEN, pPort->pBasePort->getPortName(), pTemp->szPortName );
  969. pTemp->bActive = pPort->pBasePort->isActive();
  970. pTemp->hKey = *phKey;
  971. pTemp->pNext = *ppPortUpdateList;
  972. *ppPortUpdateList = pTemp;
  973. *phKey = INVALID_HANDLE_VALUE;
  974. }
  975. }
  976. VOID
  977. PassPortUpdateListToUpdateThread(
  978. PPORT_UPDATE pNewUpdateList
  979. )
  980. {
  981. // Get access to the Update List Pointer
  982. ECS( gDynaMonInfo.UpdateListCS );
  983. // Add the new list to the current list
  984. if ( gDynaMonInfo.pUpdateList )
  985. {
  986. // THere is something in the list already so add it to the end
  987. PPORT_UPDATE pCurUpdateList = gDynaMonInfo.pUpdateList;
  988. while ( pCurUpdateList->pNext )
  989. pCurUpdateList = pCurUpdateList->pNext;
  990. pCurUpdateList->pNext = pNewUpdateList;
  991. }
  992. else
  993. gDynaMonInfo.pUpdateList = pNewUpdateList;
  994. // Release acces to the list pointer
  995. LCS( gDynaMonInfo.UpdateListCS );
  996. // Now let the Update Thread go...
  997. SetEvent( gDynaMonInfo.hUpdateEvent );
  998. }
  999. void
  1000. SafeCopy(
  1001. IN DWORD MaxBufLen,
  1002. IN LPTSTR pszInString,
  1003. IN OUT LPTSTR pszOutString
  1004. )
  1005. {
  1006. // Check if the input string is bigger than the output buffer
  1007. (VOID) StringCchCopy (pszOutString, MaxBufLen, pszInString);
  1008. }