windows-nt/Source/XPSP1/NT/printscan/ui/printui/detect.cxx

816 lines
18 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (C) Microsoft Corporation, 1995 - 1999
All rights reserved.
Module Name:
detect.cxx
Abstract:
PnP printer autodetection.
Author:
Lazar Ivanov (LazarI) May-06-1999
Revision History:
May-06-1999 - Created.
Larry Zhu (LZhu) Mar-12-2000 - Rewrote PnP detection code.
--*/
#include "precomp.hxx"
#pragma hdrstop
#include "detect.hxx"
#include <initguid.h>
#include <ntddpar.h> // for GUID_PARALLEL_DEVICE
#include <regstr.h>
#if DBG
#define DBGCHKMSG(bShowMessage, MsgAndArgs) \
{ \
if (bShowMessage) \
{ \
DBGMSG(DBG_ERROR, MsgAndArgs); \
} \
} \
#else // DBG
#define DBGCHKMSG(bShowMessage, MsgAndArgs)
#endif // DBG
/********************************************************************
PnP private stuff
********************************************************************/
//
// current 1394 printers are enumerated under LPTENUM
//
#define szParallelClassEnumerator TEXT("LPTENUM")
#define szParallelDot4PrintClassEnumerator TEXT("DOT4PRT")
#define szUsbPrintClassEnumerator TEXT("USBPRINT")
#define szInfraRedPrintClassEnumerator TEXT("IRENUM")
// This timeout is per recommendation of the PnP guys
// 1 second should be enough
#define DELAY_KICKOFF_TIMEOUT 1000
extern "C" {
//
// config mgr privates
//
DWORD
CMP_WaitNoPendingInstallEvents(
IN DWORD dwTimeout
);
}
/********************************************************************
Global functions
********************************************************************/
#if FALSE // comment out the old enum code
#define szParalelPortDevNodePath TEXT("Root\\ParallelClass\\0000")
BOOL
PnP_Enum_KickOff(
VOID
)
/*++
Routine Description:
Kicks off the PNP enumeration event over the
LPT ports only.
Arguments:
None
Return Value:
TRUE - on success
FALSE - if a failure occurs
Notes:
--*/
{
BOOL bReturn;
DEVINST hLPTDevInst;
CONFIGRET result;
//
// Find the root dev node
//
result = CM_Locate_DevNode_Ex( &hLPTDevInst,
szParalelPortDevNodePath, CM_LOCATE_DEVNODE_NORMAL, NULL );
if( result == CR_SUCCESS )
{
//
// Reenumerate from the root of the devnode tree
//
result = CM_Reenumerate_DevNode_Ex( hLPTDevInst, CM_REENUMERATE_NORMAL, NULL );
if( result == CR_SUCCESS )
{
bReturn = TRUE;
}
}
return bReturn;
}
#endif // comment out the old enum code
BOOL
PnP_Enum_KickOff(
VOID
)
/*++
Routine Description:
Kicks off the PNP enumeration event.
Arguments:
None
Return Value:
TRUE - on success
FALSE - if a failure occurs
Notes:
--*/
{
DWORD crStatus = CR_DEFAULT;
DEVINST devRoot = {0};
crStatus = CM_Locate_DevNode(&devRoot, NULL, CM_LOCATE_DEVNODE_NORMAL);
DBGCHKMSG((CR_SUCCESS != crStatus), ("CM_Locate_Devnode failed with 0x%x\n", crStatus));
if (CR_SUCCESS == crStatus)
{
crStatus = CM_Reenumerate_DevNode(devRoot, CM_REENUMERATE_RETRY_INSTALLATION);
DBGCHKMSG((CR_SUCCESS != crStatus), ("CM_Reenumerate_DevNode failed with 0x%x\n", crStatus));
}
//
// There is no point to return CR_XXX code
//
SetLastError(CR_SUCCESS == crStatus ? ERROR_SUCCESS : ERROR_INVALID_DATA);
return CR_SUCCESS == crStatus;
}
VOID
PnP_Enumerate_Sync(
VOID
)
/*++
Routine Description:
Enumerates the LPT devices sync.
Arguments:
None
Return Value:
None
Notes:
--*/
{
//
// Kick off PnP enumeration
//
if( PnP_Enum_KickOff( ) )
{
for( ;; )
{
//
// We must wait about 1 sec (DELAY_KICKOFF_TIMEOUT) to make sure the enumeration
// event has been kicked off completely, so we can wait successfully use the
// CMP_WaitNoPendingInstallEvents() private API. This is by LonnyM recommendation.
// This delay also solves the problem with multiple installs like DOT4 printers.
//
Sleep( DELAY_KICKOFF_TIMEOUT );
//
// Check to see if there are pending install events
//
if( WAIT_TIMEOUT != CMP_WaitNoPendingInstallEvents( 0 ) )
break;
//
// Wait untill no more pending install events
//
CMP_WaitNoPendingInstallEvents( INFINITE );
}
}
}
/********************************************************************
TPnPDetect - PnP printer detector class
********************************************************************/
TPnPDetect::
TPnPDetect(
VOID
)
:
_bDetectionInProgress( FALSE ),
_pInfo4Before( NULL ),
_cInfo4Before( 0 ),
_hEventDone( NULL )
/*++
Routine Description:
TPnPDetect constructor
Arguments:
None
Return Value:
None
Notes:
--*/
{
}
TPnPDetect::
~TPnPDetect(
VOID
)
/*++
Routine Description:
TPnPDetect destructor
Arguments:
None
Return Value:
None
Notes:
--*/
{
//
// Check to free memory
//
Reset( );
}
typedef bool PI4_less_type(const PRINTER_INFO_4 p1, const PRINTER_INFO_4 p2);
static bool PI4_less(const PRINTER_INFO_4 p1, const PRINTER_INFO_4 p2)
{
return (lstrcmp(p1.pPrinterName, p2.pPrinterName) < 0);
}
BOOL
TPnPDetect::
bKickOffPnPEnumeration(
VOID
)
/*++
Routine Description:
Kicks off PnP enumeration event on the LPT ports.
Arguments:
None
Return Value:
TRUE - on success
FALSE - if a failure occurs
Notes:
--*/
{
DWORD cbInfo4Before = 0;
TStatusB bStatus;
bStatus DBGNOCHK = FALSE;
//
// Check to free memory
//
Reset( );
//
// Enumerate the printers before PnP enumeration
//
bStatus DBGCHK = VDataRefresh::bEnumPrinters(
PRINTER_ENUM_LOCAL, NULL, 4, reinterpret_cast<PVOID *>( &_pInfo4Before ),
&cbInfo4Before, &_cInfo4Before );
if( bStatus )
{
//
// Sort out the PRINTER_INFO_4 structures here
//
std::sort<PRINTER_INFO_4*, PI4_less_type*>(
_pInfo4Before,
_pInfo4Before + _cInfo4Before,
PI4_less);
//
// Kick off the PnP enumeration
//
_hEventDone = CreateEvent( NULL, TRUE, FALSE, NULL );
if( _hEventDone )
{
HANDLE hEventOut = NULL;
const HANDLE hCurrentProcess = GetCurrentProcess();
bStatus DBGCHK = DuplicateHandle( hCurrentProcess,
_hEventDone,
hCurrentProcess,
&hEventOut,
0,
TRUE,
DUPLICATE_SAME_ACCESS );
if( bStatus )
{
//
// Kick off the enumeration thread.
//
DWORD dwThreadId;
HANDLE hThread = TSafeThread::Create( NULL,
0,
(LPTHREAD_START_ROUTINE)TPnPDetect::EnumThreadProc,
hEventOut,
0,
&dwThreadId );
if( hThread )
{
//
// We don't need the thread handle any more.
//
CloseHandle( hThread );
//
// Detection initiated successfully.
//
_bDetectionInProgress = TRUE;
}
else
{
//
// Failed to create the thread. Close the event.
//
CloseHandle( hEventOut );
}
}
}
//
// Check to free the allocated resources if something has failed.
//
if( !_bDetectionInProgress )
{
Reset();
}
}
return bStatus;
}
BOOL
TPnPDetect::
bDetectionInProgress(
VOID
)
/*++
Routine Description:
Checks whether there is pending detection/installation
process.
Arguments:
None
Return Value:
TRUE - yes there is pending detect/install process.
FALSE - otherwise.
Notes:
--*/
{
return _bDetectionInProgress;
}
BOOL
TPnPDetect::
bFinished(
DWORD dwTimeout
)
/*++
Routine Description:
Checks whether there is no more pending detect/install events
after the last PnP enumeration event.
Arguments:
dwTimeout - Time to wait. By default is zero (no wait - just ping)
Return Value:
TRUE - on success
FALSE - if a failure occurs
Notes:
--*/
{
//
// Check to see if the enum process has been kicked off at all.
//
if( _bDetectionInProgress && _pInfo4Before && _hEventDone )
{
//
// Ping to see whether the PnP detect/install process has finished
//
if( WAIT_OBJECT_0 == WaitForSingleObject( _hEventDone, 0 ) )
{
_bDetectionInProgress = FALSE;
}
}
//
// We must check for all the three conditions below as _bDetectionInProgress
// may be FALSE, but the enum process has never kicked off. We want to make
// sure we are in the case _bDetectionInProgress is set to FALSE after the enum
// process is finished.
//
return ( !_bDetectionInProgress && _pInfo4Before && _hEventDone );
}
BOOL
TPnPDetect::
bGetDetectedPrinterName(
TString *pstrPrinterName
)
/*++
Routine Description:
Enum the printers after the PnP enumeration has finished to
check whether new local (LPT) printers have been detected.
Arguments:
None
Return Value:
TRUE - on success
FALSE - if a failure occurs
Notes:
--*/
{
TStatusB bStatus;
bStatus DBGNOCHK = FALSE;
if( pstrPrinterName && !_bDetectionInProgress && _pInfo4Before )
{
//
// The detection is done here. Enum the printers after the PnP
// detect/install to see whether new printer has been detected.
//
PRINTER_INFO_4 *pInfo4After = NULL;
DWORD cInfo4After = 0;
DWORD cbInfo4After = 0;
bStatus DBGCHK = VDataRefresh::bEnumPrinters(
PRINTER_ENUM_LOCAL, NULL, 4, reinterpret_cast<PVOID *>( &pInfo4After ),
&cbInfo4After, &cInfo4After );
if( bStatus && cInfo4After > _cInfo4Before )
{
for( UINT uAfter = 0; uAfter < cInfo4After; uAfter++ )
{
if( !std::binary_search<PRINTER_INFO_4*, PRINTER_INFO_4, PI4_less_type*>(
_pInfo4Before,
_pInfo4Before + _cInfo4Before,
pInfo4After[uAfter],
PI4_less) )
{
//
// This printer hasn't been found in the before list
// so it must be the new printer detected. I know this is partial
// solution because we are not considering the case we detect more
// than one local PnP printer, but it is not our intention for now.
//
bStatus DBGCHK = pstrPrinterName->bUpdate( pInfo4After[uAfter].pPrinterName );
break;
}
}
}
else
{
bStatus DBGNOCHK = FALSE;
}
FreeMem( pInfo4After );
}
return bStatus;
}
DWORD WINAPI
TPnPDetect::
EnumThreadProc(
LPVOID lpParameter
)
/*++
Routine Description:
Invokes the PnP enumeration routine
Arguments:
lpParameter - Standard (see MSDN)
Return Value:
Standard (see MSDN)
Notes:
--*/
{
DWORD dwStatus = EXIT_FAILURE;
HANDLE hEventDone = (HANDLE )lpParameter;
dwStatus = hEventDone ? ERROR_SUCCESS : EXIT_FAILURE;
//
// If there is a NULL driver (meaning no driver) associated with a devnode,
// we want to set CONFIGFLAG_FINISH_INSTALL flag so that PnP manager will
// try to reinstall a driver for this device.
//
if (ERROR_SUCCESS == dwStatus)
{
dwStatus = ProcessDevNodesWithNullDriversAll();
}
//
// Invokes the enumeration routine. It executes syncroniously.
//
if (ERROR_SUCCESS == dwStatus)
{
(void)PnP_Enumerate_Sync();
}
//
// Notify the client we are done.
//
if (hEventDone)
{
SetEvent( hEventDone );
//
// We own the event handle, so we must close it now.
//
CloseHandle( hEventDone );
}
return dwStatus;
}
DWORD WINAPI
TPnPDetect::
ProcessDevNodesWithNullDriversForOneEnumerator(
IN PCTSTR pszEnumerator
)
/*++
Routine Description:
This routine enumerates devnodes of one enumerator and it sets
CONFIGFLAG_FINISH_INSTALL for devnode that has no driver (or NULL driver)
so that this device will be re-detected by PnP manager.
Arguments:
pszEnumerator - The enumerator for a particular bus, this can be
"LPTENUM", "USBPRINT", "DOT4PRT" etc.
Return Value:
Standard (see MSDN)
Notes:
*/
{
DWORD dwStatus = ERROR_SUCCESS;
HDEVINFO hDevInfoSet = INVALID_HANDLE_VALUE;
TCHAR buffer[MAX_PATH] = {0};
DWORD dwDataType = REG_NONE;
SP_DEVINFO_DATA devInfoData = {0};
DWORD cbRequiredSize = 0;
DWORD dwConfigFlags = 0;
hDevInfoSet = SetupDiGetClassDevs(NULL, pszEnumerator, NULL, DIGCF_ALLCLASSES);
dwStatus = (INVALID_HANDLE_VALUE != hDevInfoSet) ? ERROR_SUCCESS : GetLastError();
if (ERROR_SUCCESS == dwStatus)
{
devInfoData.cbSize = sizeof(devInfoData);
for (DWORD cDevIndex = 0; ERROR_SUCCESS == dwStatus; cDevIndex++)
{
dwStatus = SetupDiEnumDeviceInfo(hDevInfoSet, cDevIndex, &devInfoData) ? ERROR_SUCCESS : GetLastError();
//
// When SPDRP_DRIVER is not present, this devnode is associated
// with no driver or, in the other word, NULL driver
//
// For devnodes with NULL drivers, we will set CONFIGFLAG_FINISH_INSTALL
// so that the device will be re-detected
//
// Notes on the error code: internally SetupDiGetDeviceRegistryProperty
// first returns CR_NO_SUCH_VALUE, but this error code is remapped
// to ERROR_INVALID_DATA by setupapi
//
if (ERROR_SUCCESS == dwStatus)
{
dwStatus = SetupDiGetDeviceRegistryProperty(hDevInfoSet,
&devInfoData,
SPDRP_DRIVER,
&dwDataType,
reinterpret_cast<PBYTE>(buffer),
sizeof(buffer), &cbRequiredSize) ? ERROR_SUCCESS : GetLastError();
if (ERROR_INVALID_DATA == dwStatus)
{
dwConfigFlags = 0;
dwStatus = SetupDiGetDeviceRegistryProperty(hDevInfoSet,
&devInfoData,
SPDRP_CONFIGFLAGS,
&dwDataType,
reinterpret_cast<PBYTE>(&dwConfigFlags),
sizeof(dwConfigFlags),
&cbRequiredSize) ? (REG_DWORD == dwDataType ? ERROR_SUCCESS : ERROR_INVALID_PARAMETER) : GetLastError();
if ((ERROR_SUCCESS == dwStatus) && (!(dwConfigFlags & CONFIGFLAG_FINISH_INSTALL)))
{
dwConfigFlags |= CONFIGFLAG_FINISH_INSTALL;
dwStatus = SetupDiSetDeviceRegistryProperty(hDevInfoSet,
&devInfoData,
SPDRP_CONFIGFLAGS,
reinterpret_cast<PBYTE>(&dwConfigFlags),
sizeof(dwConfigFlags)) ? ERROR_SUCCESS : GetLastError();
}
}
}
}
dwStatus = ERROR_NO_MORE_ITEMS == dwStatus ? ERROR_SUCCESS : dwStatus;
}
else
{
dwStatus = ERROR_INVALID_DATA == dwStatus ? ERROR_SUCCESS : dwStatus;
}
if (INVALID_HANDLE_VALUE != hDevInfoSet)
{
SetupDiDestroyDeviceInfoList(hDevInfoSet);
}
return dwStatus;
}
DWORD WINAPI
TPnPDetect::
ProcessDevNodesWithNullDriversAll(
VOID
)
/*++
Routine Description:
This routine enumerates a subset of devnodes and it sets
CONFIGFLAG_FINISH_INSTALL for devnodes that have no driver (or NULL driver)
so that these devices will be re-detected by PnP manager.
Arguments:
None
Return Value:
Standard (see MSDN)
Notes:
*/
{
DWORD dwStatus = ERROR_SUCCESS;
//
// The following enumerators can have printers attached
//
static PCTSTR acszEnumerators[] =
{
szParallelClassEnumerator,
szParallelDot4PrintClassEnumerator,
szUsbPrintClassEnumerator,
szInfraRedPrintClassEnumerator,
};
for (UINT i = 0; (ERROR_SUCCESS == dwStatus) && (i < COUNTOF(acszEnumerators)); i++)
{
dwStatus = ProcessDevNodesWithNullDriversForOneEnumerator(acszEnumerators[i]);
}
return dwStatus;
}
VOID
TPnPDetect::
Reset(
VOID
)
/*++
Routine Description:
Resets the PnP detector class, so you can kick off the
PnP enumeration event again. Release the memory allocated
from calling EnumPrinters() before kicking off PnP enum.
Arguments:
None
Return Value:
None
Notes:
--*/
{
if( _hEventDone )
{
CloseHandle( _hEventDone );
_hEventDone = NULL;
}
if( _pInfo4Before )
{
FreeMem( _pInfo4Before );
_pInfo4Before = NULL;
}
_bDetectionInProgress = FALSE;
}