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.
1446 lines
42 KiB
1446 lines
42 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
All Rights Reserved
|
|
|
|
|
|
Module Name:
|
|
EnumPorts.c
|
|
|
|
Abstract:
|
|
USBMON enumports routines
|
|
|
|
Author:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
|
|
TCHAR sComma = TEXT(',');
|
|
TCHAR sZero = TEXT('\0');
|
|
TCHAR cszUSB[] = TEXT("USB");
|
|
TCHAR cszBaseName[] = TEXT("Base Name");
|
|
TCHAR cszPortNumber[] = TEXT("Port Number");
|
|
TCHAR cszRecyclable[] = TEXT("recyclable");
|
|
TCHAR cszPortDescription[] = TEXT("Port Description");
|
|
TCHAR cszUSBDescription[] = TEXT("Virtual printer port for USB");
|
|
TCHAR cszMonitorName[] = TEXT("USB Print Monitor");
|
|
|
|
|
|
DWORD gdwMonitorNameSize = sizeof(cszMonitorName);
|
|
BACKGROUND_THREAD_DATA FirstBackgroundThreadData = { NULL, NULL, NULL },
|
|
SecondBackgroundThreadData = { NULL, NULL, NULL };
|
|
|
|
#ifdef MYDEBUG
|
|
#include <stdio.h>
|
|
|
|
DWORD dwCount[10], dwTotalTime[10];
|
|
DWORD dwSkippedPorts, dwSkippedEnumPorts, dwPortUpdates;
|
|
#endif
|
|
//
|
|
// Default timeout values
|
|
//
|
|
#define READ_TIMEOUT_MULTIPLIER 0
|
|
#define READ_TIMEOUT_CONSTANT 60000
|
|
#define WRITE_TIMEOUT_MULTIPLIER 0
|
|
#define WRITE_TIMEOUT_CONSTANT 60000
|
|
|
|
|
|
|
|
/*++
|
|
This section explains how we do multiple thread synchronization between
|
|
enumport threads and background threads.
|
|
|
|
1. Only one enumports thread can walk the port list of dynamon.
|
|
Enumports uses EnumPortsCS critical section to ensure this.
|
|
|
|
2. We never want to make a spooler call from the EnumPorts thread. This is
|
|
because such a call could generate another call back to dynamon and we
|
|
do not want a deadlock. For example OpenPrinter call to netware print
|
|
provider will generate an EnumPorts call.
|
|
|
|
So when we find a need to change the printer online/offline staus,
|
|
because correponding dynamon ports are active/inactive, we will spin a
|
|
background thread to make the spooler calls to do that.
|
|
|
|
3. We want to make sure background thread does not block EnumPorts. The
|
|
reason is each EnumPorts call could spin a background thread, and
|
|
each backgound thread call a number of OpenPrinter calls, and each
|
|
OpenPrinter call generate an EnumPorts call.
|
|
|
|
So we pass a separate port update list to the background thread. And
|
|
we make sure EnumPorts thread does not wait for the background thread
|
|
to complete to return to spooler.
|
|
|
|
4. We want to control the number and execution order of background threads.
|
|
|
|
We will make sure at a time there can be only 2 background threads. One
|
|
actually processing port update lists, and another just pending
|
|
execution -- scheduled, but waiting for the active background thread
|
|
to complete execution before processing it's port update list.
|
|
|
|
--*/
|
|
|
|
|
|
PUSELESS_PORT_INFO
|
|
FindUselessEntry(
|
|
IN PUSBMON_MONITOR_INFO pMonitorInfo,
|
|
IN LPTSTR pszDevicePath,
|
|
OUT PUSELESS_PORT_INFO *ppPrev
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Searches for a device path in the useless port list
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
NULL if no entry found in the list
|
|
Else a valid USELESS_PORT_INFO pointer
|
|
Weather port is found or not *ppPrev will return the previous element
|
|
|
|
--*/
|
|
{
|
|
INT iCmp;
|
|
PUSELESS_PORT_INFO pHead;
|
|
|
|
for ( pHead = pMonitorInfo->pJunkList, *ppPrev = NULL ;
|
|
pHead && (iCmp = lstrcmp(pszDevicePath, pHead->szDevicePath)) < 0 ;
|
|
*ppPrev = pHead, pHead = pHead->pNext )
|
|
;
|
|
|
|
//
|
|
// If useless port should go in the middle but not currently there
|
|
//
|
|
if ( pHead && iCmp != 0 )
|
|
pHead = NULL;
|
|
|
|
return pHead;
|
|
}
|
|
|
|
|
|
VOID
|
|
AddUselessPortEntry(
|
|
IN PUSBMON_MONITOR_INFO pMonitorInfo,
|
|
IN LPTSTR pszDevicePath
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This adds a useless port entry to our list. So next time we see an inactive
|
|
port that is already in our known usless port list we can skip the port
|
|
entry
|
|
|
|
Arguments:
|
|
pMonitorInfo : Pointer to monitor inf
|
|
pszDevicePath : Device path for the useless port
|
|
|
|
Return Value:
|
|
None. Under normal circumstances will add a useless entry to our list
|
|
|
|
--*/
|
|
{
|
|
PUSELESS_PORT_INFO pTemp, pPrev;
|
|
|
|
pTemp = FindUselessEntry(pMonitorInfo, pszDevicePath, &pPrev);
|
|
|
|
//
|
|
// Don't add an entry that is already there
|
|
//
|
|
SPLASSERT(pTemp == NULL);
|
|
|
|
if ( pTemp = (PUSELESS_PORT_INFO) AllocSplMem(sizeof(*pTemp)) ) {
|
|
|
|
lstrcpy(pTemp->szDevicePath, pszDevicePath);
|
|
++pMonitorInfo->dwUselessPortCount;
|
|
|
|
if ( pPrev ) {
|
|
|
|
pTemp->pNext = pPrev->pNext;
|
|
pPrev->pNext = pTemp;
|
|
} else {
|
|
|
|
pTemp->pNext = pMonitorInfo->pJunkList;
|
|
pMonitorInfo->pJunkList = pTemp;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
AddToPortUpdateList(
|
|
IN OUT PPORT_UPDATE_INFO *ppPortUpdateInfo,
|
|
IN PUSBMON_PORT_INFO pPortInfo,
|
|
IN OUT HKEY *phKey
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Adds a port to the list of ports that need to status updated.
|
|
|
|
Arguments:
|
|
ppPortUpdateInfo : Pointer to the head of port update list
|
|
pPortInfo : Gives the port for which we need to update
|
|
port status
|
|
phKey : Pointer to reg handle. If port update element created
|
|
this will be passed to background thread for use and
|
|
closing
|
|
|
|
Return Value:
|
|
None
|
|
|
|
If the port update element is created then phKey is set to invalid hanlde
|
|
since ownership is going to be passed to background thread.
|
|
|
|
New port update element will be the first in the list
|
|
|
|
--*/
|
|
{
|
|
PPORT_UPDATE_INFO pTemp;
|
|
|
|
if ( pTemp = (PPORT_UPDATE_INFO) AllocSplMem(sizeof(*pTemp)) ) {
|
|
|
|
lstrcpy(pTemp->szPortName, pPortInfo->szPortName);
|
|
pTemp->bActive = (pPortInfo->dwDeviceFlags & SPINT_ACTIVE) != 0;
|
|
pTemp->hKey = *phKey;
|
|
pTemp->pNext = *ppPortUpdateInfo;
|
|
*ppPortUpdateInfo = pTemp;
|
|
|
|
*phKey = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
|
|
PUSBMON_PORT_INFO
|
|
FindPortUsingDevicePath(
|
|
IN PUSBMON_MONITOR_INFO pMonitorInfo,
|
|
IN LPTSTR pszDevicePath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Finds a port by device path.
|
|
|
|
Arguments:
|
|
pMonitorInfo : Pointer to MONITOR_INFO structure
|
|
pszDevicePath : Device path name to search for
|
|
|
|
Return Value:
|
|
If NULL port is not in list, else pointer to the PORT_INFO entry for the
|
|
given device path
|
|
|
|
--*/
|
|
{
|
|
INT iCmp;
|
|
PUSBMON_PORT_INFO pHead;
|
|
|
|
EnterCriticalSection(&pMonitorInfo->EnumPortsCS);
|
|
|
|
//
|
|
// Port list is sorted on port name, so we have to scan the whole list
|
|
//
|
|
for ( pHead = pMonitorInfo->pPortInfo ; pHead ; pHead = pHead->pNext )
|
|
if ( lstrcmp(pszDevicePath, pHead->szDevicePath) == 0 )
|
|
break;
|
|
|
|
LeaveCriticalSection(&pMonitorInfo->EnumPortsCS);
|
|
|
|
return pHead;
|
|
}
|
|
|
|
|
|
PUSBMON_PORT_INFO
|
|
FindPort(
|
|
IN PUSBMON_MONITOR_INFO pMonitorInfo,
|
|
IN LPTSTR pszPortName,
|
|
OUT PUSBMON_PORT_INFO *ppPrev
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Finds a port by name. Ports are kept in singly linked list sorted by name.
|
|
If found previous in the list is returned via *ppPrev.
|
|
|
|
Arguments:
|
|
pHead : Head pointer to port list
|
|
pszPortName : Name of port to look
|
|
ppPrev : On return will have pointer to previous element
|
|
|
|
Return Value:
|
|
If NULL port is not in list, else the found element
|
|
Weather port is found or not *ppPrev will return the previous element
|
|
|
|
--*/
|
|
{
|
|
INT iCmp;
|
|
PUSBMON_PORT_INFO pHead;
|
|
|
|
EnterCriticalSection(&pMonitorInfo->EnumPortsCS);
|
|
|
|
pHead = pMonitorInfo->pPortInfo;
|
|
for ( *ppPrev = NULL ;
|
|
pHead && (iCmp = lstrcmp(pszPortName, pHead->szPortName)) < 0 ;
|
|
*ppPrev = pHead, pHead = pHead->pNext )
|
|
;
|
|
|
|
//
|
|
// If port should go in the middle but not currently there
|
|
//
|
|
if ( pHead && iCmp != 0 )
|
|
pHead = NULL;
|
|
|
|
LeaveCriticalSection(&pMonitorInfo->EnumPortsCS);
|
|
|
|
return pHead;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AddPortToList(
|
|
LPTSTR pszPortName,
|
|
LPTSTR pszDevicePath,
|
|
DWORD dwDeviceFlags,
|
|
HKEY *phKey,
|
|
PUSBMON_MONITOR_INFO pMonitorInfo,
|
|
PUSBMON_PORT_INFO pPrevPortInfo,
|
|
PPORT_UPDATE_INFO *ppPortUpdateInfo
|
|
)
|
|
{
|
|
DWORD dwSize;
|
|
PUSBMON_PORT_INFO pPortInfo;
|
|
PUSELESS_PORT_INFO pCur, pPrev;
|
|
|
|
SPLASSERT(FindPortUsingDevicePath(pMonitorInfo, pszDevicePath) == NULL);
|
|
|
|
pPortInfo = (PUSBMON_PORT_INFO) AllocSplMem(sizeof(USBMON_PORT_INFO));
|
|
if ( !pPortInfo )
|
|
return FALSE;
|
|
|
|
pPortInfo->dwSignature = USB_SIGNATURE;
|
|
pPortInfo->hDeviceHandle = INVALID_HANDLE_VALUE;
|
|
pPortInfo->dwDeviceFlags = dwDeviceFlags;
|
|
|
|
pPortInfo->ReadTimeoutMultiplier = READ_TIMEOUT_MULTIPLIER;
|
|
pPortInfo->ReadTimeoutMultiplier = READ_TIMEOUT_MULTIPLIER;
|
|
pPortInfo->WriteTimeoutConstant = WRITE_TIMEOUT_CONSTANT;
|
|
pPortInfo->WriteTimeoutConstant = WRITE_TIMEOUT_CONSTANT;
|
|
|
|
lstrcpy(pPortInfo->szPortName, pszPortName);
|
|
lstrcpy(pPortInfo->szDevicePath, pszDevicePath);
|
|
|
|
dwSize = sizeof(pPortInfo->szPortDescription);
|
|
if ( ERROR_SUCCESS != RegQueryValueEx(*phKey,
|
|
cszPortDescription,
|
|
0,
|
|
NULL,
|
|
(LPBYTE)(pPortInfo->szPortDescription),
|
|
&dwSize) ) {
|
|
|
|
lstrcpy(pPortInfo->szPortDescription, cszUSBDescription);
|
|
}
|
|
|
|
if ( pPrevPortInfo ) {
|
|
|
|
pPortInfo->pNext = pPrevPortInfo->pNext;
|
|
pPrevPortInfo->pNext = pPortInfo;
|
|
} else {
|
|
|
|
pPortInfo->pNext = pMonitorInfo->pPortInfo;
|
|
pMonitorInfo->pPortInfo = pPortInfo;
|
|
}
|
|
|
|
//
|
|
// If this is a port that is getting recycled remove from useless list
|
|
//
|
|
if ( pCur = FindUselessEntry(pMonitorInfo, pszDevicePath, &pPrev) ) {
|
|
|
|
if ( pPrev )
|
|
pPrev->pNext = pCur->pNext;
|
|
else
|
|
pMonitorInfo->pJunkList = pCur->pNext;
|
|
|
|
--pMonitorInfo->dwUselessPortCount;
|
|
FreeSplMem(pCur);
|
|
}
|
|
|
|
//
|
|
// On spooler startup we always have to check if the online/offline status
|
|
// has to be changed. This is because spooler will remember the last state
|
|
// before previous spooler shutdown which may be incorrect
|
|
//
|
|
AddToPortUpdateList(ppPortUpdateInfo, pPortInfo, phKey);
|
|
|
|
++pMonitorInfo->dwPortCount;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
HKEY
|
|
GetPortNameAndRegKey(
|
|
IN PSETUPAPI_INFO pSetupInfo,
|
|
IN HDEVINFO hDevList,
|
|
IN PSP_DEVICE_INTERFACE_DATA pDeviceInterface,
|
|
OUT LPTSTR pszPortName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Find port name for a device interface and also return reg handle
|
|
|
|
Arguments:
|
|
hDevList : List of USB printer devices
|
|
pDeviceInterface : pointer to device interface in question
|
|
pszPortName : Port name on return.
|
|
|
|
Return Value:
|
|
INVALID_HANDLE_VALUE on some errors.
|
|
Otherwize a valid registry handle with pszPortName giving port name
|
|
|
|
--*/
|
|
{
|
|
HKEY hKey = INVALID_HANDLE_VALUE;
|
|
DWORD dwPortNumber, dwNeeded, dwLastError;
|
|
TCHAR szPortBaseName[MAX_PORT_LEN-3];
|
|
|
|
#ifdef MYDEBUG
|
|
DWORD dwTime;
|
|
|
|
dwTime = GetTickCount();
|
|
#endif
|
|
|
|
hKey = pSetupInfo->OpenDeviceInterfaceRegKey(hDevList,
|
|
pDeviceInterface,
|
|
0,
|
|
KEY_ALL_ACCESS);
|
|
#ifdef MYDEBUG
|
|
dwTime = GetTickCount() - dwTime;
|
|
++dwCount[0];
|
|
dwTotalTime[0] += dwTime;
|
|
#endif
|
|
|
|
if ( hKey == INVALID_HANDLE_VALUE ) {
|
|
|
|
dwLastError = GetLastError();
|
|
DBGMSG(DBG_ERROR,
|
|
("usbmon: WalkPortList: SetupDiOpenDeviceInterfaceRegKey failed with error %d\n",
|
|
dwLastError));
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
dwNeeded = sizeof(dwPortNumber);
|
|
if ( ERROR_SUCCESS != RegQueryValueEx(hKey, cszPortNumber, 0, NULL,
|
|
(LPBYTE)&dwPortNumber, &dwNeeded) ) {
|
|
|
|
DBGMSG(DBG_WARNING,
|
|
("usbmon: GetPortNameAndRegKey: RegQueryValueEx failed for port number\n"));
|
|
goto Fail;
|
|
}
|
|
|
|
dwNeeded = sizeof(szPortBaseName);
|
|
if ( ERROR_SUCCESS != RegQueryValueEx(hKey, cszBaseName, 0, NULL,
|
|
(LPBYTE)szPortBaseName, &dwNeeded) ) {
|
|
lstrcpy(szPortBaseName, cszUSB);
|
|
}
|
|
|
|
wsprintf(pszPortName, TEXT("%s%03u"), szPortBaseName, dwPortNumber);
|
|
|
|
return hKey;
|
|
|
|
Fail:
|
|
RegCloseKey(hKey);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SetOnlineStaus(
|
|
LPPRINTER_INFO_5 pPrinterInfo5,
|
|
BOOL bOnline
|
|
)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
HANDLE hPrinter;
|
|
PRINTER_DEFAULTS PrinterDefault = {NULL, NULL, PRINTER_ALL_ACCESS};
|
|
|
|
#ifdef MYDEBUG
|
|
DWORD dwTime;
|
|
#endif
|
|
|
|
//
|
|
// Force all DOT4 ports to remain online at all times.
|
|
//
|
|
if( lstrncmpi( pPrinterInfo5->pPortName, TEXT("DOT4"), lstrlen(TEXT("DOT4")) ) == 0 )
|
|
bOnline = TRUE;
|
|
|
|
//
|
|
// Check if spooler already has the correct status
|
|
// (can happen on spooler startup)
|
|
//
|
|
if ( bOnline ) {
|
|
|
|
if ( !(pPrinterInfo5->Attributes & PRINTER_ATTRIBUTE_WORK_OFFLINE) )
|
|
return TRUE;
|
|
} else
|
|
if ( pPrinterInfo5->Attributes & PRINTER_ATTRIBUTE_WORK_OFFLINE )
|
|
return TRUE;
|
|
|
|
#ifdef MYDEBUG
|
|
dwTime = GetTickCount();
|
|
#endif
|
|
|
|
if ( !OpenPrinter(pPrinterInfo5->pPrinterName, &hPrinter, &PrinterDefault) )
|
|
return FALSE;
|
|
|
|
if ( bOnline )
|
|
pPrinterInfo5->Attributes &= ~PRINTER_ATTRIBUTE_WORK_OFFLINE;
|
|
else
|
|
pPrinterInfo5->Attributes |= PRINTER_ATTRIBUTE_WORK_OFFLINE;
|
|
|
|
bRet = SetPrinter(hPrinter, 5, (LPBYTE)pPrinterInfo5, 0);
|
|
|
|
ClosePrinter(hPrinter);
|
|
|
|
#ifdef MYDEBUG
|
|
dwTime = GetTickCount() - dwTime;
|
|
++dwCount[7];
|
|
dwTotalTime[7] += dwTime;
|
|
#endif
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
BOOL
|
|
PortNameNeededBySpooler(
|
|
IN LPTSTR pszPortName,
|
|
IN LPPRINTER_INFO_5 pPrinterInfo5,
|
|
IN DWORD dwPrinters,
|
|
IN BOOL bActive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Tells if a port is needed by spooler. Any port to which spooler currently
|
|
has a printer going is needed.
|
|
|
|
Arguments:
|
|
pszPortName : Port name in question
|
|
pPrinterInfo5 : List of PrinterInfo5s
|
|
dwPrinters : Count of the list of printers
|
|
|
|
Return Value:
|
|
TRUE if spooler currently has a printer which is using the port
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
BOOL bPortUsedByAPrinter = FALSE, bPrinterUsesOnlyThisPort;
|
|
DWORD dwIndex;
|
|
LPTSTR pszStr1, pszStr2;
|
|
|
|
for ( dwIndex = 0 ; dwIndex < dwPrinters ; ++dwIndex, ++pPrinterInfo5 ) {
|
|
|
|
bPrinterUsesOnlyThisPort = FALSE;
|
|
//
|
|
// Port names are returned comma separated by spooler,
|
|
// and there are blanks
|
|
//
|
|
pszStr1 = pPrinterInfo5->pPortName;
|
|
|
|
if ( lstrcmpi(pszPortName, pszStr1) == 0 )
|
|
bPortUsedByAPrinter = bPrinterUsesOnlyThisPort = TRUE;
|
|
else {
|
|
|
|
//
|
|
// Look at each port in the list of ports printer uses
|
|
//
|
|
while ( pszStr2 = lstrchr(pszStr1, sComma) ) {
|
|
|
|
*pszStr2 = sZero;
|
|
if( lstrcmpi(pszPortName, pszStr1) == 0 )
|
|
bPortUsedByAPrinter = TRUE;
|
|
*pszStr2 = sComma; // Put the comma back
|
|
|
|
if ( bPortUsedByAPrinter )
|
|
break;
|
|
|
|
pszStr1 = pszStr2 + 1;
|
|
|
|
//
|
|
// Skip spaces
|
|
//
|
|
while ( *pszStr1 == TEXT(' ') )
|
|
++pszStr1;
|
|
}
|
|
|
|
if ( !bPortUsedByAPrinter )
|
|
bPortUsedByAPrinter = lstrcmpi(pszPortName, pszStr1) == 0;
|
|
}
|
|
|
|
//
|
|
// We will change only status of printer for non-pooled printers only
|
|
//
|
|
if ( bPrinterUsesOnlyThisPort )
|
|
SetOnlineStaus(pPrinterInfo5, bActive);
|
|
}
|
|
|
|
return bPortUsedByAPrinter;
|
|
}
|
|
|
|
|
|
VOID
|
|
UpdatePortInfo(
|
|
PUSBMON_PORT_INFO pPortInfo,
|
|
LPTSTR pszDevicePath,
|
|
DWORD dwDeviceFlags,
|
|
HKEY *phKey,
|
|
PPORT_UPDATE_INFO *ppPortUpdateInfo
|
|
)
|
|
{
|
|
DWORD dwSize;
|
|
|
|
lstrcpy(pPortInfo->szDevicePath, pszDevicePath);
|
|
|
|
dwSize = sizeof(pPortInfo->szPortDescription);
|
|
if ( ERROR_SUCCESS != RegQueryValueEx(*phKey,
|
|
cszPortDescription,
|
|
0,
|
|
NULL,
|
|
(LPBYTE)(pPortInfo->szPortDescription),
|
|
&dwSize) ) {
|
|
|
|
lstrcpy(pPortInfo->szPortDescription, cszUSBDescription);
|
|
}
|
|
|
|
if ( pPortInfo->dwDeviceFlags != dwDeviceFlags ) {
|
|
|
|
pPortInfo->dwDeviceFlags = dwDeviceFlags;
|
|
AddToPortUpdateList(ppPortUpdateInfo, pPortInfo, phKey);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
PrinterInfo5s(
|
|
OUT LPPRINTER_INFO_5 *ppPrinterInfo5,
|
|
OUT LPDWORD pdwReturned
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Does an EnumPrinter and returns a list of PRINTER_INFO_5s of all local
|
|
printers. Caller should free the pointer.
|
|
|
|
Arguments:
|
|
ppPrinterInfo5 : Points to PRINTER_INFO_5s on return
|
|
pdwReturned : Tells how many PRINTER_INFO_5s are returned
|
|
|
|
Return Value:
|
|
TRUE on success, FALSE else
|
|
|
|
--*/
|
|
{
|
|
BOOL bRet = FALSE;
|
|
static DWORD dwNeeded = 0;
|
|
LPBYTE pBuf = NULL;
|
|
|
|
#ifdef MYDEBUG
|
|
DWORD dwTime;
|
|
|
|
dwTime = GetTickCount();
|
|
#endif
|
|
|
|
*pdwReturned = 0;
|
|
|
|
if ( !(pBuf = AllocSplMem(dwNeeded)) )
|
|
goto Cleanup;
|
|
|
|
if ( !EnumPrinters(PRINTER_ENUM_LOCAL,
|
|
NULL,
|
|
5,
|
|
pBuf,
|
|
dwNeeded,
|
|
&dwNeeded,
|
|
pdwReturned) ) {
|
|
|
|
if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
|
|
goto Cleanup;
|
|
|
|
FreeSplMem(pBuf);
|
|
if ( !(pBuf = AllocSplMem(dwNeeded)) ||
|
|
!EnumPrinters(PRINTER_ENUM_LOCAL,
|
|
NULL,
|
|
5,
|
|
pBuf,
|
|
dwNeeded,
|
|
&dwNeeded,
|
|
pdwReturned) ) {
|
|
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
bRet = TRUE;
|
|
|
|
Cleanup:
|
|
|
|
if ( bRet && *pdwReturned ) {
|
|
|
|
*ppPrinterInfo5 = (LPPRINTER_INFO_5)pBuf;
|
|
} else {
|
|
|
|
FreeSplMem(pBuf);
|
|
|
|
*ppPrinterInfo5 = NULL;
|
|
*pdwReturned = 0;
|
|
}
|
|
|
|
#ifdef MYDEBUG
|
|
dwTime = GetTickCount() - dwTime;
|
|
++dwCount[6];
|
|
dwTotalTime[6] += dwTime;
|
|
#endif
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
VOID
|
|
BackgroundThread(
|
|
HANDLE hEvent
|
|
)
|
|
/*++
|
|
This is the body of background thread which does the following:
|
|
1. Update printer online/offline with spooler for printers using
|
|
dynamon ports
|
|
2. Mark those ports that are inactive and not needed by spooler
|
|
as recyclable
|
|
3. When exiting if there is a second background thread scheduled
|
|
then trigger it to go
|
|
--*/
|
|
{
|
|
HANDLE hEventToSet = NULL;
|
|
DWORD dwPrinters;
|
|
PPORT_UPDATE_INFO pPortUpdateList, pCur;
|
|
LPCRITICAL_SECTION pBackThreadCS;
|
|
LPPRINTER_INFO_5 pPrinterInfo5List = NULL;
|
|
|
|
#ifdef MYDEBUG
|
|
DWORD dwTime;
|
|
CHAR szBuf[200];
|
|
|
|
dwTime = GetTickCount();
|
|
#endif
|
|
|
|
//
|
|
// Background waits here to be triggered to do it's work
|
|
//
|
|
WaitForSingleObject(hEvent, INFINITE);
|
|
|
|
//
|
|
// This is the first/active background thread at this point
|
|
//
|
|
SPLASSERT(hEvent == FirstBackgroundThreadData.hWaitToStart);
|
|
|
|
//
|
|
// Until we are here (i.e. the thread is triggered to tell this is the
|
|
// active/first background thread) we can't access any of these things
|
|
//
|
|
|
|
pPortUpdateList = FirstBackgroundThreadData.pPortUpdateList;
|
|
pBackThreadCS = &FirstBackgroundThreadData.pMonitorInfo->BackThreadCS;
|
|
|
|
if ( PrinterInfo5s(&pPrinterInfo5List, &dwPrinters) ) {
|
|
|
|
for ( pCur = pPortUpdateList ; pCur ; pCur = pCur->pNext ) {
|
|
|
|
if ( !PortNameNeededBySpooler(pCur->szPortName,
|
|
pPrinterInfo5List,
|
|
dwPrinters,
|
|
pCur->bActive) &&
|
|
!pCur->bActive ) {
|
|
|
|
RegSetValueEx(pCur->hKey, cszRecyclable, 0, REG_NONE, 0, 0);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now the thread has done what it was spun off to do.
|
|
//
|
|
|
|
EnterCriticalSection(pBackThreadCS);
|
|
|
|
//
|
|
// Remove this thread from being the first/active background thread
|
|
//
|
|
FirstBackgroundThreadData.hWaitToStart = NULL;
|
|
FirstBackgroundThreadData.pPortUpdateList = NULL;
|
|
FirstBackgroundThreadData.pMonitorInfo = NULL;
|
|
CloseHandle(hEvent);
|
|
|
|
//
|
|
// If there is a second thread it becomes the first now
|
|
//
|
|
if ( SecondBackgroundThreadData.hWaitToStart ) {
|
|
|
|
hEventToSet
|
|
= FirstBackgroundThreadData.hWaitToStart
|
|
= SecondBackgroundThreadData.hWaitToStart;
|
|
FirstBackgroundThreadData.pPortUpdateList
|
|
= SecondBackgroundThreadData.pPortUpdateList;
|
|
FirstBackgroundThreadData.pMonitorInfo
|
|
= SecondBackgroundThreadData.pMonitorInfo;
|
|
|
|
SecondBackgroundThreadData.hWaitToStart = NULL;
|
|
SecondBackgroundThreadData.pPortUpdateList = NULL;
|
|
SecondBackgroundThreadData.pMonitorInfo = NULL;
|
|
}
|
|
LeaveCriticalSection(pBackThreadCS);
|
|
|
|
//
|
|
// If there is a second thread trigger it
|
|
//
|
|
if ( hEventToSet )
|
|
SetEvent(hEventToSet);
|
|
|
|
FreeSplMem(pPrinterInfo5List);
|
|
|
|
//
|
|
// Free the port update list
|
|
//
|
|
while ( pCur = pPortUpdateList ) {
|
|
|
|
pPortUpdateList = pPortUpdateList->pNext;
|
|
RegCloseKey(pCur->hKey);
|
|
FreeSplMem(pCur);
|
|
}
|
|
|
|
#ifdef MYDEBUG
|
|
dwTime = GetTickCount() - dwTime;
|
|
++dwCount[4];
|
|
dwTotalTime[4] += dwTime;
|
|
sprintf(szBuf, "BackgroundThread: %d\n",
|
|
dwTotalTime[4]/dwCount[4]);
|
|
OutputDebugStringA(szBuf);
|
|
sprintf(szBuf, "PrinterInfo5s: %d\n",
|
|
dwTotalTime[6]/dwCount[6]);
|
|
OutputDebugStringA(szBuf);
|
|
|
|
if ( dwCount[7] ) {
|
|
|
|
sprintf(szBuf, "SetOnlineStatus: %d\n",
|
|
dwTotalTime[7]/dwCount[7]);
|
|
OutputDebugStringA(szBuf);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
HANDLE
|
|
CreateBackgroundThreadAndReturnEventToTrigger(
|
|
VOID
|
|
)
|
|
/*++
|
|
Creates a background thread and passes it an event on which to wait for
|
|
starting execution. Returns the event handle.
|
|
--*/
|
|
{
|
|
HANDLE hThread = NULL, hEvent;
|
|
DWORD dwThreadId;
|
|
|
|
if ( hEvent = CreateEvent(NULL, TRUE, FALSE, NULL) ) {
|
|
|
|
if ( hThread = CreateThread(NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE)BackgroundThread,
|
|
hEvent,
|
|
0,
|
|
&dwThreadId) ) {
|
|
|
|
CloseHandle(hThread);
|
|
} else {
|
|
|
|
CloseHandle(hEvent);
|
|
hEvent = NULL;
|
|
}
|
|
}
|
|
|
|
return hEvent;
|
|
}
|
|
|
|
|
|
VOID
|
|
PassPortUpdateListToBackgroundThread(
|
|
PUSBMON_MONITOR_INFO pMonitorInfo,
|
|
PPORT_UPDATE_INFO pPortUpdateList
|
|
)
|
|
/*++
|
|
Called from EnumPorts thread with a list of port update elements to be
|
|
passed to the background thread.
|
|
a. If there is no background thread then spin one and trigger it
|
|
b. If there is only one background thread then spin the second one. First
|
|
one will trigger the second one on completion
|
|
c. If there are two background threads, one active and one waiting to be
|
|
triggered, then add the port update elements to the second one's list
|
|
--*/
|
|
{
|
|
DWORD dwThreadCount = 0;
|
|
PPORT_UPDATE_INFO pCur, pNext, pLast;
|
|
|
|
if ( pPortUpdateList == NULL )
|
|
return;
|
|
|
|
EnterCriticalSection(&pMonitorInfo->BackThreadCS);
|
|
|
|
if ( FirstBackgroundThreadData.hWaitToStart ) {
|
|
|
|
++dwThreadCount;
|
|
if ( SecondBackgroundThreadData.hWaitToStart )
|
|
++dwThreadCount;
|
|
}
|
|
|
|
switch (dwThreadCount) {
|
|
|
|
case 0:
|
|
if ( FirstBackgroundThreadData.hWaitToStart
|
|
= CreateBackgroundThreadAndReturnEventToTrigger() ) {
|
|
|
|
FirstBackgroundThreadData.pMonitorInfo = pMonitorInfo;
|
|
FirstBackgroundThreadData.pPortUpdateList = pPortUpdateList;
|
|
SetEvent(FirstBackgroundThreadData.hWaitToStart);
|
|
}
|
|
break;
|
|
case 1:
|
|
if ( SecondBackgroundThreadData.hWaitToStart
|
|
= CreateBackgroundThreadAndReturnEventToTrigger() ) {
|
|
|
|
SecondBackgroundThreadData.pMonitorInfo = pMonitorInfo;
|
|
SecondBackgroundThreadData.pPortUpdateList = pPortUpdateList;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
//
|
|
// Note: We know both lists can't be empty
|
|
//
|
|
for ( pCur = pPortUpdateList; pCur ; pCur = pNext ) {
|
|
|
|
pNext = pCur->pNext;
|
|
|
|
for ( pLast = SecondBackgroundThreadData.pPortUpdateList ;
|
|
pLast ; pLast = pLast->pNext ) {
|
|
|
|
//
|
|
// If there is a duplicate update old entry with info
|
|
// from new one and free the new entry
|
|
//
|
|
if ( !lstrcmpi(pLast->szPortName, pCur->szPortName) ) {
|
|
|
|
RegCloseKey(pLast->hKey);
|
|
pLast->hKey = pCur->hKey;
|
|
pLast->bActive = pCur->bActive;
|
|
FreeSplMem(pCur);
|
|
break; // out of inner for loop
|
|
} else if ( pLast->pNext == NULL ) {
|
|
|
|
//
|
|
// If we hit end of list then append entry
|
|
//
|
|
pLast->pNext = pCur;
|
|
pCur->pNext = NULL;
|
|
break; // out of inner for loop
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Should not happen
|
|
//
|
|
SPLASSERT(dwThreadCount == 0);
|
|
}
|
|
|
|
LeaveCriticalSection(&pMonitorInfo->BackThreadCS);
|
|
}
|
|
|
|
|
|
VOID
|
|
ProcessPortInfo(
|
|
IN PSETUPAPI_INFO pSetupApiInfo,
|
|
IN PUSBMON_MONITOR_INFO pMonitorInfo,
|
|
IN HDEVINFO hDevList,
|
|
IN PSP_DEVICE_INTERFACE_DATA pDeviceInterface,
|
|
IN PSP_DEVICE_INTERFACE_DETAIL_DATA pDeviceDetail,
|
|
IN OUT PPORT_UPDATE_INFO *ppPortUpdateInfo
|
|
)
|
|
{
|
|
HKEY hKey = INVALID_HANDLE_VALUE;
|
|
TCHAR szPortName[MAX_PORT_LEN];
|
|
PUSBMON_PORT_INFO pCur, pPrev;
|
|
|
|
#ifdef MYDEBUG
|
|
++dwPortUpdates;
|
|
#endif
|
|
|
|
hKey = GetPortNameAndRegKey(pSetupApiInfo, hDevList,
|
|
pDeviceInterface, szPortName);
|
|
|
|
if ( hKey == INVALID_HANDLE_VALUE ) {
|
|
|
|
//
|
|
// If this port is inactive and is not in our known port list
|
|
// add to useless list. Earlier we would have been opening the registry
|
|
// every time and find that port number is missing because of KM drivers
|
|
// not deleting the inactive device interfaces
|
|
//
|
|
if ( !(pDeviceInterface->Flags & SPINT_ACTIVE) &&
|
|
!FindPortUsingDevicePath(pMonitorInfo, pDeviceDetail->DevicePath) )
|
|
AddUselessPortEntry(pMonitorInfo, pDeviceDetail->DevicePath);
|
|
|
|
return;
|
|
}
|
|
|
|
pCur = FindPort(pMonitorInfo, szPortName, &pPrev);
|
|
|
|
//
|
|
// Port info is currently in our list?
|
|
//
|
|
if ( pCur ) {
|
|
|
|
//
|
|
// Did the device path or flags change?
|
|
//
|
|
if ( pCur->dwDeviceFlags != pDeviceInterface->Flags ||
|
|
lstrcmp(pDeviceDetail->DevicePath, pCur->szDevicePath) ) {
|
|
|
|
UpdatePortInfo(pCur, pDeviceDetail->DevicePath,
|
|
pDeviceInterface->Flags, &hKey, ppPortUpdateInfo);
|
|
}
|
|
} else {
|
|
|
|
AddPortToList(szPortName, pDeviceDetail->DevicePath,
|
|
pDeviceInterface->Flags, &hKey, pMonitorInfo, pPrev,
|
|
ppPortUpdateInfo);
|
|
|
|
}
|
|
|
|
if ( hKey != INVALID_HANDLE_VALUE )
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
|
|
DWORD
|
|
WalkPortList(
|
|
PSETUPAPI_INFO pSetupApiInfo,
|
|
PUSBMON_MONITOR_INFO pMonitorInfo,
|
|
PPORT_UPDATE_INFO *ppPortUpdateInfo
|
|
)
|
|
{
|
|
DWORD dwIndex, dwLastError, dwSize, dwNeeded;
|
|
HANDLE hToken;
|
|
HDEVINFO hDevList = INVALID_HANDLE_VALUE;
|
|
PUSBMON_PORT_INFO pPtr;
|
|
PUSELESS_PORT_INFO pCur, pPrev;
|
|
SP_DEVICE_INTERFACE_DATA DeviceInterface;
|
|
PSP_DEVICE_INTERFACE_DETAIL_DATA pDeviceDetail = NULL;
|
|
|
|
#ifdef MYDEBUG
|
|
DWORD dwTime;
|
|
#endif
|
|
|
|
EnterCriticalSection(&pMonitorInfo->EnumPortsCS);
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
hDevList = pSetupApiInfo->GetClassDevs((GUID *)&USB_PRINTER_GUID,
|
|
NULL,
|
|
NULL,
|
|
DIGCF_INTERFACEDEVICE);
|
|
|
|
if ( hDevList == INVALID_HANDLE_VALUE ) {
|
|
|
|
dwLastError = GetLastError();
|
|
goto Done;
|
|
}
|
|
|
|
dwSize = sizeof(PSP_DEVICE_INTERFACE_DETAIL_DATA)
|
|
+ MAX_DEVICE_PATH * sizeof(TCHAR);
|
|
|
|
pDeviceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA) AllocSplMem(dwSize);
|
|
|
|
if ( !pDeviceDetail ) {
|
|
|
|
dwLastError = GetLastError();
|
|
goto Done;
|
|
}
|
|
|
|
dwLastError = ERROR_SUCCESS;
|
|
dwIndex = 0;
|
|
pDeviceDetail->cbSize = sizeof(*pDeviceDetail);
|
|
DeviceInterface.cbSize = sizeof(DeviceInterface);
|
|
do {
|
|
|
|
#ifdef MYDEBUG
|
|
dwTime = GetTickCount();
|
|
#endif
|
|
if ( !pSetupApiInfo->EnumDeviceInterfaces(hDevList,
|
|
NULL,
|
|
(GUID *)&USB_PRINTER_GUID,
|
|
dwIndex,
|
|
&DeviceInterface) ) {
|
|
|
|
dwLastError = GetLastError();
|
|
if ( dwLastError == ERROR_NO_MORE_ITEMS )
|
|
break; // Normal exit
|
|
|
|
DBGMSG(DBG_WARNING,
|
|
("usbmon: WalkPortList: SetupDiEnumDeviceInterfaces failed with %d for inderx %d\n",
|
|
dwLastError, dwIndex));
|
|
goto Next;
|
|
}
|
|
|
|
#ifdef MYDEBUG
|
|
dwTime = GetTickCount() - dwTime;
|
|
++dwCount[1];
|
|
dwTotalTime[1] += dwTime;
|
|
|
|
dwTime = GetTickCount();
|
|
#endif
|
|
|
|
if ( !pSetupApiInfo->GetDeviceInterfaceDetail(hDevList,
|
|
&DeviceInterface,
|
|
pDeviceDetail,
|
|
dwSize,
|
|
&dwNeeded,
|
|
NULL) ) {
|
|
|
|
dwLastError = GetLastError();
|
|
DBGMSG(DBG_ERROR,
|
|
("usbmon: WalkPortList: SetupDiGetDeviceInterfaceDetail failed with error %d size %d\n",
|
|
dwLastError, dwNeeded));
|
|
goto Next;
|
|
}
|
|
|
|
#ifdef MYDEBUG
|
|
dwTime = GetTickCount() - dwTime;
|
|
++dwCount[2];
|
|
dwTotalTime[2] += dwTime;
|
|
#endif
|
|
|
|
//
|
|
// This is the only flag we care about
|
|
//
|
|
DeviceInterface.Flags &= SPINT_ACTIVE;
|
|
|
|
//
|
|
// For inactive port if it is already known as a useless port
|
|
// no need to process further
|
|
//
|
|
if ( !(DeviceInterface.Flags & SPINT_ACTIVE) &&
|
|
FindUselessEntry(pMonitorInfo, pDeviceDetail->DevicePath, &pPrev) ) {
|
|
|
|
#ifdef MYDEBUG
|
|
++dwSkippedPorts;
|
|
#endif
|
|
goto Next;
|
|
}
|
|
|
|
//
|
|
// When port active status did not change we should have nothing
|
|
// to update. By skipping the PortUpdateInfo we avoid registry access
|
|
// and it is a performance improvement
|
|
//
|
|
if ( (pPtr = FindPortUsingDevicePath(pMonitorInfo,
|
|
pDeviceDetail->DevicePath)) &&
|
|
DeviceInterface.Flags == pPtr->dwDeviceFlags ) {
|
|
|
|
#ifdef MYDEBUG
|
|
++dwSkippedPorts;
|
|
#endif
|
|
goto Next;
|
|
}
|
|
|
|
ProcessPortInfo(pSetupApiInfo, pMonitorInfo, hDevList, &DeviceInterface,
|
|
pDeviceDetail, ppPortUpdateInfo);
|
|
|
|
Next:
|
|
dwLastError = ERROR_SUCCESS;
|
|
++dwIndex;
|
|
pDeviceDetail->cbSize = sizeof(*pDeviceDetail);
|
|
DeviceInterface.cbSize = sizeof(DeviceInterface);
|
|
} while ( dwLastError == ERROR_SUCCESS);
|
|
|
|
if ( dwLastError == ERROR_NO_MORE_ITEMS )
|
|
dwLastError = ERROR_SUCCESS;
|
|
|
|
Done:
|
|
LeaveCriticalSection(&pMonitorInfo->EnumPortsCS);
|
|
if ( hDevList != INVALID_HANDLE_VALUE )
|
|
pSetupApiInfo->DestroyDeviceInfoList(hDevList);
|
|
|
|
ImpersonatePrinterClient(hToken);
|
|
FreeSplMem(pDeviceDetail);
|
|
|
|
return dwLastError;
|
|
}
|
|
|
|
|
|
LPBYTE
|
|
CopyPortToBuf(
|
|
PUSBMON_PORT_INFO pPortInfo,
|
|
DWORD dwLevel,
|
|
LPBYTE pPorts,
|
|
LPBYTE pEnd
|
|
)
|
|
{
|
|
DWORD dwLen;
|
|
LPTSTR pszStr;
|
|
LPPORT_INFO_1 pPortInfo1 = (LPPORT_INFO_1) pPorts;
|
|
LPPORT_INFO_2 pPortInfo2 = (LPPORT_INFO_2) pPorts;
|
|
|
|
switch (dwLevel) {
|
|
|
|
case 2:
|
|
dwLen = gdwMonitorNameSize;
|
|
pEnd -= dwLen;
|
|
pPortInfo2->pMonitorName = (LPTSTR)pEnd;
|
|
lstrcpy(pPortInfo2->pMonitorName, cszMonitorName);
|
|
|
|
dwLen = lstrlen(pPortInfo->szPortDescription) + 1;
|
|
dwLen *= sizeof(TCHAR);
|
|
pEnd -= dwLen;
|
|
pPortInfo2->pDescription = (LPTSTR)pEnd;
|
|
lstrcpy(pPortInfo2->pDescription, pPortInfo->szPortDescription);
|
|
|
|
//
|
|
// Fall through
|
|
//
|
|
|
|
case 1:
|
|
dwLen = lstrlen(pPortInfo->szPortName) + 1;
|
|
dwLen *= sizeof(TCHAR);
|
|
pEnd -= dwLen;
|
|
pPortInfo1->pName = (LPTSTR)pEnd;
|
|
lstrcpy(pPortInfo1->pName, pPortInfo->szPortName);
|
|
}
|
|
|
|
return pEnd;
|
|
}
|
|
|
|
|
|
BOOL
|
|
LoadSetupApiDll(
|
|
PSETUPAPI_INFO pSetupInfo
|
|
)
|
|
{
|
|
UINT uOldErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
#ifdef MYDEBUG
|
|
DWORD dwTime;
|
|
|
|
dwTime = GetTickCount();
|
|
#endif
|
|
|
|
pSetupInfo->hSetupApi = LoadLibrary(TEXT("setupapi"));
|
|
SetErrorMode(uOldErrMode);
|
|
|
|
|
|
if ( !pSetupInfo->hSetupApi )
|
|
return FALSE;
|
|
|
|
(FARPROC) pSetupInfo->DestroyDeviceInfoList
|
|
= GetProcAddress(pSetupInfo->hSetupApi,
|
|
"SetupDiDestroyDeviceInfoList");
|
|
|
|
(FARPROC) pSetupInfo->GetClassDevs
|
|
= GetProcAddress(pSetupInfo->hSetupApi,
|
|
"SetupDiGetClassDevsW");
|
|
|
|
(FARPROC) pSetupInfo->EnumDeviceInterfaces
|
|
= GetProcAddress(pSetupInfo->hSetupApi,
|
|
"SetupDiEnumDeviceInterfaces");
|
|
|
|
(FARPROC) pSetupInfo->GetDeviceInterfaceDetail
|
|
= GetProcAddress(pSetupInfo->hSetupApi,
|
|
"SetupDiGetDeviceInterfaceDetailW");
|
|
|
|
(FARPROC) pSetupInfo->OpenDeviceInterfaceRegKey
|
|
= GetProcAddress(pSetupInfo->hSetupApi,
|
|
"SetupDiOpenDeviceInterfaceRegKey");
|
|
|
|
if ( !pSetupInfo->DestroyDeviceInfoList ||
|
|
!pSetupInfo->GetClassDevs ||
|
|
!pSetupInfo->EnumDeviceInterfaces ||
|
|
!pSetupInfo->GetDeviceInterfaceDetail ||
|
|
!pSetupInfo->OpenDeviceInterfaceRegKey ) {
|
|
|
|
SPLASSERT(FALSE);
|
|
FreeLibrary(pSetupInfo->hSetupApi);
|
|
pSetupInfo->hSetupApi = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef MYDEBUG
|
|
dwTime = GetTickCount() - dwTime;
|
|
++dwCount[5];
|
|
dwTotalTime[5] += dwTime;
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
USBMON_EnumPorts(
|
|
LPTSTR pszName,
|
|
DWORD dwLevel,
|
|
LPBYTE pPorts,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbNeeded,
|
|
LPDWORD pcReturned
|
|
)
|
|
{
|
|
DWORD dwLastError = ERROR_SUCCESS, dwRequestIndex;
|
|
LPBYTE pEnd;
|
|
SETUPAPI_INFO SetupApiInfo;
|
|
PUSBMON_PORT_INFO pPortInfo;
|
|
PPORT_UPDATE_INFO pPortUpdateInfo = NULL;
|
|
|
|
#ifdef MYDEBUG
|
|
DWORD dwTime;
|
|
CHAR szBuf[200];
|
|
|
|
dwTime = GetTickCount();
|
|
#endif
|
|
|
|
dwRequestIndex = gUsbmonInfo.dwLastEnumIndex;
|
|
|
|
*pcbNeeded = *pcReturned = 0;
|
|
if ( dwLevel != 1 && dwLevel != 2 ) {
|
|
|
|
SetLastError(ERROR_INVALID_LEVEL);
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !LoadSetupApiDll(&SetupApiInfo) )
|
|
return FALSE;
|
|
|
|
EnterCriticalSection(&gUsbmonInfo.EnumPortsCS);
|
|
|
|
if ( dwRequestIndex >= gUsbmonInfo.dwLastEnumIndex ) {
|
|
|
|
//
|
|
// No complete enumeration has occurred since this request was made.
|
|
// Since the request may be an indication that something has changed,
|
|
// the full reenumeration must be done.
|
|
//
|
|
// Updated the index of enumeration before actually doing the
|
|
// work so it will show up as the most conservative
|
|
//
|
|
// Consequence of rollover on gdwLastEnumIndex:
|
|
// Any threads that recorded 0xFFFFFFFF as the dwRequestIndex
|
|
// will show as greater than the new value 0 and therefore reenum
|
|
// gratuitously. Not very much extra work.
|
|
//
|
|
++gUsbmonInfo.dwLastEnumIndex;
|
|
if ( dwLastError = WalkPortList(&SetupApiInfo, &gUsbmonInfo,
|
|
&pPortUpdateInfo) )
|
|
goto Done;
|
|
}
|
|
#ifdef MYDEBUG
|
|
else
|
|
++dwSkippedEnumPorts;
|
|
#endif
|
|
|
|
for ( pPortInfo = gUsbmonInfo.pPortInfo ;
|
|
pPortInfo ;
|
|
pPortInfo = pPortInfo->pNext ) {
|
|
|
|
if ( dwLevel == 1 )
|
|
*pcbNeeded += sizeof(PORT_INFO_1) +
|
|
(lstrlen(pPortInfo->szPortName) + 1)
|
|
* sizeof(TCHAR);
|
|
else
|
|
*pcbNeeded += sizeof(PORT_INFO_2) +
|
|
gdwMonitorNameSize +
|
|
(lstrlen(pPortInfo->szPortName) + 1 +
|
|
lstrlen(pPortInfo->szPortDescription) + 1 )
|
|
* sizeof(TCHAR);
|
|
|
|
}
|
|
|
|
if ( cbBuf < *pcbNeeded ) {
|
|
|
|
dwLastError = ERROR_INSUFFICIENT_BUFFER;
|
|
goto Done;
|
|
}
|
|
|
|
pEnd = pPorts + cbBuf;
|
|
|
|
for ( pPortInfo = gUsbmonInfo.pPortInfo ;
|
|
pPortInfo ;
|
|
pPortInfo = pPortInfo->pNext ) {
|
|
|
|
pEnd = CopyPortToBuf(pPortInfo, dwLevel, pPorts, pEnd);
|
|
|
|
if ( dwLevel == 1 )
|
|
pPorts += sizeof(PORT_INFO_1);
|
|
else
|
|
pPorts += sizeof(PORT_INFO_2);
|
|
++(*pcReturned);
|
|
}
|
|
|
|
SPLASSERT(pEnd >= pPorts);
|
|
|
|
Done:
|
|
PassPortUpdateListToBackgroundThread(&gUsbmonInfo, pPortUpdateInfo);
|
|
|
|
LeaveCriticalSection(&gUsbmonInfo.EnumPortsCS);
|
|
|
|
if ( SetupApiInfo.hSetupApi )
|
|
FreeLibrary(SetupApiInfo.hSetupApi);
|
|
|
|
++gUsbmonInfo.dwEnumPortCount;
|
|
|
|
#ifdef MYDEBUG
|
|
dwTime = GetTickCount() - dwTime;
|
|
++(dwCount[3]);
|
|
dwTotalTime[3] += dwTime;
|
|
|
|
sprintf(szBuf, "SetupDiOpenDeviceInterfaceRegKey: %d\n", dwTotalTime[0]/dwCount[0]);
|
|
OutputDebugStringA(szBuf);
|
|
sprintf(szBuf, "SetupDiSetupDiEnumDeviceInterfaces: %d\n", dwTotalTime[1]/dwCount[1]);
|
|
OutputDebugStringA(szBuf);
|
|
sprintf(szBuf, "SetupDiGetDeviceInterfaceDetail: %d\n", dwTotalTime[2]/dwCount[2]);
|
|
OutputDebugStringA(szBuf);
|
|
sprintf(szBuf, "EnumPorts: %d\n", dwTotalTime[3]/dwCount[3]);
|
|
OutputDebugStringA(szBuf);
|
|
sprintf(szBuf, "LoadSetupApi: %d\n", dwTotalTime[5]/dwCount[5]);
|
|
OutputDebugStringA(szBuf);
|
|
sprintf(szBuf, "Port updates per call %d\n", dwPortUpdates/gUsbmonInfo.dwEnumPortCount);
|
|
OutputDebugStringA(szBuf);
|
|
sprintf(szBuf, "Skipped port updates per call %d\n", dwSkippedPorts/gUsbmonInfo.dwEnumPortCount);
|
|
OutputDebugStringA(szBuf);
|
|
sprintf(szBuf, "Skipped enumport percentage %d\n", 100 * dwSkippedEnumPorts/gUsbmonInfo.dwEnumPortCount);
|
|
OutputDebugStringA(szBuf);
|
|
sprintf(szBuf, "Ports/Useless ports %d/%d\n", gUsbmonInfo.dwPortCount, gUsbmonInfo.dwUselessPortCount);
|
|
OutputDebugStringA(szBuf);
|
|
|
|
#endif
|
|
|
|
|
|
if ( dwLastError ) {
|
|
|
|
SetLastError(dwLastError);
|
|
return FALSE;
|
|
} else
|
|
return TRUE;
|
|
}
|