1707 lines
39 KiB
C
1707 lines
39 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1990-1994 Microsoft Corporation
|
||
|
All rights reserved
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
Change.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Handles implementation for WaitForPrinterChange and related apis.
|
||
|
|
||
|
FindFirstPrinterChangeNotification
|
||
|
RouterFindNextPrinterChangeNotification
|
||
|
FindClosePrinterChangeNotification
|
||
|
|
||
|
Used by providors:
|
||
|
|
||
|
ReplyPrinterChangeNotification [Function Call]
|
||
|
CallRouterFindFirstPrinterChangeNotification [Function Call]
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Albert Ting (AlbertT) 18-Jan-94
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
User Mode -Win32
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "precomp.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#include <ntfytab.h>
|
||
|
|
||
|
#define PRINTER_NOTIFY_DEFAULT_POLLTIME 10000
|
||
|
|
||
|
CRITICAL_SECTION RouterNotifySection;
|
||
|
PCHANGEINFO pChangeInfoHead;
|
||
|
HANDLE hEventPoll;
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
SetupReplyNotification(
|
||
|
PCHANGE pChange,
|
||
|
DWORD fdwOptions,
|
||
|
DWORD fdwStatus,
|
||
|
PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
|
||
|
PPRINTER_NOTIFY_INIT pPrinterNotifyInit);
|
||
|
|
||
|
BOOL
|
||
|
FindFirstPrinterChangeNotificationWorker(
|
||
|
HANDLE hPrinter,
|
||
|
DWORD fdwFilterFlags,
|
||
|
DWORD fdwOptions,
|
||
|
DWORD dwPID,
|
||
|
PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
|
||
|
PHANDLE phEvent,
|
||
|
BOOL bSpooler);
|
||
|
|
||
|
DWORD
|
||
|
FindClosePrinterChangeNotificationWorker(
|
||
|
HANDLE hPrinter);
|
||
|
|
||
|
BOOL
|
||
|
SetupChange(
|
||
|
PPRINTHANDLE pPrintHandle,
|
||
|
DWORD dwPrinterRemote,
|
||
|
LPWSTR pszLocalMachine,
|
||
|
PDWORD pfdwStatus,
|
||
|
DWORD fdwOptions,
|
||
|
DWORD fdwFilterFlags,
|
||
|
PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
|
||
|
PPRINTER_NOTIFY_INIT* pPrinterNotifyInit);
|
||
|
|
||
|
VOID
|
||
|
FailChange(
|
||
|
PPRINTHANDLE pPrintHandle);
|
||
|
|
||
|
BOOL
|
||
|
RouterRefreshPrinterChangeNotification(
|
||
|
HANDLE hPrinter,
|
||
|
DWORD dwColor,
|
||
|
PVOID pPrinterNotifyRefresh,
|
||
|
PPRINTER_NOTIFY_INFO* ppInfo);
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
WPCInit()
|
||
|
{
|
||
|
//
|
||
|
// Create non-signaled, autoreset event.
|
||
|
//
|
||
|
hEventPoll = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||
|
|
||
|
if (!hEventPoll)
|
||
|
return FALSE;
|
||
|
|
||
|
__try {
|
||
|
|
||
|
InitializeCriticalSection(&RouterNotifySection);
|
||
|
|
||
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
|
||
|
SetLastError(GetExceptionCode());
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
WPCDestroy()
|
||
|
{
|
||
|
#if 0
|
||
|
DeleteCriticalSection(&RouterNotifySection);
|
||
|
CloseHandle(hEventPoll);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
RouterFindFirstPrinterChangeNotification(
|
||
|
HANDLE hPrinter,
|
||
|
DWORD fdwFilterFlags,
|
||
|
DWORD fdwOptions,
|
||
|
DWORD dwPID,
|
||
|
PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
|
||
|
PHANDLE phEvent)
|
||
|
{
|
||
|
return FindFirstPrinterChangeNotificationWorker(
|
||
|
hPrinter,
|
||
|
fdwFilterFlags,
|
||
|
fdwOptions,
|
||
|
dwPID,
|
||
|
pPrinterNotifyOptions,
|
||
|
phEvent,
|
||
|
FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
FindFirstPrinterChangeNotificationWorker(
|
||
|
HANDLE hPrinter,
|
||
|
DWORD fdwFilterFlags,
|
||
|
DWORD fdwOptions,
|
||
|
DWORD dwPID,
|
||
|
PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
|
||
|
PHANDLE phEvent,
|
||
|
BOOL bSpooler)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Sets up notification (coming from client side, winspool.drv).
|
||
|
Create an event and duplicate it into the clients address
|
||
|
space so we can communicate with it.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
hPrinter - Printer to watch
|
||
|
|
||
|
fdwFilterFlags - Type of notification to set up (filter)
|
||
|
|
||
|
fdwOptions - user specified option (GROUPING, etc.)
|
||
|
|
||
|
dwPID - PID of the client process (needed to dup handle)
|
||
|
|
||
|
phEvent - hEvent to pass back to the client.
|
||
|
|
||
|
bSpooler - Indicates if called from spooler. If TRUE, then we don't
|
||
|
need to duplicate the event.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
|
||
|
HANDLE hProcess;
|
||
|
DWORD fdwStatus = 0;
|
||
|
PCHANGE pChange = NULL;
|
||
|
PPRINTER_NOTIFY_INIT pPrinterNotifyInit = NULL;
|
||
|
BOOL bReturn;
|
||
|
|
||
|
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
|
||
|
|
||
|
SetLastError(ERROR_INVALID_HANDLE);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
EnterRouterSem();
|
||
|
|
||
|
//
|
||
|
// Clear this out
|
||
|
//
|
||
|
*phEvent = NULL;
|
||
|
|
||
|
// Give a unique DWORD session ID for pPrintHandle
|
||
|
while (pPrintHandle->dwUniqueSessionID == 0 ||
|
||
|
pPrintHandle->dwUniqueSessionID == 0xffffffff) {
|
||
|
|
||
|
pPrintHandle->dwUniqueSessionID = dwRouterUniqueSessionID++;
|
||
|
}
|
||
|
|
||
|
if (!SetupChange(pPrintHandle,
|
||
|
pPrintHandle->dwUniqueSessionID,
|
||
|
szMachineName,
|
||
|
&fdwStatus,
|
||
|
fdwOptions,
|
||
|
fdwFilterFlags,
|
||
|
pPrinterNotifyOptions,
|
||
|
&pPrinterNotifyInit)) {
|
||
|
|
||
|
LeaveRouterSem();
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// !! LATER !!
|
||
|
//
|
||
|
// When Delegation is supported:
|
||
|
//
|
||
|
// Create the event with security access based on the impersonation
|
||
|
// token so that we can filter out bogus notifications from
|
||
|
// random people. (Save the token in the pSpool in localspl, then
|
||
|
// impersonate before RPCing back here. Then we can check if
|
||
|
// we have access to the event.)
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// Create the event here that we trigger on notifications.
|
||
|
// We will duplicate this event into the target client process.
|
||
|
//
|
||
|
pPrintHandle->pChange->hEvent = CreateEvent(
|
||
|
NULL,
|
||
|
TRUE,
|
||
|
FALSE,
|
||
|
NULL);
|
||
|
|
||
|
if (!pPrintHandle->pChange->hEvent) {
|
||
|
goto Fail;
|
||
|
}
|
||
|
|
||
|
if (bSpooler) {
|
||
|
|
||
|
//
|
||
|
// Client is in the spooler process.
|
||
|
//
|
||
|
*phEvent = pPrintHandle->pChange->hEvent;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Client is local.
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// Success, create pair
|
||
|
//
|
||
|
hProcess = OpenProcess(PROCESS_DUP_HANDLE,
|
||
|
FALSE,
|
||
|
dwPID);
|
||
|
|
||
|
if (!hProcess) {
|
||
|
goto Fail;
|
||
|
}
|
||
|
|
||
|
bReturn = DuplicateHandle(GetCurrentProcess(),
|
||
|
pPrintHandle->pChange->hEvent,
|
||
|
hProcess,
|
||
|
phEvent,
|
||
|
EVENT_ALL_ACCESS,
|
||
|
TRUE,
|
||
|
0);
|
||
|
CloseHandle(hProcess);
|
||
|
|
||
|
if (!bReturn) {
|
||
|
goto Fail;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bReturn = SetupReplyNotification(pPrintHandle->pChange,
|
||
|
fdwStatus,
|
||
|
fdwOptions,
|
||
|
pPrinterNotifyOptions,
|
||
|
pPrinterNotifyInit);
|
||
|
|
||
|
if (bReturn) {
|
||
|
|
||
|
pPrintHandle->pChange->eStatus &= ~STATUS_CHANGE_FORMING;
|
||
|
pPrintHandle->pChange->eStatus |=
|
||
|
STATUS_CHANGE_VALID|STATUS_CHANGE_CLIENT;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
Fail:
|
||
|
if (pPrintHandle->pChange->hEvent)
|
||
|
CloseHandle(pPrintHandle->pChange->hEvent);
|
||
|
|
||
|
//
|
||
|
// Local case must close handle twice since we may have
|
||
|
// duplicated the event.
|
||
|
//
|
||
|
if (!bSpooler && *phEvent)
|
||
|
CloseHandle(*phEvent);
|
||
|
|
||
|
FailChange(pPrintHandle);
|
||
|
bReturn = FALSE;
|
||
|
}
|
||
|
|
||
|
LeaveRouterSem();
|
||
|
|
||
|
return bReturn;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
RemoteFindFirstPrinterChangeNotification(
|
||
|
HANDLE hPrinter,
|
||
|
DWORD fdwFilterFlags,
|
||
|
DWORD fdwOptions,
|
||
|
LPWSTR pszLocalMachine,
|
||
|
DWORD dwPrinterRemote,
|
||
|
PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Handles FFPCN coming from other machines. Providers can use
|
||
|
the ProvidorRemoteFFPCN call to initiate the RPC which this function
|
||
|
handles. This code ships the call to the print provider. Note
|
||
|
that we don't create any events here since the client is on
|
||
|
a remote machine.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
hPrinter - printer to watch
|
||
|
|
||
|
fdwFilterFlags - type of notification to watch
|
||
|
|
||
|
fdwOptions -- options on watch
|
||
|
|
||
|
pszLocalMachine - name of local machine that requested the watch
|
||
|
|
||
|
dwPrinterRemote - remote Printer handle
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
|
||
|
BOOL bReturn;
|
||
|
DWORD fdwStatus = 0;
|
||
|
PCHANGE pChange = NULL;
|
||
|
PPRINTER_NOTIFY_INIT pPrinterNotifyInit = NULL;
|
||
|
LPWSTR pszLocalMachineCopy;
|
||
|
|
||
|
pszLocalMachineCopy = AllocSplStr(pszLocalMachine);
|
||
|
|
||
|
if (!pszLocalMachineCopy)
|
||
|
return FALSE;
|
||
|
|
||
|
EnterRouterSem();
|
||
|
|
||
|
if (!SetupChange(pPrintHandle,
|
||
|
dwPrinterRemote,
|
||
|
pszLocalMachineCopy,
|
||
|
&fdwStatus,
|
||
|
fdwOptions,
|
||
|
fdwFilterFlags,
|
||
|
pPrinterNotifyOptions,
|
||
|
&pPrinterNotifyInit)) {
|
||
|
|
||
|
LeaveRouterSem();
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
bReturn = SetupReplyNotification(pPrintHandle->pChange,
|
||
|
fdwStatus,
|
||
|
fdwOptions,
|
||
|
pPrinterNotifyOptions,
|
||
|
pPrinterNotifyInit);
|
||
|
|
||
|
if (bReturn) {
|
||
|
|
||
|
pPrintHandle->pChange->eStatus = STATUS_CHANGE_VALID;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
FailChange(pPrintHandle);
|
||
|
}
|
||
|
|
||
|
LeaveRouterSem();
|
||
|
|
||
|
return bReturn;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
RouterFindNextPrinterChangeNotification(
|
||
|
HANDLE hPrinter,
|
||
|
DWORD fdwFlags,
|
||
|
LPDWORD pfdwChangeFlags,
|
||
|
PPRINTER_NOTIFY_OPTIONS pOptions,
|
||
|
PPRINTER_NOTIFY_INFO* ppInfo)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Return information about notification that just occurred and
|
||
|
reset to look for more notifications.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
hPrinter - printer to reset event handle
|
||
|
|
||
|
fdwFlags - flags (PRINTER_NOTIFY_NEXT_INFO)
|
||
|
|
||
|
pfdwChange - return result of changes
|
||
|
|
||
|
PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions
|
||
|
|
||
|
pReplyContainer - Reply info to pass out.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
BOOL
|
||
|
|
||
|
** NOTE **
|
||
|
Always assume client process is on the same machine. The client
|
||
|
machine router always handles this call.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
|
||
|
PCHANGE pChange = pPrintHandle->pChange;
|
||
|
BOOL bReturn = FALSE;
|
||
|
|
||
|
if (ppInfo) {
|
||
|
*ppInfo = NULL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Currently only REFRESH option is defined.
|
||
|
//
|
||
|
if( pOptions && ( pOptions->Flags & ~PRINTER_NOTIFY_OPTIONS_REFRESH )){
|
||
|
|
||
|
SetLastError( ERROR_INVALID_PARAMETER );
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
EnterRouterSem();
|
||
|
|
||
|
if (pPrintHandle->signature != PRINTHANDLE_SIGNATURE ||
|
||
|
!pChange ||
|
||
|
!(pChange->eStatus & (STATUS_CHANGE_VALID|STATUS_CHANGE_CLIENT))) {
|
||
|
|
||
|
SetLastError(ERROR_INVALID_HANDLE);
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// For now, we collapse all notifications into 1.
|
||
|
//
|
||
|
pChange->dwCount = 0;
|
||
|
|
||
|
//
|
||
|
// Tell the user what changes occurred,
|
||
|
// then clear it out.
|
||
|
//
|
||
|
*pfdwChangeFlags = pChange->fdwChangeFlags;
|
||
|
pChange->fdwChangeFlags = 0;
|
||
|
|
||
|
ResetEvent(pChange->hEvent);
|
||
|
|
||
|
if (pOptions && pOptions->Flags & PRINTER_NOTIFY_OPTIONS_REFRESH) {
|
||
|
|
||
|
//
|
||
|
// Increment color.
|
||
|
//
|
||
|
pPrintHandle->pChange->dwColor++;
|
||
|
|
||
|
LeaveRouterSem();
|
||
|
|
||
|
bReturn = RouterRefreshPrinterChangeNotification(
|
||
|
hPrinter,
|
||
|
pPrintHandle->pChange->dwColor,
|
||
|
pOptions,
|
||
|
ppInfo);
|
||
|
|
||
|
return bReturn;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If they want data && (we have data || if we have flags that we wish
|
||
|
// to send to the user), copy the data.
|
||
|
//
|
||
|
if( ppInfo &&
|
||
|
(fdwFlags & PRINTER_NOTIFY_NEXT_INFO) &&
|
||
|
NotifyNeeded( pChange )){
|
||
|
|
||
|
*ppInfo = pChange->ChangeInfo.pPrinterNotifyInfo;
|
||
|
pChange->ChangeInfo.pPrinterNotifyInfo = NULL;
|
||
|
|
||
|
//
|
||
|
// If we need to notify because of a DISCARD, but we don't
|
||
|
// have a pPrinterNotifyInfo, then allocate one and return it.
|
||
|
//
|
||
|
if( !*ppInfo ){
|
||
|
|
||
|
DBGMSG( DBG_TRACE,
|
||
|
( "RFNPCN: Discard with no pPrinterNotifyInfo: pChange %x\n",
|
||
|
pChange ));
|
||
|
|
||
|
//
|
||
|
// We need to return some information back to the client, so
|
||
|
// allocate a block that has zero items. The header will be
|
||
|
// marked as DISCARDED, which tells the user to refresh.
|
||
|
//
|
||
|
*ppInfo = RouterAllocPrinterNotifyInfo( 0 );
|
||
|
|
||
|
if( !*ppInfo ){
|
||
|
|
||
|
//
|
||
|
// Failed to alloc memory; fail the call.
|
||
|
//
|
||
|
bReturn = FALSE;
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Mark the newly allocated information as DISCARDED.
|
||
|
//
|
||
|
(*ppInfo)->Flags = PRINTER_NOTIFY_INFO_DISCARDED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bReturn = TRUE;
|
||
|
|
||
|
Done:
|
||
|
LeaveRouterSem();
|
||
|
|
||
|
return bReturn;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
FindClosePrinterChangeNotification(
|
||
|
HANDLE hPrinter)
|
||
|
{
|
||
|
DWORD dwError;
|
||
|
LPPRINTHANDLE pPrintHandle = (LPPRINTHANDLE)hPrinter;
|
||
|
|
||
|
EnterRouterSem();
|
||
|
|
||
|
if (pPrintHandle->signature != PRINTHANDLE_SIGNATURE ||
|
||
|
!pPrintHandle->pChange ||
|
||
|
!(pPrintHandle->pChange->eStatus & STATUS_CHANGE_VALID)) {
|
||
|
|
||
|
DBGMSG(DBG_WARN, ("FCPCNW: Invalid handle 0x%x\n", pPrintHandle));
|
||
|
dwError = ERROR_INVALID_HANDLE;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
dwError = FindClosePrinterChangeNotificationWorker(hPrinter);
|
||
|
}
|
||
|
|
||
|
LeaveRouterSem();
|
||
|
|
||
|
if (dwError) {
|
||
|
|
||
|
SetLastError(dwError);
|
||
|
return FALSE;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
DWORD
|
||
|
FindClosePrinterChangeNotificationWorker(
|
||
|
HANDLE hPrinter)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Close a notification.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
hPrinter -- printer that we want to close
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
ERROR code
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
|
||
|
BOOL bLocal = FALSE;
|
||
|
HANDLE hNotifyRemote = NULL;
|
||
|
DWORD dwError = ERROR_SUCCESS;
|
||
|
|
||
|
DBGMSG(DBG_NOTIFY, ("FCPCN: Closing 0x%x ->pChange 0x%x\n",
|
||
|
pPrintHandle, pPrintHandle->pChange));
|
||
|
|
||
|
RouterInSem();
|
||
|
|
||
|
//
|
||
|
// If the notification exists, shut it down (this is the
|
||
|
// local case). If we are called remotely, we don't need to
|
||
|
// do this, since hEvent wasn't created.
|
||
|
//
|
||
|
if (pPrintHandle->pChange->eStatus & STATUS_CHANGE_CLIENT) {
|
||
|
|
||
|
CloseHandle(pPrintHandle->pChange->hEvent);
|
||
|
bLocal = TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Remember what the hNotifyRemote is, in case we want to delete it.
|
||
|
//
|
||
|
hNotifyRemote = pPrintHandle->pChange->hNotifyRemote;
|
||
|
|
||
|
//
|
||
|
// Failure to free implies we're using it now. In this case,
|
||
|
// don't try and free the hNotifyRemote.
|
||
|
//
|
||
|
if (!FreeChange(pPrintHandle->pChange)) {
|
||
|
hNotifyRemote = NULL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If local, don't allow new reply-s to be set up.
|
||
|
//
|
||
|
if (bLocal) {
|
||
|
|
||
|
RemoveReplyClient(pPrintHandle,
|
||
|
REPLY_TYPE_NOTIFICATION);
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// We must zero this out to prevent other threads from
|
||
|
// attempting to close this context handle (client side)
|
||
|
// at the same time we are closing it.
|
||
|
//
|
||
|
pPrintHandle->pChange = NULL;
|
||
|
|
||
|
if (!bLocal) {
|
||
|
|
||
|
//
|
||
|
// Remote case, shut down the notification handle if
|
||
|
// there is one here. (If there is a double hop, only
|
||
|
// the second hop will have a notification reply. Currently
|
||
|
// only 1 hop is support during registration, however.)
|
||
|
//
|
||
|
LeaveRouterSem();
|
||
|
|
||
|
CloseReplyRemote(hNotifyRemote);
|
||
|
|
||
|
EnterRouterSem();
|
||
|
}
|
||
|
|
||
|
LeaveRouterSem();
|
||
|
|
||
|
RouterOutSem();
|
||
|
|
||
|
if (!(*pPrintHandle->pProvidor->PrintProvidor.
|
||
|
fpFindClosePrinterChangeNotification) (pPrintHandle->hPrinter)) {
|
||
|
|
||
|
dwError = GetLastError();
|
||
|
}
|
||
|
EnterRouterSem();
|
||
|
|
||
|
return dwError;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
SetupReplyNotification(
|
||
|
PCHANGE pChange,
|
||
|
DWORD fdwStatus,
|
||
|
DWORD fdwOptions,
|
||
|
PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
|
||
|
PPRINTER_NOTIFY_INIT pPrinterNotifyInit)
|
||
|
{
|
||
|
DWORD dwReturn = ERROR_SUCCESS;
|
||
|
|
||
|
RouterInSem();
|
||
|
|
||
|
if (!pChange) {
|
||
|
dwReturn = ERROR_INVALID_PARAMETER;
|
||
|
goto Fail;
|
||
|
}
|
||
|
|
||
|
SPLASSERT(pChange->eStatus & STATUS_CHANGE_FORMING);
|
||
|
|
||
|
if (fdwStatus & PRINTER_NOTIFY_STATUS_ENDPOINT) {
|
||
|
|
||
|
//
|
||
|
// For remote notification, we must setup a reply.
|
||
|
//
|
||
|
if (_wcsicmp(pChange->pszLocalMachine, szMachineName)) {
|
||
|
|
||
|
LeaveRouterSem();
|
||
|
|
||
|
dwReturn = OpenReplyRemote(pChange->pszLocalMachine,
|
||
|
&pChange->hNotifyRemote,
|
||
|
pChange->dwPrinterRemote,
|
||
|
REPLY_TYPE_NOTIFICATION,
|
||
|
0,
|
||
|
NULL);
|
||
|
|
||
|
EnterRouterSem();
|
||
|
|
||
|
if (dwReturn)
|
||
|
goto Fail;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// The user can specify different status flags for the
|
||
|
// notfication. Process them here.
|
||
|
//
|
||
|
|
||
|
if (fdwStatus & PRINTER_NOTIFY_STATUS_POLL) {
|
||
|
|
||
|
//
|
||
|
// If there wasn't an error, then our reply notification
|
||
|
// handle is valid, and we should do the polling.
|
||
|
//
|
||
|
pChange->ChangeInfo.dwPollTime =
|
||
|
(pPrinterNotifyInit &&
|
||
|
pPrinterNotifyInit->PollTime) ?
|
||
|
pPrinterNotifyInit->PollTime :
|
||
|
PRINTER_NOTIFY_DEFAULT_POLLTIME;
|
||
|
|
||
|
pChange->ChangeInfo.dwPollTimeLeft = pChange->ChangeInfo.dwPollTime;
|
||
|
|
||
|
//
|
||
|
// Don't cause a poll the first time it's added.
|
||
|
//
|
||
|
pChange->ChangeInfo.bResetPollTime = TRUE;
|
||
|
LinkAdd(&pChange->ChangeInfo.Link, (PLINK*)&pChangeInfoHead);
|
||
|
|
||
|
SetEvent(hEventPoll);
|
||
|
|
||
|
}
|
||
|
pChange->ChangeInfo.fdwStatus = fdwStatus;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
pChange->dwPrinterRemote = 0;
|
||
|
}
|
||
|
|
||
|
if (pPrinterNotifyOptions) {
|
||
|
|
||
|
pChange->eStatus |= STATUS_CHANGE_INFO;
|
||
|
}
|
||
|
|
||
|
Fail:
|
||
|
if (dwReturn) {
|
||
|
SetLastError(dwReturn);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// The Reply* functions handle calls from the server back to the client,
|
||
|
// indicating that something changed.
|
||
|
//
|
||
|
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
ReplyPrinterChangeNotificationWorker(
|
||
|
HANDLE hPrinter,
|
||
|
DWORD dwColor,
|
||
|
DWORD fdwChangeFlags,
|
||
|
PDWORD pdwResult,
|
||
|
PPRINTER_NOTIFY_INFO pPrinterNotifyInfo
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Notifies the client that something happened. If local, simply
|
||
|
set the event. If remote, call ThreadNotify which spawns a thread
|
||
|
to RPC to the client router. (This call from the server -> client
|
||
|
requires that the spooler pipe use the NULL session, since we
|
||
|
are in local system context and RPC calls (with no impersonation)
|
||
|
use the NULL session.)
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
hPrinter -- printer that changed
|
||
|
|
||
|
dwColor -- color time stamp of data
|
||
|
|
||
|
fdwChangeFlags -- flags that changed
|
||
|
|
||
|
pdwResult -- result flags (OPTIONAL)
|
||
|
|
||
|
pPrinterNotifyInfo -- Notify info data. Note that if this is NULL,
|
||
|
we don't set the DISCARDED flag. This is different
|
||
|
than PartialRPN,
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
BOOL TRUE = success
|
||
|
FALSE = fail
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
LPPRINTHANDLE pPrintHandle = (LPPRINTHANDLE)hPrinter;
|
||
|
PCHANGE pChange;
|
||
|
BOOL bReturn = FALSE;
|
||
|
|
||
|
EnterRouterSem();
|
||
|
|
||
|
if (!pPrintHandle ||
|
||
|
pPrintHandle->signature != PRINTHANDLE_SIGNATURE ||
|
||
|
!pPrintHandle->pChange ||
|
||
|
!(pPrintHandle->pChange->eStatus & STATUS_CHANGE_VALID)) {
|
||
|
|
||
|
SetLastError(ERROR_INVALID_HANDLE);
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
pChange = pPrintHandle->pChange;
|
||
|
|
||
|
if (pdwResult) {
|
||
|
*pdwResult = 0;
|
||
|
}
|
||
|
|
||
|
if (pChange->eStatus & STATUS_CHANGE_DISCARDED) {
|
||
|
|
||
|
DBGMSG(DBG_WARNING,
|
||
|
("RPCNW: Discarded x%x, eStatus = 0x%x, pInfo = 0x%x\n",
|
||
|
pChange,
|
||
|
pChange->eStatus,
|
||
|
pChange->ChangeInfo.pPrinterNotifyInfo));
|
||
|
|
||
|
if (pdwResult && pPrinterNotifyInfo) {
|
||
|
|
||
|
*pdwResult |= (PRINTER_NOTIFY_INFO_DISCARDED |
|
||
|
PRINTER_NOTIFY_INFO_DISCARDNOTED);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the discard has already been noted, then we don't need
|
||
|
// to notify the client. If it hasn't been noted, we need to
|
||
|
// trigger a notification, since the the client needs to refresh.
|
||
|
//
|
||
|
if (pChange->eStatus & STATUS_CHANGE_DISCARDNOTED) {
|
||
|
bReturn = TRUE;
|
||
|
goto Done;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pChange->eStatus & STATUS_CHANGE_INFO && pPrinterNotifyInfo) {
|
||
|
|
||
|
*pdwResult |= AppendPrinterNotifyInfo(pPrintHandle,
|
||
|
dwColor,
|
||
|
pPrinterNotifyInfo);
|
||
|
|
||
|
//
|
||
|
// AppendPrinterNotifyInfo will set the color mismatch
|
||
|
// bit if the notification info is stale (doesn't match color).
|
||
|
//
|
||
|
if (*pdwResult & PRINTER_NOTIFY_INFO_COLORMISMATCH) {
|
||
|
|
||
|
DBGMSG(DBG_WARNING, ("RPCN: Color mismatch; discarding\n"));
|
||
|
|
||
|
bReturn = TRUE;
|
||
|
goto Done;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Store up the changes for querying later.
|
||
|
//
|
||
|
pChange->fdwChangeFlags |= fdwChangeFlags;
|
||
|
|
||
|
//
|
||
|
// Ensure that there is a valid notification waiting.
|
||
|
// This is an optimization that avoids the case where the print
|
||
|
// providor calls PartialRPCN several times, then is about to
|
||
|
// call ReplyPCN. Before it does this, we process the
|
||
|
// the notification (either the client picks it up, or it rpcs
|
||
|
// out to a remote router). Suddenly we have no notification
|
||
|
// data, so return instead of sending nothing.
|
||
|
//
|
||
|
if (!NotifyNeeded(pChange)) {
|
||
|
|
||
|
bReturn = TRUE;
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Notify Here
|
||
|
//
|
||
|
// If this is the local machine, then just set the event and update.
|
||
|
//
|
||
|
if (pChange->eStatus & STATUS_CHANGE_CLIENT) {
|
||
|
|
||
|
if (!pChange->hEvent ||
|
||
|
pChange->hEvent == INVALID_HANDLE_VALUE) {
|
||
|
|
||
|
DBGMSG(DBG_WARNING, ("ReplyNotify invalid event\n"));
|
||
|
SetLastError(ERROR_INVALID_HANDLE);
|
||
|
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
if (!SetEvent(pChange->hEvent)) {
|
||
|
|
||
|
//
|
||
|
// SetEvent failed!
|
||
|
//
|
||
|
DBGMSG(DBG_ERROR, ("ReplyNotify SetEvent Failed (ignore it!): Error %d.\n", GetLastError()));
|
||
|
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Keep count of notifications so that we return the correct
|
||
|
// number of FNPCNs.
|
||
|
//
|
||
|
pChange->dwCount++;
|
||
|
|
||
|
DBGMSG(DBG_NOTIFY, (">>>> Local trigger 0x%x\n", fdwChangeFlags));
|
||
|
bReturn = TRUE;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Remote case.
|
||
|
//
|
||
|
// Note: pPrintHandle is invalid, since hNotify is valid only in the
|
||
|
// client router address space.
|
||
|
//
|
||
|
|
||
|
DBGMSG(DBG_NOTIFY, ("*** Trigger remote event *** 0x%x\n",
|
||
|
pPrintHandle));
|
||
|
|
||
|
bReturn = ThreadNotify(pPrintHandle);
|
||
|
}
|
||
|
|
||
|
Done:
|
||
|
LeaveRouterSem();
|
||
|
return bReturn;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
FreeChange(
|
||
|
PCHANGE pChange)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Frees the change structure.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE = Deleted
|
||
|
FALSE = deferred.
|
||
|
|
||
|
NOTE: Assumes in Critical Section
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
RouterInSem();
|
||
|
|
||
|
//
|
||
|
// Remove ourselves from the list if the providor wanted us
|
||
|
// to send out polling notifications.
|
||
|
//
|
||
|
if (pChange->ChangeInfo.fdwStatus & PRINTER_NOTIFY_STATUS_POLL)
|
||
|
LinkDelete(&pChange->ChangeInfo.Link, (PLINK*)&pChangeInfoHead);
|
||
|
|
||
|
//
|
||
|
// pPrintHandle should never refer to the pChange again. This
|
||
|
// ensures that the FreePrinterHandle only frees the pChange once.
|
||
|
//
|
||
|
if (pChange->ChangeInfo.pPrintHandle) {
|
||
|
|
||
|
pChange->ChangeInfo.pPrintHandle->pChange = NULL;
|
||
|
pChange->ChangeInfo.pPrintHandle = NULL;
|
||
|
}
|
||
|
|
||
|
if (pChange->cRef || pChange->eStatus & STATUS_CHANGE_FORMING) {
|
||
|
|
||
|
pChange->eStatus |= STATUS_CHANGE_CLOSING;
|
||
|
|
||
|
DBGMSG(DBG_NOTIFY, ("FreeChange: 0x%x in use: cRef = %d\n",
|
||
|
pChange,
|
||
|
pChange->cRef));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the pszLocalMachine is ourselves, then don't free it,
|
||
|
// since there's a single instance locally.
|
||
|
//
|
||
|
if (pChange->pszLocalMachine != szMachineName && pChange->pszLocalMachine)
|
||
|
FreeSplStr(pChange->pszLocalMachine);
|
||
|
|
||
|
if (pChange->ChangeInfo.pPrinterNotifyInfo) {
|
||
|
|
||
|
RouterFreePrinterNotifyInfo(pChange->ChangeInfo.pPrinterNotifyInfo);
|
||
|
}
|
||
|
|
||
|
DBGMSG(DBG_NOTIFY, ("FreeChange: 0x%x ->pPrintHandle 0x%x\n",
|
||
|
pChange, pChange->ChangeInfo.pPrintHandle));
|
||
|
|
||
|
FreeSplMem(pChange);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
FreePrinterHandle(
|
||
|
PPRINTHANDLE pPrintHandle
|
||
|
)
|
||
|
{
|
||
|
if( !pPrintHandle ){
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
EnterRouterSem();
|
||
|
|
||
|
//
|
||
|
// If on wait list, force wait list to free it.
|
||
|
//
|
||
|
if (pPrintHandle->pChange) {
|
||
|
|
||
|
FreeChange(pPrintHandle->pChange);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Inform all notifys on this printer handle that they are gone.
|
||
|
//
|
||
|
FreePrinterHandleNotifys(pPrintHandle);
|
||
|
|
||
|
LeaveRouterSem();
|
||
|
|
||
|
DBGMSG(DBG_NOTIFY, ("FreePrinterHandle: 0x%x, pChange = 0x%x, pNotify = 0x%x\n",
|
||
|
pPrintHandle, pPrintHandle->pNotify,
|
||
|
pPrintHandle->pChange));
|
||
|
|
||
|
// Log warning to detect handle free
|
||
|
DBGMSG(DBG_TRACE, ("FreePrinterHandle: 0x%x\n", pPrintHandle));
|
||
|
|
||
|
// Close tempfile handle, if any
|
||
|
if (pPrintHandle->hFileSpooler != INVALID_HANDLE_VALUE) {
|
||
|
CloseHandle(pPrintHandle->hFileSpooler);
|
||
|
}
|
||
|
|
||
|
if (pPrintHandle->szTempSpoolFile) {
|
||
|
|
||
|
if (!DeleteFile(pPrintHandle->szTempSpoolFile)) {
|
||
|
|
||
|
MoveFileEx(pPrintHandle->szTempSpoolFile, NULL,
|
||
|
MOVEFILE_DELAY_UNTIL_REBOOT);
|
||
|
}
|
||
|
|
||
|
FreeSplMem(pPrintHandle->szTempSpoolFile);
|
||
|
pPrintHandle->szTempSpoolFile = NULL;
|
||
|
}
|
||
|
|
||
|
FreeSplStr( pPrintHandle->pszPrinter );
|
||
|
|
||
|
FreeSplMem( pPrintHandle );
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
HandlePollNotifications(
|
||
|
VOID)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This handles the pulsing of notifications for any providor that wants
|
||
|
to do polling. It never returns.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
VOID
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
VOID (also never returns)
|
||
|
|
||
|
NOTE: This thread should never exit, since hpmon uses this thread
|
||
|
for initialization. If this thread exists, certain services
|
||
|
this thread initializes quit.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
HANDLE hWaitObjects[1];
|
||
|
PCHANGEINFO pChangeInfo;
|
||
|
DWORD dwSleepTime = INFINITE, dwTimeElapsed, dwPreSleepTicks,
|
||
|
dwEvent;
|
||
|
MSG msg;
|
||
|
|
||
|
hWaitObjects[0] = hEventPoll;
|
||
|
|
||
|
|
||
|
while (TRUE) {
|
||
|
|
||
|
dwPreSleepTicks = GetTickCount();
|
||
|
|
||
|
dwEvent = MsgWaitForMultipleObjects(1,
|
||
|
hWaitObjects,
|
||
|
FALSE,
|
||
|
dwSleepTime,
|
||
|
QS_ALLEVENTS | QS_SENDMESSAGE);
|
||
|
|
||
|
if ( dwEvent == WAIT_TIMEOUT ) {
|
||
|
|
||
|
dwTimeElapsed = dwSleepTime;
|
||
|
|
||
|
} else if ( dwEvent == WAIT_OBJECT_0 + 0 ) {
|
||
|
|
||
|
dwTimeElapsed = GetTickCount() - dwPreSleepTicks;
|
||
|
} else {
|
||
|
|
||
|
while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) {
|
||
|
|
||
|
TranslateMessage(&msg);
|
||
|
DispatchMessage(&msg);
|
||
|
}
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
EnterRouterSem();
|
||
|
|
||
|
//
|
||
|
// Initialize sleep time to INFINITY.
|
||
|
//
|
||
|
dwSleepTime = INFINITE;
|
||
|
|
||
|
for (pChangeInfo = pChangeInfoHead;
|
||
|
pChangeInfo;
|
||
|
pChangeInfo = (PCHANGEINFO)pChangeInfo->Link.pNext) {
|
||
|
|
||
|
//
|
||
|
// If first time or a notification came in,
|
||
|
// we just want to reset the time.
|
||
|
//
|
||
|
if (pChangeInfo->bResetPollTime) {
|
||
|
|
||
|
pChangeInfo->dwPollTimeLeft = pChangeInfo->dwPollTime;
|
||
|
pChangeInfo->bResetPollTime = FALSE;
|
||
|
|
||
|
} else if (pChangeInfo->dwPollTimeLeft <= dwTimeElapsed) {
|
||
|
|
||
|
//
|
||
|
// Cause a notification.
|
||
|
//
|
||
|
ReplyPrinterChangeNotificationWorker(
|
||
|
pChangeInfo->pPrintHandle,
|
||
|
0,
|
||
|
pChangeInfo->fdwFilterFlags,
|
||
|
NULL,
|
||
|
NULL);
|
||
|
|
||
|
pChangeInfo->dwPollTimeLeft = pChangeInfo->dwPollTime;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// They've slept dwTimeElapsed, so take that off of
|
||
|
// their dwPollTimeLeft.
|
||
|
//
|
||
|
pChangeInfo->dwPollTimeLeft -= dwTimeElapsed;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now compute what is the least amout of time we wish
|
||
|
// to sleep before the next guy should be woken up.
|
||
|
//
|
||
|
if (dwSleepTime > pChangeInfo->dwPollTimeLeft)
|
||
|
dwSleepTime = pChangeInfo->dwPollTimeLeft;
|
||
|
}
|
||
|
|
||
|
LeaveRouterSem();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
SetupChange(
|
||
|
PPRINTHANDLE pPrintHandle,
|
||
|
DWORD dwPrinterRemote,
|
||
|
LPWSTR pszLocalMachine,
|
||
|
PDWORD pfdwStatus,
|
||
|
DWORD fdwOptions,
|
||
|
DWORD fdwFilterFlags,
|
||
|
PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
|
||
|
PPRINTER_NOTIFY_INIT* ppPrinterNotifyInit)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Sets up the pChange structure. Validates the handle,
|
||
|
then attempts to alloc it.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PCHANGE pChange;
|
||
|
BOOL bReturn;
|
||
|
|
||
|
RouterInSem();
|
||
|
|
||
|
if (!pPrintHandle || pPrintHandle->signature != PRINTHANDLE_SIGNATURE) {
|
||
|
|
||
|
SetLastError(ERROR_INVALID_HANDLE);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (pPrintHandle->pChange) {
|
||
|
|
||
|
DBGMSG(DBG_ERROR, ("FFPCN: Already watching printer handle.\n"));
|
||
|
|
||
|
//
|
||
|
// Error: already watched
|
||
|
//
|
||
|
SetLastError(ERROR_ALREADY_WAITING);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
pChange = AllocSplMem(sizeof(CHANGE));
|
||
|
|
||
|
DBGMSG(DBG_NOTIFY, ("FFPCN pChange allocated 0x%x\n", pChange));
|
||
|
|
||
|
if (!pChange) {
|
||
|
|
||
|
//
|
||
|
// Failed to allocate memory, quit.
|
||
|
//
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
pPrintHandle->pChange = pChange;
|
||
|
|
||
|
pChange->signature = CHANGEHANDLE_SIGNATURE;
|
||
|
pChange->eStatus = STATUS_CHANGE_FORMING;
|
||
|
pChange->ChangeInfo.pPrintHandle = pPrintHandle;
|
||
|
pChange->ChangeInfo.fdwOptions = fdwOptions;
|
||
|
|
||
|
//
|
||
|
// Don't watch for Failed Connections.
|
||
|
//
|
||
|
pChange->ChangeInfo.fdwFilterFlags =
|
||
|
fdwFilterFlags & ~PRINTER_CHANGE_FAILED_CONNECTION_PRINTER;
|
||
|
|
||
|
pChange->dwPrinterRemote = dwPrinterRemote;
|
||
|
pChange->pszLocalMachine = pszLocalMachine;
|
||
|
|
||
|
//
|
||
|
// As soon as we leave the critical section, pPrintHandle
|
||
|
// may vanish! If this is the case, out pChange->eStatus STATUS_CHANGE_CLOSING
|
||
|
// bit will be set.
|
||
|
//
|
||
|
LeaveRouterSem();
|
||
|
|
||
|
//
|
||
|
// Once we leave the critical section, we are may try and
|
||
|
// alter the notification. To guard against this, we always
|
||
|
// check eValid.
|
||
|
//
|
||
|
bReturn = (*pPrintHandle->pProvidor->PrintProvidor.
|
||
|
fpFindFirstPrinterChangeNotification) (pPrintHandle->hPrinter,
|
||
|
fdwFilterFlags,
|
||
|
fdwOptions,
|
||
|
(HANDLE)pPrintHandle,
|
||
|
pfdwStatus,
|
||
|
pPrinterNotifyOptions,
|
||
|
ppPrinterNotifyInit);
|
||
|
|
||
|
EnterRouterSem();
|
||
|
|
||
|
//
|
||
|
// On fail exit.
|
||
|
//
|
||
|
if (!bReturn) {
|
||
|
|
||
|
pPrintHandle->pChange = NULL;
|
||
|
|
||
|
if (pChange) {
|
||
|
|
||
|
//
|
||
|
// We no longer need to be saved, so change
|
||
|
// eStatus to be 0.
|
||
|
//
|
||
|
pChange->eStatus = 0;
|
||
|
DBGMSG(DBG_NOTIFY, ("FFPCN: Error %d, pChange deleting 0x%x %d\n",
|
||
|
GetLastError(),
|
||
|
pChange));
|
||
|
|
||
|
FreeChange(pChange);
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FailChange(
|
||
|
PPRINTHANDLE pPrintHandle)
|
||
|
{
|
||
|
LeaveRouterSem();
|
||
|
|
||
|
//
|
||
|
// Shut it down since we failed.
|
||
|
//
|
||
|
(*pPrintHandle->pProvidor->PrintProvidor.
|
||
|
fpFindClosePrinterChangeNotification) (pPrintHandle->hPrinter);
|
||
|
|
||
|
EnterRouterSem();
|
||
|
|
||
|
//
|
||
|
// We no longer need to be saved, so change
|
||
|
// eStatus to be 0.
|
||
|
//
|
||
|
pPrintHandle->pChange->eStatus = 0;
|
||
|
DBGMSG(DBG_NOTIFY, ("FFPCN: Error %d, pChange deleting 0x%x %d\n",
|
||
|
GetLastError(),
|
||
|
pPrintHandle->pChange));
|
||
|
|
||
|
FreeChange(pPrintHandle->pChange);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*------------------------------------------------------------------------
|
||
|
|
||
|
Entry points for providors.
|
||
|
|
||
|
------------------------------------------------------------------------*/
|
||
|
|
||
|
BOOL
|
||
|
ProvidorFindFirstPrinterChangeNotification(
|
||
|
HANDLE hPrinter,
|
||
|
DWORD fdwFilterFlags,
|
||
|
DWORD fdwOptions,
|
||
|
HANDLE hChange,
|
||
|
PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
|
||
|
PPRINTER_NOTIFY_INIT* ppPrinterNotifyInit)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Handles any FFPCN that originates from a providor.
|
||
|
Localspl does this when it wants to put a notification on a port.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
|
||
|
BOOL bReturnValue;
|
||
|
DWORD fdwStatus = 0;
|
||
|
|
||
|
bReturnValue = (*pPrintHandle->pProvidor->PrintProvidor.
|
||
|
fpFindFirstPrinterChangeNotification) (pPrintHandle->hPrinter,
|
||
|
fdwFilterFlags,
|
||
|
fdwOptions,
|
||
|
hChange,
|
||
|
&fdwStatus,
|
||
|
pPrinterNotifyOptions,
|
||
|
ppPrinterNotifyInit);
|
||
|
|
||
|
if (bReturnValue) {
|
||
|
|
||
|
//
|
||
|
// !! LATER !! Check return value of SetupReply...
|
||
|
//
|
||
|
EnterRouterSem();
|
||
|
|
||
|
SetupReplyNotification(((PPRINTHANDLE)hChange)->pChange,
|
||
|
fdwStatus,
|
||
|
fdwOptions,
|
||
|
pPrinterNotifyOptions,
|
||
|
ppPrinterNotifyInit ?
|
||
|
*ppPrinterNotifyInit :
|
||
|
NULL);
|
||
|
|
||
|
LeaveRouterSem();
|
||
|
}
|
||
|
|
||
|
return bReturnValue;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
CallRouterFindFirstPrinterChangeNotification(
|
||
|
HANDLE hPrinterRPC,
|
||
|
DWORD fdwFilterFlags,
|
||
|
DWORD fdwOptions,
|
||
|
HANDLE hPrinterLocal,
|
||
|
PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This is called by providers if they want to pass a notification
|
||
|
along to another machine. This notification must originate from
|
||
|
this machine but needs to be passed to another spooler.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
hPrinter - context handle to use for communication
|
||
|
|
||
|
fdwFilterFlags - watch items
|
||
|
|
||
|
fdwOptions - watch options
|
||
|
|
||
|
hPrinterLocal - pPrinter structure valid in this address space,
|
||
|
and also is the sink for the notifications.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Error code
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
DWORD ReturnValue = 0;
|
||
|
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinterLocal;
|
||
|
|
||
|
EnterRouterSem();
|
||
|
|
||
|
BeginReplyClient(pPrintHandle,
|
||
|
REPLY_TYPE_NOTIFICATION);
|
||
|
|
||
|
LeaveRouterSem();
|
||
|
|
||
|
RpcTryExcept {
|
||
|
|
||
|
ReturnValue = RpcRemoteFindFirstPrinterChangeNotificationEx(
|
||
|
hPrinterRPC,
|
||
|
fdwFilterFlags,
|
||
|
fdwOptions,
|
||
|
pPrintHandle->pChange->pszLocalMachine,
|
||
|
pPrintHandle->dwUniqueSessionID,
|
||
|
(PRPC_V2_NOTIFY_OPTIONS)pPrinterNotifyOptions);
|
||
|
|
||
|
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
|
||
|
|
||
|
ReturnValue = RpcExceptionCode();
|
||
|
|
||
|
} RpcEndExcept
|
||
|
|
||
|
//
|
||
|
// Talking to downlevel (Daytona) box.
|
||
|
//
|
||
|
if (ReturnValue == RPC_S_PROCNUM_OUT_OF_RANGE) {
|
||
|
|
||
|
if (!pPrinterNotifyOptions) {
|
||
|
|
||
|
RpcTryExcept {
|
||
|
|
||
|
ReturnValue = RpcRemoteFindFirstPrinterChangeNotification(
|
||
|
hPrinterRPC,
|
||
|
fdwFilterFlags,
|
||
|
fdwOptions,
|
||
|
pPrintHandle->pChange->pszLocalMachine,
|
||
|
pPrintHandle->dwUniqueSessionID,
|
||
|
0,
|
||
|
NULL);
|
||
|
|
||
|
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
|
||
|
|
||
|
ReturnValue = RpcExceptionCode();
|
||
|
|
||
|
} RpcEndExcept
|
||
|
}
|
||
|
}
|
||
|
|
||
|
EnterRouterSem();
|
||
|
|
||
|
EndReplyClient(pPrintHandle,
|
||
|
REPLY_TYPE_NOTIFICATION);
|
||
|
|
||
|
LeaveRouterSem();
|
||
|
|
||
|
return ReturnValue;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
ProvidorFindClosePrinterChangeNotification(
|
||
|
HANDLE hPrinter)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Handles any FCPCN that originates from a providor.
|
||
|
Localspl does this when it wants to put a notification on a port.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NOTE: Assumes a client notification was setup already.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
LPPRINTHANDLE pPrintHandle=(LPPRINTHANDLE)hPrinter;
|
||
|
|
||
|
return (*pPrintHandle->pProvidor->PrintProvidor.
|
||
|
fpFindClosePrinterChangeNotification) (pPrintHandle->hPrinter);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*------------------------------------------------------------------------
|
||
|
|
||
|
Entry points for spooler components.
|
||
|
|
||
|
------------------------------------------------------------------------*/
|
||
|
|
||
|
BOOL
|
||
|
SpoolerFindFirstPrinterChangeNotification(
|
||
|
HANDLE hPrinter,
|
||
|
DWORD fdwFilterFlags,
|
||
|
DWORD fdwOptions,
|
||
|
PHANDLE phEvent,
|
||
|
PPRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
|
||
|
PVOID pvReserved)
|
||
|
{
|
||
|
return FindFirstPrinterChangeNotificationWorker(
|
||
|
hPrinter,
|
||
|
fdwFilterFlags,
|
||
|
fdwOptions,
|
||
|
0,
|
||
|
pPrinterNotifyOptions,
|
||
|
phEvent,
|
||
|
TRUE);
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
SpoolerFindNextPrinterChangeNotification(
|
||
|
HANDLE hPrinter,
|
||
|
LPDWORD pfdwChange,
|
||
|
LPVOID pPrinterNotifyOptions,
|
||
|
LPVOID *ppPrinterNotifyInfo)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Jump routine for FindNext for internal spooler components to use.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
BOOL bReturn;
|
||
|
DWORD fdwFlags = 0;
|
||
|
|
||
|
if (ppPrinterNotifyInfo) {
|
||
|
|
||
|
fdwFlags = PRINTER_NOTIFY_NEXT_INFO;
|
||
|
}
|
||
|
|
||
|
bReturn = RouterFindNextPrinterChangeNotification(
|
||
|
hPrinter,
|
||
|
fdwFlags,
|
||
|
pfdwChange,
|
||
|
pPrinterNotifyOptions,
|
||
|
(PPRINTER_NOTIFY_INFO*)ppPrinterNotifyInfo);
|
||
|
|
||
|
return bReturn;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
SpoolerFreePrinterNotifyInfo(
|
||
|
PPRINTER_NOTIFY_INFO pInfo)
|
||
|
{
|
||
|
RouterFreePrinterNotifyInfo(pInfo);
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
SpoolerFindClosePrinterChangeNotification(
|
||
|
HANDLE hPrinter)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Jump routine for FindClose for internal spooler components to use.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
return FindClosePrinterChangeNotification(hPrinter);
|
||
|
}
|
||
|
|
||
|
|
||
|
#if DBG
|
||
|
|
||
|
VOID
|
||
|
EnterRouterSem()
|
||
|
{
|
||
|
EnterCriticalSection(&RouterNotifySection);
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
LeaveRouterSem(
|
||
|
VOID)
|
||
|
{
|
||
|
if ((ULONG_PTR)RouterNotifySection.OwningThread != GetCurrentThreadId()) {
|
||
|
DBGMSG(DBG_ERROR, ("Not in router semaphore\n"));
|
||
|
}
|
||
|
LeaveCriticalSection(&RouterNotifySection);
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
RouterInSem(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
if ((ULONG_PTR)RouterNotifySection.OwningThread != GetCurrentThreadId()) {
|
||
|
DBGMSG(DBG_ERROR, ("Not in spooler semaphore\n"));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
RouterOutSem(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
if ((ULONG_PTR)RouterNotifySection.OwningThread == GetCurrentThreadId()) {
|
||
|
DBGMSG(DBG_ERROR, ("Inside spooler semaphore !!\n"));
|
||
|
SPLASSERT((ULONG_PTR)RouterNotifySection.OwningThread != GetCurrentThreadId());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif /* DBG */
|
||
|
|
||
|
|
||
|
|