NT4/private/windows/spooler/spoolss/client/winspool.c
2020-09-30 17:12:29 +02:00

5194 lines
117 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1990-1996 Microsoft Corporation
All rights reserved
Module Name:
Winspool.c
Abstract:
Bulk of winspool.drv code
Author:
Environment:
User Mode -Win32
Revision History:
mattfe april 14 94 added caching to writeprinter
mattfe jan 95 Add SetAllocFailCount api
13-Jun-1996 Thu 15:07:16 updated -by- Daniel Chou (danielc)
Make PrinterProperties call PrinterPropertySheets and
DocumentProperties call DocumentPropertySheets
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <stddef.h>
#include <ntddrdr.h>
#include <stdio.h>
#include <string.h>
#include <rpc.h>
#include "winspl.h"
#include <offsets.h>
#include <browse.h>
#include "client.h"
#include <change.h>
#include <windows.h>
#include <winddiui.h>
#include "wingdip.h"
#include "gdispool.h"
#include "shlobj.h"
#include "shlobjp.h"
extern
LONG
DocumentPropertySheets(
PPROPSHEETUI_INFO pCPSUIInfo,
LPARAM lParam
);
extern
LONG
DevicePropertySheets(
PPROPSHEETUI_INFO pCPSUIInfo,
LPARAM lParam
);
VOID
vUpdateTrayIcon(
IN HANDLE hPrinter,
IN DWORD JobId
);
extern LPWSTR InterfaceAddress;
MODULE_DEBUG_INIT( DBG_ERROR | DBG_WARN, DBG_ERROR );
LPWSTR szEnvironment = LOCAL_ENVIRONMENT;
LPTSTR szComma = L",";
LPTSTR szFilePort = L"FILE:";
DWORD ClientHandleCount = 0;
HANDLE hShell32 = NULL;
LPWSTR
SelectFormNameFromDevMode(
HANDLE hPrinter,
PDEVMODEW pDevModeW,
LPWSTR pFormName
);
#define DM_MATCH( dm, sp ) ((((sp)+50)/100-dm)<15&&(((sp)+50)/100-dm)>-15)
#define DM_PAPER_WL (DM_PAPERWIDTH | DM_PAPERLENGTH)
#define JOB_CANCEL_CHECK_INTERVAL 2000 // 2 seconds
LPWSTR
IsaFileName(
LPWSTR pOutputFile,
LPWSTR FullPathName
);
INT
UnicodeToAnsiString(
LPWSTR pUnicode,
LPSTR pAnsi,
DWORD StringLength);
LONG
CallCommonPropertySheetUI(
HWND hWndOwner,
PFNPROPSHEETUI pfnPropSheetUI,
LPARAM lParam,
LPDWORD pResult
)
/*++
Routine Description:
This function dymically load the compstui.dll and call its entry
Arguments:
pfnPropSheetUI - Pointer to callback function
lParam - lParam for the pfnPropSheetUI
pResult - pResult for the CommonPropertySheetUI
Return Value:
LONG - as describe in compstui.h
Author:
01-Nov-1995 Wed 13:11:19 created -by- Daniel Chou (danielc)
Revision History:
--*/
{
HINSTANCE hInstCPSUI;
FARPROC pProc;
LONG Result = ERR_CPSUI_GETLASTERROR;
//
// ONLY need to call the ANSI version of LoadLibrary
//
if ((hInstCPSUI = LoadLibraryA("compstui.dll")) &&
(pProc = GetProcAddress(hInstCPSUI, "CommonPropertySheetUIW"))) {
RpcTryExcept {
Result = (*pProc)(hWndOwner, pfnPropSheetUI, lParam, pResult);
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
Result = ERR_CPSUI_GETLASTERROR;
} RpcEndExcept
}
if (hInstCPSUI) {
FreeLibrary(hInstCPSUI);
}
return(Result);
}
// Simple for Now !!!
DWORD
TranslateExceptionCode(
DWORD ExceptionCode
)
{
switch (ExceptionCode) {
case EXCEPTION_ACCESS_VIOLATION:
case EXCEPTION_DATATYPE_MISALIGNMENT:
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
case EXCEPTION_FLT_DENORMAL_OPERAND:
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
case EXCEPTION_FLT_INEXACT_RESULT:
case EXCEPTION_FLT_INVALID_OPERATION:
case EXCEPTION_FLT_OVERFLOW:
case EXCEPTION_FLT_STACK_CHECK:
case EXCEPTION_FLT_UNDERFLOW:
case EXCEPTION_INT_DIVIDE_BY_ZERO:
case EXCEPTION_INT_OVERFLOW:
case EXCEPTION_PRIV_INSTRUCTION:
case ERROR_NOACCESS:
case RPC_S_INVALID_BOUND:
return ERROR_INVALID_PARAMETER;
break;
default:
return ExceptionCode;
}
}
void
MarshallUpStructure(
LPBYTE lpStructure,
LPDWORD lpOffsets
)
{
register DWORD i=0;
while (lpOffsets[i] != -1) {
if ((*(LPBYTE *)(lpStructure+lpOffsets[i]))) {
(*(LPBYTE *)(lpStructure+lpOffsets[i]))+=(DWORD)lpStructure;
}
i++;
}
}
BOOL
EnumPrintersW(
DWORD Flags,
LPWSTR Name,
DWORD Level,
LPBYTE pPrinterEnum,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
BOOL ReturnValue;
DWORD cbStruct;
DWORD *pOffsets;
switch (Level) {
case STRESSINFOLEVEL:
pOffsets = PrinterInfoStressOffsets;
cbStruct = sizeof(PRINTER_INFO_STRESS);
break;
case 1:
pOffsets = PrinterInfo1Offsets;
cbStruct = sizeof(PRINTER_INFO_1);
break;
case 2:
pOffsets = PrinterInfo2Offsets;
cbStruct = sizeof(PRINTER_INFO_2);
break;
case 4:
pOffsets = PrinterInfo4Offsets;
cbStruct = sizeof(PRINTER_INFO_4);
break;
case 5:
pOffsets = PrinterInfo5Offsets;
cbStruct = sizeof(PRINTER_INFO_5);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
if (pPrinterEnum)
memset(pPrinterEnum, 0, cbBuf);
RpcTryExcept {
if (ReturnValue = RpcEnumPrinters(Flags, Name, Level, pPrinterEnum, cbBuf,
pcbNeeded, pcReturned)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pPrinterEnum) {
DWORD i=*pcReturned;
while (i--) {
MarshallUpStructure(pPrinterEnum, pOffsets);
pPrinterEnum+=cbStruct;
}
}
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
OpenPrinterW(
LPWSTR pPrinterName,
LPHANDLE phPrinter,
LPPRINTER_DEFAULTS pDefault
)
{
BOOL ReturnValue;
DEVMODE_CONTAINER DevModeContainer;
HANDLE hPrinter;
PSPOOL pSpool;
DWORD dwSize = 0;
//
// Pre-initialize the out parameter, so that *phPrinter is NULL
// on failure. This fixes Borland Paradox 7.
//
try {
*phPrinter = NULL;
} except( EXCEPTION_EXECUTE_HANDLER ){
SetLastError(TranslateExceptionCode(GetExceptionCode()));
return FALSE;
}
if (pDefault && pDefault->pDevMode)
{
dwSize = pDefault->pDevMode->dmSize + pDefault->pDevMode->dmDriverExtra;
if (dwSize) {
DevModeContainer.cbBuf = pDefault->pDevMode->dmSize +
pDefault->pDevMode->dmDriverExtra;
DevModeContainer.pDevMode = (LPBYTE)pDefault->pDevMode;
} else {
DevModeContainer.cbBuf = 0;
DevModeContainer.pDevMode = NULL;
}
}
else
{
DevModeContainer.cbBuf = 0;
DevModeContainer.pDevMode = NULL;
}
RpcTryExcept {
if (ReturnValue = RpcOpenPrinter(pPrinterName, &hPrinter,
pDefault ? pDefault->pDatatype : NULL,
&DevModeContainer,
pDefault ? pDefault->DesiredAccess : 0 )) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
if (ReturnValue) {
pSpool = AllocSplMem(sizeof(SPOOL));
if (pSpool) {
memset(pSpool, 0, sizeof(SPOOL));
pSpool->signature = SP_SIGNATURE;
pSpool->hPrinter = hPrinter;
pSpool->hFile = INVALID_HANDLE_VALUE;
pSpool->pBuffer = NULL;
pSpool->cCacheWrite = 0;
pSpool->dwTickCount = 0;
pSpool->cWritePrinters = 0;
//
// This is to fix passing a bad pHandle to OpenPrinter!!
//
try {
*phPrinter = pSpool;
} except(1) {
RpcClosePrinter(&hPrinter);
FreeSplMem(pSpool);
SetLastError(TranslateExceptionCode(GetExceptionCode()));
return(FALSE);
}
InterlockedIncrement( &ClientHandleCount );
} else {
RpcClosePrinter(&hPrinter);
ReturnValue = FALSE;
}
}
return ReturnValue;
}
BOOL
ResetPrinterW(
HANDLE hPrinter,
LPPRINTER_DEFAULTS pDefault
)
{
BOOL ReturnValue;
DEVMODE_CONTAINER DevModeContainer;
PSPOOL pSpool = (PSPOOL)hPrinter;
DWORD dwFlags = 0;
LPWSTR pDatatype = NULL;
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
FlushBuffer(pSpool);
if (pDefault && pDefault->pDatatype) {
if (pDefault->pDatatype == (LPWSTR)-1) {
pDatatype = NULL;
dwFlags |= RESET_PRINTER_DATATYPE;
} else {
pDatatype = pDefault->pDatatype;
}
} else {
pDatatype = NULL;
}
DevModeContainer.cbBuf = 0;
DevModeContainer.pDevMode = NULL;
if( pDefault ){
if (pDefault->pDevMode == (LPDEVMODE)-1) {
dwFlags |= RESET_PRINTER_DEVMODE;
} else if( bValidDevModeW( pDefault->pDevMode )){
DevModeContainer.cbBuf = pDefault->pDevMode->dmSize +
pDefault->pDevMode->dmDriverExtra;
DevModeContainer.pDevMode = (LPBYTE)pDefault->pDevMode;
}
}
RpcTryExcept {
if (ReturnValue = RpcResetPrinterEx(pSpool->hPrinter,
pDatatype, &DevModeContainer,
dwFlags
)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
SetJobW(
HANDLE hPrinter,
DWORD JobId,
DWORD Level,
LPBYTE pJob,
DWORD Command
)
{
BOOL ReturnValue;
GENERIC_CONTAINER GenericContainer;
GENERIC_CONTAINER *pGenericContainer;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
switch (Level) {
case 0:
break;
case 1:
case 2:
case 3:
if (!pJob) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (pJob) {
GenericContainer.Level = Level;
GenericContainer.pData = pJob;
pGenericContainer = &GenericContainer;
} else
pGenericContainer = NULL;
if (ReturnValue = RpcSetJob(pSpool->hPrinter, JobId,
(JOB_CONTAINER *)pGenericContainer,
Command)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
GetJobW(
HANDLE hPrinter,
DWORD JobId,
DWORD Level,
LPBYTE pJob,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
BOOL ReturnValue;
DWORD *pOffsets;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
FlushBuffer(pSpool);
switch (Level) {
case 1:
pOffsets = JobInfo1Offsets;
break;
case 2:
pOffsets = JobInfo2Offsets;
break;
case 3:
pOffsets = JobInfo3Offsets;
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (pJob)
memset(pJob, 0, cbBuf);
if (ReturnValue = RpcGetJob(pSpool->hPrinter, JobId, Level, pJob, cbBuf,
pcbNeeded)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
MarshallUpStructure(pJob, pOffsets);
ReturnValue = TRUE;
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
EnumJobsW(
HANDLE hPrinter,
DWORD FirstJob,
DWORD NoJobs,
DWORD Level,
LPBYTE pJob,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
BOOL ReturnValue;
DWORD i, cbStruct, *pOffsets;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
FlushBuffer(pSpool);
switch (Level) {
case 1:
pOffsets = JobInfo1Offsets;
cbStruct = sizeof(JOB_INFO_1);
break;
case 2:
pOffsets = JobInfo2Offsets;
cbStruct = sizeof(JOB_INFO_2);
break;
case 3:
pOffsets = JobInfo3Offsets;
cbStruct = sizeof(JOB_INFO_3);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (pJob)
memset(pJob, 0, cbBuf);
if (ReturnValue = RpcEnumJobs(pSpool->hPrinter, FirstJob, NoJobs, Level, pJob,
cbBuf, pcbNeeded, pcReturned)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
i=*pcReturned;
while (i--) {
MarshallUpStructure(pJob, pOffsets);
pJob += cbStruct;;
}
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
HANDLE
AddPrinterW(
LPWSTR pName,
DWORD Level,
LPBYTE pPrinter
)
{
DWORD ReturnValue;
PRINTER_CONTAINER PrinterContainer;
DEVMODE_CONTAINER DevModeContainer;
SECURITY_CONTAINER SecurityContainer;
HANDLE hPrinter;
PSPOOL pSpool = NULL;
PVOID pNewSecurityDescriptor = NULL;
SECURITY_DESCRIPTOR_CONTROL SecurityDescriptorControl = 0;
PPRINTER_INFO_2 pPrinterInfo = (PPRINTER_INFO_2)pPrinter;
switch (Level) {
case 2:
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return NULL;
}
if ( !pPrinter ) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
PrinterContainer.Level = Level;
PrinterContainer.PrinterInfo.pPrinterInfo1 = (PPRINTER_INFO_1)pPrinter;
DevModeContainer.cbBuf = 0;
DevModeContainer.pDevMode = NULL;
SecurityContainer.cbBuf = 0;
SecurityContainer.pSecurity = NULL;
if (Level == 2) {
if( bValidDevModeW( pPrinterInfo->pDevMode )){
DevModeContainer.cbBuf = pPrinterInfo->pDevMode->dmSize +
pPrinterInfo->pDevMode->dmDriverExtra;
DevModeContainer.pDevMode = (LPBYTE)pPrinterInfo->pDevMode;
}
if (pPrinterInfo->pSecurityDescriptor) {
DWORD sedlen = 0;
//
// We must construct a self relative security descriptor from
// whatever we get as input: If we get an Absolute SD we should
// convert it to a self-relative one. (this is a given) and we
// should also convert any self -relative input SD into a a new
// self relative security descriptor; this will take care of
// any holes in the Dacl or the Sacl in the self-relative sd
//
pNewSecurityDescriptor = BuildInputSD(
pPrinterInfo->pSecurityDescriptor,
&sedlen);
if (pNewSecurityDescriptor) {
SecurityContainer.cbBuf = sedlen;
SecurityContainer.pSecurity = pNewSecurityDescriptor;
}
}
}
RpcTryExcept {
if (ReturnValue = RpcAddPrinter(pName,
(PPRINTER_CONTAINER)&PrinterContainer,
(PDEVMODE_CONTAINER)&DevModeContainer,
(PSECURITY_CONTAINER)&SecurityContainer,
&hPrinter)) {
SetLastError(ReturnValue);
hPrinter = FALSE;
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
hPrinter = FALSE;
} RpcEndExcept
if (hPrinter) {
pSpool = AllocSplMem(sizeof(SPOOL));
if ( pSpool &&
( !DevModeContainer.pDevMode ||
WriteCurDevModeToRegistry(pPrinterInfo->pPrinterName,
(LPDEVMODEW)DevModeContainer.pDevMode)) ) {
pSpool->hPrinter = hPrinter;
pSpool->signature = SP_SIGNATURE;
pSpool->hFile = INVALID_HANDLE_VALUE;
InterlockedIncrement( &ClientHandleCount );
} else {
// BUGBUG MEMORY LEAK
// Doesn't free of pSpool on error path
RpcDeletePrinter(hPrinter);
RpcClosePrinter(&hPrinter);
}
}
//
// Free Memory allocated for the SecurityDescriptor
//
if (pNewSecurityDescriptor) {
LocalFree(pNewSecurityDescriptor);
}
return pSpool;
}
BOOL
DeletePrinter(
HANDLE hPrinter
)
{
BOOL ReturnValue;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
FlushBuffer(pSpool);
RpcTryExcept {
if (ReturnValue = RpcDeletePrinter(pSpool->hPrinter)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
SpoolerPrinterEvent(
LPWSTR pName,
INT PrinterEvent,
DWORD Flags,
LPARAM lParam
)
/*++
//
// Some printer drivers, like the FAX driver want to do per client
// initialization at the time a connection is established
// For example in the FAX case they want to push up UI to get all
// the client info - Name, Number etc.
// Or they might want to run Setup, in initialize some other components
// Thus on a successful conenction we call into the Printer Drivers UI
// DLL to give them this oportunity
//
// mattfe may 1 96
--*/
{
BOOL ReturnValue = FALSE;
HANDLE hPrinter;
HANDLE hModule;
INT_FARPROC pfn;
if (OpenPrinter((LPWSTR)pName, &hPrinter, NULL)) {
if (hModule = LoadPrinterDriver(hPrinter)) {
if (pfn = GetProcAddress(hModule, "DrvPrinterEvent")) {
try {
ReturnValue = (*pfn)( pName, PrinterEvent, Flags, lParam );
} except(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
}
}
FreeLibrary(hModule);
}
ClosePrinter(hPrinter);
}
return ReturnValue;
}
BOOL
AddPrinterConnectionW(
LPWSTR pName
)
{
BOOL ReturnValue;
HANDLE hPrinter, hModule;
FARPROC pfn;
RpcTryExcept {
if (ReturnValue = RpcAddPrinterConnection(pName)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue=FALSE;
} RpcEndExcept
if ( ReturnValue ) {
SpoolerPrinterEvent( pName, PRINTER_EVENT_ADD_CONNECTION, 0, (LPARAM)NULL );
}
return ReturnValue;
}
BOOL
DeletePrinterConnectionW(
LPWSTR pName
)
{
BOOL ReturnValue;
DWORD LastError;
SpoolerPrinterEvent( pName, PRINTER_EVENT_DELETE_CONNECTION, 0, (LPARAM)NULL );
RpcTryExcept {
if (LastError = RpcDeletePrinterConnection(pName)) {
SetLastError(LastError);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue=FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
SetPrinterW(
HANDLE hPrinter,
DWORD Level,
LPBYTE pPrinter,
DWORD Command
)
{
BOOL ReturnValue;
PRINTER_CONTAINER PrinterContainer;
DEVMODE_CONTAINER DevModeContainer;
SECURITY_CONTAINER SecurityContainer;
PPRINTER_INFO_2 pPrinterInfo2;
PPRINTER_INFO_3 pPrinterInfo3;
PRINTER_INFO_6 PrinterInfo6;
PSPOOL pSpool = (PSPOOL)hPrinter;
PVOID pNewSecurityDescriptor = NULL;
DWORD sedlen = 0;
DWORD dwSize = 0;
if ( !ValidatePrinterHandle(hPrinter) ) {
return(FALSE);
}
DevModeContainer.cbBuf = 0;
DevModeContainer.pDevMode = NULL;
SecurityContainer.cbBuf = 0;
SecurityContainer.pSecurity = NULL;
switch (Level) {
case STRESSINFOLEVEL:
//
// Internally we treat the Level 0, Command PRINTER_CONTROL_SET_STATUS
// as Level 6 since level 0 could be STRESS_INFO (for rpc)
//
if ( Command == PRINTER_CONTROL_SET_STATUS ) {
PrinterInfo6.dwStatus = (DWORD)pPrinter;
pPrinter = (LPBYTE)&PrinterInfo6;
Command = 0;
Level = 6;
}
break;
case 2:
pPrinterInfo2 = (PPRINTER_INFO_2)pPrinter;
if (pPrinterInfo2 == NULL) {
DBGMSG(DBG_TRACE, ("Error: SetPrinter pPrinterInfo2 is NULL\n"));
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if( bValidDevModeW( pPrinterInfo2->pDevMode )){
DevModeContainer.cbBuf = pPrinterInfo2->pDevMode->dmSize +
pPrinterInfo2->pDevMode->dmDriverExtra;
DevModeContainer.pDevMode = (LPBYTE)pPrinterInfo2->pDevMode;
}
if (pPrinterInfo2->pSecurityDescriptor) {
//
// We must construct a self relative security descriptor from
// whatever we get as input: If we get an Absolute SD we should
// convert it to a self-relative one. (this is a given) and we
// should also convert any self -relative input SD into a a new
// self relative security descriptor; this will take care of
// any holes in the Dacl or the Sacl in the self-relative sd
//
pNewSecurityDescriptor = BuildInputSD(pPrinterInfo2->pSecurityDescriptor,
&sedlen);
if (pNewSecurityDescriptor) {
SecurityContainer.cbBuf = sedlen;
SecurityContainer.pSecurity = pNewSecurityDescriptor;
}
}
break;
case 3:
pPrinterInfo3 = (PPRINTER_INFO_3)pPrinter;
if (pPrinterInfo3 == NULL) {
DBGMSG(DBG_TRACE, ("Error: SetPrinter pPrinterInfo3 is NULL\n"));
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (pPrinterInfo3->pSecurityDescriptor) {
//
// We must construct a self relative security descriptor from
// whatever we get as input: If we get an Absolute SD we should
// convert it to a self-relative one. (this is a given) and we
// should also convert any self -relative input SD into a a new
// self relative security descriptor; this will take care of
// any holes in the Dacl or the Sacl in the self-relative sd
//
pNewSecurityDescriptor = BuildInputSD(pPrinterInfo3->pSecurityDescriptor,
&sedlen);
if (pNewSecurityDescriptor) {
SecurityContainer.cbBuf = sedlen;
SecurityContainer.pSecurity = pNewSecurityDescriptor;
}
}
break;
case 4:
case 5:
if ( pPrinter == NULL ) {
DBGMSG(DBG_TRACE,("Error SetPrinter pPrinter is NULL\n"));
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
break;
case 6:
if ( pPrinter == NULL ) {
DBGMSG(DBG_TRACE,("Error SetPrinter pPrinter is NULL\n"));
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
PrinterContainer.Level = Level;
PrinterContainer.PrinterInfo.pPrinterInfo1 = (PPRINTER_INFO_1)pPrinter;
RpcTryExcept {
if (ReturnValue = RpcSetPrinter(pSpool->hPrinter,
(PPRINTER_CONTAINER)&PrinterContainer,
(PDEVMODE_CONTAINER)&DevModeContainer,
(PSECURITY_CONTAINER)&SecurityContainer,
Command)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
//
// Need to write DevMode to registry so that dos apps doing
// ExtDeviceMode can pick up the new devmode
//
if ( ReturnValue && Level == 2 && DevModeContainer.pDevMode ) {
(VOID)WriteCurDevModeToRegistry(pPrinterInfo2->pPrinterName,
(LPDEVMODEW)DevModeContainer.pDevMode);
}
//
// Did we allocate memory for a new self-relative SD?
// If we did, let's free it.
//
if (pNewSecurityDescriptor) {
LocalFree(pNewSecurityDescriptor);
}
return ReturnValue;
}
BOOL
GetPrinterW(
HANDLE hPrinter,
DWORD Level,
LPBYTE pPrinter,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
BOOL ReturnValue;
DWORD *pOffsets;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
switch (Level) {
case STRESSINFOLEVEL:
pOffsets = PrinterInfoStressOffsets;
break;
case 1:
pOffsets = PrinterInfo1Offsets;
break;
case 2:
pOffsets = PrinterInfo2Offsets;
break;
case 3:
pOffsets = PrinterInfo3Offsets;
break;
case 4:
pOffsets = PrinterInfo4Offsets;
break;
case 5:
pOffsets = PrinterInfo5Offsets;
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
if (pPrinter)
memset(pPrinter, 0, cbBuf);
RpcTryExcept {
if (ReturnValue = RpcGetPrinter(pSpool->hPrinter, Level, pPrinter, cbBuf, pcbNeeded)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pPrinter) {
MarshallUpStructure(pPrinter, pOffsets);
}
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
AddPrinterDriverW(
LPWSTR pName,
DWORD Level,
PBYTE lpbDriverInfo
)
{
BOOL ReturnValue;
DRIVER_CONTAINER DriverContainer;
BOOL bDefaultEnvironmentUsed = FALSE;
LPRPC_DRIVER_INFO_3W pRpcDriverInfo3 = NULL;
DRIVER_INFO_3 *pDriverInfo3 = NULL;
LPWSTR pStr;
//
// Validate Input Parameters
//
if (!lpbDriverInfo) {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
DriverContainer.Level = Level;
switch (Level) {
case 2:
if ( (((LPDRIVER_INFO_2)lpbDriverInfo)->pEnvironment == NULL ) ||
(*((LPDRIVER_INFO_2)lpbDriverInfo)->pEnvironment == L'\0') ) {
bDefaultEnvironmentUsed = TRUE;
((LPDRIVER_INFO_2)lpbDriverInfo)->pEnvironment = szEnvironment;
}
DriverContainer.DriverInfo.Level2 = (DRIVER_INFO_2 *)lpbDriverInfo;
break;
case 3:
if ( (((LPDRIVER_INFO_3)lpbDriverInfo)->pEnvironment == NULL ) ||
(*((LPDRIVER_INFO_3)lpbDriverInfo)->pEnvironment == L'\0') ) {
bDefaultEnvironmentUsed = TRUE;
((LPDRIVER_INFO_3)lpbDriverInfo)->pEnvironment = szEnvironment;
}
if ( !(pRpcDriverInfo3=AllocSplMem(sizeof(RPC_DRIVER_INFO_3W))) ) {
return FALSE;
}
pDriverInfo3 = (DRIVER_INFO_3 *)lpbDriverInfo;
pRpcDriverInfo3->cVersion = pDriverInfo3->cVersion;
pRpcDriverInfo3->pName = pDriverInfo3->pName;
pRpcDriverInfo3->pEnvironment = pDriverInfo3->pEnvironment;
pRpcDriverInfo3->pDriverPath = pDriverInfo3->pDriverPath;
pRpcDriverInfo3->pDataFile = pDriverInfo3->pDataFile;
pRpcDriverInfo3->pConfigFile = pDriverInfo3->pConfigFile;
pRpcDriverInfo3->pHelpFile = pDriverInfo3->pHelpFile;
pRpcDriverInfo3->pDependentFiles = pDriverInfo3->pDependentFiles;
pRpcDriverInfo3->pMonitorName = pDriverInfo3->pMonitorName;
pRpcDriverInfo3->pDefaultDataType = pDriverInfo3->pDefaultDataType;
pStr = pDriverInfo3->pDependentFiles;
if ( pStr && *pStr ) {
while ( *pStr ) {
pStr += wcslen(pStr) + 1;
}
pRpcDriverInfo3->cchDependentFiles = pStr - pDriverInfo3->pDependentFiles + 1;
} else {
pRpcDriverInfo3->cchDependentFiles = 0;
}
DriverContainer.DriverInfo.Level3 = pRpcDriverInfo3;
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (ReturnValue = RpcAddPrinterDriver(pName, &DriverContainer)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
if (bDefaultEnvironmentUsed) {
if ( Level == 2 )
((LPDRIVER_INFO_2)lpbDriverInfo)->pEnvironment = NULL;
else //Level == 3
((LPDRIVER_INFO_3)lpbDriverInfo)->pEnvironment = NULL;
}
if ( pRpcDriverInfo3 ) {
FreeSplMem(pRpcDriverInfo3);
}
return ReturnValue;
}
BOOL
EnumPrinterDriversW(
LPWSTR pName,
LPWSTR pEnvironment,
DWORD Level,
LPBYTE pDriverInfo,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
BOOL ReturnValue;
DWORD i, cbStruct;
DWORD *pOffsets;
switch (Level) {
case 1:
pOffsets = DriverInfo1Offsets;
cbStruct = sizeof(DRIVER_INFO_1);
break;
case 2:
pOffsets = DriverInfo2Offsets;
cbStruct = sizeof(DRIVER_INFO_2);
break;
case 3:
pOffsets = DriverInfo3Offsets;
cbStruct = sizeof(DRIVER_INFO_3);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = szEnvironment;
if (ReturnValue = RpcEnumPrinterDrivers(pName, pEnvironment, Level,
pDriverInfo, cbBuf,
pcbNeeded, pcReturned)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pDriverInfo) {
i = *pcReturned;
while (i--) {
MarshallUpStructure(pDriverInfo, pOffsets);
pDriverInfo += cbStruct;
}
}
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
GetPrinterDriverW(
HANDLE hPrinter,
LPWSTR pEnvironment,
DWORD Level,
LPBYTE pDriverInfo,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
BOOL ReturnValue;
DWORD *pOffsets;
PSPOOL pSpool = (PSPOOL)hPrinter;
DWORD dwServerMajorVersion;
DWORD dwServerMinorVersion;
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
switch (Level) {
case 1:
pOffsets = DriverInfo1Offsets;
break;
case 2:
pOffsets = DriverInfo2Offsets;
break;
case 3:
pOffsets = DriverInfo3Offsets;
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (pDriverInfo)
memset(pDriverInfo, 0, cbBuf);
if (!pEnvironment || !*pEnvironment)
pEnvironment = szEnvironment;
if (ReturnValue = RpcGetPrinterDriver2(pSpool->hPrinter, pEnvironment,
Level, pDriverInfo, cbBuf,
pcbNeeded,
(DWORD)-1, (DWORD)-1,
&dwServerMajorVersion,
&dwServerMinorVersion
)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pDriverInfo) {
MarshallUpStructure(pDriverInfo, pOffsets);
}
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
GetPrinterDriverDirectoryW(
LPWSTR pName,
LPWSTR pEnvironment,
DWORD Level,
LPBYTE pDriverDirectory,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
BOOL ReturnValue;
switch (Level) {
case 1:
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = szEnvironment;
if (ReturnValue = RpcGetPrinterDriverDirectory(pName, pEnvironment,
Level,
pDriverDirectory,
cbBuf, pcbNeeded)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
DeletePrinterDriverW(
LPWSTR pName,
LPWSTR pEnvironment,
LPWSTR pDriverName
)
{
BOOL ReturnValue;
if (!pDriverName || !*pDriverName) {
SetLastError(ERROR_INVALID_PARAMETER);
return (FALSE);
}
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = szEnvironment;
if (ReturnValue = RpcDeletePrinterDriver(pName,
pEnvironment,
pDriverName)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
AddPrintProcessorW(
LPWSTR pName,
LPWSTR pEnvironment,
LPWSTR pPathName,
LPWSTR pPrintProcessorName
)
{
BOOL ReturnValue;
if (!pPrintProcessorName || !*pPrintProcessorName) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (!pPathName || !*pPathName) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = szEnvironment;
if (ReturnValue = RpcAddPrintProcessor(pName, pEnvironment, pPathName,
pPrintProcessorName)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
EnumPrintProcessorsW(
LPWSTR pName,
LPWSTR pEnvironment,
DWORD Level,
LPBYTE pPrintProcessorInfo,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
BOOL ReturnValue;
DWORD i, cbStruct;
DWORD *pOffsets;
switch (Level) {
case 1:
pOffsets = PrintProcessorInfo1Offsets;
cbStruct = sizeof(PRINTPROCESSOR_INFO_1);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = szEnvironment;
if (ReturnValue = RpcEnumPrintProcessors(pName, pEnvironment, Level,
pPrintProcessorInfo, cbBuf,
pcbNeeded, pcReturned)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pPrintProcessorInfo) {
i = *pcReturned;
while (i--) {
MarshallUpStructure(pPrintProcessorInfo, pOffsets);
pPrintProcessorInfo += cbStruct;
}
}
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
GetPrintProcessorDirectoryW(
LPWSTR pName,
LPWSTR pEnvironment,
DWORD Level,
LPBYTE pPrintProcessorInfo,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
BOOL ReturnValue;
switch (Level) {
case 1:
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = szEnvironment;
if (ReturnValue = RpcGetPrintProcessorDirectory(pName, pEnvironment,
Level,
pPrintProcessorInfo,
cbBuf, pcbNeeded)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
EnumPrintProcessorDatatypesW(
LPWSTR pName,
LPWSTR pPrintProcessorName,
DWORD Level,
LPBYTE pDatatypes,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
BOOL ReturnValue;
DWORD i, cbStruct;
DWORD *pOffsets;
switch (Level) {
case 1:
pOffsets = PrintProcessorInfo1Offsets;
cbStruct = sizeof(DATATYPES_INFO_1);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (ReturnValue = RpcEnumPrintProcessorDatatypes(pName,
pPrintProcessorName,
Level,
pDatatypes,
cbBuf,
pcbNeeded,
pcReturned)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pDatatypes) {
i = *pcReturned;
while (i--) {
MarshallUpStructure(pDatatypes, pOffsets);
pDatatypes += cbStruct;
}
}
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
DWORD
StartDocPrinterW(
HANDLE hPrinter,
DWORD Level,
LPBYTE pDocInfo
)
{
BOOL ReturnValue = FALSE;
BOOL EverythingWorked = FALSE;
BOOL PrintingToFile = FALSE;
GENERIC_CONTAINER DocInfoContainer;
DWORD JobId, cbNeeded, cbIgnore;
PSPOOL pSpool = (PSPOOL)hPrinter;
BYTE Data[1024];
PADDJOB_INFO_1 pAddJob = (PADDJOB_INFO_1)Data;
PJOB_INFO_1 pJob;
PDOC_INFO_1 pDocInfo1 = (PDOC_INFO_1)pDocInfo;
WCHAR FullPathName[MAX_PATH];
try {
if (!ValidatePrinterHandle(hPrinter)) {
return 0;
}
if ( pSpool->Status & SPOOL_STATUS_STARTDOC ) {
SetLastError(ERROR_INVALID_PRINTER_STATE);
return 0;
}
DBGMSG(DBG_TRACE,("Entered StartDocPrinterW client side hPrinter = %x\n", hPrinter));
switch (Level) {
case 1:
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return 0;
}
//
// Earlier on, if we had a non-null string, we assumed it to be printing to file. Print to file will not
// go thru the client-side optimization code. Now gdi is passing us pOutputFile name irrespective of
// whether it is file or not. We must determine if pOutputFile is really a file name
//
if (pDocInfo1->pOutputFile && (*(pDocInfo1->pOutputFile) != L'\0') && IsaFileName(pDocInfo1->pOutputFile, FullPathName))
PrintingToFile = TRUE;
if (!PrintingToFile && AddJobW(hPrinter, 1, Data, sizeof(Data), &cbNeeded)) {
pSpool->JobId = pAddJob->JobId;
pSpool->hFile = CreateFile(pAddJob->Path, GENERIC_WRITE,
FILE_SHARE_READ, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL |
FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (pSpool->hFile != INVALID_HANDLE_VALUE) {
if (pSpool->JobId == (DWORD)-1) {
IO_STATUS_BLOCK Iosb;
NTSTATUS Status;
QUERY_PRINT_JOB_INFO JobInfo;
Status = NtFsControlFile(pSpool->hFile, NULL, NULL, NULL,
&Iosb,
FSCTL_GET_PRINT_ID,
NULL, 0,
&JobInfo, sizeof(JobInfo));
if (NT_SUCCESS(Status)) {
pSpool->JobId = JobInfo.JobId;
}
}
if (!GetJob(hPrinter, pSpool->JobId, 1, NULL, 0, &cbNeeded)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
pJob = AllocSplMem(cbNeeded);
if (pJob) {
if (GetJob(hPrinter, pSpool->JobId, 1,
(LPBYTE)pJob, cbNeeded, &cbIgnore)) {
pJob->pDocument = pDocInfo1->pDocName;
if (pDocInfo1->pDatatype)
pJob->pDatatype = pDocInfo1->pDatatype;
pJob->Position = JOB_POSITION_UNSPECIFIED;
if (SetJob(hPrinter, pSpool->JobId, 1,
(LPBYTE)pJob, 0)) {
EverythingWorked = TRUE;
}
}
FreeSplMem(pJob);
}
}
}
}
if (!PrintingToFile && !EverythingWorked) {
if (pSpool->hFile != INVALID_HANDLE_VALUE)
CloseHandle(pSpool->hFile);
SetJob(hPrinter,pSpool->JobId, 0, NULL, JOB_CONTROL_CANCEL);
ScheduleJob(hPrinter, pSpool->JobId);
pSpool->hFile = INVALID_HANDLE_VALUE;
pSpool->JobId = 0;
}
}
if (EverythingWorked) {
ReturnValue = pSpool->JobId;
} else {
//
// If it's invalid datatype, fail immediately instead of trying
// StartDocPrinter.
//
if( GetLastError() == ERROR_INVALID_DATATYPE ){
return 0;
}
pSpool->hFile = INVALID_HANDLE_VALUE;
pSpool->JobId = 0;
DocInfoContainer.Level = Level;
DocInfoContainer.pData = pDocInfo;
RpcTryExcept {
if (ReturnValue = RpcStartDocPrinter(pSpool->hPrinter,
(LPDOC_INFO_CONTAINER)&DocInfoContainer,
&JobId)) {
SetLastError(ReturnValue);
ReturnValue = 0;
} else
ReturnValue = JobId;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = 0;
} RpcEndExcept
}
if (ReturnValue) {
pSpool->Status |= SPOOL_STATUS_STARTDOC;
}
//
// If the tray icon has not been notified, then do so now. Set
// the flag so that we won't call it multiple times.
//
if( ReturnValue && !( pSpool->Status & SPOOL_STATUS_TRAYICON_NOTIFIED )){
vUpdateTrayIcon( hPrinter, ReturnValue );
}
} except (1) {
SetLastError(TranslateExceptionCode(GetExceptionCode()));
ReturnValue = 0;
}
return ReturnValue;
}
BOOL
StartPagePrinter(
HANDLE hPrinter
)
{
BOOL ReturnValue;
PSPOOL pSpool = (PSPOOL)hPrinter;
try {
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
FlushBuffer(pSpool);
RpcTryExcept {
if (ReturnValue = RpcStartPagePrinter(pSpool->hPrinter)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
} except (1) {
SetLastError(ERROR_INVALID_HANDLE);
return(FALSE);
}
}
BOOL
FlushBuffer(
PSPOOL pSpool
)
{
DWORD ReturnValue = TRUE;
DWORD cbWritten = 0;
SPLASSERT (pSpool != NULL);
SPLASSERT (pSpool->signature == SP_SIGNATURE);
DBGMSG(DBG_TRACE, ("FlushBuffer - pSpool %x\n",pSpool));
if (pSpool->cbBuffer) {
SPLASSERT(pSpool->pBuffer != NULL);
DBGMSG(DBG_TRACE, ("FlushBuffer - Number Cached WritePrinters before Flush %d\n", pSpool->cCacheWrite));
pSpool->cCacheWrite = 0;
pSpool->cFlushBuffers++;
if (pSpool->hFile != INVALID_HANDLE_VALUE) {
// FileIO
ReturnValue = WriteFile( pSpool->hFile,
pSpool->pBuffer,
pSpool->cbBuffer,
&cbWritten, NULL);
DBGMSG(DBG_TRACE, ("FlushBuffer - WriteFile pSpool %x hFile %x pBuffer %x cbBuffer %d cbWritten %d\n",
pSpool, pSpool->hFile, pSpool->pBuffer, pSpool->cbBuffer, cbWritten));
} else {
// RPC IO
RpcTryExcept {
if (ReturnValue = RpcWritePrinter(pSpool->hPrinter,
pSpool->pBuffer,
pSpool->cbBuffer,
&cbWritten)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
DBGMSG(DBG_WARNING, ("FlushBuffer - RpcWritePrinter Failed Error %d\n",GetLastError() ));
} else {
ReturnValue = TRUE;
DBGMSG(DBG_TRACE, ("FlushBuffer - RpcWritePrinter Success hPrinter %x pBuffer %x cbBuffer %x cbWritten %x\n",
pSpool->hPrinter, pSpool->pBuffer,
pSpool->cbBuffer, cbWritten));
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
DBGMSG(DBG_WARNING, ("RpcWritePrinter Exception Error %d\n",GetLastError()));
} RpcEndExcept
}
if (pSpool->cbBuffer <= cbWritten) {
if ( pSpool->cbBuffer < cbWritten) {
// BUGBUG
// The following case should NEVER EVER happen this needs to be
// debugged further, however we need to ship Daytona
// we were seeing it daily during stress before we shipped.
//
// MATTFE Sept 2 1994
DBGMSG( DBG_WARNING, ("FlushBuffer cbBuffer %d < cbWritten %d ReturnValue %x LastError %d\n",
pSpool->cbBuffer, cbWritten, ReturnValue, GetLastError() ));
}
// Successful IO
// Empty the cache buffer count
pSpool->cbBuffer = 0;
} else if ( cbWritten != 0 ) {
// Partial IO
// Adjust the buffer so it contains the data that was not
// written
SPLASSERT(pSpool->cbBuffer <= BUFFER_SIZE);
SPLASSERT(cbWritten <= BUFFER_SIZE);
SPLASSERT(pSpool->cbBuffer >= cbWritten);
DBGMSG(DBG_WARNING, ("Partial IO adjusting buffer data\n"));
MoveMemory(pSpool->pBuffer,
pSpool->pBuffer + cbWritten,
BUFFER_SIZE - cbWritten);
pSpool->cbBuffer -= cbWritten;
}
}
DBGMSG(DBG_TRACE, ("FlushBuffer returns %d\n",ReturnValue));
return ReturnValue;
}
BOOL
WritePrinter(
HANDLE hPrinter,
LPVOID pBuf,
DWORD cbBuf,
LPDWORD pcWritten
)
{
BOOL ReturnValue=TRUE;
DWORD cb;
DWORD cbWritten = 0;
DWORD cTotalWritten = 0;
LPBYTE pBuffer = pBuf;
PSPOOL pSpool = (PSPOOL)hPrinter;
PJOB_INFO_1 pJob;
DWORD cbNeeded;
DWORD dwTickCount, dwTickCount1;
DBGMSG(DBG_TRACE, ("WritePrinter - hPrinter %x pBuf %x cbBuf %d pcWritten %x\n",
hPrinter, pBuf, cbBuf, pcWritten));
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
*pcWritten = 0;
if ( !(pSpool->Status & SPOOL_STATUS_STARTDOC) ) {
SetLastError(ERROR_SPL_NO_STARTDOC);
return FALSE;
}
// Check if local job is cancelled every JOB_CANCEL_CHECK_INTERVAL bytes
if (!pSpool->cWritePrinters) {
pSpool->dwTickCount = GetTickCount();
pSpool->dwCheckJobInterval = JOB_CANCEL_CHECK_INTERVAL;
}
if ( pSpool->hFile != INVALID_HANDLE_VALUE &&
pSpool->dwTickCount + pSpool->dwCheckJobInterval < (dwTickCount = GetTickCount())) {
if (!(ReturnValue = GetJob((HANDLE) pSpool, pSpool->JobId, 1, NULL, 0, &cbNeeded)) &&
(GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
pJob = AllocSplMem(cbNeeded);
if (pJob && (ReturnValue = GetJob((HANDLE) pSpool, pSpool->JobId, 1, (LPBYTE)pJob, cbNeeded, &cbNeeded))) {
// Don't allow GetJob calls to take more than 1% pSpool->dwCheckJobInterval
dwTickCount1 = GetTickCount();
if (dwTickCount1 > dwTickCount + (pSpool->dwCheckJobInterval/100)) {
pSpool->dwCheckJobInterval *= 2;
} else if (dwTickCount1 - dwTickCount < JOB_CANCEL_CHECK_INTERVAL/100) {
pSpool->dwCheckJobInterval = JOB_CANCEL_CHECK_INTERVAL;
}
if (!pJob->pStatus && (pJob->Status & JOB_STATUS_DELETING)) {
SetLastError(ERROR_PRINT_CANCELLED);
FreeSplMem(pJob);
return FALSE;
}
FreeSplMem(pJob);
}
}
pSpool->dwTickCount = GetTickCount();
}
pSpool->cWritePrinters++;
// WritePrinter will cache on the client side all IO's
// into BUFFER_SIZE writes. This is done to minimize
// the number of RPC calls if the app is doing a lot of small
// sized IO's.
while (cbBuf && ReturnValue) {
// Special Case FileIO's since file system prefers large
// writes, RPC is optimal with smaller writes.
if ((pSpool->hFile != INVALID_HANDLE_VALUE) &&
(pSpool->cbBuffer == 0) &&
(cbBuf > BUFFER_SIZE)) {
ReturnValue = WriteFile(pSpool->hFile, pBuffer, cbBuf, &cbWritten, NULL);
DBGMSG(DBG_TRACE, ("WritePrinter - WriteFile pSpool %x hFile %x pBuffer %x cbBuffer %d cbWritten %d\n",
pSpool, pSpool->hFile, pBuffer, pSpool->cbBuffer, *pcWritten));
} else {
// Fill cache buffer so IO is optimal size.
SPLASSERT(pSpool->cbBuffer <= BUFFER_SIZE);
cb = min((BUFFER_SIZE - pSpool->cbBuffer), cbBuf);
if (cb != 0) {
if (pSpool->pBuffer == NULL) {
pSpool->pBuffer = VirtualAlloc(NULL, BUFFER_SIZE, MEM_COMMIT, PAGE_READWRITE);
if (pSpool->pBuffer == NULL) {
DBGMSG(DBG_WARNING, ("VirtualAlloc Failed to allocate 4k buffer %d\n",GetLastError()));
return FALSE;
}
}
CopyMemory( pSpool->pBuffer + pSpool->cbBuffer, pBuffer, cb);
pSpool->cbBuffer += cb;
cbWritten = cb;
pSpool->cCacheWrite++;
}
if (pSpool->cbBuffer == BUFFER_SIZE) {
ReturnValue = FlushBuffer(pSpool);
}
}
// Update Total Byte Count after the Flush or File IO
// This is done because the IO might fail and thus
// the correct value written might have changed.
SPLASSERT(cbBuf >= cbWritten);
cbBuf -= cbWritten;
pBuffer += cbWritten;
cTotalWritten += cbWritten;
}
// Return the number of bytes written.
*pcWritten = cTotalWritten;
DBGMSG(DBG_TRACE, ("WritePrinter cbWritten %d ReturnValue %d\n",*pcWritten, ReturnValue));
return ReturnValue;
}
BOOL
EndPagePrinter(
HANDLE hPrinter
)
{
BOOL ReturnValue;
PSPOOL pSpool = (PSPOOL)hPrinter;
try {
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
FlushBuffer(pSpool);
if (pSpool->hFile != INVALID_HANDLE_VALUE)
return TRUE;
RpcTryExcept {
if (ReturnValue = RpcEndPagePrinter(pSpool->hPrinter)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
} except (1) {
SetLastError(ERROR_INVALID_HANDLE);
return(FALSE);
}
}
BOOL
AbortPrinter(
HANDLE hPrinter
)
{
BOOL ReturnValue;
PSPOOL pSpool = (PSPOOL)hPrinter;
DWORD dwNumWritten = 0;
DWORD dwPointer = 0;
if (!ValidatePrinterHandle(hPrinter)){
return(FALSE);
}
//
// No longer in StartDoc mode; also resetting the tray icon notification
// flag so that upcoming StartDocPrinter/AddJobs indicate a new job.
//
pSpool->Status &= ~(SPOOL_STATUS_STARTDOC|SPOOL_STATUS_TRAYICON_NOTIFIED);
if (pSpool->hFile != INVALID_HANDLE_VALUE) {
if (pSpool->Status & SPOOL_STATUS_ADDJOB) {
// Close your handle to the .SPL file, otherwise the
// DeleteJob will fail in the Spooler
if (pSpool->hFile) {
if (CloseHandle(pSpool->hFile)){
pSpool->hFile = INVALID_HANDLE_VALUE;
}
}
if (!SetJob(hPrinter,pSpool->JobId, 0, NULL, JOB_CONTROL_DELETE)) {
DBGMSG(DBG_WARNING, ("Error: SetJob cancel returned failure with %d\n", GetLastError()));
// return FALSE;
}
return (ScheduleJob(hPrinter, pSpool->JobId));
} else {
DBGMSG(DBG_WARNING, ("Error: pSpool->hFile != INVALID_HANDLE_VALUE and pSpool's status is not SPOOL_STATUS_ADDJOB\n"));
}
}
RpcTryExcept {
if (ReturnValue = RpcAbortPrinter(pSpool->hPrinter)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
ReadPrinter(
HANDLE hPrinter,
LPVOID pBuf,
DWORD cbBuf,
LPDWORD pNoBytesRead
)
{
BOOL ReturnValue=TRUE;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
FlushBuffer(pSpool);
if (pSpool->hFile != INVALID_HANDLE_VALUE) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
RpcTryExcept {
cbBuf = min(BUFFER_SIZE, cbBuf);
if (ReturnValue = RpcReadPrinter(pSpool->hPrinter, pBuf, cbBuf, pNoBytesRead)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
EndDocPrinter(
HANDLE hPrinter
)
{
BOOL ReturnValue;
PSPOOL pSpool = (PSPOOL)hPrinter;
try {
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
FlushBuffer(pSpool);
//
// No longer in StartDoc mode; also resetting the tray icon
// notification flag so that upcoming StartDocPrinter/AddJobs
// indicate a new job.
//
pSpool->Status &= ~(SPOOL_STATUS_STARTDOC|SPOOL_STATUS_TRAYICON_NOTIFIED);
if (pSpool->hFile != INVALID_HANDLE_VALUE) {
CloseHandle(pSpool->hFile);
ReturnValue = ScheduleJob(hPrinter, pSpool->JobId);
pSpool->hFile = INVALID_HANDLE_VALUE;
pSpool->Status &= ~SPOOL_STATUS_ADDJOB;
DBGMSG(DBG_TRACE, ("Exit EndDocPrinter - client side hPrinter %x\n", hPrinter));
return ReturnValue;
}
RpcTryExcept {
if (ReturnValue = RpcEndDocPrinter(pSpool->hPrinter)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
DBGMSG(DBG_TRACE, ("Exit EndDocPrinter - client side hPrinter %x\n", hPrinter));
return ReturnValue;
} except (1) {
SetLastError(ERROR_INVALID_HANDLE);
return(FALSE);
}
}
BOOL
AddJobW(
HANDLE hPrinter,
DWORD Level,
LPBYTE pData,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
BOOL ReturnValue;
PSPOOL pSpool = (PSPOOL)hPrinter;
try {
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
switch (Level) {
case 1:
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (ReturnValue = RpcAddJob(pSpool->hPrinter, Level, pData,
cbBuf, pcbNeeded)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
MarshallUpStructure(pData, AddJobOffsets);
pSpool->Status |= SPOOL_STATUS_ADDJOB;
ReturnValue = TRUE;
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
if( ReturnValue ){
//
// Notify the tray icon that a new job has been sent.
//
vUpdateTrayIcon( hPrinter, ((PADDJOB_INFO_1)pData)->JobId );
}
return ReturnValue;
} except (1) {
SetLastError(TranslateExceptionCode(GetExceptionCode()));
return(FALSE);
}
}
BOOL
ScheduleJob(
HANDLE hPrinter,
DWORD JobId
)
{
BOOL ReturnValue;
PSPOOL pSpool = (PSPOOL)hPrinter;
try {
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
//
// The job has been scheduled, so reset the flag that indicates
// the tray icon has been notified. Any new AddJob/StartDocPrinter/
// StartDoc events should send a new notification, since it's really
// a new job.
//
pSpool->Status &= ~SPOOL_STATUS_TRAYICON_NOTIFIED;
FlushBuffer(pSpool);
RpcTryExcept {
if (ReturnValue = RpcScheduleJob(pSpool->hPrinter, JobId)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
pSpool->Status &= ~SPOOL_STATUS_ADDJOB;
ReturnValue = TRUE;
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
} except (1) {
SetLastError(TranslateExceptionCode(GetExceptionCode()));
return(FALSE);
}
}
BOOL
PrinterProperties(
HWND hWnd,
HANDLE hPrinter
)
/*++
Routine Description:
This is main PrinterProperties entri point and will call into the
our DevicePropertySheets() for UI pop up
Arguments:
hWnd - Handle to the window parent
hPrinter - Handle to the printer interested
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE.
To get extended error information, call GetLastError.
Author:
13-Jun-1996 Thu 15:22:36 created -by- Daniel Chou (danielc)
Revision History:
--*/
{
PRINTER_INFO_2 *pPI2 = NULL;
DEVICEPROPERTYHEADER DPHdr;
LONG Result;
DWORD cb;
DWORD dwValue = 1;
//
// Ensure the printer handle is valid
//
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
DPHdr.cbSize = sizeof(DPHdr);
DPHdr.hPrinter = hPrinter;
DPHdr.Flags = DPS_NOPERMISSION;
//
// Do a GetPrinter() level2 to get the printer name.
//
if ((!GetPrinter(hPrinter, 2, NULL, 0, &cb)) &&
(GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
(pPI2 = (PPRINTER_INFO_2)LocalAlloc(LMEM_FIXED, cb)) &&
(GetPrinter(hPrinter, 2, (LPBYTE)pPI2, cb, &cb))) {
//
// Set the printer name.
//
DPHdr.pszPrinterName = pPI2->pPrinterName;
} else {
DPHdr.pszPrinterName = NULL;
}
//
// Attempt to set the printer data to determine access privilages.
//
if (SetPrinterData( hPrinter,
TEXT( "PrinterPropertiesPermission" ),
REG_DWORD,
(LPBYTE)&dwValue,
sizeof( dwValue ) ) == STATUS_SUCCESS ) {
//
// Indicate we have permissions.
//
DPHdr.Flags &= ~DPS_NOPERMISSION;
}
//
// Call Common UI to call do the and call the driver.
//
if ( CallCommonPropertySheetUI(hWnd,
DevicePropertySheets,
(LPARAM)&DPHdr,
(LPDWORD)&Result) < 0 ) {
Result = FALSE;
} else {
Result = TRUE;
}
if (pPI2) {
LocalFree((HLOCAL)pPI2);
}
return Result;
}
DWORD
GetPrinterDataW(
HANDLE hPrinter,
LPWSTR pValueName,
LPDWORD pType,
LPBYTE pData,
DWORD nSize,
LPDWORD pcbNeeded
)
{
DWORD ReturnValue = 0;
DWORD ReturnType = 0;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (!ValidatePrinterHandle(hPrinter)) {
return ERROR_INVALID_HANDLE;
}
//
// The user should be able to pass in NULL for buffer, and
// 0 for size. However, the RPC interface specifies a ref pointer,
// so we must pass in a valid pointer. Pass in a pointer to
// ReturnValue (this is just a dummy pointer).
//
if( !pData && !nSize ){
pData = (PBYTE)&ReturnValue;
}
if (!pType) {
pType = (PDWORD) &ReturnType;
}
RpcTryExcept {
ReturnValue = RpcGetPrinterData(pSpool->hPrinter, pValueName, pType,
pData, nSize, pcbNeeded);
} RpcExcept(1) {
ReturnValue = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
return ReturnValue;
}
DWORD
EnumPrinterDataW(
HANDLE hPrinter,
DWORD dwIndex, // index of value to query
LPWSTR pValueName, // address of buffer for value string
DWORD cbValueName, // size of pValueName
LPDWORD pcbValueName, // address for size of value buffer
LPDWORD pType, // address of buffer for type code
LPBYTE pData, // address of buffer for value data
DWORD cbData, // size of pData
LPDWORD pcbData // address for size of data buffer
)
{
DWORD ReturnValue = 0;
DWORD ReturnType = 0;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (!ValidatePrinterHandle(hPrinter)) {
return ERROR_INVALID_HANDLE;
}
//
// The user should be able to pass in NULL for buffer, and
// 0 for size. However, the RPC interface specifies a ref pointer,
// so we must pass in a valid pointer. Pass in a pointer to
// a dummy pointer.
//
if (!pValueName && !cbValueName)
pValueName = (LPWSTR) &ReturnValue;
if( !pData && !cbData )
pData = (PBYTE)&ReturnValue;
if (!pType)
pType = (PDWORD) &ReturnType;
RpcTryExcept {
ReturnValue = RpcEnumPrinterData( pSpool->hPrinter,
dwIndex,
pValueName,
cbValueName,
pcbValueName,
pType,
pData,
cbData,
pcbData);
} RpcExcept(1) {
ReturnValue = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
return ReturnValue;
}
DWORD
DeletePrinterDataW(
HANDLE hPrinter,
LPWSTR pValueName
)
{
DWORD ReturnValue = 0;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (!ValidatePrinterHandle(hPrinter)) {
return ERROR_INVALID_HANDLE;
}
RpcTryExcept {
ReturnValue = RpcDeletePrinterData(pSpool->hPrinter, pValueName);
} RpcExcept(1) {
ReturnValue = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
return ReturnValue;
}
DWORD
SetPrinterDataW(
HANDLE hPrinter,
LPWSTR pValueName,
DWORD Type,
LPBYTE pData,
DWORD cbData
)
{
DWORD ReturnValue = 0;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (!ValidatePrinterHandle(hPrinter)) {
return ERROR_INVALID_HANDLE;
}
RpcTryExcept {
ReturnValue = RpcSetPrinterData(pSpool->hPrinter, pValueName, Type,
pData, cbData);
} RpcExcept(1) {
ReturnValue = TranslateExceptionCode(RpcExceptionCode());
} RpcEndExcept
return ReturnValue;
}
HANDLE
LoadPrinterDriver(
HANDLE hPrinter
)
{
PDRIVER_INFO_2 pDriverInfo;
DWORD cbNeeded;
HANDLE hModule=FALSE;
// Should preallocate MAX_DRIVER_INFO_2
// So we only call the spooler once
if (!GetPrinterDriver(hPrinter, NULL, 2, NULL, 0, &cbNeeded)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
if (pDriverInfo = (PDRIVER_INFO_2)LocalAlloc(LMEM_FIXED,
cbNeeded)) {
if (GetPrinterDriver(hPrinter, NULL, 2, (LPBYTE)pDriverInfo,
cbNeeded, &cbNeeded))
hModule = LoadLibrary(pDriverInfo->pConfigFile);
LocalFree(pDriverInfo);
}
}
}
return hModule;
}
LONG
DocumentPropertiesW(
HWND hWnd,
HANDLE hPrinter,
LPWSTR pDeviceName,
PDEVMODE pDevModeOutput,
PDEVMODE pDevModeInput,
DWORD fMode
)
/*++
Routine Description:
DocumentProperties entry point to call DocumentPropertySheets() depends on
the DM_PROMPT
Arguments:
Return Value:
Author:
13-Jun-1996 Thu 15:35:25 created -by- Daniel Chou (danielc)
Revision History:
--*/
{
DOCUMENTPROPERTYHEADER DPHdr;
PDEVMODE pDM;
LONG Result = -1;
HANDLE hTmpPrinter = NULL;
//
// Compatibility with Win95
// Win95 allows for hPrinter to be NULL
//
if (hPrinter == NULL) {
//
// Open th printer for default access.
//
if (!OpenPrinter( pDeviceName, &hTmpPrinter, NULL )) {
hTmpPrinter = NULL;
}
} else {
hTmpPrinter = hPrinter;
}
//
// Ensure the printer handle is valid
//
if (ValidatePrinterHandle(hTmpPrinter)) {
//
// If fMode doesn't specify DM_IN_BUFFER, then zero out
// pDevModeInput.
//
// Old 3.51 (version 1-0) drivers used to ignore the absence of
// DM_IN_BUFFER and use pDevModeInput if it was not NULL. It
// probably did this because Printman.exe was broken.
//
// If the devmode is invalid, then don't pass one in.
// This fixes MS Imager32 (which passes dmSize == 0) and
// Milestones etc. 4.5.
//
// Note: this assumes that pDevModeOutput is still the
// correct size!
//
if( !(fMode & DM_IN_BUFFER) || !bValidDevModeW( pDevModeInput )){
//
// If either are not set, make sure both are not set.
//
pDevModeInput = NULL;
fMode &= ~DM_IN_BUFFER;
}
DPHdr.cbSize = sizeof(DPHdr);
DPHdr.Reserved = 0;
DPHdr.hPrinter = hTmpPrinter;
DPHdr.pszPrinterName = pDeviceName;
if (pDevModeOutput) {
//
// Get the driver devmode size at here
//
DPHdr.pdmIn = NULL;
DPHdr.pdmOut = NULL;
DPHdr.fMode = 0;
DPHdr.cbOut = DocumentPropertySheets(NULL, (LPARAM)&DPHdr);
} else {
DPHdr.cbOut = 0;
}
DPHdr.pdmIn = (PDEVMODE)pDevModeInput;
DPHdr.pdmOut = (PDEVMODE)pDevModeOutput;
DPHdr.fMode = fMode;
if (fMode & DM_PROMPT) {
Result = CPSUI_CANCEL;
if ((CallCommonPropertySheetUI(hWnd,
DocumentPropertySheets,
(LPARAM)&DPHdr,
(LPDWORD)&Result)) < 0) {
Result = -1;
} else {
Result = (Result == CPSUI_OK) ? IDOK : IDCANCEL;
}
} else {
Result = DocumentPropertySheets(NULL, (LPARAM)&DPHdr);
}
}
if (hPrinter == NULL) {
if( hTmpPrinter ){
ClosePrinter(hTmpPrinter);
}
}
return(Result);
}
LONG
AdvancedDocumentPropertiesW(
HWND hWnd,
HANDLE hPrinter,
LPWSTR pDeviceName,
PDEVMODE pDevModeOutput,
PDEVMODE pDevModeInput
)
/*++
Routine Description:
AdvanceDocumentProperties() will call DocumentProperties() with DM_ADVANCED
flag mode set
Arguments:
Return Value:
TRUE/FALSE
Author:
13-Jun-1996 Thu 16:00:13 created -by- Daniel Chou (danielc)
Revision History:
--*/
{
return((DocumentPropertiesW(hWnd,
hPrinter,
pDeviceName,
pDevModeOutput,
pDevModeInput,
DM_PROMPT |
DM_MODIFY |
DM_COPY |
DM_ADVANCED) == CPSUI_OK) ? 1 : 0);
}
LONG
AdvancedSetupDialogW(
HWND hWnd,
HANDLE hInst,
LPDEVMODE pDevModeInput,
LPDEVMODE pDevModeOutput
)
{
HANDLE hPrinter;
LONG ReturnValue = -1;
if (OpenPrinterW(pDevModeInput->dmDeviceName, &hPrinter, NULL)) {
ReturnValue = AdvancedDocumentPropertiesW(hWnd, hPrinter,
pDevModeInput->dmDeviceName,
pDevModeOutput,
pDevModeInput);
ClosePrinter(hPrinter);
}
return ReturnValue;
}
int
WINAPI
DeviceCapabilitiesW(
LPCWSTR pDevice,
LPCWSTR pPort,
WORD fwCapability,
LPWSTR pOutput,
CONST DEVMODEW *pDevMode
)
{
HANDLE hPrinter, hModule;
int ReturnValue=-1;
INT_FARPROC pfn;
//DbgPrint("winspool.drv!DeviceCapabilitiesW(%ws, %ws, %d) called\n", pDevice, pPort, fwCapability);
if (OpenPrinter((LPWSTR)pDevice, &hPrinter, NULL)) {
if (hModule = LoadPrinterDriver(hPrinter)) {
if (pfn = GetProcAddress(hModule, "DrvDeviceCapabilities")) {
try {
ReturnValue = (*pfn)(hPrinter, pDevice, fwCapability,
pOutput, pDevMode);
} except(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = -1;
}
}
FreeLibrary(hModule);
}
ClosePrinter(hPrinter);
}
return ReturnValue;
}
BOOL
AddFormW(
HANDLE hPrinter,
DWORD Level,
LPBYTE pForm
)
{
BOOL ReturnValue;
GENERIC_CONTAINER FormContainer;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
switch (Level) {
case 1:
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
FormContainer.Level = Level;
FormContainer.pData = pForm;
RpcTryExcept {
if (ReturnValue = RpcAddForm(pSpool->hPrinter,
(PFORM_CONTAINER)&FormContainer)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
DeleteFormW(
HANDLE hPrinter,
LPWSTR pFormName
)
{
BOOL ReturnValue;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
RpcTryExcept {
if (ReturnValue = RpcDeleteForm(pSpool->hPrinter, pFormName)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
GetFormW(
HANDLE hPrinter,
LPWSTR pFormName,
DWORD Level,
LPBYTE pForm,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
BOOL ReturnValue;
DWORD *pOffsets;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
switch (Level) {
case 1:
pOffsets = FormInfo1Offsets;
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (pForm)
memset(pForm, 0, cbBuf);
if (ReturnValue = RpcGetForm(pSpool->hPrinter, pFormName, Level, pForm,
cbBuf, pcbNeeded)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pForm) {
MarshallUpStructure(pForm, pOffsets);
}
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
SetFormW(
HANDLE hPrinter,
LPWSTR pFormName,
DWORD Level,
LPBYTE pForm
)
{
BOOL ReturnValue;
GENERIC_CONTAINER FormContainer;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
switch (Level) {
case 1:
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
FormContainer.Level = Level;
FormContainer.pData = pForm;
RpcTryExcept {
if (ReturnValue = RpcSetForm(pSpool->hPrinter, pFormName,
(PFORM_CONTAINER)&FormContainer)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
EnumFormsW(
HANDLE hPrinter,
DWORD Level,
LPBYTE pForm,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
BOOL ReturnValue;
DWORD cbStruct;
DWORD *pOffsets;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
switch (Level) {
case 1:
pOffsets = FormInfo1Offsets;
cbStruct = sizeof(FORM_INFO_1);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (pForm)
memset(pForm, 0, cbBuf);
if (ReturnValue = RpcEnumForms(pSpool->hPrinter, Level, pForm, cbBuf,
pcbNeeded, pcReturned)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pForm) {
DWORD i=*pcReturned;
while (i--) {
MarshallUpStructure(pForm, pOffsets);
pForm+=cbStruct;
}
}
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
EnumPortsW(
LPWSTR pName,
DWORD Level,
LPBYTE pPort,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
BOOL ReturnValue;
DWORD cbStruct;
DWORD *pOffsets;
switch (Level) {
case 1:
pOffsets = PortInfo1Offsets;
cbStruct = sizeof(PORT_INFO_1);
break;
case 2:
pOffsets = PortInfo2Offsets;
cbStruct = sizeof(PORT_INFO_2);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (pPort)
memset(pPort, 0, cbBuf);
if (ReturnValue = RpcEnumPorts(pName, Level, pPort, cbBuf,
pcbNeeded, pcReturned)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
if (pPort) {
DWORD i=*pcReturned;
while (i--) {
MarshallUpStructure(pPort, pOffsets);
pPort+=cbStruct;
}
}
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
EnumMonitorsW(
LPWSTR pName,
DWORD Level,
LPBYTE pMonitor,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcReturned
)
{
BOOL ReturnValue;
DWORD cbStruct;
DWORD *pOffsets;
switch (Level) {
case 1:
pOffsets = MonitorInfo1Offsets;
cbStruct = sizeof(MONITOR_INFO_1);
break;
case 2:
pOffsets = MonitorInfo2Offsets;
cbStruct = sizeof(MONITOR_INFO_2);
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if (pMonitor)
memset(pMonitor, 0, cbBuf);
if (ReturnValue = RpcEnumMonitors(pName, Level, pMonitor, cbBuf,
pcbNeeded, pcReturned)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
if (pMonitor) {
DWORD i=*pcReturned;
while (i--) {
MarshallUpStructure(pMonitor, pOffsets);
pMonitor+=cbStruct;
}
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
typedef struct {
LPWSTR pName;
HWND hWnd;
LPWSTR pPortName;
HANDLE Complete;
DWORD ReturnValue;
DWORD Error;
INT_FARPROC pfn;
} CONFIGUREPORT_PARAMETERS;
void
PortThread(
CONFIGUREPORT_PARAMETERS *pParam
)
{
DWORD ReturnValue;
/* It's no use setting errors here, because they're kept on a per-thread
* basis. Instead we have to pass any error code back to the calling
* thread and let him set it.
*/
RpcTryExcept {
if (ReturnValue = (*pParam->pfn)(pParam->pName, pParam->hWnd,
pParam->pPortName)) {
pParam->Error = ReturnValue;
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
pParam->Error = TranslateExceptionCode(RpcExceptionCode());
ReturnValue = FALSE;
} RpcEndExcept
pParam->ReturnValue = ReturnValue;
SetEvent(pParam->Complete);
}
BOOL
KickoffThread(
LPWSTR pName,
HWND hWnd,
LPWSTR pPortName,
INT_FARPROC pfn
)
{
CONFIGUREPORT_PARAMETERS Parameters;
HANDLE ThreadHandle;
MSG msg;
DWORD ThreadId;
EnableWindow(hWnd, FALSE);
Parameters.pName = pName;
Parameters.hWnd = hWnd;
Parameters.pPortName = pPortName;
Parameters.Complete = CreateEvent(NULL, TRUE, FALSE, NULL);
Parameters.pfn = pfn;
ThreadHandle = CreateThread(NULL, 4*1024,
(LPTHREAD_START_ROUTINE)PortThread,
&Parameters, 0, &ThreadId);
CloseHandle(ThreadHandle);
while (MsgWaitForMultipleObjects(1, &Parameters.Complete, FALSE, INFINITE,
QS_ALLEVENTS | QS_SENDMESSAGE) == 1) {
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
CloseHandle(Parameters.Complete);
EnableWindow(hWnd, TRUE);
SetForegroundWindow(hWnd);
SetFocus(hWnd);
if(!Parameters.ReturnValue)
SetLastError(Parameters.Error);
return Parameters.ReturnValue;
}
BOOL
AddPortW(
LPWSTR pName,
HWND hWnd,
LPWSTR pMonitorName
)
{
return KickoffThread(pName, hWnd, pMonitorName, (INT_FARPROC)RpcAddPort);
}
BOOL
ConfigurePortW(
LPWSTR pName,
HWND hWnd,
LPWSTR pPortName
)
{
return KickoffThread(pName, hWnd, pPortName, (INT_FARPROC)RpcConfigurePort);
}
BOOL
DeletePortW(
LPWSTR pName,
HWND hWnd,
LPWSTR pPortName
)
{
return KickoffThread(pName, hWnd, pPortName, (INT_FARPROC)RpcDeletePort);
}
HANDLE
CreatePrinterIC(
HANDLE hPrinter,
LPDEVMODEW pDevMode
)
{
HANDLE ReturnValue;
DWORD Error;
DEVMODE_CONTAINER DevModeContainer;
HANDLE hGdi;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
if( bValidDevModeW( pDevMode )){
DevModeContainer.cbBuf = pDevMode->dmSize + pDevMode->dmDriverExtra;
DevModeContainer.pDevMode = (LPBYTE)pDevMode;
} else {
DevModeContainer.cbBuf = 0;
DevModeContainer.pDevMode = (LPBYTE)pDevMode;
}
RpcTryExcept {
if (Error = RpcCreatePrinterIC( pSpool->hPrinter,
&hGdi,
&DevModeContainer )){
SetLastError(Error);
ReturnValue = FALSE;
} else
ReturnValue = hGdi;
} RpcExcept(1) {
SetLastError(RpcExceptionCode());
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
PlayGdiScriptOnPrinterIC(
HANDLE hPrinterIC,
LPBYTE pIn,
DWORD cIn,
LPBYTE pOut,
DWORD cOut,
DWORD ul
)
{
BOOL ReturnValue;
RpcTryExcept {
if (ReturnValue = RpcPlayGdiScriptOnPrinterIC(hPrinterIC, pIn, cIn,
pOut, cOut, ul)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
DeletePrinterIC(
HANDLE hPrinterIC
)
{
BOOL ReturnValue;
RpcTryExcept {
if (ReturnValue = RpcDeletePrinterIC(&hPrinterIC)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
/****************************************************************************
* INT QueryRemoteFonts( HANDLE, PUNIVERSAL_FONT_ID, ULONG )
*
* This is a hacky version of QueryRemoteFonts that doesn't do any
* caching based on the time stamp returned by QueryFonts. Additionally,
* it uses the CreatePrinterIC/PlayGdiScriptOnDC mechanism since it was
* already in place. It may be better to eliminate CreatePrinterIC and use
* an HPRINTER instead.
*
* Note that if the user doesn't pass in a buffer large enough to hold all
* the fonts we truncate the list and copy only enough fonts for which there
* is room but will still return success. This is okay because the worst
* that can happen in this case is that we may download unecessary fonts in
* the spool stream.
*
*
* History:
* 5/25/1995 by Gerrit van Wingerden [gerritv]
* Wrote it.
*****************************************************************************/
INT QueryRemoteFonts(
HANDLE hPrinter,
PUNIVERSAL_FONT_ID pufi,
ULONG nBufferSize
)
{
HANDLE hPrinterIC;
PBYTE pBuf;
DWORD dwDummy,cOut;
INT iRet = -1;
hPrinterIC = CreatePrinterIC( hPrinter, NULL );
if( hPrinterIC )
{
cOut = (nBufferSize * sizeof(UNIVERSAL_FONT_ID)) + sizeof(INT);
pBuf = LocalAlloc( LMEM_FIXED, cOut );
if( pBuf )
{
// Just call PlayGdiScriptOnPrinterIC for now since the piping is in place.
// For some reason the RPC stuff doesn't like NULL pointers for pIn so we
// use &dwDummy instead;
if(PlayGdiScriptOnPrinterIC(hPrinterIC,(PBYTE) &dwDummy,
sizeof(dwDummy),pBuf,cOut, 0))
{
DWORD dwSize = *((DWORD*) pBuf );
iRet = (INT) dwSize;
SPLASSERT( iRet >= 0 );
if( dwSize > nBufferSize )
{
dwSize = nBufferSize;
}
memcpy(pufi,pBuf+sizeof(DWORD),dwSize * sizeof(UNIVERSAL_FONT_ID));
}
LocalFree( pBuf );
}
DeletePrinterIC( hPrinterIC );
}
return(iRet);
}
DWORD
PrinterMessageBoxW(
HANDLE hPrinter,
DWORD Error,
HWND hWnd,
LPWSTR pText,
LPWSTR pCaption,
DWORD dwType
)
{
PSPOOL pSpool = (PSPOOL)hPrinter;
DWORD dw;
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
RpcTryExcept {
dw = RpcPrinterMessageBox(pSpool->hPrinter, Error, (DWORD)hWnd, pText,
pCaption, dwType);
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
dw = 0;
} RpcEndExcept
return dw;
}
BOOL
AddMonitorW(
LPWSTR pName,
DWORD Level,
LPBYTE pMonitorInfo
)
{
BOOL ReturnValue;
MONITOR_CONTAINER MonitorContainer;
MONITOR_INFO_2 MonitorInfo2;
switch (Level) {
case 2:
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
if (pMonitorInfo)
MonitorInfo2 = *(PMONITOR_INFO_2)pMonitorInfo;
else
memset(&MonitorInfo2, 0, sizeof(MonitorInfo2));
if (!MonitorInfo2.pEnvironment || !*MonitorInfo2.pEnvironment) {
MonitorInfo2.pEnvironment = szEnvironment;
}
MonitorContainer.Level = Level;
MonitorContainer.MonitorInfo.pMonitorInfo2 = (MONITOR_INFO_2 *)&MonitorInfo2;
RpcTryExcept {
if (ReturnValue = RpcAddMonitor(pName, &MonitorContainer)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
DeleteMonitorW(
LPWSTR pName,
LPWSTR pEnvironment,
LPWSTR pMonitorName
)
{
BOOL ReturnValue;
if (!pMonitorName || !*pMonitorName) {
SetLastError(ERROR_INVALID_PARAMETER);
return (FALSE);
}
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = szEnvironment;
if (ReturnValue = RpcDeleteMonitor(pName,
pEnvironment,
pMonitorName)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
DeletePrintProcessorW(
LPWSTR pName,
LPWSTR pEnvironment,
LPWSTR pPrintProcessorName
)
{
BOOL ReturnValue;
if (!pPrintProcessorName || !*pPrintProcessorName) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = szEnvironment;
if (ReturnValue = RpcDeletePrintProcessor(pName,
pEnvironment,
pPrintProcessorName)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
AddPrintProvidorW(
LPWSTR pName,
DWORD Level,
LPBYTE pProvidorInfo
)
{
BOOL ReturnValue;
PROVIDOR_CONTAINER ProvidorContainer;
switch (Level) {
case 1:
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
ProvidorContainer.Level = Level;
ProvidorContainer.ProvidorInfo.pProvidorInfo1 = (PROVIDOR_INFO_1 *)pProvidorInfo;
RpcTryExcept {
if (ReturnValue = RpcAddPrintProvidor(pName, &ProvidorContainer)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
DeletePrintProvidorW(
LPWSTR pName,
LPWSTR pEnvironment,
LPWSTR pPrintProvidorName
)
{
BOOL ReturnValue;
RpcTryExcept {
if (!pEnvironment || !*pEnvironment)
pEnvironment = szEnvironment;
if (ReturnValue = RpcDeletePrintProvidor(pName,
pEnvironment,
pPrintProvidorName)) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
LPWSTR
IsaFileName(
LPWSTR pOutputFile,
LPWSTR FullPathName
)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
LPWSTR pFileName=NULL;
//
// Hack for Word20c.Win
//
if (!_wcsicmp(pOutputFile, L"FILE")) {
return(NULL);
}
if (GetFullPathName(pOutputFile, MAX_PATH, FullPathName, &pFileName)) {
DBGMSG(DBG_TRACE, ("Fully qualified filename is %ws\n", FullPathName));
hFile = CreateFile(pOutputFile,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile != INVALID_HANDLE_VALUE) {
if (GetFileType(hFile) == FILE_TYPE_DISK) {
CloseHandle(hFile);
return(FullPathName);
} else {
CloseHandle(hFile);
}
}
}
return(NULL);
}
BOOL IsaPortName(
PKEYDATA pKeyData,
LPWSTR pOutputFile
)
{
DWORD i = 0;
UINT uStrLen;
if (!pKeyData) {
return(FALSE);
}
for (i=0; i < pKeyData->cTokens; i++) {
if (!lstrcmpi(pKeyData->pTokens[i], szFilePort)) {
if ((!wcsncmp(pOutputFile, L"Ne", 2)) &&
(*(pOutputFile + 4) == L':')) {
return(FALSE);
} else {
continue;
}
}
if (!lstrcmpi(pKeyData->pTokens[i], pOutputFile)) {
return(TRUE);
}
}
//
// Hack for NeXY: ports
//
if (!_wcsnicmp(pOutputFile, L"Ne", 2)) {
uStrLen = wcslen( pOutputFile );
//
// Ne00: or Ne00 if app truncates it
//
if (( uStrLen == 5 ) || ( uStrLen == 4 ) ) {
// Check for two Digits
if (( pOutputFile[2] >= L'0' ) && ( pOutputFile[2] <= L'9' ) &&
( pOutputFile[3] >= L'0' ) && ( pOutputFile[3] <= L'9' )) {
//
// Check for the final : as in Ne01:,
// note some apps will truncate it.
//
if (( uStrLen == 5 ) && (pOutputFile[4] != L':')) {
return FALSE;
}
return TRUE;
}
}
}
return(FALSE);
}
BOOL HasAFilePort(PKEYDATA pKeyData)
{
DWORD i = 0;
if (!pKeyData) {
return(FALSE);
}
for (i=0; i < pKeyData->cTokens; i++) {
if (!lstrcmpi(pKeyData->pTokens[i], szFilePort)) {
return(TRUE);
}
}
return(FALSE);
}
LPWSTR
StartDocDlgW(
HANDLE hPrinter,
DOCINFO *pDocInfo
)
{
DWORD dwError = 0;
DWORD dwStatus = FALSE;
LPWSTR lpFileName = NULL;
DWORD rc = 0;
PKEYDATA pKeyData = NULL;
LPWSTR pPortNames = NULL;
WCHAR FullPathName[MAX_PATH];
WCHAR CurrentDirectory[MAX_PATH];
PKEYDATA pOutputList = NULL;
WCHAR PortNames[MAX_PATH];
DWORD i = 0;
#if DBG
GetCurrentDirectory(MAX_PATH, CurrentDirectory);
DBGMSG(DBG_TRACE, ("The Current Directory is %ws\n", CurrentDirectory));
#endif
if (pDocInfo) {
DBGMSG(DBG_TRACE, ("lpOutputFile is %ws\n", pDocInfo->lpszOutput ? pDocInfo->lpszOutput: L""));
}
memset(FullPathName, 0, sizeof(WCHAR)*MAX_PATH);
pPortNames = GetPrinterPortList(hPrinter);
pKeyData = CreateTokenList(pPortNames);
//
// Check for the presence of multiple ports in the lpszOutput field
// the assumed delimiter is the comma. Thus there can be no files with commas
// BugBug: printing wide, Hpmon can have ports with commas this should be fixed
//
if (pDocInfo && pDocInfo->lpszOutput && pDocInfo->lpszOutput[0]) {
//
// Make a copy of the pDocInfo->lpszOutput because CreateTokenList is destructive
//
wcscpy(PortNames, pDocInfo->lpszOutput);
pOutputList = CreateTokenList(PortNames);
if ((pOutputList->cTokens > 1) &&
!lstrcmpi(pPortNames, pDocInfo->lpszOutput))
{
for (i= 0; i < pOutputList->cTokens; i++) {
if (!lstrcmpi(pOutputList->pTokens[i], szFilePort)) {
//
// !! BUGBUG !!
//
// We are about to corrupt some user memory
// here if lpszOutput is just a few chars long.
//
wcscpy((LPWSTR)pDocInfo->lpszOutput, szFilePort);
break;
}
}
if (i == pOutputList->cTokens) {
wcscpy((LPWSTR)pDocInfo->lpszOutput, pOutputList->pTokens[0]);
}
}
FreeSplMem(pOutputList);
}
if (pDocInfo && pDocInfo->lpszOutput && pDocInfo->lpszOutput[0]) {
if (IsaPortName(pKeyData, (LPWSTR)pDocInfo->lpszOutput)) {
lpFileName = NULL;
goto StartDocDlgWReturn;
}
if (IsaFileName((LPWSTR)pDocInfo->lpszOutput, FullPathName)) {
//
// Fully Qualify the pathname for Apps like PageMaker and QuatroPro
//
if (lpFileName = LocalAlloc(LPTR, (wcslen(FullPathName)+1)*sizeof(WCHAR))) {
wcscpy(lpFileName, FullPathName);
}
goto StartDocDlgWReturn;
}
}
if ((HasAFilePort(pKeyData)) ||
(pDocInfo && pDocInfo->lpszOutput
&& (!_wcsicmp(pDocInfo->lpszOutput, L"FILE:") ||
!_wcsicmp(pDocInfo->lpszOutput, L"FILE"))))
{
DBGMSG(DBG_TRACE, ("We returned True from has file\n"));
rc = DialogBoxParam( hInst,
MAKEINTRESOURCE( DLG_PRINTTOFILE ),
NULL, (DLGPROC)PrintToFileDlg,
(LPARAM)&lpFileName );
if (rc == -1) {
DBGMSG(DBG_TRACE, ("Error from DialogBoxParam- %d\n", GetLastError()));
lpFileName = (LPWSTR)-1;
goto StartDocDlgWReturn;
} else if (rc == 0) {
DBGMSG(DBG_TRACE, ("User cancelled the dialog\n"));
lpFileName = (LPWSTR)-2;
SetLastError( ERROR_CANCELLED );
goto StartDocDlgWReturn;
} else {
DBGMSG(DBG_TRACE, ("The string was successfully returned\n"));
DBGMSG(DBG_TRACE, ("The string is %ws\n", lpFileName? lpFileName: L"NULL"));
goto StartDocDlgWReturn;
}
} else {
lpFileName = (LPWSTR)NULL;
}
StartDocDlgWReturn:
if (pKeyData) {
FreeSplMem(pKeyData);
}
if (pPortNames) {
FreeSplStr(pPortNames);
}
return(lpFileName);
}
BOOL
AddPortExW(
LPWSTR pName,
DWORD Level,
LPBYTE lpBuffer,
LPWSTR lpMonitorName
)
{
DWORD ReturnValue;
PORT_CONTAINER PortContainer;
PORT_VAR_CONTAINER PortVarContainer;
PPORT_INFO_FF pPortInfoFF;
PPORT_INFO_1 pPortInfo1;
if (!lpBuffer) {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
switch (Level) {
case (DWORD)-1:
pPortInfoFF = (PPORT_INFO_FF)lpBuffer;
PortContainer.Level = Level;
PortContainer.PortInfo.pPortInfoFF = (PPORT_INFO_FF)pPortInfoFF;
PortVarContainer.cbMonitorData = pPortInfoFF->cbMonitorData;
PortVarContainer.pMonitorData = pPortInfoFF->pMonitorData;
break;
case 1:
pPortInfo1 = (PPORT_INFO_1)lpBuffer;
PortContainer.Level = Level;
PortContainer.PortInfo.pPortInfo1 = (PPORT_INFO_1)pPortInfo1;
PortVarContainer.cbMonitorData = 0;
PortVarContainer.pMonitorData = NULL;
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return(FALSE);
}
RpcTryExcept {
if (ReturnValue = RpcAddPortEx(pName, (LPPORT_CONTAINER)&PortContainer,
(LPPORT_VAR_CONTAINER)&PortVarContainer,
lpMonitorName
)) {
SetLastError(ReturnValue);
return(FALSE);
} else {
return(TRUE);
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
}
BOOL
DevQueryPrint(
HANDLE hPrinter,
LPDEVMODE pDevMode,
DWORD *pResID
)
{
BOOL Ok = FALSE;
HANDLE hModule;
INT_FARPROC pfn;
if (hModule = LoadPrinterDriver(hPrinter)) {
if (pfn = GetProcAddress(hModule, "DevQueryPrint")) {
try {
Ok = (*pfn)(hPrinter, pDevMode, pResID);
} except(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
Ok = FALSE;
}
}
FreeLibrary(hModule);
}
return(Ok);
}
BOOL
DevQueryPrintEx(
PDEVQUERYPRINT_INFO pDQPInfo
)
{
BOOL Ok = FALSE;
HANDLE hModule;
INT_FARPROC pfn;
if (hModule = LoadPrinterDriver(pDQPInfo->hPrinter)) {
if (pfn = GetProcAddress(hModule, "DevQueryPrintEx")) {
try {
Ok = (*pfn)(pDQPInfo);
} except(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
Ok = FALSE;
}
}
FreeLibrary(hModule);
}
return(Ok);
}
BOOL
SpoolerDevQueryPrintW(
HANDLE hPrinter,
LPDEVMODE pDevMode,
DWORD *pResID,
LPWSTR pszBuffer,
DWORD cchBuffer
)
{
BOOL Ok = FALSE;
HANDLE hModule;
INT_FARPROC pfn;
if (hModule = LoadPrinterDriver(hPrinter)) {
if (pfn = GetProcAddress(hModule, "DevQueryPrintEx")) {
DEVQUERYPRINT_INFO DQPInfo;
DQPInfo.cbSize = sizeof(DQPInfo);
DQPInfo.Level = 1;
DQPInfo.hPrinter = hPrinter;
DQPInfo.pDevMode = pDevMode;
DQPInfo.pszErrorStr = (LPTSTR)pszBuffer;
DQPInfo.cchErrorStr = (WORD)cchBuffer;
DQPInfo.cchNeeded = 0;
try {
*pResID = (Ok = (*pfn)(&DQPInfo)) ? 0 : 0xDCDCDCDC;
} except(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
Ok = FALSE;
}
} else if (pfn = GetProcAddress(hModule, "DevQueryPrint")) {
try {
if ((Ok = (*pfn)(hPrinter, pDevMode, pResID)) &&
(*pResID)) {
UINT cch;
*pszBuffer = L'\0';
SelectFormNameFromDevMode(hPrinter, pDevMode, pszBuffer);
if (cch = lstrlen(pszBuffer)) {
pszBuffer += cch;
*pszBuffer++ = L' ';
*pszBuffer++ = L'-';
*pszBuffer++ = L' ';
cchBuffer -= (cch + 3);
}
LoadString(hModule, *pResID, pszBuffer, cchBuffer);
}
} except(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
Ok = FALSE;
}
}
FreeLibrary(hModule);
}
return(Ok);
}
LPWSTR
SelectFormNameFromDevMode(
HANDLE hPrinter,
PDEVMODEW pDevModeW,
LPWSTR pFormName
)
/*++
Routine Description:
This function pick the current form associated with current devmode and
return a form name pointer
Arguments:
hPrinter - Handle to the printer object
pDevModeW - Pointer to the unicode devmode for this printer
FormName - Pointer to the formname to be filled
Return Value:
Either a pointer to the FormName passed in if we do found one form,
otherwise it return NULL to signal a failue
Author:
21-Mar-1995 Tue 16:57:51 created -by- Daniel Chou (danielc)
Revision History:
--*/
{
DWORD cb;
DWORD cRet;
LPFORM_INFO_1 pFIBase;
LPFORM_INFO_1 pFI;
//
// 1. If the DM_FORMNAME is turned on, then we want to check this bit first
// because it only specific to the NT which using form. The form name
// supposed set by any NT driver but not win31 or Win95.Use the
// dmFormName only if dmPaperSize, dmPaperLength and dmPaperWidth fields
// are not set. If any of them is set then we have to find a form using
// the value in these fields.
//
if ( (pDevModeW->dmFields & DM_FORMNAME)
&& (!(pDevModeW->dmFields & (DM_PAPERSIZE |
DM_PAPERLENGTH |
DM_PAPERWIDTH))) ) {
wcscpy(pFormName, pDevModeW->dmFormName);
return(pFormName);
}
//
// For all other cases we need to get forms data base first, but we want
// to set the form name to NULL so that we can check if we found one
//
cb =
cRet = 0;
pFIBase =
pFI = NULL;
if ((!EnumForms(hPrinter, 1, NULL, 0, &cb, &cRet)) &&
(GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
(pFIBase = (LPFORM_INFO_1)LocalAlloc(LPTR, cb)) &&
(EnumForms(hPrinter, 1, (LPBYTE)pFIBase, cb, &cb, &cRet))) {
//
// 2. If user specified dmPaperSize then honor it, otherwise, it must
// be a custom form, and we will check to see if it match one of
// in the database
//
if ((pDevModeW->dmFields & DM_PAPERSIZE) &&
(pDevModeW->dmPaperSize >= DMPAPER_FIRST) &&
(pDevModeW->dmPaperSize <= (SHORT)cRet)) {
//
// We go the valid index now
//
pFI = pFIBase + (pDevModeW->dmPaperSize - DMPAPER_FIRST);
} else if ((pDevModeW->dmFields & DM_PAPER_WL) == DM_PAPER_WL) {
LPFORM_INFO_1 pFICur = pFIBase;
while (cRet--) {
if ((DM_MATCH(pDevModeW->dmPaperWidth, pFICur->Size.cx)) &&
(DM_MATCH(pDevModeW->dmPaperLength, pFICur->Size.cy))) {
//
// We found the match which has discern size differences
//
pFI = pFICur;
break;
}
pFICur++;
}
}
}
//
// If we found the form then copy the name down, otherwise set the
// formname to be NULL
//
if (pFI) {
wcscpy(pFormName, pFI->pName);
} else {
*pFormName = L'\0';
pFormName = NULL;
}
if (pFIBase) {
LocalFree((HLOCAL)pFIBase);
}
return(pFormName);
}
BOOL
SetAllocFailCount(
HANDLE hPrinter,
DWORD dwFailCount,
LPDWORD lpdwAllocCount,
LPDWORD lpdwFreeCount,
LPDWORD lpdwFailCountHit
)
{
BOOL ReturnValue;
PSPOOL pSpool = hPrinter;
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
RpcTryExcept {
if (ReturnValue = RpcSetAllocFailCount( pSpool->hPrinter,
dwFailCount,
lpdwAllocCount,
lpdwFreeCount,
lpdwFailCountHit )) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else
ReturnValue = TRUE;
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
WINAPI
EnumPrinterPropertySheets(
HANDLE hPrinter,
HWND hWnd,
LPVOID lpfnAdd,
LPARAM lParam
)
{
SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
return FALSE;
}
VOID
vUpdateTrayIcon(
IN HANDLE hPrinter,
IN DWORD JobId
)
{
SHCNF_PRINTJOB_DATA JobData;
LPPRINTER_INFO_1 pPrinterInfo1;
FARPROC pfnSHChangeNotify;
PSPOOL pSpool = (PSPOOL)hPrinter;
SPLASSERT( JobId );
//
// Avoid sending multiple notifications by setting this flag.
// When other calls (notably StartDocPrinter) see this,
// they will avoid sending a notification.
//
pSpool->Status |= SPOOL_STATUS_TRAYICON_NOTIFIED;
ZeroMemory( &JobData, sizeof( JobData ));
JobData.JobId = JobId;
//
// Get a copy of the real printer name
//
pPrinterInfo1 = (LPPRINTER_INFO_1)AllocSplMem( MAX_PRINTER_INFO1 );
if( pPrinterInfo1 ){
DWORD dwNeeded;
if( GetPrinter( hPrinter,
1,
(PBYTE)pPrinterInfo1,
MAX_PRINTER_INFO1,
&dwNeeded )){
if ( !hShell32 )
hShell32 = LoadLibrary( TEXT( "shell32.dll" ) );
if( hShell32 ) {
pfnSHChangeNotify = GetProcAddress( hShell32,
"SHChangeNotify" );
if( pfnSHChangeNotify ){
(*pfnSHChangeNotify)(
SHCNE_CREATE,
SHCNF_PRINTJOB | SHCNF_FLUSH | SHCNF_FLUSHNOWAIT,
pPrinterInfo1->pName,
(DWORD)&JobData );
}
}
}
FreeSplMem( pPrinterInfo1 );
}
}
INT
DocumentEvent(
HANDLE hPrinter,
HDC hdc,
INT iEsc,
ULONG cbIn,
PULONG pulIn,
ULONG cbOut,
PULONG pulOut
)
/*++
Routine Description:
Allow the driver UI dll to hook specific print events.
Arguments:
Return Value:
-1 error, stop printing
-2 cancel by user
0 call not supported
>0 success
--*/
{
LPDRIVER_INFO_2 pDriverInfo = NULL;
DWORD cbNeeded;
HANDLE hLibrary;
INT_FARPROC pfn;
INT ReturnValue=0;
PSPOOL pSpool = (PSPOOL)hPrinter;
if (!ValidatePrinterHandle(hPrinter)) {
return(FALSE);
}
if( DOCUMENTEVENT_EVENT( iEsc ) == DOCUMENTEVENT_CREATEDCPRE ){
//
// Before every CreateDC, re-enable DocumentEvent.
// If it fails on the first try, then don't try again
// until the next CreateDC.
//
pSpool->Status |= SPOOL_STATUS_DOCUMENTEVENT_ENABLED;
}
//
// Call Document event only if it's succeeded previously on
// CREATEDCPRE.
//
if( pSpool->Status & SPOOL_STATUS_DOCUMENTEVENT_ENABLED ){
if ( hLibrary = LoadPrinterDriver( hPrinter )) {
if( pfn = GetProcAddress( hLibrary, "DrvDocumentEvent" )){
//
// Disable the call so we don't recurse if the
// callback calls StartPage, etc.
//
pSpool->Status &= ~SPOOL_STATUS_DOCUMENTEVENT_ENABLED;
try {
ReturnValue = (*pfn)( hPrinter,
hdc,
iEsc,
cbIn,
pulIn,
cbOut,
pulOut);
} except(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
}
//
// Renable it now that we are done.
//
pSpool->Status |= SPOOL_STATUS_DOCUMENTEVENT_ENABLED;
}
FreeLibrary(hLibrary);
}
//
// If this is CREATEDCPRE and we failed, then don't retry until
// the next CreateDC.
//
if( DOCUMENTEVENT_EVENT( iEsc ) == DOCUMENTEVENT_CREATEDCPRE &&
ReturnValue <= 0 ){
pSpool->Status &= ~SPOOL_STATUS_DOCUMENTEVENT_ENABLED;
}
}
//
// If it's a StartDocPost, a job was just added. Notify the
// tray icon if we haven't already.
//
if( DOCUMENTEVENT_EVENT( iEsc ) == DOCUMENTEVENT_STARTDOCPOST ){
if( !( pSpool->Status & SPOOL_STATUS_TRAYICON_NOTIFIED )){
//
// If we have a StartDocPost, then issue a notification so that
// the user's tray starts polling. pulIn[0] holds the JobId.
//
vUpdateTrayIcon( hPrinter, (DWORD)pulIn[0] );
}
} else {
//
// If we have sent a notification, then by the next time we get a
// document event, we have completed any additional AddJobs or
// StartDocPrinters. Therefore we can reset the TRAYICON_NOTIFIED
// flag, since any more AddJobs/StartDocPrinters are really new
// jobs.
//
pSpool->Status &= ~SPOOL_STATUS_TRAYICON_NOTIFIED;
}
return ReturnValue;
}
/****************************************************************************
* BOOL QuerySpoolMode( hPrinter, pflSpoolMode, puVersion )
*
* This function is called by GDI at StartDoc time when printing to an EMF.
* It tell GDI whether to embed fonts in the job as well as what version of
* EMF to generate.
*
* For now I am doing something hacky: I'm calling GetPrinterInfo to determine
* if the target is a remote machine and if so always telling GDI to embed
* fonts which don't exist on the server into spool file. Eventually this
* call will be routed to the print processor on the target machine which
* will use some UI/registry setting to determine what to do with fonts and
* set the version number correctly.
*
* History:
* 5/13/1995 by Gerrit van Wingerden [gerritv]
* Wrote it.
*****************************************************************************/
// !!later move this define to the appropriate header file
#define QSM_DOWNLOADFONTS 0x00000001
BOOL
QuerySpoolMode(
HANDLE hPrinter,
LONG *pflSpoolMode,
ULONG *puVersion
)
{
DWORD dwPrinterInfoSize = 0;
PRINTER_INFO_2 *pPrinterInfo2 = NULL;
BOOL bRet = FALSE;
GetPrinter( hPrinter, 2, NULL, 0, &dwPrinterInfoSize );
pPrinterInfo2 = (PRINTER_INFO_2*) LocalAlloc(LPTR, dwPrinterInfoSize);
if( pPrinterInfo2 == NULL )
{
DBGMSG( DBG_WARNING, ( "Unable to alloc %d bytes.\n", dwPrinterInfoSize ));
return bRet;
}
if( GetPrinter( hPrinter,
2,
(LPBYTE) pPrinterInfo2,
dwPrinterInfoSize,
&dwPrinterInfoSize ) )
{
*puVersion = 0x00010000; // version 1.0
//
// No server means we are printing locally
//
*pflSpoolMode = ( pPrinterInfo2->pServerName == NULL ) ?
0 :
QSM_DOWNLOADFONTS;
bRet = TRUE;
}
else
{
DBGMSG( DBG_WARNING, ( "QuerySpoolMode: GetPrinter failed %d.\n", GetLastError( )));
}
LocalFree( pPrinterInfo2 );
return bRet;
}
BOOL
SetPortW(
LPWSTR pszName,
LPWSTR pszPortName,
DWORD dwLevel,
LPBYTE pPortInfo
)
{
BOOL ReturnValue;
PORT_CONTAINER PortContainer;
switch (dwLevel) {
case 3:
if ( !pPortInfo ) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
PortContainer.Level = dwLevel;
PortContainer.PortInfo.pPortInfo3 = (PPORT_INFO_3)pPortInfo;
break;
default:
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
RpcTryExcept {
if ( ReturnValue = RpcSetPort(pszName, pszPortName, &PortContainer) ) {
SetLastError(ReturnValue);
ReturnValue = FALSE;
} else {
ReturnValue = TRUE;
}
} RpcExcept(1) {
SetLastError(TranslateExceptionCode(RpcExceptionCode()));
ReturnValue = FALSE;
} RpcEndExcept
return ReturnValue;
}
BOOL
bValidDevModeW(
const DEVMODE *pDevMode
)
/*++
Routine Description:
Check whether a devmode is valid to be RPC'd across to the spooler.
Arguments:
pDevMode - DevMode to check.
Return Value:
TRUE - Devmode can be RPC'd to spooler.
FALSE - Invalid Devmode.
--*/
{
if( !pDevMode ){
return FALSE;
}
if( pDevMode->dmSize < MIN_DEVMODE_SIZEW ){
//
// The only valid case is if pDevModeW is NULL. If it's
// not NULL, then a bad devmode was passed in and the
// app should fix it's code.
//
SPLASSERT( pDevMode->dmSize >= MIN_DEVMODE_SIZEW );
return FALSE;
}
return TRUE;
}