Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2918 lines
79 KiB

/******************************Module*Header*******************************\
* Module Name: spool.cxx
*
* Created: 21-Feb-1995 10:13:18
* Author: Eric Kutter [erick]
*
* Copyright (c) 1993-1999 Microsoft Corporation
*
\**************************************************************************/
#include "precomp.hxx"
DWORD DriverInfo1Offsets[]={offsetof(DRIVER_INFO_1W, pName),
0xFFFFFFFF};
DWORD DriverInfo2Offsets[]={offsetof(DRIVER_INFO_2W, pName),
offsetof(DRIVER_INFO_2W, pEnvironment),
offsetof(DRIVER_INFO_2W, pDriverPath),
offsetof(DRIVER_INFO_2W, pDataFile),
offsetof(DRIVER_INFO_2W, pConfigFile),
0xFFFFFFFF};
DWORD DriverInfo3Offsets[]={offsetof(DRIVER_INFO_3W, pName),
offsetof(DRIVER_INFO_3W, pEnvironment),
offsetof(DRIVER_INFO_3W, pDriverPath),
offsetof(DRIVER_INFO_3W, pDataFile),
offsetof(DRIVER_INFO_3W, pConfigFile),
offsetof(DRIVER_INFO_3W, pHelpFile),
offsetof(DRIVER_INFO_3W, pDependentFiles),
offsetof(DRIVER_INFO_3W, pMonitorName),
offsetof(DRIVER_INFO_3W, pDefaultDataType),
0xFFFFFFFF};
DWORD DriverInfo1Strings[]={offsetof(DRIVER_INFO_1W, pName),
0xFFFFFFFF};
DWORD DriverInfo2Strings[]={offsetof(DRIVER_INFO_2W, pName),
offsetof(DRIVER_INFO_2W, pEnvironment),
offsetof(DRIVER_INFO_2W, pDriverPath),
offsetof(DRIVER_INFO_2W, pDataFile),
offsetof(DRIVER_INFO_2W, pConfigFile),
0xFFFFFFFF};
DWORD DriverInfo3Strings[]={offsetof(DRIVER_INFO_3W, pName),
offsetof(DRIVER_INFO_3W, pEnvironment),
offsetof(DRIVER_INFO_3W, pDriverPath),
offsetof(DRIVER_INFO_3W, pDataFile),
offsetof(DRIVER_INFO_3W, pConfigFile),
offsetof(DRIVER_INFO_3W, pHelpFile),
offsetof(DRIVER_INFO_3W, pMonitorName),
offsetof(DRIVER_INFO_3W, pDefaultDataType),
0xFFFFFFFF};
DWORD FormInfo1Offsets[] = { offsetof(FORM_INFO_1W, pName),
0xFFFFFFFF};
DWORD PrinterInfo1Offsets[]={offsetof(PRINTER_INFO_1W, pDescription),
offsetof(PRINTER_INFO_1W, pName),
offsetof(PRINTER_INFO_1W, pComment),
0xFFFFFFFF};
DWORD PrinterInfo2Offsets[]={offsetof(PRINTER_INFO_2W, pServerName),
offsetof(PRINTER_INFO_2W, pPrinterName),
offsetof(PRINTER_INFO_2W, pShareName),
offsetof(PRINTER_INFO_2W, pPortName),
offsetof(PRINTER_INFO_2W, pDriverName),
offsetof(PRINTER_INFO_2W, pComment),
offsetof(PRINTER_INFO_2W, pLocation),
offsetof(PRINTER_INFO_2W, pDevMode),
offsetof(PRINTER_INFO_2W, pSepFile),
offsetof(PRINTER_INFO_2W, pPrintProcessor),
offsetof(PRINTER_INFO_2W, pDatatype),
offsetof(PRINTER_INFO_2W, pParameters),
offsetof(PRINTER_INFO_2W, pSecurityDescriptor),
0xFFFFFFFF};
DWORD PrinterInfo3Offsets[]={offsetof(PRINTER_INFO_3, pSecurityDescriptor),
0xFFFFFFFF};
DWORD PrinterInfo4Offsets[]={offsetof(PRINTER_INFO_4W, pPrinterName),
offsetof(PRINTER_INFO_4W, pServerName),
0xFFFFFFFF};
DWORD PrinterInfo5Offsets[]={offsetof(PRINTER_INFO_5W, pPrinterName),
offsetof(PRINTER_INFO_5W, pPortName),
0xFFFFFFFF};
/*********************************Class************************************\
* SPOOLMSG
*
* structure containg a message waiting to be grabbed by a spooler thread.
* This is a private structure to communicate between the applications thread
* and the spoolers thread
*
* History:
* 27-Mar-1995 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/
#define SPOOLMSG_DELETE_INBUF 0x00000002
#define SPOOLMSG_MAXINPUT_BUF 4
#define NO_CLIENT 1
#define NON_SPOOL_INSTANCE ((DWORD) -1)
#define MIN_DEVMODE_SIZE 72 // Win 3.1 DevMode size. Also hard-coded in yspool
typedef struct _SPOOLMSG
{
ULONG cj;
ULONG iMsg;
ULONG fl;
HSPOOLOBJ hso;
PETHREAD pthreadClient;
SECURITY_CLIENT_CONTEXT sccSecurity;
// in order to avoid extra copying into a single buffer, there are multiple
// input buffers. For example, WritePrinter uses a header buffer on the
// stack with a second buffer containing the output data.
ULONG cjIn; // combined size of input buffers
ULONG cBuf; // number of input buffers
PULONG apulIn[SPOOLMSG_MAXINPUT_BUF];// input buffers
ULONG acjIn[SPOOLMSG_MAXINPUT_BUF];
PULONG pulOut; // location to put output data
ULONG cjOut; // size of output
ULONG ulRet; // return value
ULONG ulErr; // transfer last error from spooler thread to client
struct _SPOOLMSG *pNext;
} SPOOLMSG, *PSPOOLMSG;
/*********************************Class************************************\
* SPOOLOBJ : public OBJECT
*
* a SPOOLOBJ is GDI's internal spooler object containing data need to access
* the spooler for a particular print job.
*
* Public Interface:
*
* History:
* 21-Jun-1995 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/
class SPOOLOBJ : public OBJECT
{
public:
KEVENT *pkevent;
HANDLE hSpool;
PVOID pvMsg;
SPOOLMSG sm;
DWORD dwFlags;
DWORD dwSpoolInstance;
GREWRITEPRINTER WritePrinter;
DWORD dwWritePrinterReturn;
};
typedef SPOOLOBJ *PSPOOLOBJ;
/**************************************************************************\
*
\**************************************************************************/
BOOL gbInitSpool = FALSE; // set/cleared as the spooler comes and goes
PW32PROCESS gpidSpool = 0; // process ID of the spooler
PEPROCESS gpeSpool = 0; // process pointer of the spooler
PKEVENT gpeventGdiSpool; // spooler lock
PKEVENT gpeventSpoolerTermination; // signals spooler termination so client threads in spooler process can exit
DWORD gdwSpoolInstance = 0; // Keeps track of the spooler instance
LONG gpInfiniteWait = TRUE; // LONG used to ensure we have one spooler thread waiting with INFINITE timeoue
// there is a queue of spool messages. Elements are removed from the head
// and added to the tail
PSPOOLMSG gpsmSpoolHead = NULL;
PSPOOLMSG gpsmSpoolTail = NULL;
int gcSpoolMsg = 0;
int gcSpoolMsgCurrent = 0;
int gcSpoolMsgMax = 0;
#define LOCKSPOOLER GreAcquireSemaphore(ghsemGdiSpool)
#define UNLOCKSPOOLER GreReleaseSemaphore(ghsemGdiSpool)
#define POFFSET(pBase,pCur) ((PBYTE)(pCur) - (PBYTE)(pBase))
/*********************************Class************************************\
* class SPOOLREF
*
* Public Interface:
*
* History:
* 22-May-1995 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/
class SPOOLREF
{
public:
PSPOOLOBJ pso;
SPOOLREF() {}
SPOOLREF(HANDLE hspool)
{
pso = (PSPOOLOBJ)HmgLock((HOBJ)hspool,SPOOL_TYPE);
}
~SPOOLREF()
{
if (bValid())
{
DEC_EXCLUSIVE_REF_CNT(pso);
pso = NULL;
}
}
BOOL bDelete();
BOOL GreEscapeSpool();
BOOL bValid() {return(pso != NULL); }
HSPOOLOBJ hGet() {return((HSPOOLOBJ)pso->hGet());}
PSPOOLMSG psm() {return(&pso->sm); }
// we need to know the lock count for cleanup so we don't free up a message
// when it still may be accessed.
ULONG cAltLock()
{
return(((POBJ) (pso))->ulShareCount);
}
};
class SPOOLALTREF : public SPOOLREF
{
public:
SPOOLALTREF(HANDLE hspool)
{
pso = (PSPOOLOBJ)HmgShareLock((HOBJ)hspool,SPOOL_TYPE);
}
~SPOOLALTREF()
{
if (bValid())
{
DEC_SHARE_REF_CNT(pso);
pso = NULL;
}
}
};
typedef SPOOLALTREF *PSPOOLALTREF;
class SPOOLMEMOBJ : public SPOOLREF
{
public:
SPOOLMEMOBJ();
~SPOOLMEMOBJ() {}
};
ULONG
ulFinishMessage(
PSPOOLMSG psm,
PSPOOLESC psesc,
PSPOOLALTREF sr,
PBYTE pjEscData,
ULONG cjEscData
);
BOOL AddMessage2Q(
PSPOOLMSG psm,
DWORD dwSpoolInstance
);
VOID SubtractMessageFromQ(PSPOOLMSG psmIn);
/******************************Public*Routine******************************\
*
*
* History:
* 22-May-1995 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/
SPOOLMEMOBJ::SPOOLMEMOBJ()
{
KEVENT *pkevent = (PKEVENT) GdiAllocPoolNonPagedNS(
sizeof(KEVENT), 'lpsG');
if (pkevent == NULL)
{
pso = NULL;
}
else
{
pso = (PSPOOLOBJ)HmgAlloc(sizeof(SPOOLOBJ),SPOOL_TYPE, HMGR_ALLOC_LOCK);
if (bValid())
{
LOCKSPOOLER;
pso->dwSpoolInstance = gdwSpoolInstance;
UNLOCKSPOOLER;
pso->pkevent = pkevent;
KeInitializeEvent(
pso->pkevent,
SynchronizationEvent,
FALSE);
}
else
{
VFREEMEM(pkevent);
}
}
}
/******************************Public*Routine******************************\
*
*
* History:
* 22-May-1995 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/
BOOL SPOOLREF::bDelete()
{
if (bValid())
{
VFREEMEM(pso->pkevent);
HmgFree((HOBJ)hGet());
pso = NULL;
}
return(TRUE);
}
/******************************Public*Routine******************************\
* bIsProcessLocalSystem()
*
* History:
* 19-Jun-2001 -by- Barton House [bhouse]
* Wrote it.
\**************************************************************************/
static BOOL bIsProcessLocalSystem(void)
{
BOOL bResult = FALSE;
PEPROCESS peprocess;
PACCESS_TOKEN paccessToken;
NTSTATUS status;
PTOKEN_USER ptu;
peprocess = PsGetCurrentProcess();
paccessToken = PsReferencePrimaryToken(peprocess);
status = SeQueryInformationToken(paccessToken, TokenUser, (PVOID *) &ptu);
PsDereferencePrimaryToken(paccessToken);
if (NT_SUCCESS(status) == TRUE)
{
bResult = RtlEqualSid(SeExports->SeLocalSystemSid, ptu->User.Sid);
ExFreePool(ptu);
}
return bResult;
}
/******************************Public*Routine******************************\
* GreInitSpool()
*
* History:
* 21-Feb-1995 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/
BOOL NtGdiInitSpool()
{
BOOL bRet = FALSE;
LOCKSPOOLER;
if (gbInitSpool)
{
EngSetLastError(ERROR_INVALID_ACCESS);
WARNING("GreInitSpool - already called\n");
}
else if(!bIsProcessLocalSystem())
{
EngSetLastError(ERROR_INVALID_ACCESS);
WARNING("GreInitSpool - caller is not system\n");
}
else
{
NTSTATUS status;
// intialize the spooler events
gpeventGdiSpool = (PKEVENT) GdiAllocPoolNonPagedNS(
sizeof(KEVENT), 'gdis');
gpeventSpoolerTermination = (PKEVENT) GdiAllocPoolNonPagedNS(
sizeof(KEVENT), 'gdis');
if (gpeventGdiSpool && gpeventSpoolerTermination)
{
KeInitializeEvent(
gpeventGdiSpool,
SynchronizationEvent,
FALSE);
KeInitializeEvent(
gpeventSpoolerTermination,
NotificationEvent,
FALSE);
gbInitSpool = TRUE;
bRet = TRUE;
gpeSpool = PsGetCurrentProcess();
}
else
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
if (gpeventGdiSpool)
{
VFREEMEM(gpeventGdiSpool);
gpeventGdiSpool = NULL;
}
if (gpeventSpoolerTermination)
{
VFREEMEM(gpeventSpoolerTermination);
gpeventSpoolerTermination = NULL;
}
}
gpidSpool = W32GetCurrentProcess();
if (++gdwSpoolInstance == NON_SPOOL_INSTANCE)
++gdwSpoolInstance;
}
UNLOCKSPOOLER;
return(bRet);
}
/******************************Public*Routine******************************\
*
*
* History:
* 01-Jun-1995 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/
VOID vCleanupSpool()
{
if (gpidSpool == W32GetCurrentProcess())
{
//DbgPrint("vCleanupSpool() - cleaning up the spooler process\n");
LOCKSPOOLER;
if (gbInitSpool == TRUE)
{
while (gpsmSpoolHead != NULL)
{
//DbgPrint("vCleanupSpool() - got another\n");
PSPOOLMSG psm = gpsmSpoolHead;
gpsmSpoolHead = psm->pNext;
// and some stats
ASSERTGDI(gcSpoolMsgCurrent > 0, "GreGetSpoolMsg - invalid count\n");
gcSpoolMsgCurrent--;
SPOOLALTREF sr(psm->hso);
if (sr.bValid())
{
KeSetEvent(sr.pso->pkevent,0,FALSE);
}
}
gpsmSpoolTail = NULL;
VFREEMEM(gpeventGdiSpool);
VFREEMEM(gpeventSpoolerTermination);
gpeventGdiSpool = NULL;
gpeventSpoolerTermination = NULL;
gbInitSpool = FALSE;
gpeSpool = NULL;
gcSpoolMsg = 0;
gcSpoolMsgCurrent = 0;
gcSpoolMsgMax = 0;
gpidSpool = NULL;
//DbgPrint("Done cleaning up spooler for this thread\n");
}
UNLOCKSPOOLER;
}
}
/******************************Public*Routine******************************\
* GreGetSpoolMessage()
*
* This is intended to be called from the spooler process (GdiGetSpoolMessage)
* to get the next spooler message out of the kernel.
*
*
* if (output buffer exists)
* copy out output buffer
*
* wait for next message
*
* copy in input buffer
*
* input:
*
* psesc - buffer to place message
* cjmsg - size of message buffer
* pulOut - buffer containing data to be copied to output buffer
* cjOut - size of output buffer
*
*
* returns:
*
* size of data placed in psesc.
*
*
* Note: the output buffer is filled by the calling routine before calling
* this function again.
*
* History:
* 21-Feb-1995 -by- Eric Kutter [erick]
*
* 6-Aug-1995 - Added Input buffer growing (Steve Wilson [swilson])
*
* Wrote it.
\**************************************************************************/
SECURITY_QUALITY_OF_SERVICE Sqos =
{
sizeof(SECURITY_QUALITY_OF_SERVICE),
SecurityImpersonation,
SECURITY_DYNAMIC_TRACKING,
FALSE
};
ULONG GreGetSpoolMessage(
PSPOOLESC psesc,
PBYTE pjEscData, // this must only be accessed under a try/except
ULONG cjEscData,
PULONG pulOut,
ULONG cjOut
)
{
ULONG ulRet = 0;
NTSTATUS status;
LONG lState;
//DbgPrint("Entered GreGetSpoolMessage\n");
if (!gbInitSpool)
{
WARNING("GreGetSpoolMessage - not initialized\n");
}
else if (gpidSpool != W32GetCurrentProcess())
{
WARNING("GreGetSpoolMessage - called from non-spooler process\n");
}
else
{
KEVENT *pkevent = NULL;
// see if we need to copy any data out
if (psesc->hso)
{
SPOOLALTREF sr(psesc->hso);
if (sr.bValid())
{
// we have found the spool obj. Now see if we really have
// an output buffer and need to copy anything.
PSPOOLMSG psm = (PSPOOLMSG)sr.pso->pvMsg;
// if we asked the spooler to grow the buffer and it succeeded
// finish copying the message and return. If it failed, we still
// need to cleanup this message and release the thread.
if (psesc->iMsg == GDISPOOL_INPUT2SMALL)
{
if (psesc->ulRet && psm)
{
return(ulFinishMessage(psm, psesc, &sr, pjEscData, cjEscData));
}
pulOut = NULL;
psesc->ulRet = 0;
}
// if we actualy have a message, we need to copy out the data
// if there is any and remember the event in order to let the
// client continue.
if (psm)
{
// if we are still impersonating the client, undo it
if (psm->pthreadClient)
{
SeStopImpersonatingClient();
SeDeleteClientSecurity(&psm->sccSecurity);
psm->pthreadClient = NULL;
}
// see if we have anything to copy out
if (pulOut && psm->pulOut)
{
if (cjOut > psm->cjOut)
cjOut = psm->cjOut;
__try
{
// this is the only place that pulOut is validated
ProbeForRead(pulOut,cjOut,sizeof(ULONG));
RtlCopyMemory(psm->pulOut,pulOut,cjOut);
psm->cjOut = cjOut;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
psm->cjOut = 0;
}
}
else
{
psm->cjOut = 0;
}
psm->ulRet = psesc->ulRet;
psm->ulErr = EngGetLastError();
// Since to have output the message must have been synchronous,
// we need to let the other thread go now.
pkevent = sr.pso->pkevent;
}
// the spool obj is now done with this message
sr.pso->pvMsg = NULL;
}
else
{
WARNING("GreGetSpoolMessage - hso but no pso\n");
}
}
// the release of the other thread needs to be done once the SPOOLOBJ has
// been unlocked.
if (pkevent)
{
lState = KeSetEvent(pkevent,0,FALSE);
ASSERTGDI(lState == 0,"GreGetSpoolMessage - lState not 0, a\n");
}
// done with the last message. Wait for the next message.
PSPOOLMSG psm = NULL;
BOOL bDone;
LARGE_INTEGER Timeout;
// 600/(100*10(-9)) : negative value is interval, positive is absolute
Timeout.QuadPart = (LONGLONG) -6000000000; // Timeout is in 100 nsec, so 6,000,000,000 == 10 min
do
{
BOOL bGrabIt = TRUE;
bDone = TRUE;
if (gpsmSpoolHead == NULL)
{
LONG bInfiniteWait = InterlockedExchange(&gpInfiniteWait, FALSE);
//DbgPrint("\nGDISPOOL(%lx): waiting for message\n",psesc);
status = KeWaitForSingleObject(
gpeventGdiSpool,
(KWAIT_REASON)WrExecutive,
UserMode,
FALSE,
(bInfiniteWait ? NULL : &Timeout));
if(bInfiniteWait)
InterlockedExchange(&gpInfiniteWait, TRUE);
if (status == STATUS_TIMEOUT)
{
SendSimpleMessage(0, GDISPOOL_TERMINATETHREAD, NON_SPOOL_INSTANCE);
}
else if (status == STATUS_USER_APC) // Upon this return, User mode spooler does not execute
{
KeSetEvent(gpeventSpoolerTermination,0,FALSE);
return (ULONG) -1;
}
else if (!NT_SUCCESS(status))
{
WARNING("GDISPOOL: wait error\n");
bGrabIt = FALSE;
}
}
else
{
//DbgPrint("\nGDISPOOL(%lx): message already there\n",psesc);
}
if (bGrabIt)
{
// now find the message and the spoolobj
LOCKSPOOLER;
if (gpsmSpoolHead == NULL)
{
bDone = FALSE;
}
else
{
psm = gpsmSpoolHead;
gpsmSpoolHead = psm->pNext;
if (gpsmSpoolHead == NULL)
gpsmSpoolTail = NULL;
// and some stats
ASSERTGDI(gcSpoolMsgCurrent > 0, "GreGetSpoolMsg - invalid count\n");
gcSpoolMsgCurrent--;
//DbgPrint(" got a message(%lx), hso = %lx - count = %ld\n",psesc,psm->hso,gcSpoolMsgCurrent);
}
UNLOCKSPOOLER;
}
} while ((psm == NULL) && !bDone);
// did we get a message?
if (psm != NULL)
{
if (psm->iMsg == GDISPOOL_TERMINATETHREAD || psm->iMsg == GDISPOOL_CLOSEPRINTER)
{
// going to terminate the thread so just get out.
ulRet = sizeof(SPOOLESC);
psesc->cj = sizeof(SPOOLESC);
psesc->iMsg = psm->iMsg;
psesc->hso = psm->hso;
if (psm->iMsg & GDISPOOL_API) // Let API messages have a spool handle
{
psesc->hSpool = psm->hso;
if (psm->iMsg & GDISPOOL_CLOSEPRINTER)
{
psesc->hso = NULL;
}
}
psesc->cjOut = 0;
VFREEMEM(psm);
}
else // Got a non-null, non-TERMINATETHREAD message to send to spooler
{
SPOOLALTREF sr(psm->hso);
if (sr.bValid())
{
if (cjEscData < psm->cjIn)
{
// set up the header
ulRet = offsetof(SPOOLESC, ajData);
psesc->cj = sizeof(SPOOLESC);
psesc->iMsg = GDISPOOL_INPUT2SMALL;
psesc->hSpool = sr.pso->hSpool;
psesc->hso = psm->hso;
sr.pso->pvMsg = (PVOID)psm;
// required message buffer size
psesc->cjOut = psm->cjIn + offsetof(SPOOLESC,ajData);
}
else
{
ulRet = ulFinishMessage(psm, psesc, &sr, pjEscData, cjEscData);
}
}
}
}
}
return(ulRet);
}
/******************************Public*Routine******************************\
* ulFinishMessage()
*
* Fills in psesc structure and impersonates client
*
*
* History:
* 8-Aug-95 -by- Steve Wilson [swilson]
* Wrote it.
\**************************************************************************/
ULONG
ulFinishMessage(
PSPOOLMSG psm,
PSPOOLESC psesc, // this must only be accessed under a try/except
PSPOOLALTREF psr,
PBYTE pjEscData,
ULONG cjEscDAta
)
{
NTSTATUS status;
ULONG ulRet;
// impersonate the client
status = SeCreateClientSecurity(
psm->pthreadClient,
&Sqos,
FALSE,
&psm->sccSecurity);
if (NT_SUCCESS(status))
{
status = SeImpersonateClientEx(&psm->sccSecurity,NULL);
}
if (!NT_SUCCESS(status))
{
WARNING("FinishMessage - CreateClientSecurity failed\n");
psm->pthreadClient = NULL;
}
// copy the data
ulRet = 0;
if (psm->cjIn)
{
__try
{
// copy all the buffers into the input buffer
ulRet = 0;
for (DWORD i = 0; i < psm->cBuf; ++i)
{
RtlCopyMemory(pjEscData+ulRet,psm->apulIn[i],psm->acjIn[i]);
ulRet += psm->acjIn[i];
}
ASSERTGDI(ulRet == psm->cjIn,"ulFinishMessage - invalid size\n");
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
ulRet = 0;
}
}
// if the input buffer was only temporary, delete it.
if (psm->fl & SPOOLMSG_DELETE_INBUF)
{
ASSERTGDI(psm->cBuf == 1,"ulFinishMessage - delete more than 1\n");
VFREEMEM(psm->apulIn[0]);
psm->apulIn[0] = NULL;
}
// set up the header
ulRet += offsetof(SPOOLESC,ajData);
psesc->iMsg = psm->iMsg;
psesc->cj = ulRet;
psesc->hSpool = psr->pso->hSpool;
psesc->cjOut = psm->cjOut;
psesc->hso = psm->hso;
psr->pso->pvMsg = (PVOID)psm;
return(ulRet);
}
/******************************Public*Routine******************************\
* GreEscapeSpool()
*
* given a spool message, add it to the queue and notify the spooler thread
*
*
* History:
* 21-Feb-1995 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/
BOOL SPOOLREF::GreEscapeSpool()
{
BOOL bRet = FALSE;
NTSTATUS status;
ASSERTGDI(psm()->iMsg != GDISPOOL_TERMINATETHREAD,"GreEscapeSpool - GDISPOOL_TERMINATETHREAD\n");
ASSERTGDI(psm()->iMsg != GDISPOOL_CLOSEPRINTER,"GreEscapeSpool - GDISPOOL_CLOSEPRINTER\n");
ASSERTGDI(psm() != NULL,"GreEscapeSpool - null\n");
//DbgPrint("Entered GreEscapeSpool\n");
if (!gbInitSpool)
{
EngSetLastError(ERROR_NOT_READY);
WARNING("GreEscapeSpool - not initialized\n");
}
else if (pso->dwFlags & NO_CLIENT)
{
//DbgPrint(" GreEscapeSpool: NO_CLIENT, message received: %x\n",psm()->iMsg);
EngSetLastError(ERROR_PROCESS_ABORTED);
}
else
{ // add the message to the queue
//NOTE: we may want to reference count this in the future
psm()->pthreadClient = PsGetCurrentThread();
if (AddMessage2Q(psm(), pso->dwSpoolInstance))
{
PVOID pEvent[3];
pEvent[0] = gpeSpool;
pEvent[1] = pso->pkevent;
pEvent[2] = gpeventSpoolerTermination;
status = KeWaitForMultipleObjects( 3,
pEvent,
WaitAny,
(KWAIT_REASON)0,
UserMode,
FALSE,
0,
NULL);
switch (status)
{
case 0: // Spooler terminated
SubtractMessageFromQ(psm());
EngSetLastError(ERROR_PROCESS_ABORTED);
ASSERTGDI(cAltLock() == 0,"GreEscapeSpool - invalid lock 0\n");
bRet = FALSE;
break;
case 1: // Spooler returned
bRet = TRUE;
EngSetLastError(psm()->ulErr);
ASSERTGDI(cAltLock() == 0,"GreEscapeSpool - invalid lock 1\n");
break;
case STATUS_USER_APC: // Client terminated
case 2: // Spooler terminated, this client may be the spooler
// Stress Failure Note:
// AddMessage2Q is called above here to add a message to the message queue.
// After the message is in the queue, we leave the spooler lock and set
// gpeventGdiSpool, which wakes up the spooler thread. The spooler thread
// then grabs the message and removes it from the message queue inside the spooler
// lock. It then returns to user mode and creates a new message thread. All this
// typically works fine.
//
// Now suppose just after AddMessage2Q adds a new message and before gpeventGdiSpool
// is set, the spooler shuts down. When the spooler shuts down, STATUS_USER_APC is
// returned from the gpeventGdiSpool Wait and all spooler messaging threads will set
// gpeventSpoolerTermination, which is case 2 in this switch statement. This wakes
// up the client thread, which proceeds to terminate and frees the psm before exiting.
// Meanwhile, the spooler is down to its very last thread and calls vCleanupSpool, which
// traverses and frees the psm queue. vCleanupSpool will AV when it hits one of the freed
// messages in the queue.
//
// The problem was rarely encountered because it would only occur if the spooler shut down
// after a message was entered in the queue and before the gpeventGdiSpool event was seen.
//
// Removing the message from the queue when the spooler or client terminates should solve
// the problem. Note that this was always done for the STATUS_USER_APC case, and has now
// been added to case 0 and case 2.
//
// Steve Wilson (NT)
//
SubtractMessageFromQ(psm());
WARNING("Win32K spool: Client is dying!\n");
pso->dwFlags |= NO_CLIENT;
EngSetLastError(ERROR_PROCESS_ABORTED);
bRet = FALSE;
// we need to make sure there is no alt lock
pso->pvMsg = NULL;
while (cAltLock())
KeDelayExecutionThread(KernelMode,FALSE,gpLockShortDelay);
break;
default:
#if 1
DbgPrint("GreEscapeSpool - WaitForSingleObject failed w/ status 0x%lx\n", status);
DbgBreakPoint();
#else
WARNING("GreEscapeSpool - WaitForSingleObject failed\n");
#endif
break;
}
}
}
return bRet;
}
/******************************Public*Routine******************************\
* SendSimpleMessage()
*
* allow a client app to send a spool message
*
*
* History:
* 21-Feb-1995 -by- Eric Kutter [erick]
* 7-Jun-1996 - Steve Wilson [SWilson] - Changed from NtGdiSpoolEsc to SendSimpleMessage
*
\**************************************************************************/
ULONG SendSimpleMessage(
HANDLE hSpool,
ULONG iMsg,
DWORD dwSpoolInstance)
{
ULONG ulRet = 0;
if (!gbInitSpool)
{
WARNING("GreEscapeSpool - not initialized\n");
}
else if (iMsg == GDISPOOL_TERMINATETHREAD || iMsg == GDISPOOL_CLOSEPRINTER)
{
PSPOOLMSG psm = (PSPOOLMSG) PALLOCMEM(sizeof(SPOOLMSG),'lpsG');
if (psm)
{
psm->cj = sizeof(SPOOLMSG);
psm->iMsg = iMsg;
psm->hso = (HSPOOLOBJ) hSpool; // This is Spooler's handle, not kernel handle
ulRet = AddMessage2Q(psm, dwSpoolInstance);
if (ulRet == FALSE)
VFREEMEM(psm);
}
}
return ulRet;
}
/*****************************
SubtractMessageFromQ()
*****************************/
VOID SubtractMessageFromQ(PSPOOLMSG psmIn)
{
PSPOOLMSG psm;
PSPOOLMSG psmPrev = NULL;
//DbgPrint("Enter SubtractMessageFromQ\n");
LOCKSPOOLER;
for (psm = gpsmSpoolHead ; psm ; psmPrev = psm , psm = psm->pNext)
{
if (psm == psmIn)
{
if (psmPrev)
{
psmPrev->pNext = psm->pNext;
if (!psmPrev->pNext)
{
ASSERTGDI(psm == gpsmSpoolTail,"SubtractMessageFromQ: Bad gpsmSpoolTail!\n");
gpsmSpoolTail = psmPrev;
}
}
else
{
gpsmSpoolHead = psm->pNext;
if (!gpsmSpoolHead)
{
ASSERTGDI(psm == gpsmSpoolTail,"SubtractMessageFromQ: Bad gpsmSpoolTail!\n");
gpsmSpoolTail = gpsmSpoolHead;
}
}
// gcSpool stuff is for debug purposes only
gcSpoolMsgCurrent--;
break;
}
}
UNLOCKSPOOLER;
}
/******************************Public*Routine******************************\
* BOOL AddMessage2Q (PSPOOLMSG psm)
*
* History:
* 6-Aug-1995 -by- Steve Wilson [swilson]
*
\**************************************************************************/
BOOL AddMessage2Q(PSPOOLMSG psm, DWORD dwSpoolInstance)
{
BOOL bRet = FALSE;
// add the message to the queue
LOCKSPOOLER;
if (psm == NULL)
{
bRet = FALSE;
}
else if ((dwSpoolInstance != gdwSpoolInstance) && (dwSpoolInstance != NON_SPOOL_INSTANCE))
{
EngSetLastError(ERROR_INVALID_HANDLE);
bRet = FALSE;
}
else
{
if (gpsmSpoolTail)
{
// list isn't empty, so add it to the list
ASSERTGDI(gpsmSpoolHead != NULL,"GreEscapeSpool - head is null\n");
gpsmSpoolTail->pNext = psm;
}
else
{
ASSERTGDI(gpsmSpoolHead == NULL,"GreEscapeSpool - head not null\n");
gpsmSpoolHead = psm;
}
// the tail now always points to the new element
gpsmSpoolTail = psm;
psm->pNext = NULL;
// and some stats
gcSpoolMsg++;
gcSpoolMsgCurrent++;
if (gcSpoolMsgCurrent > gcSpoolMsgMax)
gcSpoolMsgMax = gcSpoolMsgCurrent;
bRet = TRUE;
}
UNLOCKSPOOLER;
// notify the spooler that there is a message ready
if (bRet == TRUE)
{
KeSetEvent(gpeventGdiSpool,0,FALSE);
}
return bRet;
}
/******************************Public*Routine******************************\
* GreOpenPrinterW()
*
* History:
* 28-Mar-1995 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/
BOOL
WINAPI
GreOpenPrinterW(
GREOPENPRINTER *pOpenPrinter,
LPHANDLE phPrinter)
{
ULONG bRet;
ULONG ul;
SPOOLMEMOBJ spmo; // Creates (PSPOOLOBJ) spmo.pso, containing pkevent, hSpool, pvMsg
//DbgPrint("Enter GreOpenPrinterW\n");
if (spmo.bValid())
{
// setup the message
PSPOOLMSG psm = spmo.psm();
psm->cj = sizeof(SPOOLMSG);
psm->iMsg = GDISPOOL_OPENPRINTER;
psm->fl = 0;
psm->cBuf = 1;
psm->cjIn = pOpenPrinter->cj;
psm->acjIn[0] = pOpenPrinter->cj;
psm->apulIn[0]= (PULONG)pOpenPrinter;
psm->pulOut = (PULONG)&spmo.pso->hSpool;
psm->cjOut = sizeof(*phPrinter);
psm->hso = spmo.hGet(); // returns ((HSPOOLOBJ) pso)
if (spmo.GreEscapeSpool() && psm->ulRet)
{
*phPrinter = (HANDLE)spmo.hGet();
bRet = TRUE;
}
else
{
spmo.bDelete();
bRet = FALSE;
}
}
else
{
bRet = FALSE;
}
return(bRet);
}
/*****************************************************************************
* GreEnumFormsW
*
* History:
* 25/7/95 by Steve Wilson [swilson]
* Wrote it.
*******************************************************************************/
BOOL
WINAPI
GreEnumFormsW(
HANDLE hSpool,
GREENUMFORMS *pEnumForms,
GREENUMFORMS *pEnumFormsReturn,
LONG cjOut )
{
ULONG bRet = FALSE;
SPOOLREF sr(hSpool);
if (sr.bValid())
{
PSPOOLMSG psm = sr.psm();
//DbgPrint("Enter GreEnumFormsW\n");
// setup the message
psm->cj = sizeof(SPOOLMSG);
psm->iMsg = GDISPOOL_ENUMFORMS;
psm->fl = 0;
psm->cBuf = 1;
psm->cjIn = pEnumForms->cj;
psm->acjIn[0] = pEnumForms->cj;
psm->apulIn[0]= (PULONG) pEnumForms;
psm->pulOut = (PULONG) pEnumFormsReturn;
psm->cjOut = cjOut;
psm->hso = (HSPOOLOBJ) hSpool;
bRet = sr.GreEscapeSpool() ? psm->ulRet : FALSE;
}
return bRet;
}
/*****************************************************************************
* GreGenericW
*
* History:
* 25-Jul-95 by Steve Wilson [swilson]
* Wrote it.
*******************************************************************************/
BOOL
GreGenericW(
HANDLE hSpool,
PULONG pX,
PULONG pXReturn,
LONG cjOut,
LONG MessageID,
ULONG ulFlag )
{
ULONG bRet = FALSE;
SPOOLREF sr(hSpool);
if (sr.bValid())
{
PSPOOLMSG psm = sr.psm();
//DbgPrint("Enter GreGenericW\n");
// setup the message
psm->cj = sizeof(SPOOLMSG);
psm->iMsg = MessageID;
psm->fl = ulFlag;
psm->cBuf = 1;
psm->cjIn = pX ? *(PULONG) pX : 0; // Must be sure first element of pX is LONG cj: (pX->cj)
psm->acjIn[0] = pX ? *(PULONG) pX : 0; // Must be sure first element of pX is LONG cj: (pX->cj)
psm->apulIn[0]= (PULONG) pX;
psm->pulOut = (PULONG) pXReturn;
psm->cjOut = cjOut;
psm->hso = (HSPOOLOBJ) hSpool;
bRet = sr.GreEscapeSpool() ? psm->ulRet : FALSE;
}
return bRet;
}
/*****************************************************************************\
* GrePrinterDriverUnloadW
*
* This function is used to a send a message to the spooler when a printer
* driver is unloaded (i.e the DC count on the driver goes to zero)
* On receipt of this message, the spooler attempts to upgrade the driver if
* neccesary.
*
* History:
* 11/17/97 by Ramanathan N Venkatapathy
* Wrote it.
\*****************************************************************************/
BOOL GrePrinterDriverUnloadW(
LPWSTR pDriverName
)
{
BOOL bRet = FALSE;
ULONG cbDriverName;
LPWSTR pDriverFile = NULL;
SPOOLMEMOBJ spmo;
// Check for invalid printer driver names.
if (!pDriverName || !*pDriverName)
{
return bRet;
}
// Copy the driver name into another buffer.
cbDriverName = (wcslen(pDriverName) + 1) * sizeof(WCHAR);
pDriverFile = (LPWSTR) PALLOCMEM(cbDriverName, 'lpsG');
if (spmo.bValid() && pDriverFile)
{
PSPOOLMSG psm = spmo.psm();
memcpy(pDriverFile, pDriverName, cbDriverName);
psm->cj = sizeof(SPOOLMSG);
psm->iMsg = GDISPOOL_UNLOADDRIVER_COMPLETE;
psm->fl = 0;
psm->cBuf = 1;
psm->cjIn = cbDriverName;
psm->apulIn[0] = (PULONG) pDriverFile;
psm->acjIn[0] = cbDriverName;
psm->pulOut = NULL;
psm->cjOut = 0;
psm->hso = spmo.hGet();
bRet = spmo.GreEscapeSpool() && psm->ulRet;
spmo.bDelete();
}
if (pDriverFile) {
VFREEMEM(pDriverFile);
}
return bRet;
}
/*****************************************************************************
* GreGetPrinterDriverW
*
* History:
* 4/14/1995 by Gerrit van Wingerden [gerritv]
* Wrote it.
*******************************************************************************/
BOOL WINAPI GreGetPrinterDriverW(
HANDLE hSpool,
GREGETPRINTERDRIVER *pGetPrinterDriver,
GREGETPRINTERDRIVER *pGetPrinterDriverReturn,
LONG cjOut
)
{
ULONG bRet = FALSE;
SPOOLREF sr(hSpool);
if (sr.bValid())
{
PSPOOLMSG psm = sr.psm();
//DbgPrint("Enter GreGetPrinterDriverW\n");
// setup the message
psm->cj = sizeof(SPOOLMSG);
psm->iMsg = GDISPOOL_GETPRINTERDRIVER;
psm->fl = 0;
psm->cBuf = 1;
psm->cjIn = pGetPrinterDriver->cj;
psm->acjIn[0] = pGetPrinterDriver->cj;
psm->apulIn[0]= (PULONG) pGetPrinterDriver;
psm->pulOut = (PULONG)pGetPrinterDriverReturn;
psm->cjOut = cjOut;
psm->hso = (HSPOOLOBJ)hSpool;
if( sr.GreEscapeSpool() )
{
bRet = psm->ulRet;
}
else
{
bRet = FALSE;
}
}
return(bRet);
}
/****************************************************************************
* BOOL StartPagePrinter( HANDLE hPrinter )
*
* History:
* 4/28/1995 by Gerrit van Wingerden [gerritv]
* Wrote it.
*****************************************************************************/
BOOL
WINAPI
StartPagePrinter(
HANDLE hSpool
)
{
BOOL bRet = FALSE;
SPOOLREF sr(hSpool);
if (sr.bValid())
{
PSPOOLMSG psm = sr.psm();
//DbgPrint("Enter StartPagePrinter\n");
psm->cj = sizeof(SPOOLMSG);
psm->iMsg = GDISPOOL_STARTPAGEPRINTER;
psm->fl = 0;
psm->cBuf = 0;
psm->cjIn = 0;
psm->pulOut = NULL;
psm->cjOut = 0;
psm->hso = (HSPOOLOBJ)hSpool;
if( sr.GreEscapeSpool() )
{
bRet = psm->ulRet;
}
}
return(bRet);
}
/****************************************************************************
* BOOL EndPagePrinter( HANDLE hPrinter )
*
* History:
* 4/28/1995 by Gerrit van Wingerden [gerritv]
* Wrote it.
*****************************************************************************/
BOOL
WINAPI
EndPagePrinter(
HANDLE hSpool
)
{
BOOL bRet = FALSE;
SPOOLREF sr(hSpool);
if (sr.bValid())
{
PSPOOLMSG psm = sr.psm();
//DbgPrint("Enter EndPagePrinter\n");
psm->cj = sizeof(SPOOLMSG);
psm->iMsg = GDISPOOL_ENDPAGEPRINTER;
psm->fl = 0;
psm->cBuf = 0;
psm->cjIn = 0;
psm->pulOut = NULL;
psm->cjOut = 0;
psm->hso = (HSPOOLOBJ)hSpool;
if(sr.GreEscapeSpool())
{
bRet = psm->ulRet;
}
}
return(bRet);
}
/****************************************************************************
* BOOL EndDocPrinter( HANDLE hPrinter )
*
* History:
* 4/28/1995 by Gerrit van Wingerden [gerritv]
* Wrote it.
*****************************************************************************/
BOOL
WINAPI
EndDocPrinter(
HANDLE hSpool
)
{
BOOL bRet = FALSE;
SPOOLREF sr(hSpool);
if (sr.bValid())
{
PSPOOLMSG psm = sr.psm();
//DbgPrint("Enter EndDocPrinter\n");
psm->cj = sizeof(SPOOLMSG);
psm->iMsg = GDISPOOL_ENDDOCPRINTER;
psm->fl = 0;
psm->cBuf = 0;
psm->cjIn = 0;
psm->pulOut = NULL;
psm->cjOut = 0;
psm->hso = (HSPOOLOBJ)hSpool;
if( sr.GreEscapeSpool() )
{
bRet = (DWORD) psm->ulRet;
}
}
return(bRet);
}
/****************************************************************************
* ClosePrinter( HANDLE hPrinter )
*
* History:
* 12-Feb-1996 -by- Steve Wilson (swilson)
* Wrote it.
*****************************************************************************/
BOOL
WINAPI
ClosePrinter(
HANDLE hSpool
)
{
SPOOLREF sr(hSpool);
if (sr.bValid())
{
//DbgPrint("Enter ClosePrinter: %d\n", sr.pso->hSpool);
SendSimpleMessage(sr.pso->hSpool, GDISPOOL_CLOSEPRINTER, sr.pso->dwSpoolInstance);
sr.bDelete();
}
return TRUE;
}
/******************************Public*Routine******************************\
* AbortPrinter()
*
* History:
* 18-Jul-1995 -by- Steve Wilson (swilson)
* Wrote it.
\**************************************************************************/
BOOL
WINAPI
AbortPrinter(
HANDLE hPrinter)
{
BOOL bRet = FALSE;
SPOOLREF sr(hPrinter);
if (sr.bValid())
{
PSPOOLMSG psm = sr.psm();
//DbgPrint("Enter AbortPrinter\n");
psm->cj = sizeof(SPOOLMSG);
psm->iMsg = GDISPOOL_ABORTPRINTER;
psm->fl = 0;
psm->cBuf = 0;
psm->cjIn = 0;
psm->pulOut = NULL;
psm->cjOut = 0;
psm->hso = (HSPOOLOBJ)hPrinter;
if( sr.GreEscapeSpool() )
{
bRet = (DWORD) psm->ulRet;
}
}
return(bRet);
}
/****************************************************************************
* StartDocPrinter()
*
* History:
* 4/28/1995 by Gerrit van Wingerden [gerritv]
* Wrote it.
*****************************************************************************/
DWORD
WINAPI
StartDocPrinterW(
HANDLE hPrinter,
DWORD dwLevel,
LPBYTE lpbDocInfo
)
{
LONG cj = offsetof(GRESTARTDOCPRINTER,alData);
LONG cjDocName = 0;
LONG cjOutputFile = 0;
LONG cjDatatype = 0;
DOC_INFO_1W* pDocInfo = (DOC_INFO_1W*) lpbDocInfo;
DWORD ret = 0;
//DbgPrint("Enter StartDocPrinterW\n");
ASSERTGDI( dwLevel == 1, "StartDocPrinter: dwLevel != 1\n" );
ASSERTGDI( lpbDocInfo != NULL, "StarDocPrinter lpbDocInfo is NULL\n");
// first we need to compute the sizes.
if (pDocInfo->pDocName)
{
cjDocName = (wcslen(pDocInfo->pDocName) + 1) * sizeof(WCHAR);
cj += cjDocName;
cj = (cj + 3) & ~3;
}
if (pDocInfo->pOutputFile)
{
cjOutputFile = (wcslen(pDocInfo->pOutputFile) + 1) * sizeof(WCHAR);
cj += cjOutputFile;
cj = (cj + 3) & ~3;
}
if (pDocInfo->pDatatype)
{
cjDatatype = (wcslen(pDocInfo->pDatatype) + 1) * sizeof(WCHAR);
cj += cjDatatype;
cj = (cj + 3) & ~3;
}
SPOOLREF sr(hPrinter);
if (sr.bValid())
{
PLONG plData;
GRESTARTDOCPRINTER *pStartDocPrinter;
pStartDocPrinter = (GRESTARTDOCPRINTER*)PALLOCNOZ(cj,'lpsG');
if (pStartDocPrinter)
{
PSPOOLMSG psm = sr.psm();
// we got memory, now copy the stuff in
pStartDocPrinter->cj = cj;
plData = pStartDocPrinter->alData;
pStartDocPrinter->cjDocName = (cjDocName + 3) & ~3;
pStartDocPrinter->cjOutputFile = (cjOutputFile + 3) & ~3;
pStartDocPrinter->cjDatatype = (cjDatatype + 3) & ~3;
if (pDocInfo->pDocName)
{
memcpy(plData,pDocInfo->pDocName,cjDocName);
plData += (cjDocName+3)/4;
}
if (pDocInfo->pOutputFile)
{
memcpy(plData,pDocInfo->pOutputFile,cjOutputFile);
plData += (cjOutputFile+3)/4;
}
if (pDocInfo->pDatatype)
{
memcpy(plData,pDocInfo->pDatatype,cjDatatype);
plData += (cjDatatype+3)/4;
}
ASSERTGDI(POFFSET(pStartDocPrinter,plData) == cj,
"EngStartDocPrinter - sizes are wrong\n");
// pStartDocPrinter now contains all needed data, call out
// setup the message
psm->cj = sizeof(SPOOLMSG);
psm->iMsg = GDISPOOL_STARTDOCPRINTER;
psm->fl = 0;
psm->cBuf = 1;
psm->cjIn = pStartDocPrinter->cj;
psm->acjIn[0] = pStartDocPrinter->cj;
psm->apulIn[0]= (PULONG)pStartDocPrinter;
psm->pulOut = NULL;
psm->cjOut = 0;
psm->hso = (HSPOOLOBJ)hPrinter;
if( sr.GreEscapeSpool() )
{
ret = (DWORD) psm->ulRet;
}
VFREEMEM(pStartDocPrinter);
}
}
return(ret);
}
/******************************Public*Routine******************************\
* OpenPrinterW()
*
* History:
* 05-Apr-1995 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/
BOOL
WINAPI
OpenPrinterW(
LPWSTR pPrinterName,
LPHANDLE phPrinter,
LPPRINTER_DEFAULTSW pDefault)
{
LONG cjName = 0;
LONG cjDatatype = 0;
LONG cjDevMode = 0;
LONG cj = offsetof(GREOPENPRINTER,alData);
BOOL bRet = FALSE;
PLONG plData;
GREOPENPRINTER *pOpenPrinter;
//DbgPrint("Enter OpenPrinterW\n");
// first we need to compute the sizes.
if (pPrinterName)
{
cjName = (wcslen(pPrinterName) + 1) * sizeof(WCHAR);
cj += cjName;
cj = (cj + 3) & ~3;
}
if (pDefault)
{
if (pDefault->pDatatype)
{
cjDatatype = (wcslen(pDefault->pDatatype) + 1) * sizeof(WCHAR);
cj += cjDatatype;
cj = (cj + 3) & ~3;
}
if (pDefault->pDevMode)
{
//DbgPrint("cjMinDevmode = %d, dmSize = %d, dmDriverExtra = %d\n", MIN_DEVMODE_SIZE, pDefault->pDevMode->dmSize, pDefault->pDevMode->dmDriverExtra);
cjDevMode = pDefault->pDevMode->dmSize + pDefault->pDevMode->dmDriverExtra;
if (cjDevMode < MIN_DEVMODE_SIZE)
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return bRet;
}
cj += cjDevMode;
cj = (cj + 3) & ~3;
}
}
// allocate the memory
pOpenPrinter = (GREOPENPRINTER*)PALLOCNOZ(cj,'lpsG');
if (pOpenPrinter)
{
// we got memory, now copy the stuff in
pOpenPrinter->cj = cj;
pOpenPrinter->pd = *pDefault;
plData = pOpenPrinter->alData;
pOpenPrinter->cjName = (cjName + 3) & ~3;
pOpenPrinter->cjDatatype = (cjDatatype + 3) & ~3;
pOpenPrinter->cjDevMode = (cjDevMode + 3) & ~3;
if (pPrinterName)
{
memcpy(plData,pPrinterName,cjName);
plData += (cjName+3)/4;
}
if (pDefault)
{
if (pDefault->pDatatype)
{
pOpenPrinter->pd.pDatatype = (WCHAR*)POFFSET(pOpenPrinter,plData);
memcpy(plData,pDefault->pDatatype,cjDatatype);
plData += (cjDatatype+3)/4;
}
if (pDefault->pDevMode)
{
pOpenPrinter->pd.pDevMode = (PDEVMODEW)POFFSET(pOpenPrinter,plData);
memcpy(plData,pDefault->pDevMode,cjDevMode);
plData += (cjDevMode+3)/4;
}
}
ASSERTGDI(POFFSET(pOpenPrinter,plData) == cj,
"EngOpenPrinter - sizes are wrong\n");
// pOpenPrinter now contains all needed data, call out
bRet = GreOpenPrinterW(pOpenPrinter,phPrinter);
VFREEMEM(pOpenPrinter);
}
return(bRet);
}
/*******************************************************************************
* void MarshallUpStructure( LPBYTE lpStructure, LPDWORD lpOffsets )
*
* This routine does pointer adjustment to offsets within the buffer
*
* History:
* 6/30/1995 by Muhunthan Sivapragasam (MuhuntS)
* Got from spoolss code
*******************************************************************************/
void
MarshallUpStructure(
LPBYTE lpStructure,
LPDWORD lpOffsets
)
{
register DWORD i=0;
while (lpOffsets[i] != -1) {
if ((*(LPBYTE *)(lpStructure+lpOffsets[i]))) {
(*(LPBYTE *)(lpStructure+lpOffsets[i]))+=(ULONG_PTR)lpStructure;
}
i++;
}
}
/*******************************************************************************
* BOOL ValidateString( LPWSTR pString, PBYTE pBuffer, LONG cjLength )
*
* This routine validates a LPWSTR to make sure that it really lies inside of a buffer.
*
* History:
* 4/19/1995 by Gerrit van Wingerden [gerritv]
* Wrote it.
*******************************************************************************/
BOOL
ValidateString(
LPWSTR pString,
PBYTE pBuffer,
LONG cjLength )
{
LPWSTR pEnd = (LPWSTR) ( pBuffer + cjLength );
if( pString > pEnd || pString < (LPWSTR) pBuffer )
{
return(FALSE);
}
while( pString < pEnd && *pString)
{
pString++;
}
return( pString < pEnd );
}
/*******************************************************************************
* BOOL ValidateStrings( LPBYTE lpStructure, LPDWORD lpOffsets, LONG cjLength )
*
* This routine validates all the strings in the structure to make sure they really lie inside of a buffer.
*
* History:
* 6/30/1995 by Muhunthan Sivapragasam (MuhuntS)
* Wrote it.
*******************************************************************************/
BOOL
ValidateStrings(
LPBYTE lpStructure,
LPDWORD lpOffsets,
LONG cjLength
)
{
register DWORD i=0;
while (lpOffsets[i] != -1)
{
if ( (*(LPWSTR *) (lpStructure+lpOffsets[i])) &&
!ValidateString(*(LPWSTR *) (lpStructure+lpOffsets[i]),
lpStructure,
cjLength) )
{
return FALSE;
}
i++;
}
return TRUE;
}
/*******************************************************************************
* BOOL ValidateDependentFiles( LPWSTR pString, PBYTE pBuffer, LONG cjLength )
*
* This routine validates DependentFiles field (which is a list of strings
* up to \0\0) to make sure that it really lies inside of a buffer.
*
* History:
* 6/30/1995 by Muhunthan Sivapragasam
* Wrote it.
*******************************************************************************/
BOOL
ValidateDependentFiles(
LPWSTR pString,
PBYTE pBuffer,
LONG cjLength )
{
LPWSTR pEnd = (LPWSTR) (pBuffer + cjLength) - 1;
if (pString < (LPWSTR) pBuffer)
{
return(FALSE);
}
while (pString < pEnd)
{
if (*pString==0 && *(pString+1)==0) return TRUE;
pString++;
}
return (FALSE);
}
/*******************************************************************************
* EngEnumForms()
*
* History:
* 7/24/1995 by Steve Wilson [swilson]
* Wrote it.
*******************************************************************************/
BOOL
WINAPI
EngEnumForms(
HANDLE hPrinter,
DWORD dwLevel,
LPBYTE lpbForms,
DWORD cbBuf,
LPDWORD pcbNeeded,
LPDWORD pcbReturned
)
{
LONG cj;
DWORD cjReturn;
BOOL bRet = FALSE;
GREENUMFORMS *pEnumForms, *pEnumFormsReturn;
//DbgPrint("Enter EngEnumFormsW\n");
if(!pcbNeeded || !pcbReturned)
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
cj = sizeof(GREENUMFORMS);
// Allocate TO buffer
pEnumForms = (GREENUMFORMS *) PALLOCMEM(cj, 'lpsG');
// Marshall TO buffer
if (pEnumForms) {
pEnumForms->cj = cj;
pEnumForms->cjData = cbBuf;
pEnumForms->dwLevel = dwLevel;
// Allocate RETURN buffer
pEnumFormsReturn = (GREENUMFORMS *) PALLOCNOZ(cjReturn = sizeof(GREENUMFORMS) + cbBuf, 'lpsG');
// SEND MESSAGE
if (pEnumFormsReturn) {
bRet = GreEnumFormsW(hPrinter, pEnumForms, pEnumFormsReturn, cjReturn);
// Fill in return sizes
*pcbNeeded = pEnumFormsReturn->cjData; // # return data bytes
*pcbReturned = pEnumFormsReturn->nForms; // # forms in return data
// UnMarshall Message
if (bRet) {
ASSERTGDI(*pcbNeeded <= cbBuf,"EnumFormsW - error\n");
if (lpbForms && *pcbNeeded <= cbBuf) {
// Copy returned data into supplied FORM_INFO_1 structure
memcpy(lpbForms, pEnumFormsReturn->alData, pEnumFormsReturn->cjData);
DWORD i;
for (i = 0 ; i < *pcbReturned ; ++i, lpbForms += sizeof(FORM_INFO_1W)) {
MarshallUpStructure(lpbForms, FormInfo1Offsets);
}
}
}
VFREEMEM(pEnumFormsReturn);
}
VFREEMEM(pEnumForms);
}
return bRet;
}
/*******************************************************************************
* EngGetPrinter()
*
* History:
* 9/30/1995 by Steve Wilson [swilson]
* Wrote it.
*******************************************************************************/
BOOL
WINAPI
EngGetPrinter(
HANDLE hPrinter,
DWORD dwLevel,
LPBYTE pPrinter,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
LONG cj;
DWORD cjReturn;
BOOL bRet = FALSE;
GREGETPRINTER *pGetPrinter, *pGetPrinterReturn;
DWORD *pOffsets;
//DbgPrint("Enter EngGetPrinter\n");
if (!pcbNeeded)
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
switch (dwLevel)
{
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:
EngSetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
cj = sizeof(GREGETPRINTER);
// Allocate TO buffer
pGetPrinter = (GREGETPRINTER *) PALLOCMEM(cj, 'lpsG');
// Marshall TO buffer
if (pGetPrinter)
{
pGetPrinter->cj = cj;
pGetPrinter->cjData = cbBuf;
pGetPrinter->dwLevel = dwLevel;
// Allocate RETURN buffer
pGetPrinterReturn = (GREGETPRINTER *) PALLOCNOZ(cjReturn = sizeof(GREGETPRINTER) + cbBuf, 'lpsG');
// SEND MESSAGE
if (pGetPrinterReturn)
{
bRet = GreGenericW(hPrinter, (PULONG) pGetPrinter, (PULONG) pGetPrinterReturn, cjReturn, GDISPOOL_GETPRINTER, 0);
// Fill in return size
*pcbNeeded = pGetPrinterReturn->cjData;
// UnMarshall Message
if (bRet)
{
ASSERTGDI(*pcbNeeded <= cbBuf,"EngGetPrinter - error\n");
if (pPrinter && *pcbNeeded <= cbBuf)
{
memcpy(pPrinter, pGetPrinterReturn->alData, pGetPrinterReturn->cjData);
MarshallUpStructure(pPrinter, pOffsets);
}
}
VFREEMEM(pGetPrinterReturn);
}
VFREEMEM(pGetPrinter);
}
return bRet;
}
/*******************************************************************************
* EngGetForm()
*
* History:
* 7/24/1995 by Steve Wilson [swilson]
* Wrote it.
*******************************************************************************/
BOOL
WINAPI
EngGetForm(
HANDLE hPrinter,
LPWSTR pFormName,
DWORD dwLevel,
LPBYTE lpbForm,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
LONG cj, cjFormName;
DWORD cjReturn;
BOOL bRet = FALSE;
GREGETFORM *pGetForm, *pGetFormReturn;
//DbgPrint("Enter EngGetForm\n");
if (!pcbNeeded)
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
// Accumulate sizes of base struct plus strings
cj = sizeof(GREGETFORM);
cj += (cjFormName = pFormName ? (wcslen(pFormName) + 1)*sizeof *pFormName : 0);
// Allocate TO buffer
pGetForm = (GREGETFORM *) PALLOCMEM(cj, 'lpsG');
// Marshall TO buffer
if (pGetForm)
{
pGetForm->cj = cj;
pGetForm->cjData = cbBuf;
pGetForm->dwLevel = dwLevel;
if (pFormName)
{
memcpy(pGetForm->alData,pFormName,cjFormName);
}
// Allocate RETURN buffer
pGetFormReturn = (GREGETFORM *) PALLOCNOZ(cjReturn = sizeof(GREGETFORM) + cbBuf, 'lpsG');
// SEND MESSAGE
if (pGetFormReturn)
{
bRet = GreGenericW(hPrinter, (PULONG) pGetForm, (PULONG) pGetFormReturn, cjReturn, GDISPOOL_GETFORM, 0);
if (bRet)
{
// Fill in return sizes
*pcbNeeded = pGetFormReturn->cjData; // # return data bytes
// UnMarshall Message
if (lpbForm && bRet)
{
if (*pcbNeeded <= cbBuf)
memcpy(lpbForm, pGetFormReturn->alData, pGetFormReturn->cjData);
else
ASSERTGDI(*pcbNeeded <= cbBuf,"GetFormW - error\n");
MarshallUpStructure(lpbForm, FormInfo1Offsets);
}
}
VFREEMEM(pGetFormReturn);
}
VFREEMEM(pGetForm);
}
return bRet;
}
/*******************************************************************************
* GetPrinterDriverW()
*
* History:
* 4/19/1995 by Gerrit van Wingerden [gerritv]
* Wrote it.
*******************************************************************************/
BOOL WINAPI GetPrinterDriverW(
HANDLE hPrinter,
LPWSTR pEnvironment,
DWORD dwLevel,
LPBYTE lpbDrvInfo,
DWORD cbBuf,
LPDWORD pcbNeeded
)
{
BOOL bRet = FALSE;
LONG cj, cjEnvironment;
DWORD *pOffsets, *pStrings;
//DbgPrint("Enter GetPrinterDriverW\n");
if (!pcbNeeded)
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
cj = sizeof(GREGETPRINTERDRIVER);
cjEnvironment = 0;
*pcbNeeded = 0;
if( pEnvironment )
{
cjEnvironment += (wcslen(pEnvironment) + 1) * sizeof(WCHAR);
}
cj += cjEnvironment;
GREGETPRINTERDRIVER *pGetPrinterDriver;
pGetPrinterDriver = (GREGETPRINTERDRIVER*)PALLOCMEM(cj, 'lpsG');
if( pGetPrinterDriver )
{
pGetPrinterDriver->cj = cj;
pGetPrinterDriver->cjData = cbBuf;
pGetPrinterDriver->dwLevel = dwLevel;
if( pEnvironment )
{
memcpy(pGetPrinterDriver->alData,pEnvironment,cjEnvironment);
}
GREGETPRINTERDRIVER *pGetPrinterDriverReturn = NULL;
UINT cjSize = cbBuf + sizeof(GREGETPRINTERDRIVER);
pGetPrinterDriverReturn = (GREGETPRINTERDRIVER*) PALLOCNOZ(cjSize, 'lpsG');
if( pGetPrinterDriverReturn )
{
bRet = GreGetPrinterDriverW(hPrinter,pGetPrinterDriver, pGetPrinterDriverReturn, cjSize );
DWORD cjData = pGetPrinterDriverReturn->cjData;
if (bRet == FALSE)
{
if (cjData > cbBuf)
{
// need to return the size needed.
*pcbNeeded = pGetPrinterDriverReturn->cjData;
}
}
else
{
ASSERTGDI(cjData <= cbBuf,"GetPrinterDriverW - error\n");
// return the size needed whether everything fits or not
*pcbNeeded = cjData;
// only copy data and return success if everything fits
switch (dwLevel) {
case 1:
pOffsets = DriverInfo1Offsets;
pStrings = DriverInfo1Strings;
break;
case 2:
pOffsets = DriverInfo2Offsets;
pStrings = DriverInfo2Strings;
break;
case 3:
pOffsets = DriverInfo3Offsets;
pStrings = DriverInfo3Strings;
break;
default:
// We should not get here
ASSERTGDI(0, "GetPrinterDriverW invalid level\n");
}
if (lpbDrvInfo)
{
memcpy( lpbDrvInfo, pGetPrinterDriverReturn->alData, cjData );
MarshallUpStructure((LPBYTE)lpbDrvInfo, pOffsets);
if ( !ValidateStrings((LPBYTE)lpbDrvInfo, pStrings, cjData) ||
(dwLevel == 3 &&
((PDRIVER_INFO_3W) lpbDrvInfo)->pDependentFiles &&
!ValidateDependentFiles(((PDRIVER_INFO_3W) lpbDrvInfo)->pDependentFiles,
(LPBYTE)lpbDrvInfo, cjData) ) ) {
WARNING("GetPrinterDriverW: String does not fit in buffer\n");
bRet = FALSE;
}
}
}
VFREEMEM(pGetPrinterDriverReturn);
}
VFREEMEM(pGetPrinterDriver);
}
return(bRet);
}
/******************************Public*Routine******************************\
*
* Routine Name:
*
* EngGetPrinterDriver
*
* Routine Description:
*
* Arguments:
*
* Called by:
*
* Return Value:
*
\**************************************************************************/
extern "C" BOOL APIENTRY EngGetPrinterDriver(
HANDLE hPrinter
, LPWSTR pEnvironment
, DWORD dwLevel
, BYTE *lpbDrvInfo
, DWORD cbBuf
, DWORD *pcbNeeded
)
{
BOOL ReturnValue;
ReturnValue = GetPrinterDriverW(
hPrinter
, pEnvironment
, dwLevel
, lpbDrvInfo
, cbBuf
, pcbNeeded
);
return( ReturnValue );
}
/*******************************************************************************
* EngGetPrinterDataW()
*
* History:
* 25-Jul-95 by Steve Wilson [swilson]
* Wrote it.
*******************************************************************************/
DWORD
WINAPI
EngGetPrinterData(
HANDLE hPrinter, // IN
LPWSTR pValueName, // IN
LPDWORD pType, // OUT -- may be NULL
LPBYTE lpbData, // OUT
DWORD cbBuf, // IN
LPDWORD pcbNeeded // OUT
)
{
LONG cj, cjValueName;
DWORD cjReturn;
DWORD dwLastError = ERROR_NOT_ENOUGH_MEMORY;
GREGETPRINTERDATA *pX, *pXReturn;
//DbgPrint("Enter EngGetPrinterData\n");
if (!pcbNeeded)
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return ERROR_INVALID_PARAMETER;
}
// Accumulate sizes of base struct plus strings
cj = sizeof *pX;
cj += (cjValueName = pValueName ? (wcslen(pValueName) + 1)*sizeof *pValueName : 0);
// Allocate TO buffer
pX = (GREGETPRINTERDATA *) PALLOCMEM(cj, 'lpsG');
// Marshall TO buffer
if (pX)
{
pX->cj = cj;
pX->cjData = cbBuf; // Client's buffer size
// Write strings at end of GRE struct
if (pValueName) {
memcpy(pX->alData,pValueName,cjValueName);
pX->cjValueName = cjValueName;
}
// Allocate RETURN buffer
pXReturn = (GREGETPRINTERDATA *) PALLOCNOZ(cjReturn = sizeof *pX + cbBuf, 'lpsG');
// SEND MESSAGE
if (pXReturn)
{
// GreGenericW return value indicates success or failure of GreEscapeSpool() and KMGetPrinterData()
GreGenericW( hPrinter,
(PULONG) pX,
(PULONG) pXReturn,
cjReturn,
(LONG) GDISPOOL_GETPRINTERDATA, 0);
dwLastError = EngGetLastError();
// return from GreGenericW may be 0, meaning any number of things, including ERROR_MORE_DATA
if (dwLastError != ERROR_PROCESS_ABORTED)
{
// Fill in return sizes
if (pcbNeeded)
{
*pcbNeeded = pXReturn->dwNeeded; // # return data bytes
//DbgPrint("EngGetPrinterData *pcbNeeded = %d\n", *pcbNeeded);
}
if (pType)
*pType = pXReturn->dwType;
if (dwLastError == ERROR_SUCCESS)
{
// Copy returned data into supplied structure
if (lpbData)
{
if ((DWORD) pXReturn->cjData <= cbBuf)
memcpy(lpbData, pXReturn->alData, pXReturn->cjData);
else
ASSERTGDI((DWORD) pXReturn->cjData <= cbBuf, "GetPrinterDataW - Bad spooler buffer size\n");
}
}
}
VFREEMEM(pXReturn);
}
VFREEMEM(pX);
}
//DbgPrint("GetPrinterData return: %d\n", dwLastError);
return dwLastError;
}
/*******************************************************************************
* EngSetPrinterData()
*
* History:
* 25-Oct-95 by Steve Wilson [swilson]
* Wrote it.
*******************************************************************************/
DWORD
WINAPI
EngSetPrinterData(
HANDLE hPrinter, // IN
LPWSTR pType, // IN
DWORD dwType, // IN
LPBYTE lpbPrinterData, // IN
DWORD cjPrinterData // IN
)
{
LONG cj, cjType;
DWORD cjReturn;
DWORD dwLastError = ERROR_NOT_ENOUGH_MEMORY;
GRESETPRINTERDATA *pTo, *pFrom;
//DbgPrint("Enter EngSetPrinterData\n");
// Accumulate sizes of base struct plus strings
cj = sizeof *pTo;
cj += (cjType = pType ? (wcslen(pType) + 1)*sizeof *pType : 0);
cj += lpbPrinterData ? cjPrinterData : 0;
// Allocate TO buffer
pTo = (GRESETPRINTERDATA *) PALLOCMEM(cj, 'lpsG');
// Marshall TO buffer
if (pTo)
{
pTo->cj = cj;
// Write incoming data at end of GRE struct
if (pType) {
memcpy(pTo->alData,pType,cjType);
pTo->cjType = cjType;
}
if (lpbPrinterData) {
memcpy((BYTE *)pTo->alData + cjType,lpbPrinterData,cjPrinterData);
pTo->cjPrinterData = cjPrinterData;
}
// Allocate RETURN buffer
cjReturn = sizeof *pTo;
pFrom = (GRESETPRINTERDATA *) PALLOCNOZ(cjReturn, 'lpsG');
// SEND MESSAGE
if (pFrom)
{
pTo->dwType = dwType;
// GreGenericW return value indicates success or failure of GreEscapeSpool() and KMGetPrinterData()
GreGenericW( hPrinter,
(PULONG) pTo,
(PULONG) pFrom,
cjReturn,
(LONG) GDISPOOL_SETPRINTERDATA, 0);
dwLastError = EngGetLastError();
VFREEMEM(pFrom);
}
VFREEMEM(pTo);
}
//DbgPrint("GetPrinterData return: %d\n", dwLastError);
return dwLastError;
}
/******************************Public*Routine******************************\
*
*
* History:
* 27-Feb-1995 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/
BOOL
WINAPI
EngWritePrinter(
HANDLE hPrinter,
LPVOID pBuf,
DWORD cbBuf,
LPDWORD pcWritten
)
{
DWORD ulRet = 0;
SPOOLREF sr(hPrinter);
if (sr.bValid())
{
PSPOOLMSG psm = sr.psm();
LPVOID pKmBuf = NULL;
GREWRITEPRINTER *wp = &sr.pso->WritePrinter;
//DbgPrint("Enter EngWritePrinter\n");
wp->cj = offsetof(GREWRITEPRINTER,alData) + cbBuf;
wp->cjData = cbBuf;
if (gpeSpool == PsGetCurrentProcess() &&
pBuf <= MM_HIGHEST_USER_ADDRESS &&
pBuf >= MM_LOWEST_USER_ADDRESS)
{
wp->pUserModeData = (PULONG) pBuf;
wp->cjUserModeData = cbBuf;
psm->cBuf = 1;
psm->cjIn = offsetof(GREWRITEPRINTER, alData);
}
else
{
//
// if we recevie a user mode buffer, make a kernel copy.
// This is to patch PSCRIPT 4 driver running on NT5.
//
if (pBuf <= MM_HIGHEST_USER_ADDRESS &&
pBuf >= MM_LOWEST_USER_ADDRESS)
{
if (pKmBuf = PALLOCNOZ(cbBuf,'lpsG'))
{
__try
{
ProbeAndReadBuffer(pKmBuf,pBuf,cbBuf);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
VFREEMEM(pKmBuf);
return (FALSE);
}
pBuf = pKmBuf;
}
else
{
WARNING ("failed to allocate memory in EngWritePrinter\n");
return (FALSE);
}
}
wp->pUserModeData = NULL;
wp->cjUserModeData = 0;
psm->cBuf = 2;
psm->apulIn[1]= (PULONG)pBuf;
psm->acjIn[1] = cbBuf;
psm->cjIn = wp->cj;
}
// setup the message
psm->cj = sizeof(SPOOLMSG);
psm->iMsg = GDISPOOL_WRITE;
psm->fl = 0;
// there are two buffers, one for the GREWRITEPRINTER header and one
// for the data.
psm->apulIn[0]= (PULONG)wp;
psm->acjIn[0] = offsetof(GREWRITEPRINTER,alData);
// place the return value in ulRet
psm->pulOut = &sr.pso->dwWritePrinterReturn;
psm->cjOut = sizeof(sr.pso->dwWritePrinterReturn);
psm->hso = (HSPOOLOBJ)hPrinter;
if (!sr.GreEscapeSpool())
{
ulRet = 0;
}
else
{
ulRet = sr.pso->dwWritePrinterReturn;
}
if (pcWritten)
*pcWritten = ulRet;
if (pKmBuf)
{
VFREEMEM(pKmBuf);
}
}
// return TRUE or FALSE
return(!!ulRet);
}
/*******************************************************************************
* BOOL GetFontPathName( WCHAR *pFullPath, WCHAR *pFileName )
*
* Goes to the spooler and does a search path in the font path to find the
* full path given a font file name. We expect pFullName and pFileName to
* point to the same piece of memory although we don't require this to be the case.
*
* History
* 7-31-95 Gerrit van Wingerden [gerritv]
* Wrote it.
*
*******************************************************************************/
BOOL GetFontPathName( WCHAR *pFullPath, WCHAR *pFileName )
{
BOOL bRet = FALSE;
SPOOLMEMOBJ spmo;
if (spmo.bValid())
{
PSPOOLMSG psm = spmo.psm();
psm->cj = sizeof(SPOOLMSG);
psm->iMsg = GDISPOOL_GETPATHNAME;
psm->fl = 0;
psm->cBuf = 1;
psm->cjIn = (wcslen(pFileName) + 1) * sizeof(WCHAR);
psm->apulIn[0] = (PULONG) pFileName;
psm->acjIn[0] = psm->cjIn;
psm->pulOut = (PULONG) pFullPath;
psm->cjOut = sizeof(WCHAR) * (MAX_PATH+1);
psm->hso = spmo.hGet();
bRet = spmo.GreEscapeSpool() && psm->ulRet;
spmo.bDelete();
}
return(bRet);
}
/******************************Public*Routine******************************\
* EngEscape()
*
* History:
* 18-Sep-1995 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/
ULONG APIENTRY EngEscape(
HANDLE hPrinter,
ULONG iEsc,
ULONG cjIn,
PVOID pvIn,
ULONG cjOut,
PVOID pvOut)
{
BOOL ulRet = FALSE;
SPOOLREF sr(hPrinter);
if (sr.bValid())
{
PSPOOLMSG psm = sr.psm();
psm->cj = sizeof(SPOOLMSG);
psm->iMsg = GDISPOOL_ENDDOCPRINTER;
psm->fl = 0;
psm->cBuf = 2;
psm->cjIn = sizeof(ULONG)+cjIn;
psm->apulIn[0]= &iEsc;
psm->acjIn[0] = sizeof(ULONG);
psm->apulIn[1]= (PULONG)pvIn;
psm->acjIn[1] = cjIn;
psm->pulOut = (PULONG)pvOut;
psm->cjOut = cjOut;
psm->hso = (HSPOOLOBJ)hPrinter;
if( sr.GreEscapeSpool() )
{
ulRet = (DWORD) psm->ulRet;
}
}
return(ulRet);
}