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.
407 lines
11 KiB
407 lines
11 KiB
/*++
|
|
|
|
Copyright (c) 1990-2003 Microsoft Corporation
|
|
All rights reserved
|
|
|
|
Module Name:
|
|
|
|
winspool.c
|
|
|
|
Abstract:
|
|
|
|
Implements the spooler supported apis for printing.
|
|
|
|
// @@BEGIN_DDKSPLIT
|
|
Author:
|
|
|
|
Environment:
|
|
|
|
User Mode -Win32
|
|
|
|
Revision History:
|
|
// @@END_DDKSPLIT
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
WCHAR szNULL[] = L"";
|
|
WCHAR szLcmDeviceNameHeader[] = L"\\Device\\NamedPipe\\Spooler\\";
|
|
WCHAR szWindows[] = L"windows";
|
|
WCHAR szINIKey_TransmissionRetryTimeout[] = L"TransmissionRetryTimeout";
|
|
|
|
|
|
//
|
|
// Timeouts for serial printing
|
|
//
|
|
#define WRITE_TOTAL_TIMEOUT 3000 // 3 seconds
|
|
#define READ_TOTAL_TIMEOUT 5000 // 5 seconds
|
|
#define READ_INTERVAL_TIMEOUT 200 // 0.2 second
|
|
|
|
|
|
BOOL
|
|
DeletePortNode(
|
|
PINILOCALMON pIniLocalMon,
|
|
PINIPORT pIniPort
|
|
)
|
|
{
|
|
PINIPORT pPort, pPrevPort;
|
|
|
|
for( pPort = pIniLocalMon->pIniPort;
|
|
pPort && pPort != pIniPort;
|
|
pPort = pPort->pNext){
|
|
|
|
pPrevPort = pPort;
|
|
}
|
|
|
|
if (pPort) { // found the port
|
|
if (pPort == pIniLocalMon->pIniPort) {
|
|
pIniLocalMon->pIniPort = pPort->pNext;
|
|
} else {
|
|
pPrevPort->pNext = pPort->pNext;
|
|
}
|
|
FreeSplMem(pPort);
|
|
|
|
return TRUE;
|
|
}
|
|
else // port not found
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
RemoveDosDeviceDefinition(
|
|
PINIPORT pIniPort
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Removes the NONSPOOLED.. dos device definition created by localmon
|
|
|
|
Arguments:
|
|
pIniPort : Pointer to the INIPORT
|
|
|
|
Return Value:
|
|
TRUE on success, FALSE on error
|
|
|
|
--*/
|
|
{
|
|
WCHAR TempDosDeviceName[MAX_PATH];
|
|
|
|
if( ERROR_SUCCESS != StrNCatBuffW( TempDosDeviceName, COUNTOF(TempDosDeviceName),
|
|
L"NONSPOOLED_", pIniPort->pName, NULL ))
|
|
return FALSE;
|
|
|
|
LcmRemoveColon(TempDosDeviceName);
|
|
|
|
return DefineDosDevice(DDD_REMOVE_DEFINITION, TempDosDeviceName, NULL);
|
|
}
|
|
|
|
// @@BEGIN_DDKSPLIT
|
|
DWORD
|
|
HandleLptQueryRemove(
|
|
LPVOID pData
|
|
)
|
|
{
|
|
DWORD dwRet = NO_ERROR;
|
|
PINIPORT pIniPort = (PINIPORT)pData;
|
|
|
|
SPLASSERT(pIniPort && pIniPort->signature == IPO_SIGNATURE
|
|
&& pIniPort->hNotify != NULL );
|
|
|
|
LcmEnterSplSem();
|
|
//
|
|
// Fix is not multi-thread safe now
|
|
//
|
|
if ( pIniPort->Status & PP_STARTDOC ) {
|
|
|
|
dwRet = ERROR_BUSY;
|
|
goto Done;
|
|
}
|
|
|
|
// InitializeCriticalSection(pIniPort->&CritSection);
|
|
CloseHandle(pIniPort->hFile);
|
|
SplUnregisterForDeviceEvents(pIniPort->hNotify);
|
|
pIniPort->hNotify = NULL;
|
|
pIniPort->hFile = INVALID_HANDLE_VALUE;
|
|
|
|
Done:
|
|
LcmLeaveSplSem();
|
|
return dwRet;
|
|
}
|
|
// @@END_DDKSPLIT
|
|
|
|
BOOL
|
|
ValidateDosDevicePort(
|
|
PINIPORT pIniPort
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Checks if the given port corresponds to a dos device.
|
|
For a dos device port the following is done:
|
|
-- Dos device definition for the NONSPOOLED.. is created
|
|
-- CreateFile is done on the NONSPOOLED.. port
|
|
|
|
Arguments:
|
|
pIniPort : Pointer to the INIPORT
|
|
|
|
Return Value:
|
|
TRUE on all validations passing, FALSE otherwise
|
|
|
|
Side effect:
|
|
For dos devices :
|
|
a. CreateFile is called on the NONSPOOLED.. name
|
|
b. PP_DOSDEVPORT flag is set
|
|
c. pIniPort->pDeviceName is set to the first string found on
|
|
QueryDosDefition this could be used to see if the definition changed
|
|
(ex. when user did a net use lpt1 \\server\printer the connection
|
|
is effective only when the user is logged in)
|
|
d. PP_COMM_PORT is set for real LPT/COM port
|
|
(ie. GetCommTimeouts worked, not a net use lpt1 case)
|
|
|
|
--*/
|
|
{
|
|
DCB dcb;
|
|
COMMTIMEOUTS cto;
|
|
WCHAR TempDosDeviceName[MAX_PATH];
|
|
HANDLE hToken = NULL;
|
|
WCHAR DeviceNames[MAX_PATH];
|
|
WCHAR DosDeviceName[MAX_PATH];
|
|
WCHAR NewNtDeviceName[MAX_PATH];
|
|
WCHAR *pDeviceNames=DeviceNames;
|
|
BOOL bRet = FALSE;
|
|
LPWSTR pDeviceName = NULL;
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
if (!hToken)
|
|
goto Done;
|
|
|
|
if( ERROR_SUCCESS != StrNCatBuffW( DosDeviceName, COUNTOF(DosDeviceName),
|
|
pIniPort->pName, NULL ))
|
|
goto Done;
|
|
|
|
LcmRemoveColon(DosDeviceName);
|
|
|
|
//
|
|
// If the port is not a dos device port nothing to do -- return success
|
|
//
|
|
if ( !QueryDosDevice(DosDeviceName, DeviceNames, COUNTOF (DeviceNames)) ) {
|
|
|
|
bRet = TRUE;
|
|
goto Done;
|
|
}
|
|
|
|
pDeviceName = AllocSplStr(pDeviceNames);
|
|
if ( !pDeviceName )
|
|
goto Done;
|
|
|
|
if( ERROR_SUCCESS != StrNCatBuffW( NewNtDeviceName, COUNTOF(NewNtDeviceName),
|
|
szLcmDeviceNameHeader, pIniPort->pName, NULL ))
|
|
goto Done;
|
|
|
|
LcmRemoveColon(NewNtDeviceName);
|
|
|
|
//
|
|
// Search for the first non-matching name in pDeviceNames list.
|
|
//
|
|
while ( lstrcmpi(pDeviceNames, NewNtDeviceName) == 0 ) {
|
|
|
|
pDeviceNames+=wcslen(pDeviceNames)+1;
|
|
}
|
|
|
|
if( ERROR_SUCCESS != StrNCatBuffW( TempDosDeviceName, COUNTOF(TempDosDeviceName),
|
|
L"NONSPOOLED_", pIniPort->pName, NULL ))
|
|
goto Done;
|
|
|
|
LcmRemoveColon(TempDosDeviceName);
|
|
|
|
//
|
|
// Delete any existing definition for TempDosDeviceName. This ensures that
|
|
// there exists only one definition for the nonspooled_port device name.
|
|
//
|
|
DefineDosDevice(DDD_REMOVE_DEFINITION, TempDosDeviceName, NULL);
|
|
DefineDosDevice(DDD_RAW_TARGET_PATH, TempDosDeviceName, pDeviceNames);
|
|
|
|
ImpersonatePrinterClient(hToken);
|
|
hToken = NULL;
|
|
|
|
if( ERROR_SUCCESS != StrNCatBuffW( TempDosDeviceName, COUNTOF(TempDosDeviceName),
|
|
L"\\\\.\\NONSPOOLED_", pIniPort->pName, NULL ))
|
|
goto Done;
|
|
|
|
LcmRemoveColon(TempDosDeviceName);
|
|
|
|
pIniPort->hFile = CreateFile(TempDosDeviceName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL |
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL);
|
|
|
|
//
|
|
// If CreateFile fails remove redirection and fail the call
|
|
//
|
|
if ( pIniPort->hFile == INVALID_HANDLE_VALUE ) {
|
|
|
|
(VOID)RemoveDosDeviceDefinition(pIniPort);
|
|
goto Done;
|
|
}
|
|
|
|
pIniPort->Status |= PP_DOSDEVPORT;
|
|
|
|
SetEndOfFile(pIniPort->hFile);
|
|
|
|
if ( IS_COM_PORT (pIniPort->pName) ) {
|
|
|
|
if ( GetCommState(pIniPort->hFile, &dcb) ) {
|
|
|
|
GetCommTimeouts(pIniPort->hFile, &cto);
|
|
GetIniCommValues (pIniPort->pName, &dcb, &cto);
|
|
SetCommState (pIniPort->hFile, &dcb);
|
|
cto.WriteTotalTimeoutConstant = WRITE_TOTAL_TIMEOUT;
|
|
cto.WriteTotalTimeoutMultiplier = 0;
|
|
cto.ReadTotalTimeoutConstant = READ_TOTAL_TIMEOUT;
|
|
cto.ReadIntervalTimeout = READ_INTERVAL_TIMEOUT;
|
|
SetCommTimeouts(pIniPort->hFile, &cto);
|
|
|
|
pIniPort->Status |= PP_COMM_PORT;
|
|
} else {
|
|
|
|
DBGMSG(DBG_WARNING,
|
|
("ERROR: Failed GetCommState pIniPort->hFile %x\n",pIniPort->hFile) );
|
|
}
|
|
} else if ( IS_LPT_PORT (pIniPort->pName) ) {
|
|
|
|
if ( GetCommTimeouts(pIniPort->hFile, &cto) ) {
|
|
|
|
cto.WriteTotalTimeoutConstant =
|
|
GetProfileInt(szWindows,
|
|
szINIKey_TransmissionRetryTimeout,
|
|
45 );
|
|
cto.WriteTotalTimeoutConstant*=1000;
|
|
SetCommTimeouts(pIniPort->hFile, &cto);
|
|
|
|
// @@BEGIN_DDKSPLIT
|
|
hToken = RevertToPrinterSelf();
|
|
pIniPort->hNotify = SplRegisterForDeviceEvents(
|
|
pIniPort->hFile,
|
|
(LPVOID)pIniPort,
|
|
HandleLptQueryRemove);
|
|
ImpersonatePrinterClient(hToken);
|
|
hToken = NULL;
|
|
// @@END_DDKSPLIT
|
|
|
|
pIniPort->Status |= PP_COMM_PORT;
|
|
} else {
|
|
|
|
DBGMSG(DBG_WARNING,
|
|
("ERROR: Failed GetCommTimeouts pIniPort->hFile %x\n",pIniPort->hFile) );
|
|
}
|
|
}
|
|
|
|
FreeSplStr( pIniPort->pDeviceName );
|
|
|
|
pIniPort->pDeviceName = pDeviceName;
|
|
bRet = TRUE;
|
|
|
|
Done:
|
|
if (hToken)
|
|
ImpersonatePrinterClient(hToken);
|
|
|
|
if ( !bRet && pDeviceName )
|
|
FreeSplStr(pDeviceName);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
BOOL
|
|
FixupDosDeviceDefinition(
|
|
PINIPORT pIniPort
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Called before every StartDocPort for a DOSDEVPORT. The routine will check if
|
|
the dos device defintion has changed (if a user logged and his connection
|
|
is remembered). Also for a connection case the CreateFile is called since
|
|
that needs to be done per job
|
|
|
|
Arguments:
|
|
pIniPort : Pointer to the INIPORT
|
|
|
|
Return Value:
|
|
TRUE on all validations passing, FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
WCHAR DeviceNames[MAX_PATH];
|
|
WCHAR DosDeviceName[MAX_PATH];
|
|
HANDLE hToken;
|
|
|
|
//
|
|
// If the port is not a real LPT port we open it per job
|
|
// @@BEGIN_DDKSPLIT
|
|
// Also parallel ports could be closed on QUERYREMOVE if user undocks
|
|
// then it will be opened on next job's StartDocPort
|
|
// @@END_DDKSPLIT
|
|
//
|
|
|
|
if ( !(pIniPort->Status & PP_COMM_PORT) ||
|
|
pIniPort->hFile == INVALID_HANDLE_VALUE )
|
|
return ValidateDosDevicePort(pIniPort);
|
|
|
|
if( ERROR_SUCCESS != StrNCatBuffW( DosDeviceName, COUNTOF (DosDeviceName),
|
|
pIniPort->pName, NULL ))
|
|
return FALSE;
|
|
|
|
LcmRemoveColon(DosDeviceName);
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
if (!hToken) {
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !QueryDosDevice(DosDeviceName, DeviceNames, COUNTOF (DeviceNames) ) ) {
|
|
|
|
ImpersonatePrinterClient(hToken);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If strings are same then definition has not changed
|
|
//
|
|
if ( !lstrcmpi(DeviceNames, pIniPort->pDeviceName) )
|
|
{
|
|
ImpersonatePrinterClient(hToken);
|
|
return TRUE;
|
|
}
|
|
|
|
(VOID)RemoveDosDeviceDefinition(pIniPort);
|
|
|
|
CloseHandle(pIniPort->hFile);
|
|
pIniPort->hFile = INVALID_HANDLE_VALUE;
|
|
|
|
// @@BEGIN_DDKSPLIT
|
|
if ( pIniPort->hNotify ) {
|
|
|
|
SplUnregisterForDeviceEvents(pIniPort->hNotify);
|
|
pIniPort->hNotify = NULL;
|
|
}
|
|
// @@END_DDKSPLIT
|
|
|
|
pIniPort->Status &= ~(PP_COMM_PORT | PP_DOSDEVPORT);
|
|
|
|
FreeSplStr(pIniPort->pDeviceName);
|
|
pIniPort->pDeviceName = NULL;
|
|
|
|
ImpersonatePrinterClient(hToken);
|
|
|
|
return ValidateDosDevicePort(pIniPort);
|
|
}
|
|
|
|
|