windows-nt/Source/XPSP1/NT/printscan/print/spooler/spoolss/dll/winproc.c
2020-09-26 16:20:57 +08:00

396 lines
11 KiB
C

/*++
Copyright (c) 1991-92 Microsoft Corporation
Module Name:
winproc.c
Abstract:
Spooler window processing code
Author:
Muhunthan Sivapragasam (MuhuntS) 5-Nov-96 port of win95 code
Environment:
User Mode - Win32
Notes:
Revision History:
BabakJ: Jan 1999, Added thread sync code to allow only one thread doing enumeration, and only
one thread waiting. This helps performance specially when Dynamon has many Hydra ports.
--*/
#include "precomp.h"
#include "local.h"
#pragma hdrstop
#include <cfgmgr32.h>
static const GUID USB_PRINTER_GUID =
{ 0x28d78fad, 0x5a12, 0x11d1,
{ 0xae, 0x5b, 0x0, 0x0, 0xf8, 0x3, 0xa8, 0xc2 } };
static const GUID GUID_DEVCLASS_INFRARED =
{ 0x6bdd1fc5L, 0x810f, 0x11d0,
{ 0xbe, 0xc7, 0x08, 0x00, 0x2b, 0xe2, 0x09, 0x2f } };
typedef struct _DEVICE_REGISTER_INFO {
struct _DEVICE_REGISTER_INFO *pNext;
HANDLE hDevice;
LPVOID pData;
PFN_QUERYREMOVE_CALLBACK pfnQueryRemove;
HDEVNOTIFY hNotify;
} DEVICE_REGISTER_INFO, *PDEVICE_REGISTER_INFO;
PDEVICE_REGISTER_INFO gpDevRegnInfo = NULL;
VOID
ConfigChangeThread(
)
{
HINSTANCE hLib;
VOID (*pfnSplConfigChange)();
WaitForSpoolerInitialization();
if ( hLib = LoadLibrary(L"localspl.dll") ) {
if ( pfnSplConfigChange = GetProcAddress(hLib, "SplConfigChange") ) {
pfnSplConfigChange();
}
FreeLibrary(hLib);
}
}
VOID
ReenumeratePortsThreadWorker(
)
{
HINSTANCE hLib;
VOID (*pfnSplReenumeratePorts)();
WaitForSpoolerInitialization();
if ( hLib = LoadLibrary(L"localspl.dll") ) {
if ( pfnSplReenumeratePorts = GetProcAddress(hLib, "SplReenumeratePorts") ) {
pfnSplReenumeratePorts();
}
FreeLibrary(hLib);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////
/////
///// To improve performance, and prevent too many unnecessary port enumerations, specially for Hydra/Dynamon:
/////
///// - We want to allow only one Device Arrival thread to be doing port enumeration.
///// - If above is happneing, we allow only one more Device Arrival thread be waiting to go in.
///// - All other threads will be turned away, as there is no need for them to do port enumeration.
/////
////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////
CRITICAL_SECTION DeviceArrivalCS; // Used to synchronize threads bringing device arrival messages.
HANDLE ThdOutEvent; // Signalled after a thread is done doing the EnumPort work; not signaled when created.
VOID
ReenumeratePortsThread(
)
{
static BOOL fThdIn; // TRUE if a thread is doing enum work at the moment
static BOOL fThdWaiting; // TRUE if a 2nd thread is waiting behind the thread that is inside.
EnterCriticalSection( &DeviceArrivalCS ); // Enter the crit section initialized for this at localspl init code
if( fThdWaiting ) {
LeaveCriticalSection( &DeviceArrivalCS );
return; // A 2nd thread is already waiting to go in. No need for holding more threads.
}
else {
if( fThdIn ) {
fThdWaiting = TRUE; // There is a thread inside doing Enum work. Have the current thread wait for it to finish.
LeaveCriticalSection( &DeviceArrivalCS );
WaitForSingleObject( ThdOutEvent, INFINITE );
EnterCriticalSection( &DeviceArrivalCS );
fThdWaiting = FALSE;
}
fThdIn = TRUE; // The current thread is now going in to do Enum work.
LeaveCriticalSection( &DeviceArrivalCS );
ReenumeratePortsThreadWorker();
EnterCriticalSection( &DeviceArrivalCS );
fThdIn = FALSE;
if( fThdWaiting )
SetEvent( ThdOutEvent );
LeaveCriticalSection( &DeviceArrivalCS );
return;
}
}
DWORD
QueryRemove(
HANDLE hDevice
)
{
LPVOID pData = NULL;
PFN_QUERYREMOVE_CALLBACK pfnQueryRemove = NULL;
PDEVICE_REGISTER_INFO pDevRegnInfo;
EnterRouterSem();
for ( pDevRegnInfo = gpDevRegnInfo ;
pDevRegnInfo ;
pDevRegnInfo = pDevRegnInfo->pNext ) {
if ( pDevRegnInfo->hDevice == hDevice ) {
pfnQueryRemove = pDevRegnInfo->pfnQueryRemove;
pData = pDevRegnInfo->pData;
break;
}
}
LeaveRouterSem();
return pfnQueryRemove ? pfnQueryRemove(pData) : NO_ERROR;
}
DWORD
SplProcessPnPEvent(
DWORD dwEventType,
LPVOID lpEventData,
PVOID pVoid
)
{
HANDLE hThread;
DWORD dwThread, dwReturn = NO_ERROR;
PDEV_BROADCAST_HANDLE pBroadcast;
DBGMSG(DBG_INFO,
("SplProcessPnPEvent: dwEventType: %d\n", dwEventType));
switch (dwEventType) {
case DBT_CONFIGCHANGED:
hThread = CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE)ConfigChangeThread,
NULL,
0,
&dwThread);
if ( hThread )
CloseHandle(hThread);
break;
case DBT_DEVICEARRIVAL:
case DBT_DEVICEREMOVECOMPLETE:
//
// In case of device arrival we need to see if there are new ports
// and in case of device removal monitors might want to mark ports
// as removed so next reboot they do not have to enumerate them
// ex. USB does this.
//
// We use the default process stack size for this thread. Currently 16KB.
//
hThread = CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE)ReenumeratePortsThread,
NULL,
0,
&dwThread);
if ( hThread )
CloseHandle(hThread);
break;
case DBT_DEVICEQUERYREMOVE:
pBroadcast = (PDEV_BROADCAST_HANDLE)lpEventData;
//
// These checks are to see if we really care about this
//
if ( !pBroadcast ||
pBroadcast->dbch_devicetype != DBT_DEVTYP_HANDLE )
break;
dwReturn = QueryRemove(pBroadcast->dbch_handle);
break;
case DBT_SHELLLOGGEDON:
default:
break;
}
return dwReturn;
}
VOID
RegisterForPnPEvents(
VOID
)
{
DEV_BROADCAST_DEVICEINTERFACE Filter;
// Init the sync objects needed for device arrival thread management
InitializeCriticalSection( &DeviceArrivalCS );
ThdOutEvent = CreateEvent(NULL, FALSE, FALSE, NULL); // Manual reset, non-signaled state
ZeroMemory(&Filter, sizeof(Filter));
Filter.dbcc_size = sizeof(Filter);
Filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
CopyMemory(&Filter.dbcc_classguid,
(LPGUID)&USB_PRINTER_GUID,
sizeof(Filter.dbcc_classguid));
if ( !RegisterDeviceNotification(ghSplHandle,
&Filter,
DEVICE_NOTIFY_SERVICE_HANDLE) ) {
DBGMSG(DBG_INFO,
("RegisterForPnPEvents: RegisterDeviceNotification failed for USB. Error %d\n",
GetLastError()));
} else {
DBGMSG(DBG_WARNING,
("RegisterForPnPEvents: RegisterDeviceNotification succesful for USB\n"));
}
CopyMemory(&Filter.dbcc_classguid,
(LPGUID)&GUID_DEVCLASS_INFRARED,
sizeof(Filter.dbcc_classguid));
if ( !RegisterDeviceNotification(ghSplHandle,
&Filter,
DEVICE_NOTIFY_SERVICE_HANDLE) ) {
DBGMSG(DBG_INFO,
("RegisterForPnPEvents: RegisterDeviceNotification failed for IRDA. Error %d\n",
GetLastError()));
} else {
DBGMSG(DBG_WARNING,
("RegisterForPnPEvents: RegisterDeviceNotification succesful for IRDA\n"));
}
}
BOOL
SplUnregisterForDeviceEvents(
HANDLE hNotify
)
{
PDEVICE_REGISTER_INFO pDevRegnInfo, pPrev;
EnterRouterSem();
//
// Find the registration in our list, remove it and then leave CS to
// call unregister on it
//
for ( pDevRegnInfo = gpDevRegnInfo, pPrev = NULL ;
pDevRegnInfo ;
pPrev = pDevRegnInfo, pDevRegnInfo = pDevRegnInfo->pNext ) {
if ( pDevRegnInfo->hNotify == hNotify ) {
if ( pPrev )
pPrev->pNext = pDevRegnInfo->pNext;
else
gpDevRegnInfo = pDevRegnInfo->pNext;
break;
}
}
LeaveRouterSem();
if ( pDevRegnInfo ) {
UnregisterDeviceNotification(pDevRegnInfo->hNotify);
FreeSplMem(pDevRegnInfo);
return TRUE;
}
return FALSE;
}
HANDLE
SplRegisterForDeviceEvents(
HANDLE hDevice,
LPVOID pData,
PFN_QUERYREMOVE_CALLBACK pfnQueryRemove
)
{
DEV_BROADCAST_HANDLE Filter;
PDEVICE_REGISTER_INFO pDevRegnInfo;
ZeroMemory(&Filter, sizeof(Filter));
Filter.dbch_size = sizeof(Filter);
Filter.dbch_devicetype = DBT_DEVTYP_HANDLE;
Filter.dbch_handle = hDevice;
pDevRegnInfo = (PDEVICE_REGISTER_INFO)
AllocSplMem(sizeof(DEVICE_REGISTER_INFO));
if ( !pDevRegnInfo )
goto Fail;
pDevRegnInfo->hDevice = hDevice;
pDevRegnInfo->pData = pData;
pDevRegnInfo->pfnQueryRemove = pfnQueryRemove;
pDevRegnInfo->hNotify = RegisterDeviceNotification(
ghSplHandle,
&Filter,
DEVICE_NOTIFY_SERVICE_HANDLE);
if ( pDevRegnInfo->hNotify ) {
EnterRouterSem();
pDevRegnInfo->pNext = gpDevRegnInfo;
gpDevRegnInfo = pDevRegnInfo;
LeaveRouterSem();
return pDevRegnInfo->hNotify;
}
FreeSplMem(pDevRegnInfo);
Fail:
return NULL;
}