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.
2160 lines
55 KiB
2160 lines
55 KiB
/*
|
|
|
|
Copyright (c) 1992-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
psexe.c
|
|
|
|
Abstract:
|
|
|
|
This file contains the code required to actually communicate with the
|
|
PSTODIB dll and rasterize a job. Any required information is passed via
|
|
some named shared memory, the name of which is passed on the command line.
|
|
This name is guaranteed to be unique to the system and thus multiple
|
|
postscript jobs can be imaged at the same time.
|
|
|
|
--*/
|
|
|
|
#include <windows.h>
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <winspool.h>
|
|
#include <winsplp.h>
|
|
#include <prtdefs.h>
|
|
#include "..\..\lib\psdiblib.h"
|
|
|
|
#include "..\..\..\ti\psglobal\pstodib.h"
|
|
|
|
|
|
#include "..\psprint\psshmem.h"
|
|
#include "psexe.h"
|
|
#include <excpt.h>
|
|
#include <string.h>
|
|
#include "debug.h"
|
|
|
|
|
|
// By defining the following, each page is blited to the desktop this way
|
|
// one can verify the interpreter is running without waiting for the printer
|
|
// to print
|
|
//
|
|
//#define BLIT_TO_DESKTOP
|
|
|
|
// By defining the following, you will cause all the code to get executed
|
|
// except the part that REALLY writes to the printer. This is most useful in
|
|
// conjuction with the BLIT_TO_DESKTOP so you can run the intrepeter and
|
|
// have the output go to the desktop. I found this very useful during
|
|
// development. It may see useful when porting to new windows NT platforms.
|
|
//#define IGNORE_REAL_PRINTING
|
|
|
|
|
|
// This is a hack, it should eventually be taken out, the reason this is here,
|
|
// is becuse simply setting the windows page type in the DEVMODE structure,
|
|
// is not good enough for the RASTER printer driver (of which most printer
|
|
// drivers are based. With this table we borrowed from the spooler, we
|
|
// can set the correct FORM name which the printer drivers respect and can
|
|
// test different page sizes from the mac...
|
|
// DJC HACK HACK
|
|
|
|
|
|
PTSTR forms[] = {
|
|
L"Letter",
|
|
L"Letter Small",
|
|
L"Tabloid",
|
|
L"Ledger",
|
|
L"Legal",
|
|
L"Statement",
|
|
L"Executive",
|
|
L"A3",
|
|
L"A4",
|
|
L"A4 Small",
|
|
L"A5",
|
|
L"B4",
|
|
L"B5",
|
|
L"Folio",
|
|
L"Quarto",
|
|
L"10x14",
|
|
L"11x17",
|
|
L"Note",
|
|
L"Envelope #9",
|
|
L"Envelope #10",
|
|
L"Envelope #11",
|
|
L"Envelope #12",
|
|
L"Envelope #14",
|
|
L"C size sheet",
|
|
L"D size sheet",
|
|
L"E size sheet",
|
|
L"Envelope DL",
|
|
L"Envelope C5",
|
|
L"Envelope C3",
|
|
L"Envelope C4",
|
|
L"Envelope C6",
|
|
L"Envelope C65",
|
|
L"Envelope B4",
|
|
L"Envelope B5",
|
|
L"Envelope B6",
|
|
L"Envelope",
|
|
L"Envelope Monarch",
|
|
L"6 3/4 Envelope",
|
|
L"US Std Fanfold",
|
|
L"German Std Fanfold",
|
|
L"German Legal Fanfold",
|
|
NULL,
|
|
};
|
|
|
|
|
|
|
|
|
|
// This table translates from internal PSERR_* errors, defined in pstodib.h
|
|
// to errors in the event log file that the user can see in the event viewer
|
|
//
|
|
typedef struct {
|
|
DWORD dwOutputError;
|
|
DWORD dwPsError;
|
|
} PS_TRANSLATE_ERRORCODES;
|
|
|
|
PS_TRANSLATE_ERRORCODES adwTranslate[] = {
|
|
|
|
EVENT_PSTODIB_INIT_ACCESS, PSERR_INTERPRETER_INIT_ACCESS_VIOLATION,
|
|
EVENT_PSTODIB_JOB_ACCESS, PSERR_INTERPRETER_JOB_ACCESS_VIOLATION,
|
|
EVENT_PSTODIB_STRING_SEQ, PSERR_LOG_ERROR_STRING_OUT_OF_SEQUENCE,
|
|
EVENT_PSTODIB_FRAME_ALLOC, PSERR_FRAME_BUFFER_MEM_ALLOC_FAILED,
|
|
EVENT_PSTODIB_FONTQUERYFAIL, PSERR_FONT_QUERY_PROBLEM,
|
|
EVENT_PSTODIB_INTERNAL_FONT, PSERR_EXCEEDED_INTERNAL_FONT_LIMIT,
|
|
EVENT_PSTODIB_MEM_FAIL, PSERR_LOG_MEMORY_ALLOCATION_FAILURE
|
|
|
|
|
|
};
|
|
|
|
|
|
// Globals, this is global because the abortproc we pass in to the
|
|
// graphics engine does not allow us to specify any data to pass to the
|
|
// abort proc
|
|
PSEXEDATA Data;
|
|
|
|
|
|
/*** PsPrintCallBack
|
|
*
|
|
* This is the function pstodib calls whenever certain events occur
|
|
*
|
|
* Entry:
|
|
* pPsToDib = Pointer to the current PSTODIB structure
|
|
* pPsEvent = defines the event that is occuring
|
|
*
|
|
* Returns:
|
|
* True = Event was handled, interpreter should continue execution
|
|
* False = Abnormal termination, the interpreter should stop
|
|
*
|
|
*/
|
|
|
|
BOOL
|
|
CALLBACK
|
|
PsPrintCallBack(
|
|
IN struct _PSDIBPARMS *pPsToDib,
|
|
IN OUT PPSEVENTSTRUCT pPsEvent)
|
|
{
|
|
BOOL bRetVal=TRUE; // Success in case we dont support
|
|
|
|
// Decide on a course of action based on the event passed in
|
|
//
|
|
|
|
switch( pPsEvent->uiEvent ) {
|
|
|
|
case PSEVENT_PAGE_READY:
|
|
|
|
// The data in the pPsEvent signifies the data we need to paint..
|
|
// for know we will treat the data as one text item null
|
|
// terminated simply for testing...
|
|
//
|
|
bRetVal = PsPrintGeneratePage( pPsToDib, pPsEvent );
|
|
break;
|
|
|
|
|
|
case PSEVENT_STDIN:
|
|
|
|
// The interpreter is asking for some data so simply call
|
|
// the print subsystem to try to satisfy the request
|
|
//
|
|
bRetVal = PsHandleStdInputRequest( pPsToDib, pPsEvent );
|
|
break;
|
|
|
|
case PSEVENT_SCALE:
|
|
//
|
|
// Here is an opportunity to modify the current transformation
|
|
// matrix x and y values, this is used to image a 150 dpi job
|
|
// on a 300 dpi interpreter frame.
|
|
//
|
|
bRetVal = PsHandleScaleEvent( pPsToDib, pPsEvent);
|
|
break;
|
|
case PSEVENT_ERROR_REPORT:
|
|
//
|
|
// End of job, there may have been errors so the psexe component
|
|
// may print an error page to the target printer depending on the
|
|
// extent of the errors
|
|
//
|
|
bRetVal = PsGenerateErrorPage( pPsToDib, pPsEvent);
|
|
break;
|
|
case PSEVENT_GET_CURRENT_PAGE_TYPE:
|
|
//
|
|
// The interpreter usually queries the current page type at
|
|
// startup time, and uses this page type if the JOB did not
|
|
// specifically specify a page type.
|
|
//
|
|
bRetVal = PsGetCurrentPageType( pPsToDib, pPsEvent);
|
|
break;
|
|
case PSEVENT_NON_PS_ERROR:
|
|
//
|
|
// Some sort of error occured, that was NOT a postscript error.
|
|
// Examples might be resource allocation failure, or access to
|
|
// a resource has unexpectantly ended.
|
|
//
|
|
bRetVal = PsLogNonPsError( pPsToDib, pPsEvent );
|
|
break;
|
|
|
|
}
|
|
|
|
return bRetVal;
|
|
}
|
|
|
|
|
|
/*** PsPrintTranslateErrorCode
|
|
*
|
|
* This routine simply uses the error table to translate between errors
|
|
* internal to pstodib dib, and errors in the macprint.exe event file
|
|
* which may be reported in the event log
|
|
*
|
|
* Entry:
|
|
* dwPsErr = PsToDib internal error number
|
|
*
|
|
*
|
|
* Returns:
|
|
* The corresponding error number in the event error file.
|
|
*
|
|
*
|
|
*/
|
|
DWORD
|
|
PsTranslateErrorCode(
|
|
IN DWORD dwPsErr )
|
|
{
|
|
int i;
|
|
|
|
for ( i = 0 ;i < sizeof(adwTranslate)/sizeof(adwTranslate[0]) ;i++ ) {
|
|
if (dwPsErr == adwTranslate[i].dwPsError) {
|
|
return( adwTranslate[i].dwOutputError);
|
|
}
|
|
}
|
|
return ( EVENT_PSTODIB_UNDEFINED_ERROR );
|
|
}
|
|
|
|
|
|
/*** PsLogNonPsError
|
|
*
|
|
* This function logs an internal pstodib error
|
|
*
|
|
* Entry:
|
|
* pPsToDib = Pointer to the current PSTODIB structure
|
|
* pPsEvent = defines the NonPsError event structure with info
|
|
* about the error that is occuring
|
|
*
|
|
* Returns:
|
|
* True = Success, the event was logged
|
|
* False = Failure could not log the error
|
|
*
|
|
*/
|
|
BOOL
|
|
PsLogNonPsError(
|
|
IN PPSDIBPARMS pPsToDib,
|
|
IN PPSEVENTSTRUCT pPsEvent )
|
|
{
|
|
|
|
PPSEVENT_NON_PS_ERROR_STRUCT pPsError;
|
|
PPSEXEDATA pData;
|
|
LPTSTR aStrs[2];
|
|
DWORD dwEventError;
|
|
TCHAR atchar[10];
|
|
WORD wStringCount;
|
|
|
|
if (!(pData = ValidateHandle(pPsToDib->hPrivateData))) {
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
pPsError = (PPSEVENT_NON_PS_ERROR_STRUCT) pPsEvent->lpVoid;
|
|
dwEventError = PsTranslateErrorCode( pPsError->dwErrorCode);
|
|
|
|
|
|
if (dwEventError == EVENT_PSTODIB_UNDEFINED_ERROR) {
|
|
wsprintf( atchar,TEXT("%d"), pPsError->dwErrorCode);
|
|
aStrs[1] = atchar;
|
|
wStringCount = 2;
|
|
}else{
|
|
wStringCount = 1;
|
|
}
|
|
//
|
|
// Set the document name so it gets recorded in the log file
|
|
//
|
|
aStrs[0] = pData->pDocument;
|
|
|
|
|
|
PsLogEvent( dwEventError,
|
|
wStringCount,
|
|
aStrs,
|
|
pPsError->bError ? PSLOG_ERROR : 0 );
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/*** PsGenerateErrorPage
|
|
*
|
|
* This function generates an error page showing the last few postscript
|
|
* errors that occured.
|
|
*
|
|
* Entry:
|
|
* pPsToDib = Pointer to the current PSTODIB structure
|
|
* pPsEvent = defines the postscript errors that may have occured
|
|
*
|
|
* Returns:
|
|
* True = Event was handled, interpreter should continue execution
|
|
* False = Abnormal termination, the interpreter should stop
|
|
*
|
|
*/
|
|
|
|
BOOL
|
|
PsGenerateErrorPage(
|
|
IN PPSDIBPARMS pPsToDib,
|
|
IN OUT PPSEVENTSTRUCT pPsEvent)
|
|
{
|
|
|
|
PPSEVENT_ERROR_REPORT_STRUCT pPsErr;
|
|
PPSEXEDATA pData;
|
|
BOOL bDidStartPage = FALSE;
|
|
HGDIOBJ hOldFont=NULL;
|
|
HGDIOBJ hNewFont=NULL;
|
|
HGDIOBJ hNewBoldFont=NULL;
|
|
LOGFONT lfFont;
|
|
HPEN hNewPen=NULL;
|
|
HPEN hOldPen=NULL;
|
|
BOOL bRetVal=TRUE;
|
|
|
|
int iCurXPos;
|
|
int iCurYPos;
|
|
int i;
|
|
TEXTMETRIC tm;
|
|
int iYMove;
|
|
PCHAR pChar;
|
|
int iLen;
|
|
LPJOB_INFO_1 lpJob1 = NULL;
|
|
DWORD dwRequired;
|
|
SIZE UserNameSize;
|
|
HGDIOBJ hStockFont;
|
|
|
|
|
|
|
|
if (!(pData = ValidateHandle(pPsToDib->hPrivateData))) {
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Clean up access to our error struct for easier access
|
|
//
|
|
pPsErr = (PPSEVENT_ERROR_REPORT_STRUCT) pPsEvent->lpVoid;
|
|
|
|
//
|
|
// Only report the error page if there are actual errors and ONLY
|
|
// if the job had a FLUSHING mode, ie the error was critical enough
|
|
// to dump the rest of the postscript job. This is done becuase
|
|
// some jobs have warnings, but they are not fatal and the jobs actually
|
|
// print fine, there is no need to confuse the user with an error page
|
|
// if the job came out fine.
|
|
//
|
|
if( pPsErr->dwErrCount &&
|
|
(pPsErr->dwErrFlags & PSEVENT_ERROR_REPORT_FLAG_FLUSHING )) {
|
|
|
|
//
|
|
// Set up a do, so we can break out of any errors without using a
|
|
// goto.
|
|
//
|
|
do {
|
|
|
|
|
|
if (!GetJob( pData->hPrinter,
|
|
pData->JobId,
|
|
1,
|
|
(LPBYTE) NULL,
|
|
0,
|
|
&dwRequired)) {
|
|
|
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
|
|
|
// Get some memory
|
|
lpJob1 = (LPJOB_INFO_1) LocalAlloc( LPTR, dwRequired);
|
|
if (lpJob1) {
|
|
if (!GetJob(pData->hPrinter,
|
|
pData->JobId, 1,
|
|
(LPBYTE) lpJob1,
|
|
dwRequired,
|
|
&dwRequired)) {
|
|
|
|
LocalFree( (HLOCAL) lpJob1 );
|
|
lpJob1 = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify that we have a DC created!!!
|
|
if( !PsVerifyDCExistsAndCreateIfRequired( pData )) {
|
|
bRetVal = FALSE;
|
|
break;
|
|
}
|
|
|
|
if( StartPage( pData->hDC ) <= 0 ) {
|
|
bRetVal = FALSE;
|
|
break;
|
|
}
|
|
bDidStartPage = TRUE;
|
|
|
|
|
|
SetMapMode( pData->hDC, MM_LOENGLISH );
|
|
|
|
hStockFont = GetStockObject( ANSI_VAR_FONT );
|
|
if (hStockFont == (HGDIOBJ) NULL) {
|
|
bRetVal = FALSE;
|
|
break;
|
|
}
|
|
|
|
if( GetObject( hStockFont,
|
|
sizeof(lfFont),
|
|
(LPVOID) &lfFont) == 0 ) {
|
|
bRetVal = FALSE;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Create the error item font in the correct size
|
|
//
|
|
lfFont.lfHeight = PS_ERR_FONT_SIZE;
|
|
lfFont.lfWidth = 0;
|
|
|
|
hNewFont = CreateFontIndirect( &lfFont);
|
|
|
|
if (hNewFont == (HFONT) NULL) {
|
|
bRetVal = FALSE;
|
|
break;
|
|
}
|
|
|
|
// Create the error Header font
|
|
//
|
|
lfFont.lfHeight = PS_ERR_HEADER_FONT_SIZE;
|
|
lfFont.lfWeight = FW_BOLD;
|
|
|
|
hNewBoldFont = CreateFontIndirect( &lfFont );
|
|
if (hNewBoldFont == (HFONT) NULL) {
|
|
bRetVal = FALSE;
|
|
break;
|
|
}
|
|
|
|
hOldFont = SelectObject( pData->hDC, hNewBoldFont);
|
|
if (hOldFont == (HFONT)NULL) {
|
|
bRetVal = FALSE;
|
|
break;
|
|
}
|
|
|
|
if (!GetTextMetrics(pData->hDC, &tm)) {
|
|
bRetVal = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Set up how much to move vertically for positioning each line
|
|
//
|
|
iYMove = tm.tmHeight + tm.tmExternalLeading;
|
|
|
|
//
|
|
// Set the starting positiongs
|
|
//
|
|
iCurXPos = PS_INCH;
|
|
iCurYPos = -PS_INCH;
|
|
|
|
//
|
|
// If we have info about the job then show it
|
|
//
|
|
if (lpJob1) {
|
|
|
|
if (lpJob1->pUserName != NULL) {
|
|
|
|
if (!GetTextExtentPoint( pData->hDC,
|
|
lpJob1->pUserName,
|
|
lstrlen(lpJob1->pUserName),
|
|
&UserNameSize) ) {
|
|
bRetVal = FALSE;
|
|
break;
|
|
}
|
|
|
|
if( !TextOut( pData->hDC,
|
|
iCurXPos,
|
|
iCurYPos,
|
|
lpJob1->pUserName,
|
|
lstrlen(lpJob1->pUserName))) {
|
|
bRetVal = FALSE;
|
|
break;
|
|
}
|
|
|
|
}else{
|
|
UserNameSize.cx = 0;
|
|
UserNameSize.cy = 0;
|
|
}
|
|
|
|
if (lpJob1->pDocument != NULL) {
|
|
|
|
if( !TextOut( pData->hDC,
|
|
iCurXPos + PS_QUART_INCH + UserNameSize.cx,
|
|
iCurYPos,
|
|
lpJob1->pDocument,
|
|
lstrlen(lpJob1->pDocument))) {
|
|
bRetVal = FALSE;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Adjust the current position
|
|
//
|
|
iCurYPos -= (iYMove + PS_ERR_LINE_WIDTH );
|
|
|
|
// Draw A nice line
|
|
//
|
|
hNewPen = CreatePen( PS_SOLID, PS_ERR_LINE_WIDTH, RGB(0,0,0));
|
|
if (hNewPen == (HPEN) NULL ) {
|
|
bRetVal = FALSE;
|
|
break;
|
|
}
|
|
|
|
// Make our new pen active
|
|
//
|
|
hOldPen = SelectObject( pData->hDC, hNewPen );
|
|
|
|
|
|
// Draw the line
|
|
//
|
|
MoveToEx( pData->hDC, iCurXPos, iCurYPos, NULL);
|
|
LineTo( pData->hDC, iCurXPos + PS_ERR_LINE_LEN, iCurYPos);
|
|
|
|
// Put the old pen back
|
|
//
|
|
SelectObject( pData->hDC, hOldPen);
|
|
|
|
// Delete the pen we created
|
|
//
|
|
DeleteObject( hNewPen );
|
|
|
|
// Reset the line
|
|
//
|
|
iCurYPos -= PS_ERR_LINE_WIDTH;
|
|
|
|
// Now Select the normal font
|
|
//
|
|
SelectObject( pData->hDC, hNewFont);
|
|
|
|
// Get the updated text metrics for the new font
|
|
//
|
|
if (!GetTextMetrics(pData->hDC, &tm)){
|
|
bRetVal = FALSE;
|
|
break;
|
|
}
|
|
iYMove = tm.tmHeight + tm.tmExternalLeading;
|
|
|
|
|
|
// Now display each of the errors that came from PSTODIB
|
|
//
|
|
i = (int) pPsErr->dwErrCount;
|
|
|
|
while (--i) {
|
|
pChar = pPsErr->paErrs[i];
|
|
iLen = lstrlenA( pChar );
|
|
|
|
if ( !TextOutA( pData->hDC,
|
|
iCurXPos,
|
|
iCurYPos,
|
|
pChar,
|
|
iLen)) {
|
|
bRetVal = FALSE;
|
|
break;
|
|
}
|
|
iCurYPos -= (iYMove + iYMove / 3);
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
} while ( 1 );
|
|
|
|
|
|
if (!bRetVal) {
|
|
PsLogEventAndIncludeLastError(EVENT_PSTODIB_ERROR_STARTPG_FAIL,TRUE);
|
|
}
|
|
// Clenup the DC.
|
|
//
|
|
if (hOldFont != (HFONT) NULL) {
|
|
SelectObject( pData->hDC, hOldFont);
|
|
}
|
|
|
|
if (hNewFont != (HFONT) NULL) {
|
|
DeleteObject( hNewFont);
|
|
}
|
|
if (hNewBoldFont != (HFONT)NULL) {
|
|
DeleteObject( hNewBoldFont );
|
|
}
|
|
|
|
|
|
if (bDidStartPage) {
|
|
EndPage( pData->hDC );
|
|
}
|
|
|
|
// Free the job info memory if we had any
|
|
if (lpJob1) {
|
|
LocalFree((HLOCAL) lpJob1);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return(bRetVal);
|
|
|
|
|
|
}
|
|
|
|
|
|
/*** PsHandleScaleEvent
|
|
*
|
|
* This function handles scaling the current transformation matrix
|
|
* (the way logical units are mapped to device units in the interpreter) in
|
|
* order to simulate different page sizes for non 300 dpi devices. this
|
|
* done by scaling the transformation matrix such that only the portion
|
|
* of the frame buffer which can be transfered to the target printer is used.
|
|
* for example if we were going to a 150 dpi device then exactly half of
|
|
* 300 dpi frame buffer would be showable on the 150 dpi device, the rest of the
|
|
* frame buffer would be useless as it would extend beyond the imageable area
|
|
* of the printer. Thus if we scaled the current tranformation matrix by the
|
|
* ration of the target device resolution over 300 (the default pstodib resolution)
|
|
* then graphic objects that used to be 8 inches would now be 4 and thus take
|
|
* up HALF the space they used to.
|
|
*
|
|
* errors that occured.
|
|
*
|
|
* Entry:
|
|
* pPsToDib = Pointer to the current PSTODIB structure
|
|
* pPsEvent = defines the current scale information for the current
|
|
* postscript tranformation matrix
|
|
*
|
|
* Returns:
|
|
* True = This can never fail
|
|
*
|
|
*/
|
|
BOOL
|
|
PsHandleScaleEvent(
|
|
IN PPSDIBPARMS pPsToDib,
|
|
IN OUT PPSEVENTSTRUCT pPsEvent)
|
|
{
|
|
|
|
PPS_SCALE pScale;
|
|
|
|
|
|
pScale = (PPS_SCALE) pPsEvent->lpVoid;
|
|
|
|
|
|
|
|
pScale->dbScaleX = (double) pPsToDib->uiXDestRes / (double) pScale->uiXRes;
|
|
pScale->dbScaleY = (double) pPsToDib->uiYDestRes / (double) pScale->uiYRes;
|
|
|
|
|
|
#ifdef BLIT_TO_DESKTOP
|
|
pScale->dbScaleX *= .25;
|
|
pScale->dbScaleY *= .25;
|
|
#endif
|
|
|
|
return(TRUE);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*** ValidateHandle
|
|
*
|
|
* Validates the handle passed in to make sure its correct, if its not
|
|
* it also logs an error.
|
|
*
|
|
*
|
|
* Entry:
|
|
* hQProc = Handle to data block
|
|
*
|
|
* Returns:
|
|
* A valid ptr to a session block.
|
|
* NULL - failure the handle passed in was not what was expected.
|
|
*/
|
|
PPSEXEDATA
|
|
ValidateHandle(
|
|
HANDLE hQProc
|
|
)
|
|
{
|
|
PPSEXEDATA pData = (PPSEXEDATA)hQProc;
|
|
|
|
if (pData && pData->signature == PSEXE_SIGNATURE) {
|
|
return( pData );
|
|
} else {
|
|
|
|
//
|
|
// Log an error so we know something is wrong
|
|
//
|
|
PsLogEvent( EVENT_PSTODIB_LOG_INVALID_HANDLE,
|
|
0,
|
|
NULL,
|
|
PSLOG_ERROR );
|
|
|
|
DBGOUT(("Validate handle failed..."));
|
|
|
|
return( (PPSEXEDATA) NULL );
|
|
}
|
|
}
|
|
|
|
|
|
/*** PsHandleStdInputRequest
|
|
*
|
|
* This function handles std input requests from the interpreter by reading
|
|
* more data from the Win32 spooler.
|
|
*
|
|
*
|
|
* Entry:
|
|
* pPsToDib = Pointer to the current PSTODIB structure
|
|
* pPsEvent = A pointer to a structure that defines where to put the
|
|
* newly acquired data
|
|
*
|
|
* Returns:
|
|
* True = Success
|
|
* FALSE = Failure the interepreter should stop processing postscript upon
|
|
* return from this function.
|
|
*/
|
|
BOOL
|
|
PsHandleStdInputRequest(
|
|
IN PPSDIBPARMS pPsToDib,
|
|
IN OUT PPSEVENTSTRUCT pPsEvent)
|
|
{
|
|
|
|
PPSEXEDATA pData;
|
|
PPSEVENT_STDIN_STRUCT pStdinStruct;
|
|
DWORD dwAmtToCopy;
|
|
|
|
|
|
if (!(pData = ValidateHandle(pPsToDib->hPrivateData))) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// No matter what check for block or abort
|
|
//
|
|
if ( PsCheckForWaitAndAbort( pData )) {
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Cast the data to the correct structure
|
|
//
|
|
pStdinStruct = (PPSEVENT_STDIN_STRUCT) pPsEvent->lpVoid;
|
|
|
|
|
|
|
|
//
|
|
// Look to see if we have any data left over from our cache...
|
|
// if so return that data instead of really reading from the spooler
|
|
//
|
|
|
|
if (pData->cbBinaryBuff != 0) {
|
|
|
|
|
|
//
|
|
// A little bit of math here in case the buffer passed in to
|
|
// us is not big enough to hold the entire cache buffer
|
|
//
|
|
|
|
dwAmtToCopy = min( pData->cbBinaryBuff, pStdinStruct->dwBuffSize);
|
|
|
|
//
|
|
// There is data so lets copy it first...
|
|
//
|
|
|
|
memcpy( pStdinStruct->lpBuff,
|
|
pData->lpBinaryPosToReadFrom,
|
|
dwAmtToCopy );
|
|
|
|
//
|
|
// Now upate the pointer and counts;
|
|
//
|
|
|
|
pData->cbBinaryBuff -= dwAmtToCopy;
|
|
pData->lpBinaryPosToReadFrom += dwAmtToCopy;
|
|
pStdinStruct->dwActualBytes = dwAmtToCopy;
|
|
|
|
}else{
|
|
|
|
//
|
|
// Read from the printer the amount of data the interpreter
|
|
// claims he can handle
|
|
//
|
|
|
|
if( !ReadPrinter( pData->hPrinter,
|
|
pStdinStruct->lpBuff,
|
|
pStdinStruct->dwBuffSize,
|
|
&(pStdinStruct->dwActualBytes ))) {
|
|
//
|
|
// something is wrong... so reset the bytes read to 0
|
|
//
|
|
pStdinStruct->dwActualBytes = 0;
|
|
|
|
//
|
|
// If something unexpected happened log it
|
|
//
|
|
if (GetLastError() != ERROR_PRINT_CANCELLED) {
|
|
//
|
|
// Something happened... log it and abort
|
|
//
|
|
PsLogEventAndIncludeLastError(EVENT_PSTODIB_GET_DATA_FAILED,TRUE);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// If the number of bytes returned is 0 then were done...
|
|
//
|
|
if (pStdinStruct->dwActualBytes == 0) {
|
|
// we read nothing from the file... declare an EOF
|
|
pStdinStruct->uiFlags |= PSSTDIN_FLAG_EOF;
|
|
}
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
/*** PsCheckForWaitAndAbort
|
|
*
|
|
* This function checks to see if the user
|
|
* more data from the Win32 spooler.
|
|
*
|
|
*
|
|
* Entry:
|
|
* pData = A pointer to our current job structure
|
|
*
|
|
* Returns:
|
|
* True = An abort has been requested
|
|
* False = Ok, normal processing
|
|
*/
|
|
BOOL
|
|
PsCheckForWaitAndAbort(
|
|
IN PPSEXEDATA pData )
|
|
{
|
|
|
|
BOOL bRetVal = FALSE;
|
|
|
|
// 1st verify we are not blocked... if we are, wait for the
|
|
// semaphore before continuing since someone decided to PAUSE us
|
|
//
|
|
WaitForSingleObject(pData->semPaused, INFINITE);
|
|
|
|
|
|
// Check the ABORT flag that would have been sent if the user aborted
|
|
// us from printman
|
|
//
|
|
bRetVal = *(pData->pdwFlags) & PS_SHAREDMEM_ABORTED;
|
|
#ifdef MYPSDEBUG
|
|
if (bRetVal) {
|
|
DBGOUT(("\nAbort requested...."));
|
|
}
|
|
#endif
|
|
|
|
return(bRetVal);
|
|
}
|
|
|
|
|
|
|
|
/*** PsSetPrintingInfo
|
|
*
|
|
* This function brings the current devmode structure up to date, based
|
|
* on number of copies and or current page type to use for the next
|
|
* page.
|
|
*
|
|
*
|
|
* Entry:
|
|
* pData = Pointer to the current job data structure
|
|
* ppsPage = A pointer to a structure that defines the current page
|
|
* to image, based on data from the intrepreter.
|
|
*
|
|
* Returns:
|
|
* True = A change occured, the devmode has been made up to date, and
|
|
* somethign changed (signals that ResetDC should be called.
|
|
*
|
|
* False = Ok, no changes were observed
|
|
*/
|
|
BOOL
|
|
PsSetPrintingInfo(
|
|
IN OUT PPSEXEDATA pData,
|
|
IN PPSEVENT_PAGE_READY_STRUCT ppsPage )
|
|
{
|
|
|
|
BOOL bRetVal = FALSE;
|
|
LPDEVMODE lpDevmode;
|
|
|
|
|
|
lpDevmode = pData->printEnv.lpDevmode;
|
|
|
|
|
|
if (lpDevmode != (LPDEVMODE) NULL ) {
|
|
|
|
// We have a devmode so go ahead and look for changes
|
|
|
|
if (lpDevmode->dmFields & DM_PAPERSIZE) {
|
|
|
|
if (lpDevmode->dmPaperSize != ppsPage->iWinPageType) {
|
|
lpDevmode->dmPaperSize = (short)(ppsPage->iWinPageType);
|
|
bRetVal = TRUE;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// HACKHACK DJCTEST hack hack , until page stuff gets resolved
|
|
// once we can simply pass a new page type in this code will go away
|
|
//
|
|
if (lpDevmode->dmFields & DM_FORMNAME) {
|
|
if (wcscmp( forms[ppsPage->iWinPageType-1], lpDevmode->dmFormName)) {
|
|
wcscpy( lpDevmode->dmFormName, forms[ppsPage->iWinPageType-1]);
|
|
bRetVal = TRUE;
|
|
}
|
|
|
|
}
|
|
// DJC end hack....
|
|
|
|
|
|
|
|
// DJC need a decision here because if the driver does not support
|
|
// multiple copies we need to simulate it ????
|
|
if (lpDevmode->dmFields & DM_COPIES) {
|
|
if (lpDevmode->dmCopies != (short) ppsPage->uiCopies) {
|
|
lpDevmode->dmCopies = (short)(ppsPage->uiCopies);
|
|
bRetVal = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return( bRetVal );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*** PsPrintGeneratePage
|
|
*
|
|
* This function is called whenever the interpreter gets a showpage
|
|
* and is responsible for managing the DC of the printer we are currently
|
|
* printing to.
|
|
*
|
|
* Entry:
|
|
* pPsToDib = Pointer to the current PSTODIB structure
|
|
* pPsEvent = A pointer to a structure that defines the attributes for
|
|
* the frame buffer which is ready to be imaged. This function
|
|
* will verify that we are not paused/aborted, verify a device
|
|
* context is available to draw into,
|
|
* double check and
|
|
* update the current DEVMODE, reset the DC if required and
|
|
* finally call the code to actualy draw the frame buffer.
|
|
*
|
|
* Returns:
|
|
* True = Success
|
|
* FALSE = Failure the interepreter should stop processing postscript upon
|
|
* return from this function.
|
|
*/
|
|
BOOL
|
|
PsPrintGeneratePage(
|
|
IN PPSDIBPARMS pPsToDib,
|
|
IN PPSEVENTSTRUCT pPsEvent)
|
|
{
|
|
|
|
PPSEXEDATA pData;
|
|
PPSEVENT_PAGE_READY_STRUCT ppsPageReady;
|
|
|
|
|
|
if (!(pData = ValidateHandle(pPsToDib->hPrivateData))) {
|
|
|
|
// do something here,,,, we have a major problem...
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
// Verify were not aborting....
|
|
if ( PsCheckForWaitAndAbort( pData)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
ppsPageReady = (PPSEVENT_PAGE_READY_STRUCT) pPsEvent->lpVoid;
|
|
|
|
// Verify that the current set of data were imaging under is correct
|
|
// If not lets set it up so it is, PAGE_SIZE, COPIES for now
|
|
|
|
if (PsSetPrintingInfo( pData, ppsPageReady) &&
|
|
(pData->hDC != (HDC)NULL )) {
|
|
|
|
DBGOUT(("\nReseting the DC"));
|
|
|
|
if( ResetDC( pData->hDC, pData->printEnv.lpDevmode) == (HDC) NULL ) {
|
|
PsLogEventAndIncludeLastError(EVENT_PSTODIB_RESETDC_FAILED,FALSE);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// We may not have a dc set up yet, in this case we need to create it
|
|
// with the new devmode data we just modified. If we do this then
|
|
// we dont need to do a reset dc.
|
|
#ifndef IGNORE_REAL_PRINTING
|
|
if(!PsVerifyDCExistsAndCreateIfRequired( pData )) {
|
|
return(FALSE);
|
|
}
|
|
#endif
|
|
|
|
// Everything is ready to actuall image the frame buffer to the Target
|
|
// device context, so do it...
|
|
//
|
|
return PsPrintStretchTheBitmap( pData, ppsPageReady );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*** PsPrintStretchTheBitmap
|
|
*
|
|
* This function actually manages the target surface and either blits or
|
|
* stretchBlits (based on the resolution of the target printer) the frame
|
|
* buffer.
|
|
*
|
|
* Entry:
|
|
* pData = Pointer to the current job structure
|
|
* ppsPageReady = Pointer to the page ready event structure that
|
|
* the pstodib component prepared for us..
|
|
* Returns:
|
|
* True = Success
|
|
* FALSE = Failure the interepreter should stop processing postscript upon
|
|
* return from this function.
|
|
*/
|
|
BOOL
|
|
PsPrintStretchTheBitmap(
|
|
IN PPSEXEDATA pData,
|
|
IN PPSEVENT_PAGE_READY_STRUCT ppsPageReady )
|
|
{
|
|
|
|
BOOL bOk = TRUE;
|
|
|
|
int iXres, iYres;
|
|
int iDestWide, iDestHigh;
|
|
int iPageCount;
|
|
int iYOffset;
|
|
int iXSrc;
|
|
int iYSrc;
|
|
int iNumPagesToPrint;
|
|
int iBlit;
|
|
int iNewY;
|
|
int iNewX;
|
|
|
|
|
|
|
|
|
|
// Now do some calculations so we decide if we really need to
|
|
// stretch the bitmap or not. If the true resolution of the target
|
|
// printer is less than pstodibs (PSTDOBI_*_DPI) then we will shring
|
|
// the effective area so we actually only grab a portion of the bitmap
|
|
// However if the Target DPI is greater that PSTODIBS there is nothing
|
|
// we can do other than actually stretch (grow) the bitmap.
|
|
//
|
|
#ifndef BLIT_TO_DESKTOP
|
|
iXres = GetDeviceCaps(pData->hDC, LOGPIXELSX);
|
|
iYres = GetDeviceCaps(pData->hDC, LOGPIXELSY);
|
|
#else
|
|
iXres = 300;
|
|
iYres = 300;
|
|
#endif
|
|
|
|
// Get the DPI of the target dc anc calculate how much of the frame buffer
|
|
// we must want in order to show the page correctly
|
|
//
|
|
iDestWide = (ppsPageReady->dwWide * iXres) / PSTODIB_X_DPI;
|
|
iDestHigh = (ppsPageReady->dwHigh * iYres) / PSTODIB_Y_DPI;
|
|
|
|
|
|
// If the resolution of the target printer is larger that the resolution
|
|
// of the interpreter, then were forced to actually stretch the data, so
|
|
// we can fill the page
|
|
//
|
|
if (iDestHigh > (int) ppsPageReady->dwHigh ) {
|
|
iYSrc = ppsPageReady->dwHigh;
|
|
iYOffset = 0;
|
|
} else {
|
|
iYSrc = iDestHigh;
|
|
iYOffset = ppsPageReady->dwHigh - iDestHigh;
|
|
}
|
|
|
|
if (iDestWide > (int) ppsPageReady->dwWide) {
|
|
iXSrc = ppsPageReady->dwWide;
|
|
} else {
|
|
iXSrc = iDestWide;
|
|
}
|
|
|
|
|
|
// Set up the number of pages to print, if the printer driver does not
|
|
// support multiple pages on its own we need to simulate it...
|
|
//
|
|
if ((pData->printEnv.lpDevmode == (LPDEVMODE) NULL ) ||
|
|
!(pData->printEnv.lpDevmode->dmFields & DM_COPIES )) {
|
|
|
|
iNumPagesToPrint = ppsPageReady->uiCopies;
|
|
DBGOUT(("\nSimulating copies settting to %d", iNumPagesToPrint));
|
|
} else {
|
|
DBGOUT(("\nUsing devmode copies of %d", pData->printEnv.lpDevmode->dmCopies));
|
|
|
|
iNumPagesToPrint = 1; // The driver will do it for us
|
|
|
|
}
|
|
|
|
//
|
|
// Set the starting point of the image so we respect the fact that
|
|
// postscript jobs have 0,0 at the bottom of the page and grow upwards
|
|
// based on this info we need to compare the imageable area of the
|
|
// device context and determine how much we need to offset the top,left
|
|
// corner of our image such that the bottom of our image lines up with the
|
|
// bottom of the REAL imageable area of the device. This is our best hope
|
|
// of having the image appear in the correct place on the page.
|
|
//
|
|
iNewX = (GetDeviceCaps( pData->hDC, HORZRES) - iDestWide) / 2;
|
|
iNewY = (GetDeviceCaps( pData->hDC, VERTRES) - iDestHigh) / 2;
|
|
|
|
// If the printer driver does not support multiple copies then we need
|
|
// to handle appropriately
|
|
//
|
|
for ( iPageCount = 0 ;
|
|
iPageCount < iNumPagesToPrint ;
|
|
iPageCount++ ) {
|
|
|
|
#ifndef IGNORE_REAL_PRINTING
|
|
if (StartPage( pData->hDC) <= 0 ) {
|
|
PsLogEventAndIncludeLastError(EVENT_PSTODIB_FAIL_IMAGE,TRUE);
|
|
bOk = FALSE;
|
|
break;
|
|
}
|
|
#endif
|
|
#ifdef BLIT_TO_DESKTOP
|
|
{
|
|
HDC hDC;
|
|
|
|
//TEST DJC, sanity
|
|
|
|
hDC = GetDC(GetDesktopWindow());
|
|
|
|
SetStretchBltMode( hDC, BLACKONWHITE);
|
|
StretchDIBits ( hDC,
|
|
0,
|
|
0,
|
|
iDestWide,
|
|
iDestHigh,
|
|
0,
|
|
iYOffset,
|
|
iXSrc,
|
|
iYSrc,
|
|
(LPVOID) ppsPageReady->lpBuf,
|
|
ppsPageReady->lpBitmapInfo,
|
|
DIB_RGB_COLORS,
|
|
SRCCOPY );
|
|
|
|
|
|
ReleaseDC(GetDesktopWindow(), hDC);
|
|
|
|
|
|
}
|
|
#endif
|
|
|
|
#ifdef MYPSDEBUG
|
|
printf("\nDevice True size wxh %d,%d", GetDeviceCaps(pData->hDC,HORZRES),
|
|
GetDeviceCaps(pData->hDC,VERTRES));
|
|
|
|
printf("\nDevice Res %d x %d stretching from %d x %d, to %d x %d\nTo location %d %d",
|
|
iXres,
|
|
iYres,
|
|
iXSrc,
|
|
iYSrc,
|
|
iDestWide,
|
|
iDestHigh,
|
|
iNewX,
|
|
iNewY);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef IGNORE_REAL_PRINTING
|
|
#ifdef MYPSDEBUG
|
|
{
|
|
TCHAR szBuff[512];
|
|
wsprintf( szBuff,
|
|
TEXT("PSTODIB True device res %d x %d, Job:%ws"),
|
|
GetDeviceCaps(pData->hDC,HORZRES),
|
|
GetDeviceCaps(pData->hDC,VERTRES),
|
|
pData->pDocument );
|
|
|
|
|
|
TextOut( pData->hDC, 0 , 0, szBuff,lstrlen(szBuff));
|
|
}
|
|
#endif
|
|
|
|
// Set the stretch mode in case we really stretch
|
|
//
|
|
SetStretchBltMode( pData->hDC, BLACKONWHITE);
|
|
|
|
|
|
//
|
|
// Do a check to keep the stretch from occuring unless it HAS to
|
|
//
|
|
if ((iDestWide == iXSrc) &&
|
|
(iDestHigh == iYSrc) ) {
|
|
|
|
iBlit = SetDIBitsToDevice( pData->hDC,
|
|
iNewX,
|
|
iNewY,
|
|
iDestWide,
|
|
iDestHigh,
|
|
0,
|
|
iYOffset,
|
|
0,
|
|
ppsPageReady->dwHigh,
|
|
(LPVOID) ppsPageReady->lpBuf,
|
|
ppsPageReady->lpBitmapInfo,
|
|
DIB_RGB_COLORS );
|
|
} else {
|
|
|
|
iBlit = StretchDIBits( pData->hDC,
|
|
iNewX,
|
|
iNewY,
|
|
iDestWide,
|
|
iDestHigh,
|
|
0,
|
|
iYOffset,
|
|
iXSrc,
|
|
iYSrc,
|
|
(LPVOID) ppsPageReady->lpBuf,
|
|
ppsPageReady->lpBitmapInfo,
|
|
DIB_RGB_COLORS,
|
|
SRCCOPY );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( iBlit == GDI_ERROR ){
|
|
|
|
PsLogEventAndIncludeLastError(EVENT_PSTODIB_FAIL_IMAGE,TRUE);
|
|
|
|
bOk = FALSE;
|
|
break;
|
|
}
|
|
#endif
|
|
#ifndef IGNORE_REAL_PRINTING
|
|
if ( EndPage( pData->hDC ) < 0 ) {
|
|
PsLogEventAndIncludeLastError(EVENT_PSTODIB_FAIL_IMAGE,TRUE);
|
|
bOk = FALSE;
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
return(bOk);
|
|
}
|
|
|
|
VOID
|
|
PsLogEventAndIncludeLastError(
|
|
IN DWORD dwErrorEvent,
|
|
IN BOOL bError )
|
|
{
|
|
TCHAR atBuff[20];
|
|
TCHAR *aStrs[2];
|
|
|
|
wsprintf( atBuff,TEXT("%d"), GetLastError());
|
|
|
|
aStrs[0] = atBuff;
|
|
|
|
PsLogEvent( dwErrorEvent,
|
|
1,
|
|
aStrs,
|
|
PSLOG_ERROR );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*** PsVerifyDCExistsAndCreateIfRequired
|
|
*
|
|
*
|
|
* This function checks to see if a DC already exists, and if it does not
|
|
* then it creates one with the current devmode.
|
|
*
|
|
* Entry:
|
|
* pData = Pointer to the current job structure
|
|
*
|
|
* Returns:
|
|
* True = Success
|
|
* FALSE = Failure the interepreter should stop processing postscript upon
|
|
* return from this function.
|
|
*/
|
|
BOOL
|
|
PsVerifyDCExistsAndCreateIfRequired(
|
|
IN OUT PPSEXEDATA pData )
|
|
{
|
|
BOOL bRetVal = TRUE;
|
|
DOCINFO docInfo;
|
|
|
|
|
|
//
|
|
// We will only create a dc if it has not already done so
|
|
//
|
|
if (pData->hDC == (HDC) NULL ) {
|
|
|
|
pData->hDC = CreateDC(TEXT(""),
|
|
(LPCTSTR) pData->pPrinterName,
|
|
TEXT(""),
|
|
pData->printEnv.lpDevmode );
|
|
|
|
if (pData->hDC == (HDC) NULL) {
|
|
PsLogEventAndIncludeLastError( EVENT_PSTODIB_CANNOT_CREATE_DC,TRUE );
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
// Now set the abort proc, this proc will be called occasioanly by the
|
|
// system to see if we want to abort.....
|
|
//
|
|
SetAbortProc( pData->hDC, (ABORTPROC)PsPrintAbortProc );
|
|
|
|
|
|
docInfo.cbSize = sizeof(DOCINFO);
|
|
docInfo.lpszDocName = pData->pDocument;
|
|
docInfo.lpszOutput = NULL;
|
|
|
|
|
|
if ( StartDoc( pData->hDC, &docInfo) == SP_ERROR ) {
|
|
|
|
PsLogEventAndIncludeLastError( EVENT_PSTODIB_CANNOT_DO_STARTDOC,TRUE );
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
//
|
|
// Set a flag saying we did the startdoc, this is so we can
|
|
// return an error to the spooler if we did not, and force
|
|
// deletion of the job
|
|
//
|
|
pData->printEnv.dwFlags |= PS_PRINT_STARTDOC_INITIATED;
|
|
|
|
}
|
|
|
|
return( TRUE );
|
|
|
|
}
|
|
|
|
/*** PsGetDefaultDevmode
|
|
*
|
|
*
|
|
* This function retrieves the current default DEVMODE for the printer
|
|
* we are asked to image a job on.
|
|
*
|
|
* Entry:
|
|
* pData = Pointer to the current job structure
|
|
*
|
|
* Returns:
|
|
* True = Success
|
|
* FALSE = Failure the interepreter should stop processing postscript upon
|
|
* return from this function.
|
|
*/
|
|
BOOL
|
|
PsGetDefaultDevmode(
|
|
IN OUT PPSEXEDATA pData )
|
|
{
|
|
DWORD dwMemRequired;
|
|
PRINTER_INFO_2 *pPrinterInfo;
|
|
|
|
|
|
|
|
if( !GetPrinter( pData->hPrinter,
|
|
2,
|
|
(LPBYTE) NULL,
|
|
0,
|
|
&dwMemRequired ) &&
|
|
GetLastError() != ERROR_INSUFFICIENT_BUFFER ) {
|
|
PsLogEventAndIncludeLastError( EVENT_PSTODIB_GETDEFDEVMODE_FAIL,TRUE );
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
|
|
pPrinterInfo = (PRINTER_INFO_2 *) LocalAlloc( LPTR, dwMemRequired );
|
|
|
|
if (pPrinterInfo == (PRINTER_INFO_2 *) NULL) {
|
|
|
|
PsLogEvent( EVENT_PSTODIB_MEM_ALLOC_FAILURE,
|
|
0,
|
|
NULL,
|
|
0 );
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
if ( !GetPrinter( pData->hPrinter,
|
|
2,
|
|
(LPBYTE) pPrinterInfo,
|
|
dwMemRequired,
|
|
&dwMemRequired ) ) {
|
|
|
|
LocalFree( (HLOCAL) pPrinterInfo );
|
|
|
|
PsLogEventAndIncludeLastError( EVENT_PSTODIB_GETDEFDEVMODE_FAIL,TRUE );
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
|
|
dwMemRequired = DocumentProperties( (HWND) NULL,
|
|
pData->hPrinter,
|
|
pPrinterInfo->pPrinterName,
|
|
NULL,
|
|
NULL,
|
|
0 );
|
|
|
|
|
|
pData->printEnv.lpDevmode = (LPDEVMODE) LocalAlloc( LPTR, dwMemRequired );
|
|
if (pData->printEnv.lpDevmode == (LPDEVMODE) NULL) {
|
|
|
|
LocalFree( (HLOCAL) pPrinterInfo );
|
|
PsLogEvent( EVENT_PSTODIB_MEM_ALLOC_FAILURE,
|
|
0,
|
|
NULL,
|
|
0 );
|
|
return(FALSE);
|
|
|
|
} else {
|
|
|
|
DocumentProperties( (HWND) NULL,
|
|
pData->hPrinter,
|
|
pPrinterInfo->pPrinterName,
|
|
pData->printEnv.lpDevmode,
|
|
NULL,
|
|
DM_COPY );
|
|
|
|
pData->printEnv.dwFlags |= PS_PRINT_FREE_DEVMODE;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
LocalFree( (HLOCAL) pPrinterInfo );
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
/*** PsGetCurrentPageType
|
|
*
|
|
*
|
|
* This function retrieves the current pagetype based on what was
|
|
* initially in the devmode.
|
|
*
|
|
* Entry:
|
|
* pData = Pointer to the current job structure
|
|
* pPsEvent = Pointer to the structure which contains the default
|
|
* page type to use...
|
|
*
|
|
* Returns:
|
|
* True = Success
|
|
* False = The interpreter should stop processing the current job
|
|
*
|
|
*/
|
|
BOOL
|
|
PsGetCurrentPageType(
|
|
IN PPSDIBPARMS pPsToDib,
|
|
IN OUT PPSEVENTSTRUCT pPsEvent)
|
|
|
|
{
|
|
|
|
|
|
PPSEXEDATA pData;
|
|
PPSEVENT_CURRENT_PAGE_STRUCT ppsCurPage;
|
|
LPDEVMODE lpDevmode;
|
|
|
|
|
|
|
|
if (!(pData = ValidateHandle(pPsToDib->hPrivateData))) {
|
|
return(FALSE);
|
|
}
|
|
|
|
ppsCurPage = (PPSEVENT_CURRENT_PAGE_STRUCT) pPsEvent->lpVoid;
|
|
|
|
lpDevmode = pData->printEnv.lpDevmode;
|
|
|
|
if (lpDevmode!= (LPDEVMODE) NULL ) {
|
|
//
|
|
// We have a devmode so look at it
|
|
//
|
|
if ( lpDevmode->dmFields & DM_PAPERSIZE) {
|
|
|
|
ppsCurPage->dmPaperSize = lpDevmode->dmPaperSize;
|
|
return(TRUE);
|
|
|
|
}
|
|
}
|
|
// True is returned in either case, since not having a devmode is
|
|
// not necessaraly a fatal error
|
|
//
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/*** PsMakeDefaultDevmodeModsAndSetupResolution
|
|
*
|
|
*
|
|
* This function sets up our initial devmode and sets up some info
|
|
* so we can determine the scaling ratio based on the comparison
|
|
* of true device DPI, and internal interpreter DPI
|
|
*
|
|
* Entry:
|
|
* pData = Pointer to the current job structure
|
|
* pPsEvent = Pointer to the structure which contains the
|
|
* intrepreter session info.
|
|
*
|
|
* Returns:
|
|
* VOID
|
|
*
|
|
*/
|
|
VOID
|
|
PsMakeDefaultDevmodeModsAndSetupResolution(
|
|
IN PPSEXEDATA pData,
|
|
IN OUT PPSDIBPARMS ppsDibParms )
|
|
{
|
|
|
|
HDC hIC;
|
|
LPDEVMODE lpDevmode;
|
|
BOOL bVerifyNewRes = FALSE;
|
|
|
|
|
|
lpDevmode = pData->printEnv.lpDevmode;
|
|
|
|
|
|
|
|
ppsDibParms->uiXDestRes = PSTODIB_X_DPI;
|
|
ppsDibParms->uiYDestRes = PSTODIB_Y_DPI;
|
|
|
|
// 1st thing to here is create an information context so we can
|
|
// determine the default resolution of the printer
|
|
|
|
hIC = CreateIC(TEXT(""),
|
|
(LPCTSTR) pData->pPrinterName,
|
|
TEXT(""),
|
|
lpDevmode );
|
|
|
|
|
|
if ( hIC != (HDC) NULL ) {
|
|
|
|
|
|
ppsDibParms->uiXDestRes = GetDeviceCaps(hIC, LOGPIXELSX);
|
|
ppsDibParms->uiYDestRes = GetDeviceCaps(hIC, LOGPIXELSY);
|
|
|
|
if (( GetDeviceCaps(hIC, LOGPIXELSX) > PSTODIB_X_DPI ) &&
|
|
( GetDeviceCaps(hIC, LOGPIXELSY) > PSTODIB_Y_DPI ) &&
|
|
( lpDevmode != (LPDEVMODE) NULL )) {
|
|
|
|
// Both resolutions are bigger lets go ahead and try to get the driver
|
|
// to go to PSTODIB_*_DPIS
|
|
//
|
|
lpDevmode->dmFields |= (DM_PRINTQUALITY | DM_YRESOLUTION);
|
|
lpDevmode->dmPrintQuality = PSTODIB_X_DPI;
|
|
lpDevmode->dmYResolution = PSTODIB_Y_DPI;
|
|
|
|
// Since we changed the resolution, it may not work so we need to
|
|
// reget the IC with the new devmode to see if it works.
|
|
bVerifyNewRes = TRUE;
|
|
}
|
|
|
|
DeleteDC( hIC );
|
|
|
|
|
|
if (bVerifyNewRes) {
|
|
|
|
hIC = CreateIC(TEXT(""),
|
|
(LPCTSTR) pData->pPrinterName,
|
|
TEXT(""),
|
|
lpDevmode);
|
|
|
|
if (hIC != (HDC) NULL) {
|
|
|
|
// Set the resolution for the job to the new value...
|
|
// we dont expect this to change for the duration ofthe
|
|
// job
|
|
ppsDibParms->uiXDestRes = GetDeviceCaps(hIC, LOGPIXELSX);
|
|
ppsDibParms->uiYDestRes = GetDeviceCaps(hIC, LOGPIXELSY);
|
|
|
|
|
|
DeleteDC(hIC);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if (lpDevmode != (LPDEVMODE)NULL) {
|
|
|
|
lpDevmode->dmFields |= (DM_ORIENTATION | DM_PAPERSIZE);
|
|
lpDevmode->dmOrientation = DMORIENT_PORTRAIT;
|
|
lpDevmode->dmCopies = 1;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*** PsInitPrintEnv
|
|
*
|
|
*
|
|
* Initializes the data that tracks the DEVMODE for the current job
|
|
*
|
|
* Entry:
|
|
* pData = Pointer to the current job structure
|
|
* lpDevmode = Pointer to the current devmode to use for the job
|
|
*
|
|
* Returns:
|
|
* VOID
|
|
*
|
|
*/
|
|
VOID PsInitPrintEnv( PPSEXEDATA pData, LPDEVMODE lpDevmode )
|
|
{
|
|
DWORD dwTotDevMode;
|
|
|
|
|
|
//
|
|
// Set the inital state of our flags
|
|
//
|
|
|
|
pData->printEnv.dwFlags = 0;
|
|
pData->printEnv.lpDevmode = (LPDEVMODE) NULL;
|
|
|
|
|
|
|
|
if (lpDevmode != (LPDEVMODE) NULL) {
|
|
|
|
//
|
|
// Since there is a devmode make a local copy cause we might
|
|
// be changing it.
|
|
//
|
|
|
|
|
|
dwTotDevMode = lpDevmode->dmSize + lpDevmode->dmDriverExtra;
|
|
|
|
|
|
pData->printEnv.lpDevmode = (LPDEVMODE) LocalAlloc( NONZEROLPTR,
|
|
dwTotDevMode );
|
|
|
|
|
|
if (pData->printEnv.lpDevmode != (LPDEVMODE) NULL) {
|
|
|
|
//
|
|
// Set the flag so we know to free this later
|
|
//
|
|
|
|
pData->printEnv.dwFlags |= PS_PRINT_FREE_DEVMODE;
|
|
|
|
//
|
|
// Now go and copy it
|
|
//
|
|
|
|
memcpy( (PVOID) pData->printEnv.lpDevmode,
|
|
(PVOID) lpDevmode,
|
|
dwTotDevMode );
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** PsHandleBinaryFileLogicAndReturnBinaryStatus
|
|
*
|
|
*
|
|
* This routine will look at the begining buffer of data from the ps job
|
|
* and determine whether the job is BINARY. This is done by looking at the
|
|
* beg of the job and looking for a string the mac Spooler inserts. If
|
|
* this string exists it is converted to spaces and not passed through to the
|
|
* interpreter. At this point it is a BINARY job.
|
|
*
|
|
* Entry:
|
|
* pData = Pointer to the current job structure
|
|
*
|
|
* Returns:
|
|
* TRUE/FALSE = True means this job should be treated as BINARY.
|
|
*
|
|
*
|
|
*/
|
|
BOOL PsHandleBinaryFileLogicAndReturnBinaryStatus( PPSEXEDATA pData )
|
|
{
|
|
|
|
DWORD dwIndex;
|
|
BOOL bRetVal = FALSE;
|
|
|
|
pData->lpBinaryPosToReadFrom = &pData->BinaryBuff[0];
|
|
pData->cbBinaryBuff = 0;
|
|
|
|
|
|
if( !ReadPrinter( pData->hPrinter,
|
|
pData->lpBinaryPosToReadFrom,
|
|
sizeof(pData->BinaryBuff),
|
|
&(pData->cbBinaryBuff) )) {
|
|
|
|
if (GetLastError() != ERROR_PRINT_CANCELLED) {
|
|
//
|
|
// Something happened... log it
|
|
//
|
|
PsLogEventAndIncludeLastError(EVENT_PSTODIB_GET_DATA_FAILED,TRUE);
|
|
|
|
|
|
#ifdef MYPSDEBUG
|
|
printf("\nSFMPsexe: Error from ReadPrinter when trying to get Binary buffer data ");
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
// Now do the compare
|
|
|
|
|
|
if (IsJobFromMac(pData))
|
|
{
|
|
bRetVal = TRUE;
|
|
}
|
|
|
|
//
|
|
// we retain this code just in case the job from an older SFM spooler
|
|
// that still prepends those strings
|
|
//
|
|
else if (!strncmp(pData->BinaryBuff, FILTERCONTROL, SIZE_FC) ||
|
|
!strncmp(pData->BinaryBuff, FILTERCONTROL_OLD, SIZE_FCOLD))
|
|
{
|
|
//
|
|
// turn filtering off & clear filter message
|
|
//
|
|
for (dwIndex = 0; dwIndex < SIZE_FC; dwIndex++) {
|
|
pData->BinaryBuff[dwIndex] = '\n' ;
|
|
}
|
|
|
|
bRetVal = TRUE;
|
|
}
|
|
}
|
|
|
|
return(bRetVal);
|
|
}
|
|
/*** PsPrintAbortProc
|
|
*
|
|
*
|
|
* The abort procedure the system occasiaonly calls to see if we should abort
|
|
* the current job
|
|
*
|
|
* Entry:
|
|
* hdc = The current device context were drawing into
|
|
* iError = A spooler error, we dont need to worry about this
|
|
*
|
|
* Returns:
|
|
* TRUE The job should continue to be processed
|
|
* FALSE The job should be aborted
|
|
*
|
|
*
|
|
*/
|
|
BOOL CALLBACK PsPrintAbortProc( HDC hdc, int iError )
|
|
{
|
|
|
|
// If the print processor set the shared memory abort the job flag,
|
|
// then kill the job
|
|
//
|
|
if( *(Data.pdwFlags) & PS_SHAREDMEM_ABORTED ) {
|
|
return( FALSE );
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/*** main
|
|
*
|
|
*
|
|
* This is the main entry point for the application and only interface to
|
|
* pstodib
|
|
*
|
|
*
|
|
* Entry:
|
|
* argc = Count of arguments
|
|
* argv = ptr to array of ptrs to each argument on the command
|
|
* line.
|
|
*
|
|
* Returns:
|
|
* 0 = OK, job finished via normal processing
|
|
* 99 = error of some sort
|
|
*/
|
|
|
|
int __cdecl
|
|
main(
|
|
IN int argc,
|
|
IN TCHAR *argv[] )
|
|
|
|
{
|
|
|
|
PPSEXEDATA pData=&Data;
|
|
PSDIBPARMS psDibParms;
|
|
BOOL bRetVal = FALSE;
|
|
LPTSTR lpCommandLine;
|
|
|
|
|
|
// Get to the first item, which is the name of the shared memory that
|
|
// has all the info we need.
|
|
//
|
|
lpCommandLine = GetCommandLine();
|
|
|
|
while (*lpCommandLine && *lpCommandLine != ' ') {
|
|
lpCommandLine++;
|
|
}
|
|
while (*lpCommandLine && *lpCommandLine == ' ') {
|
|
lpCommandLine++;
|
|
}
|
|
|
|
|
|
|
|
// First clear out our structure
|
|
memset( (PVOID) pData, 0, sizeof(*pData));
|
|
|
|
// Set up our local structure
|
|
//
|
|
pData->signature = PSEXE_SIGNATURE;
|
|
|
|
|
|
|
|
|
|
// First thing to do is get the name of the object we will use for
|
|
// getting at the memory
|
|
if (lstrlen(lpCommandLine) == 0) {
|
|
// This is an error condition
|
|
PsCleanUpAndExitProcess(pData, TRUE);
|
|
}
|
|
|
|
pData->hShared = OpenFileMapping( FILE_MAP_READ, FALSE, lpCommandLine);
|
|
|
|
if (pData->hShared == (HANDLE) NULL ) {
|
|
|
|
|
|
PsLogEventAndIncludeLastError(EVENT_PSTODIB_INIT_FAILED,TRUE);
|
|
PsCleanUpAndExitProcess( pData, TRUE );
|
|
|
|
} else{
|
|
|
|
pData->pShared = (PPSPRINT_SHARED_MEMORY) MapViewOfFile( pData->hShared,
|
|
FILE_MAP_READ,
|
|
0,
|
|
0,
|
|
0 );
|
|
|
|
|
|
if (pData->pShared == (PPSPRINT_SHARED_MEMORY) NULL) {
|
|
PsLogEventAndIncludeLastError(EVENT_PSTODIB_INIT_FAILED,TRUE);
|
|
PsCleanUpAndExitProcess( pData, TRUE );
|
|
}
|
|
|
|
|
|
// Now set up the data from the shared memory region
|
|
pData->pDocument = (LPTSTR) UTLPSRETURNPTRFROMITEM(pData->pShared,
|
|
pData->pShared->dwDocumentName);
|
|
|
|
pData->pPrinterName = (LPTSTR) UTLPSRETURNPTRFROMITEM(pData->pShared,
|
|
pData->pShared->dwPrinterName);
|
|
|
|
|
|
PsInitPrintEnv( pData, (LPDEVMODE) UTLPSRETURNPTRFROMITEM( pData->pShared,
|
|
pData->pShared->dwDevmode));
|
|
|
|
|
|
|
|
|
|
pData->pDocumentPrintDocName = (LPTSTR)
|
|
UTLPSRETURNPTRFROMITEM( pData->pShared,
|
|
pData->pShared->dwPrintDocumentDocName);
|
|
|
|
|
|
|
|
|
|
pData->semPaused = OpenEvent( EVENT_ALL_ACCESS,
|
|
FALSE,
|
|
(LPWSTR) UTLPSRETURNPTRFROMITEM( pData->pShared,
|
|
pData->pShared->dwControlName));
|
|
if (pData->semPaused == (HANDLE) NULL) {
|
|
PsLogEventAndIncludeLastError(EVENT_PSTODIB_INIT_FAILED,TRUE);
|
|
PsCleanUpAndExitProcess( pData, TRUE );
|
|
}
|
|
|
|
pData->pdwFlags = (LPDWORD) &pData->pShared->dwFlags;
|
|
pData->JobId = pData->pShared->dwJobId;
|
|
|
|
|
|
//
|
|
// Now check our Abort immediately flag. If its set GET OUT!
|
|
// This flag means that the print processor was not able to
|
|
// correclty set the AccessToken of the primage thread of this
|
|
// process to imporsonate the user which submitted the print job.
|
|
// because of this we immediately exit.
|
|
//
|
|
if (*(pData->pdwFlags) & PS_SHAREDMEM_SECURITY_ABORT ) {
|
|
#ifdef MYPSDEBUG
|
|
printf("\nSFMPSEXE: Aborting due to security violation request from sfmpsprt");
|
|
#endif
|
|
PsCleanUpAndExitProcess(pData,TRUE);
|
|
}
|
|
|
|
if (!OpenPrinter(pData->pDocumentPrintDocName,
|
|
&pData->hPrinter,
|
|
(LPPRINTER_DEFAULTS) NULL)) {
|
|
PsLogEventAndIncludeLastError(EVENT_PSTODIB_INIT_FAILED,TRUE);
|
|
PsCleanUpAndExitProcess(pData,TRUE);
|
|
}
|
|
|
|
// if there was no devmode get the default one...
|
|
if ( pData->printEnv.lpDevmode == (LPDEVMODE) NULL) {
|
|
PsGetDefaultDevmode( pData );
|
|
}
|
|
|
|
|
|
PsMakeDefaultDevmodeModsAndSetupResolution( pData, &psDibParms );
|
|
|
|
|
|
// Now build up the structure for Starting PStoDIB
|
|
psDibParms.uiOpFlags = 0; //Clear out to begin with..
|
|
psDibParms.fpEventProc = PsPrintCallBack;
|
|
psDibParms.hPrivateData = (HANDLE) pData;
|
|
|
|
//
|
|
// Now before we kick off the interpreter lets read in the beg of the job
|
|
// and decide if the data is to be interpreted binary.
|
|
//
|
|
if(PsHandleBinaryFileLogicAndReturnBinaryStatus( pData )) {
|
|
//
|
|
// Its a binary job so set the flag telling the interpreter such
|
|
//
|
|
|
|
psDibParms.uiOpFlags |= PSTODIBFLAGS_INTERPRET_BINARY;
|
|
|
|
#ifdef MYPSDEBUG
|
|
printf("\nSFMPSEXE:Binary requested");
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
bRetVal = !PStoDIB(&psDibParms);
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// This function will clean up and call ExitProcess()
|
|
// thus we will NEVER get past this code
|
|
//
|
|
PsCleanUpAndExitProcess( pData, bRetVal);
|
|
|
|
// keep the compiler happy...
|
|
//
|
|
return(0);
|
|
}
|
|
|
|
|
|
/*** PsCleanUpAndExitProcess
|
|
*
|
|
* This function cleans up any resources allocated, then calls ExitProcess
|
|
* to terminate.
|
|
*
|
|
*
|
|
* Entry:
|
|
* pData = Pointer to current job structure
|
|
* bAbort = if true we are aborting.
|
|
*
|
|
* Returns:
|
|
* Never returns ANYTHING process actually ends HERE!
|
|
*/
|
|
VOID
|
|
PsCleanUpAndExitProcess(
|
|
IN PPSEXEDATA pData,
|
|
IN BOOL bAbort )
|
|
{
|
|
|
|
// First clean up the DC, if we had one...
|
|
//
|
|
if (pData->hDC != (HDC) NULL) {
|
|
|
|
if (bAbort) {
|
|
AbortDoc( pData->hDC );
|
|
} else {
|
|
EndDoc( pData->hDC );
|
|
}
|
|
DeleteDC( pData->hDC);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Now reset the error flag to error if we never did the startdoc
|
|
// this will force the spooler to remove the job.
|
|
//
|
|
|
|
if ( !(pData->printEnv.dwFlags & PS_PRINT_STARTDOC_INITIATED ) ){
|
|
bAbort = TRUE;
|
|
}
|
|
|
|
|
|
// Clean up the devmode if we allocated it
|
|
if (pData->printEnv.dwFlags & PS_PRINT_FREE_DEVMODE) {
|
|
LocalFree( (HLOCAL) pData->printEnv.lpDevmode);
|
|
}
|
|
|
|
// Clean up the printer handle
|
|
//
|
|
if (pData->hPrinter != (HANDLE) NULL) {
|
|
ClosePrinter( pData->hPrinter);
|
|
}
|
|
|
|
// Close the semaphore event
|
|
if (pData->semPaused != (HANDLE) NULL) {
|
|
CloseHandle( pData->semPaused);
|
|
}
|
|
if (pData->pShared != (LPVOID) NULL) {
|
|
UnmapViewOfFile( (LPVOID) pData->pShared);
|
|
}
|
|
if (pData->hShared != (HANDLE) NULL) {
|
|
CloseHandle( pData->hShared);
|
|
}
|
|
|
|
ExitProcess(bAbort ? PSEXE_ERROR_EXIT:PSEXE_OK_EXIT);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
IsJobFromMac(
|
|
IN PPSEXEDATA pData
|
|
)
|
|
{
|
|
PJOB_INFO_2 pji2GetJob=NULL;
|
|
DWORD dwNeeded;
|
|
DWORD dwRetCode;
|
|
BOOL fJobCameFromMac;
|
|
|
|
|
|
fJobCameFromMac = FALSE;
|
|
|
|
//
|
|
// get pParameters field of the jobinfo to see if this job came from a Mac
|
|
//
|
|
|
|
dwNeeded = 2000;
|
|
while (1)
|
|
{
|
|
pji2GetJob = LocalAlloc( LMEM_FIXED, dwNeeded );
|
|
if (pji2GetJob == NULL)
|
|
{
|
|
dwRetCode = GetLastError();
|
|
break;
|
|
}
|
|
|
|
dwRetCode = 0;
|
|
if (!GetJob( pData->hPrinter,pData->JobId, 2,
|
|
(LPBYTE)pji2GetJob, dwNeeded, &dwNeeded ))
|
|
{
|
|
dwRetCode = GetLastError();
|
|
}
|
|
|
|
if ( dwRetCode == ERROR_INSUFFICIENT_BUFFER )
|
|
{
|
|
LocalFree(pji2GetJob);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (dwRetCode == 0)
|
|
{
|
|
//
|
|
// if there is pParameter field present, and if it matches with our string,
|
|
// then the job came from a Mac
|
|
//
|
|
if (pji2GetJob->pParameters)
|
|
{
|
|
if ( (wcslen(pji2GetJob->pParameters) == LSIZE_FC) &&
|
|
(_wcsicmp(pji2GetJob->pParameters, LFILTERCONTROL) == 0) )
|
|
{
|
|
fJobCameFromMac = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pji2GetJob)
|
|
{
|
|
LocalFree(pji2GetJob);
|
|
}
|
|
|
|
return(fJobCameFromMac);
|
|
}
|
|
|