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
1218 lines
35 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
All Rights Reserved
|
|
|
|
|
|
Module Name:
|
|
EnumUtil.cpp
|
|
|
|
Abstract:
|
|
Utiltiy functions used by the EnumPorts function.
|
|
|
|
Author: M. Fenelon
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
TCHAR sComma = TEXT(',');
|
|
TCHAR sNull = TEXT('\0');
|
|
|
|
DWORD
|
|
SpinUpdateThread( void )
|
|
{
|
|
HANDLE hThread = NULL;
|
|
DWORD dwStatus = ERROR_SUCCESS;
|
|
|
|
hThread = CreateThread (
|
|
NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE)UpdateThread,
|
|
&gDynaMonInfo,
|
|
0,
|
|
NULL
|
|
);
|
|
if (hThread)
|
|
{
|
|
CloseHandle(hThread);
|
|
}
|
|
else
|
|
{
|
|
dwStatus = GetLastError ();
|
|
}
|
|
return dwStatus;
|
|
}
|
|
|
|
|
|
VOID
|
|
UpdateThread(
|
|
PDYNAMON_MONITOR_INFO pMonInfo
|
|
)
|
|
{
|
|
PPORT_UPDATE pUpdateList = NULL,
|
|
pNext;
|
|
DWORD dwPrinters;
|
|
LPPRINTER_INFO_5 pPrinterInfo5List = NULL;
|
|
BOOL bCheck;
|
|
|
|
// Loop indefinitely
|
|
while ( 1 )
|
|
{
|
|
// Wait for the Event to be signaled
|
|
WaitForSingleObject( pMonInfo->hUpdateEvent, INFINITE );
|
|
|
|
// Get access to the Update List Pointer
|
|
ECS( pMonInfo->UpdateListCS );
|
|
|
|
// Get the current list
|
|
pUpdateList = pMonInfo->pUpdateList;
|
|
pMonInfo->pUpdateList = NULL;
|
|
|
|
// Release acces to the list pointer
|
|
LCS( pMonInfo->UpdateListCS );
|
|
|
|
dwPrinters = 0;
|
|
pPrinterInfo5List = NULL;
|
|
bCheck = GetPrinterInfo( &pPrinterInfo5List, &dwPrinters );
|
|
|
|
// If there is anything in the list process it....
|
|
while ( pUpdateList )
|
|
{
|
|
// First get a pointer to the next update
|
|
pNext = pUpdateList->pNext;
|
|
|
|
if ( bCheck &&
|
|
!PortNameNeededBySpooler( pUpdateList->szPortName,
|
|
pPrinterInfo5List,
|
|
dwPrinters,
|
|
pUpdateList->bActive ) &&
|
|
!pUpdateList->bActive )
|
|
{
|
|
RegSetValueEx( pUpdateList->hKey, cszRecyclable, 0, REG_NONE, 0, 0);
|
|
}
|
|
|
|
// Close the Reg Key & Free the memory
|
|
RegCloseKey( pUpdateList->hKey);
|
|
FreeSplMem( pUpdateList );
|
|
pUpdateList = pNext;
|
|
}
|
|
|
|
if ( pPrinterInfo5List )
|
|
FreeSplMem( pPrinterInfo5List );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BOOL
|
|
GetPrinterInfo(
|
|
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;
|
|
|
|
*pdwReturned = 0;
|
|
|
|
if ( !(pBuf = (LPBYTE) 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 = (LPBYTE) 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;
|
|
}
|
|
|
|
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 ( _tcsicmp( (LPCTSTR) pszPortName, pszStr1 ) == 0 )
|
|
bPortUsedByAPrinter = bPrinterUsesOnlyThisPort = TRUE;
|
|
else
|
|
{
|
|
//
|
|
// Look at each port in the list of ports printer uses
|
|
//
|
|
while ( pszStr2 = _tcschr( pszStr1, sComma ) )
|
|
{
|
|
*pszStr2 = sNull;
|
|
if ( _tcsicmp( 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 = _tcsicmp( pszPortName, pszStr1 ) == 0;
|
|
}
|
|
|
|
// We will change only status of printer for non-pooled printers only
|
|
if ( bPrinterUsesOnlyThisPort )
|
|
SetOnlineStaus( pPrinterInfo5, bActive );
|
|
}
|
|
|
|
return bPortUsedByAPrinter;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SetOnlineStaus(
|
|
LPPRINTER_INFO_5 pPrinterInfo5,
|
|
BOOL bOnline
|
|
)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
HANDLE hPrinter;
|
|
PRINTER_DEFAULTS PrinterDefault = {NULL, NULL, PRINTER_ALL_ACCESS};
|
|
|
|
//
|
|
// Don't change Online Status at all for TS ports
|
|
//
|
|
if ( _tcsnicmp( pPrinterInfo5->pPortName, cszTS, _tcslen(cszTS) ) == 0 )
|
|
return TRUE;
|
|
|
|
//
|
|
// Force all DOT4 ports to remain online at all times.
|
|
//
|
|
if ( _tcsnicmp( pPrinterInfo5->pPortName, cszDOT4, _tcslen(cszDOT4) ) == 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;
|
|
|
|
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 );
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
DWORD
|
|
BuildPortList(
|
|
PDYNAMON_MONITOR_INFO pMonitorInfo,
|
|
PPORT_UPDATE* ppPortUpdateList
|
|
)
|
|
{
|
|
DWORD dwLastError;
|
|
SETUPAPI_INFO SetupApiInfo;
|
|
|
|
if ( !LoadSetupApiDll( &SetupApiInfo ) )
|
|
return GetLastError();
|
|
|
|
ECS( pMonitorInfo->EnumPortsCS );
|
|
|
|
dwLastError = ProcessGUID( &SetupApiInfo, pMonitorInfo,
|
|
ppPortUpdateList, (LPGUID) &USB_PRINTER_GUID );
|
|
|
|
LCS( pMonitorInfo->EnumPortsCS );
|
|
|
|
if ( SetupApiInfo.hSetupApi )
|
|
FreeLibrary(SetupApiInfo.hSetupApi);
|
|
|
|
return dwLastError;
|
|
}
|
|
|
|
|
|
BOOL
|
|
LoadSetupApiDll(
|
|
PSETUPAPI_INFO pSetupInfo
|
|
)
|
|
{
|
|
UINT uOldErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
pSetupInfo->hSetupApi = LoadLibrary(TEXT("setupapi"));
|
|
SetErrorMode(uOldErrMode);
|
|
|
|
|
|
if ( !pSetupInfo->hSetupApi )
|
|
return FALSE;
|
|
|
|
pSetupInfo->DestroyDeviceInfoList = (pfSetupDiDestroyDeviceInfoList) GetProcAddress(pSetupInfo->hSetupApi,
|
|
"SetupDiDestroyDeviceInfoList");
|
|
|
|
pSetupInfo->GetClassDevs = (pfSetupDiGetClassDevs) GetProcAddress(pSetupInfo->hSetupApi,
|
|
"SetupDiGetClassDevsW");
|
|
|
|
pSetupInfo->EnumDeviceInfo = (pfSetupDiEnumDeviceInfo) GetProcAddress(pSetupInfo->hSetupApi,
|
|
"SetupDiEnumDeviceInfo");
|
|
|
|
pSetupInfo->EnumDeviceInterfaces = (pfSetupDiEnumDeviceInterfaces) GetProcAddress(pSetupInfo->hSetupApi,
|
|
"SetupDiEnumDeviceInterfaces");
|
|
|
|
pSetupInfo->GetDeviceInterfaceDetail = (pfSetupDiGetDeviceInterfaceDetail) GetProcAddress(pSetupInfo->hSetupApi,
|
|
"SetupDiGetDeviceInterfaceDetailW");
|
|
|
|
pSetupInfo->OpenDeviceInterfaceRegKey = (pfSetupDiOpenDeviceInterfaceRegKey) GetProcAddress(pSetupInfo->hSetupApi,
|
|
"SetupDiOpenDeviceInterfaceRegKey");
|
|
|
|
if ( !pSetupInfo->DestroyDeviceInfoList ||
|
|
!pSetupInfo->GetClassDevs ||
|
|
!pSetupInfo->EnumDeviceInfo ||
|
|
!pSetupInfo->EnumDeviceInterfaces ||
|
|
!pSetupInfo->GetDeviceInterfaceDetail ||
|
|
!pSetupInfo->OpenDeviceInterfaceRegKey )
|
|
{
|
|
SPLASSERT(FALSE);
|
|
FreeLibrary(pSetupInfo->hSetupApi);
|
|
pSetupInfo->hSetupApi = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ProcessGUID(
|
|
PSETUPAPI_INFO pSetupApiInfo,
|
|
PDYNAMON_MONITOR_INFO pMonitorInfo,
|
|
PPORT_UPDATE* ppPortUpdateList,
|
|
LPGUID pGUID
|
|
)
|
|
{
|
|
DWORD dwIndex, dwLastError, dwSize, dwNeeded;
|
|
BOOL bIsPortActive;
|
|
HANDLE hToken;
|
|
HDEVINFO hDevList = INVALID_HANDLE_VALUE;
|
|
PDYNAMON_PORT pPtr;
|
|
PUSELESS_PORT pCur, pPrev;
|
|
SP_DEVICE_INTERFACE_DATA DeviceInterface;
|
|
PSP_DEVICE_INTERFACE_DETAIL_DATA pDeviceDetail = NULL;
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
hDevList = pSetupApiInfo->GetClassDevs( pGUID,
|
|
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
|
|
{
|
|
if ( !pSetupApiInfo->EnumDeviceInterfaces( hDevList,
|
|
NULL,
|
|
pGUID,
|
|
dwIndex,
|
|
&DeviceInterface) )
|
|
{
|
|
dwLastError = GetLastError();
|
|
if ( dwLastError == ERROR_NO_MORE_ITEMS )
|
|
break; // Normal exit
|
|
|
|
DBGMSG(DBG_WARNING,
|
|
("DynaMon: ProcessGUID: SetupDiEnumDeviceInterfaces failed with %d for inderx %d\n",
|
|
dwLastError, dwIndex));
|
|
goto Next;
|
|
}
|
|
|
|
if ( !pSetupApiInfo->GetDeviceInterfaceDetail( hDevList,
|
|
&DeviceInterface,
|
|
pDeviceDetail,
|
|
dwSize,
|
|
&dwNeeded,
|
|
NULL) )
|
|
{
|
|
dwLastError = GetLastError();
|
|
DBGMSG(DBG_ERROR,
|
|
("DynaMon: ProcessGUID: SetupDiGetDeviceInterfaceDetail failed with error %d size %d\n",
|
|
dwLastError, dwNeeded));
|
|
goto Next;
|
|
}
|
|
|
|
//
|
|
// This is the only flag we care about
|
|
//
|
|
bIsPortActive = (DeviceInterface.Flags & SPINT_ACTIVE);
|
|
|
|
|
|
//
|
|
// For inactive port if it is already known as a useless port
|
|
// no need to process further
|
|
//
|
|
if ( !bIsPortActive && FindUselessEntry( pMonitorInfo, pDeviceDetail->DevicePath, &pPrev) )
|
|
{
|
|
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 ) ) &&
|
|
pPtr->pBasePort->compActiveState( bIsPortActive ) )
|
|
{
|
|
if (!bIsPortActive && pPtr-> pBasePort-> getPortType () == TSPORT)
|
|
{
|
|
//
|
|
// Potential update
|
|
//
|
|
}
|
|
else
|
|
{
|
|
goto Next;
|
|
}
|
|
}
|
|
|
|
ProcessPortInfo( pSetupApiInfo, pMonitorInfo, hDevList, &DeviceInterface,
|
|
pDeviceDetail, bIsPortActive, ppPortUpdateList);
|
|
|
|
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:
|
|
if ( hDevList != INVALID_HANDLE_VALUE )
|
|
pSetupApiInfo->DestroyDeviceInfoList( hDevList );
|
|
|
|
if ( !ImpersonatePrinterClient(hToken) )
|
|
dwLastError = GetLastError();
|
|
|
|
FreeSplMem(pDeviceDetail);
|
|
|
|
return dwLastError;
|
|
}
|
|
|
|
|
|
PUSELESS_PORT
|
|
FindUselessEntry(
|
|
IN PDYNAMON_MONITOR_INFO pMonitorInfo,
|
|
IN LPTSTR pszDevicePath,
|
|
OUT PUSELESS_PORT* 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 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;
|
|
}
|
|
|
|
|
|
PDYNAMON_PORT
|
|
FindPortUsingDevicePath(
|
|
IN PDYNAMON_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;
|
|
PDYNAMON_PORT pHead;
|
|
|
|
ECS( pMonitorInfo->EnumPortsCS );
|
|
|
|
//
|
|
// Port list is sorted on port name, so we have to scan the whole list
|
|
//
|
|
for ( pHead = pMonitorInfo->pPortList ; pHead ; pHead = pHead->pNext )
|
|
if ( pHead->pBasePort->compDevicePath( pszDevicePath ) == 0 )
|
|
break;
|
|
|
|
LCS( pMonitorInfo->EnumPortsCS );
|
|
|
|
return pHead;
|
|
}
|
|
|
|
|
|
VOID
|
|
ProcessPortInfo(
|
|
IN PSETUPAPI_INFO pSetupApiInfo,
|
|
IN PDYNAMON_MONITOR_INFO pMonitorInfo,
|
|
IN HDEVINFO hDevList,
|
|
IN PSP_DEVICE_INTERFACE_DATA pDeviceInterface,
|
|
IN PSP_DEVICE_INTERFACE_DETAIL_DATA pDeviceDetail,
|
|
IN BOOL bIsPortActive,
|
|
IN OUT PPORT_UPDATE* ppPortUpdateList
|
|
)
|
|
{
|
|
HKEY hKey = INVALID_HANDLE_VALUE;
|
|
TCHAR szPortName[MAX_PORT_LEN];
|
|
PDYNAMON_PORT pCur, pPrev;
|
|
PORTTYPE portType = USBPORT;
|
|
|
|
hKey = GetPortNameAndRegKey( pSetupApiInfo, hDevList, pDeviceInterface,
|
|
szPortName, COUNTOF (szPortName), &portType );
|
|
|
|
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 ( !bIsPortActive &&
|
|
!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?
|
|
//
|
|
BOOL bActiveStateChanged = !pCur->pBasePort->
|
|
compActiveState (
|
|
bIsPortActive
|
|
);
|
|
BOOL bDevicePathChanged = pCur->pBasePort->
|
|
compDevicePath (
|
|
pDeviceDetail->DevicePath
|
|
);
|
|
if (bActiveStateChanged || bDevicePathChanged ||
|
|
(!bIsPortActive && pCur-> pBasePort-> getPortType () == TSPORT)
|
|
)
|
|
{
|
|
//
|
|
// Even nothing has been changed, for INACTIVE TS ports
|
|
// we need to call UpdatePortInfo to check if recyclable flag is set.
|
|
//
|
|
if (bDevicePathChanged)
|
|
{
|
|
for (
|
|
PDYNAMON_PORT pDuplicate = pMonitorInfo-> pPortList;
|
|
pDuplicate;
|
|
pDuplicate = pDuplicate-> pNext
|
|
)
|
|
{
|
|
if (pDuplicate == pCur || pDuplicate-> pBasePort-> isActive ())
|
|
{
|
|
continue;
|
|
}
|
|
//
|
|
// Search for CBasePort with the same DevicePath
|
|
// but with a different PortName...
|
|
//
|
|
if (pDuplicate-> pBasePort-> compDevicePath (pDeviceDetail-> DevicePath) == 0 &&
|
|
pDuplicate-> pBasePort-> compPortName (szPortName) != 0
|
|
)
|
|
{
|
|
//
|
|
// Clear the port's device path and description...
|
|
//
|
|
pDuplicate-> pBasePort-> setDevicePath (L"");
|
|
pDuplicate-> pBasePort-> setPortDesc (L"");
|
|
}
|
|
}//end for
|
|
//
|
|
// If pJunkList has the entry with the DeviceName equal to pCur,
|
|
// this entry has to be deleted.
|
|
//
|
|
PUSELESS_PORT pPrevUseless = NULL;
|
|
PUSELESS_PORT pUseless =
|
|
FindUselessEntry (
|
|
pMonitorInfo,
|
|
pDeviceDetail-> DevicePath,
|
|
&pPrevUseless
|
|
);
|
|
if (pUseless)
|
|
{
|
|
if (pPrevUseless)
|
|
{
|
|
pPrevUseless-> pNext = pUseless-> pNext;
|
|
}
|
|
else
|
|
{
|
|
pMonitorInfo-> pJunkList = pUseless-> pNext;
|
|
}
|
|
FreeSplMem (pUseless);
|
|
}
|
|
}//end if
|
|
UpdatePortInfo (
|
|
pCur,
|
|
pDeviceDetail->DevicePath,
|
|
bIsPortActive,
|
|
&hKey,
|
|
ppPortUpdateList
|
|
);
|
|
}//end if
|
|
}
|
|
else
|
|
{
|
|
|
|
AddPortToList( portType, szPortName, pDeviceDetail->DevicePath,
|
|
bIsPortActive, &hKey, pMonitorInfo, pPrev,
|
|
ppPortUpdateList);
|
|
|
|
}
|
|
|
|
if ( hKey != INVALID_HANDLE_VALUE )
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
|
|
HKEY
|
|
GetPortNameAndRegKey(
|
|
IN PSETUPAPI_INFO pSetupInfo,
|
|
IN HDEVINFO hDevList,
|
|
IN PSP_DEVICE_INTERFACE_DATA pDeviceInterface,
|
|
OUT LPTSTR pszPortName,
|
|
IN size_t cchPortName,
|
|
OUT PORTTYPE* pPortType
|
|
)
|
|
/*++
|
|
|
|
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];
|
|
|
|
hKey = pSetupInfo->OpenDeviceInterfaceRegKey(hDevList,
|
|
pDeviceInterface,
|
|
0,
|
|
KEY_ALL_ACCESS);
|
|
if ( hKey == INVALID_HANDLE_VALUE )
|
|
{
|
|
dwLastError = GetLastError();
|
|
DBGMSG(DBG_WARNING,
|
|
("DynaMon: GetPortNameAndRegKey: 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) )
|
|
{
|
|
|
|
dwLastError = GetLastError();
|
|
DBGMSG(DBG_WARNING,
|
|
("DynaMon: GetPortNameAndRegKey: RegQueryValueEx failed for port number with error %d\n", dwLastError));
|
|
goto Fail;
|
|
}
|
|
|
|
dwNeeded = sizeof(szPortBaseName);
|
|
if ( ERROR_SUCCESS != (dwLastError = RegQueryValueEx(hKey, cszBaseName, 0, NULL,
|
|
(LPBYTE)szPortBaseName, &dwNeeded) ) )
|
|
{
|
|
dwLastError = GetLastError();
|
|
DBGMSG(DBG_WARNING,
|
|
("GetPortNameAndRegKey: RegQueryValueEx failed for Base Name with error %d\n", dwLastError));
|
|
goto Fail;
|
|
}
|
|
|
|
*pPortType = USBPORT;
|
|
if ( _tcsncmp( szPortBaseName, cszDOT4, _tcslen(cszDOT4) ) == 0 )
|
|
*pPortType = DOT4PORT;
|
|
else if ( _tcsncmp( szPortBaseName, csz1394, _tcslen(csz1394) ) == 0 )
|
|
*pPortType = P1394PORT;
|
|
else if ( _tcsncmp( szPortBaseName, cszTS, _tcslen(cszTS) ) == 0 )
|
|
*pPortType = TSPORT;
|
|
|
|
(VOID)StringCchPrintf (pszPortName, cchPortName, TEXT("%s%03u"), szPortBaseName, dwPortNumber);
|
|
|
|
return hKey;
|
|
|
|
Fail:
|
|
RegCloseKey(hKey);
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
AddUselessPortEntry(
|
|
IN PDYNAMON_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 pTemp, pPrev;
|
|
|
|
pTemp = FindUselessEntry( pMonitorInfo, pszDevicePath, &pPrev );
|
|
|
|
//
|
|
// Don't add an entry that is already there
|
|
//
|
|
SPLASSERT(pTemp == NULL);
|
|
|
|
if ( pTemp = (PUSELESS_PORT) AllocSplMem(sizeof(*pTemp)) )
|
|
{
|
|
SafeCopy(MAX_PATH, pszDevicePath, pTemp->szDevicePath);
|
|
|
|
if ( pPrev )
|
|
{
|
|
pTemp->pNext = pPrev->pNext;
|
|
pPrev->pNext = pTemp;
|
|
}
|
|
else
|
|
{
|
|
pTemp->pNext = pMonitorInfo->pJunkList;
|
|
pMonitorInfo->pJunkList = pTemp;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
PDYNAMON_PORT
|
|
FindPort(
|
|
IN PDYNAMON_MONITOR_INFO pMonitorInfo,
|
|
IN LPTSTR pszPortName,
|
|
OUT PDYNAMON_PORT* 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;
|
|
PDYNAMON_PORT pHead;
|
|
|
|
ECS( pMonitorInfo->EnumPortsCS );
|
|
|
|
pHead = pMonitorInfo->pPortList;
|
|
for ( *ppPrev = NULL ;
|
|
pHead && ( iCmp = pHead->pBasePort->compPortName( pszPortName) ) < 0 ;
|
|
*ppPrev = pHead, pHead = pHead->pNext )
|
|
;
|
|
|
|
//
|
|
// If port should go in the middle but not currently there
|
|
//
|
|
if ( pHead && iCmp != 0 )
|
|
pHead = NULL;
|
|
|
|
LCS( pMonitorInfo->EnumPortsCS );
|
|
|
|
return pHead;
|
|
}
|
|
|
|
|
|
VOID
|
|
UpdatePortInfo(
|
|
PDYNAMON_PORT pPort,
|
|
LPTSTR pszDevicePath,
|
|
BOOL bIsPortActive,
|
|
HKEY* phKey,
|
|
PPORT_UPDATE* ppPortUpdateList
|
|
)
|
|
{
|
|
DWORD dwSize;
|
|
CBasePort* pCurrentPort = pPort->pBasePort;
|
|
|
|
if (bIsPortActive && !pCurrentPort-> isActive ())
|
|
{
|
|
//
|
|
// This port goes active.
|
|
// 'recyclable' flag has to be removed
|
|
//
|
|
DWORD dwRetVal =
|
|
RegDeleteValue (
|
|
*phKey,
|
|
cszRecyclable
|
|
);
|
|
if (dwRetVal != ERROR_SUCCESS &&
|
|
dwRetVal != ERROR_FILE_NOT_FOUND
|
|
)
|
|
{
|
|
//
|
|
// 'recyclable' flag removing failed.
|
|
// This port cannot be activated
|
|
//
|
|
return;
|
|
}
|
|
}
|
|
|
|
pCurrentPort->setDevicePath( pszDevicePath );
|
|
|
|
TCHAR szPortDescription[MAX_PORT_DESC_LEN];
|
|
dwSize = sizeof(szPortDescription);
|
|
if ( ERROR_SUCCESS == RegQueryValueEx( *phKey,
|
|
cszPortDescription,
|
|
0,
|
|
NULL,
|
|
(LPBYTE) szPortDescription,
|
|
&dwSize) )
|
|
{
|
|
pCurrentPort->setPortDesc( szPortDescription );
|
|
}
|
|
|
|
if ( !pCurrentPort->compActiveState( bIsPortActive ) )
|
|
{
|
|
pCurrentPort->setActive( bIsPortActive );
|
|
AddToPortUpdateList(ppPortUpdateList, pPort, phKey);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// TS Ports only
|
|
//
|
|
if (!bIsPortActive && pCurrentPort-> getPortType () == TSPORT)
|
|
{
|
|
//
|
|
// Is it already recyclable?
|
|
//
|
|
DWORD dwRetValue =
|
|
RegQueryValueEx (
|
|
*phKey,
|
|
cszRecyclable,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (dwRetValue == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
//
|
|
// The port is inactive and it is not recyclable.
|
|
// I have to add this port in update list to check is it still needed
|
|
// by the spooler. Only UpdateThread checks if the port name is in use.
|
|
AddToPortUpdateList(ppPortUpdateList, pPort, phKey);
|
|
}
|
|
//
|
|
// If dwRetValue is equal to ERROR_SUCCESS, then it is already recyclable.
|
|
// If dwRetVal has some other value, some error occured but next EnumPort
|
|
// will check this status again.
|
|
//
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
AddPortToList(
|
|
PORTTYPE portType,
|
|
LPTSTR pszPortName,
|
|
LPTSTR pszDevicePath,
|
|
BOOL bIsPortActive,
|
|
HKEY* phKey,
|
|
PDYNAMON_MONITOR_INFO pMonitorInfo,
|
|
PDYNAMON_PORT pPrevPort,
|
|
PPORT_UPDATE* ppPortUpdateList
|
|
)
|
|
{
|
|
DWORD dwSize, dwLastError;
|
|
PDYNAMON_PORT pPort;
|
|
PUSELESS_PORT pCur, pPrev;
|
|
CBasePort* pNewPort;
|
|
|
|
SPLASSERT(FindPortUsingDevicePath(pMonitorInfo, pszDevicePath) == NULL);
|
|
|
|
if (bIsPortActive)
|
|
{
|
|
//
|
|
// If the port goes active, recyclable flag should be removed
|
|
//
|
|
DWORD dwRetVal =
|
|
RegDeleteValue (
|
|
*phKey,
|
|
cszRecyclable
|
|
);
|
|
if (dwRetVal != ERROR_SUCCESS &&
|
|
dwRetVal != ERROR_FILE_NOT_FOUND
|
|
)
|
|
{
|
|
//
|
|
// Recyclable flag removing failed. Don't use this port.
|
|
//
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
pPort = (PDYNAMON_PORT) AllocSplMem(sizeof(DYNAMON_PORT));
|
|
if ( !pPort )
|
|
return FALSE;
|
|
|
|
pPort->dwSignature = DYNAMON_SIGNATURE;
|
|
|
|
// Now create the port based on Port Type.
|
|
switch ( portType )
|
|
{
|
|
case DOT4PORT:
|
|
pNewPort = new CDot4Port( bIsPortActive, pszPortName, pszDevicePath );
|
|
break;
|
|
case TSPORT:
|
|
pNewPort = new CTSPort( bIsPortActive, pszPortName, pszDevicePath );
|
|
break;
|
|
case P1394PORT:
|
|
pNewPort = new C1394Port( bIsPortActive, pszPortName, pszDevicePath );
|
|
break;
|
|
case USBPORT:
|
|
default:
|
|
pNewPort = new CUSBPort( bIsPortActive, pszPortName, pszDevicePath );
|
|
break;
|
|
}
|
|
|
|
if ( !pNewPort )
|
|
{
|
|
dwLastError = GetLastError();
|
|
FreeSplMem( pPort );
|
|
SetLastError( dwLastError );
|
|
return FALSE;
|
|
}
|
|
|
|
TCHAR szPortDescription[MAX_PORT_DESC_LEN];
|
|
dwSize = sizeof(szPortDescription);
|
|
if ( ERROR_SUCCESS == RegQueryValueEx(*phKey,
|
|
cszPortDescription,
|
|
0,
|
|
NULL,
|
|
(LPBYTE) szPortDescription,
|
|
&dwSize) )
|
|
{
|
|
pNewPort->setPortDesc( szPortDescription );
|
|
}
|
|
|
|
// See if the port has a max data size restriction
|
|
DWORD dwMaxBufferSize;
|
|
dwSize = sizeof(dwMaxBufferSize);
|
|
if ( ERROR_SUCCESS == RegQueryValueEx(*phKey,
|
|
cszMaxBufferSize,
|
|
0,
|
|
NULL,
|
|
(LPBYTE) &dwMaxBufferSize,
|
|
&dwSize) )
|
|
{
|
|
pNewPort->setMaxBuffer( dwMaxBufferSize );
|
|
}
|
|
|
|
// Assign Object Pointer to port list entry
|
|
pPort->pBasePort = pNewPort;
|
|
|
|
if ( pPrevPort )
|
|
{
|
|
pPort->pNext = pPrevPort->pNext;
|
|
pPrevPort->pNext = pPort;
|
|
}
|
|
else
|
|
{
|
|
pPort->pNext = pMonitorInfo->pPortList;
|
|
pMonitorInfo->pPortList = pPort;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
|
|
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(ppPortUpdateList, pPort, phKey);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
AddToPortUpdateList(
|
|
IN OUT PPORT_UPDATE* ppPortUpdateList,
|
|
IN PDYNAMON_PORT pPort,
|
|
IN OUT HKEY* phKey
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Adds a port to the list of ports that need to status updated.
|
|
|
|
Arguments:
|
|
ppPortUpdateList : Pointer to the head of port update list
|
|
pPort : 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 pTemp;
|
|
|
|
if ( pTemp = (PPORT_UPDATE) AllocSplMem( sizeof(PORT_UPDATE) ) )
|
|
{
|
|
SafeCopy( MAX_PORT_LEN, pPort->pBasePort->getPortName(), pTemp->szPortName );
|
|
pTemp->bActive = pPort->pBasePort->isActive();
|
|
pTemp->hKey = *phKey;
|
|
pTemp->pNext = *ppPortUpdateList;
|
|
*ppPortUpdateList = pTemp;
|
|
|
|
*phKey = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
PassPortUpdateListToUpdateThread(
|
|
PPORT_UPDATE pNewUpdateList
|
|
)
|
|
{
|
|
// Get access to the Update List Pointer
|
|
ECS( gDynaMonInfo.UpdateListCS );
|
|
|
|
// Add the new list to the current list
|
|
if ( gDynaMonInfo.pUpdateList )
|
|
{
|
|
// THere is something in the list already so add it to the end
|
|
PPORT_UPDATE pCurUpdateList = gDynaMonInfo.pUpdateList;
|
|
while ( pCurUpdateList->pNext )
|
|
pCurUpdateList = pCurUpdateList->pNext;
|
|
|
|
pCurUpdateList->pNext = pNewUpdateList;
|
|
}
|
|
else
|
|
gDynaMonInfo.pUpdateList = pNewUpdateList;
|
|
|
|
// Release acces to the list pointer
|
|
LCS( gDynaMonInfo.UpdateListCS );
|
|
|
|
// Now let the Update Thread go...
|
|
SetEvent( gDynaMonInfo.hUpdateEvent );
|
|
|
|
}
|
|
|
|
void
|
|
SafeCopy(
|
|
IN DWORD MaxBufLen,
|
|
IN LPTSTR pszInString,
|
|
IN OUT LPTSTR pszOutString
|
|
)
|
|
{
|
|
// Check if the input string is bigger than the output buffer
|
|
(VOID) StringCchCopy (pszOutString, MaxBufLen, pszInString);
|
|
}
|