windows-nt/Source/XPSP1/NT/printscan/fax/print/faxprint/faxui/docevent.c

1874 lines
42 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
docevent.c
Abstract:
Implementation of DrvDocumentEvent
Environment:
Fax driver user interface
Revision History:
01/13/96 -davidx-
Created it.
mm/dd/yy -author-
description
--*/
#include "faxui.h"
#include "tapiutil.h"
#include <gdispool.h>
#include "prtcovpg.h"
#include "jobtag.h"
#include "faxreg.h"
//
// Data structure passed in during CREATEDCPRE document event
//
typedef struct {
LPTSTR pDriverName; // driver name
LPTSTR pPrinterName; // printer name
PDEVMODE pdmInput; // input devmode
ULONG fromCreateIC; // whether called from CreateIC
} CREATEDCDATA, *PCREATEDCDATA;
//
// Data structure passed in during ESCAPE document event
//
typedef struct {
ULONG iEscape; // nEscape parameter passed to ExtEscape
ULONG cbInput; // cbInput parameter passed to ExtEscape
LPCSTR pInput; // pszInData parameter passed to ExtEscape
} ESCAPEDATA, *PESCAPEDATA;
//
// Check if a document event requires a device context
//
#define DocEventRequiresDC(iEsc) \
((iEsc) >= DOCUMENTEVENT_RESETDCPRE && (iEsc) <= DOCUMENTEVENT_LAST)
//
// Present the Send Fax Wizard to the user
//
BOOL
SendFaxWizard(
PUSERMEM pUserMem
);
BOOL
DoFirstTimeInitStuff(
HWND hwnd,
BOOL WarnOnPrint
);
PUSERMEM
GetPDEVUserMem(
HDC hdc
)
/*++
Routine Description:
Retrieve a pointer to the user mode memory structure associated with a PDEV
Arguments:
hdc - Specifies the printer device context
Return Value:
Pointer to user mode memory structure, NULL if there is an error
--*/
{
PUSERMEM pUserMem;
//
// Get a pointer to the user mode memory structure associated
// with the specified device context
//
EnterDrvSem();
pUserMem = gUserMemList;
while (pUserMem && hdc != pUserMem->hdc)
pUserMem = pUserMem->pNext;
LeaveDrvSem();
//
// Make sure the user memory structure is valid
//
if (pUserMem) {
if (! ValidPDEVUserMem(pUserMem)) {
Error(("Corrupted user mode memory structure\n"));
pUserMem = NULL;
}
} else
Error(("DC has no associated user mode memory structure\n"));
return pUserMem;
}
VOID
FreeRecipientList(
PUSERMEM pUserMem
)
/*++
Routine Description:
Free up the list of recipients associated with each fax job
Arguments:
pUserMem - Points to the user mode memory structure
Return Value:
NONE
--*/
{
PRECIPIENT pNextRecipient, pFreeRecipient;
//
// Free the list of recipients
//
pNextRecipient = pUserMem->pRecipients;
while (pNextRecipient) {
pFreeRecipient = pNextRecipient;
pNextRecipient = pNextRecipient->pNext;
FreeRecipient(pFreeRecipient);
}
pUserMem->pRecipients = NULL;
}
VOID
FreePDEVUserMem(
PUSERMEM pUserMem
)
/*++
Routine Description:
Free up the user mode memory associated with each PDEV
Arguments:
pUserMem - Points to the user mode memory structure
Return Value:
NONE
--*/
{
if (pUserMem) {
FreeRecipientList(pUserMem);
MemFree(pUserMem->pPrinterName);
MemFree(pUserMem->pSubject);
MemFree(pUserMem->pNoteMessage);
MemFree(pUserMem->pEnvVar);
MemFree(pUserMem->pPrintFile);
MemFree(pUserMem);
}
}
INT
DocEventCreateDCPre(
HANDLE hPrinter,
HDC hdc,
PCREATEDCDATA pCreateDCData,
PDEVMODE *ppdmOutput
)
/*++
Routine Description:
Handle CREATEDCPRE document event
Arguments:
hPrinter - Handle to the printer object
hdc - Specifies the printer device context
pCreateDCData - Pointer to CREATEDCDATA structure passed in from GDI
ppdmOutput - Buffer for returning a devmode pointer
Return Value:
Return value for DrvDocumentEvent
--*/
{
PUSERMEM pUserMem;
PPRINTER_INFO_2 pPrinterInfo2 = NULL;
Verbose(("Document event: CREATEDCPRE%s\n", pCreateDCData->fromCreateIC ? "*" : ""));
*ppdmOutput = NULL;
//
// Allocate space for user mode memory data structure
//
if ((pUserMem = MemAllocZ(sizeof(USERMEM))) == NULL ||
(pPrinterInfo2 = MyGetPrinter(hPrinter, 2)) == NULL ||
(pUserMem->pPrinterName = DuplicateString(pPrinterInfo2->pPrinterName)) == NULL)
{
Error(("Memory allocation failed\n"));
MemFree(pUserMem);
MemFree(pPrinterInfo2);
return DOCUMENTEVENT_FAILURE;
}
//
// Merge the input devmode with the driver and system defaults
//
pUserMem->hPrinter = hPrinter;
pUserMem->isLocalPrinter = (pPrinterInfo2->pServerName == NULL);
GetCombinedDevmode(&pUserMem->devmode, pCreateDCData->pdmInput, hPrinter, pPrinterInfo2, FALSE);
MemFree(pPrinterInfo2);
//
// Special code path for EFC server printing - if FAXDM_EFC_SERVER bit is
// set in DMPRIVATE.flags, then we'll bypass the fax wizard and let the
// job through without any intervention.
//
if (pUserMem->devmode.dmPrivate.flags & FAXDM_NO_WIZARD) {
pUserMem->directPrinting = TRUE;
}
//
// Mark the private fields of our devmode
//
MarkPDEVUserMem(pUserMem);
*ppdmOutput = (PDEVMODE) &pUserMem->devmode;
return DOCUMENTEVENT_SUCCESS;
}
INT
DocEventResetDCPre(
HDC hdc,
PUSERMEM pUserMem,
PDEVMODE pdmInput,
PDEVMODE *ppdmOutput
)
/*++
Routine Description:
Handle RESETDCPRE document event
Arguments:
hdc - Specifies the printer device context
pUserMem - Points to the user mode memory structure
pdmInput - Points to the input devmode passed to ResetDC
ppdmOutput - Buffer for returning a devmode pointer
Return Value:
Return value for DrvDocumentEvent
--*/
{
if (pdmInput == (PDEVMODE) &pUserMem->devmode) {
//
// ResetDC was called by ourselves - assume the devmode is already valid
//
} else {
//
// Merge the input devmode with driver and system default
//
GetCombinedDevmode(&pUserMem->devmode, pdmInput, pUserMem->hPrinter, NULL, TRUE);
//
// Mark the private fields of our devmode
//
MarkPDEVUserMem(pUserMem);
}
*ppdmOutput = (PDEVMODE) &pUserMem->devmode;
return DOCUMENTEVENT_SUCCESS;
}
BOOL
IsPrintingToFile(
LPCTSTR pDestStr
)
/*++
Routine Description:
Check if the destination of a print job is a file.
Arguments:
pDestStr - Job destination specified in DOCINFO.lpszOutput
Return Value:
TRUE if the destination is a disk file, FALSE otherwse
--*/
{
DWORD fileAttrs, fileType;
HANDLE hFile;
//
// If the destination is NULL, then we're not printing to file
//
// Otherwise, attempt to use the destination string as the name of a file.
// If we failed to get file attributes or the name refers to a directory,
// then we're not printing to file.
//
if (pDestStr == NULL) {
return FALSE;
}
//
// make sure it's not a directory
//
fileAttrs = GetFileAttributes(pDestStr);
if (fileAttrs != 0xffffffff) {
if (fileAttrs & FILE_ATTRIBUTE_DIRECTORY) {
return FALSE;
}
}
//
// check if file exists...if it doesn't try to create it.
//
hFile = CreateFile(pDestStr, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
hFile = CreateFile(pDestStr, 0, 0, NULL, CREATE_NEW, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
return FALSE;
}
}
//
// verify that this file is really a disk file, not a link to LPT1 or something evil like that
//
fileType = GetFileType(hFile);
CloseHandle(hFile);
if ((fileType & FILE_TYPE_DISK)==0) {
return FALSE;
}
//
// it must be a file
//
return TRUE;
}
INT
DocEventStartDocPre(
HDC hdc,
PUSERMEM pUserMem,
LPDOCINFO pDocInfo
)
/*++
Routine Description:
Handle STARTDOCPRE document event
Arguments:
hdc - Specifies the printer device context
pUserMem - Points to the user mode memory structure
pDocInfo - Points to DOCINFO structure that was passed in from GDI
Return Value:
Return value for DrvDocumentEvent
--*/
{
//
// Initialize user mode memory structure
//
pUserMem->pageCount = 0;
FreeRecipientList(pUserMem);
//
// Present the fax wizard here if necessary
//
if (pDocInfo && IsPrintingToFile(pDocInfo->lpszOutput)) {
//
// Printing to file case: don't get involved
//
Warning(("Printing direct: %ws\n", pDocInfo->lpszOutput));
pUserMem->jobType = JOBTYPE_DIRECT;
} else {
//
// check to see if we should print to file
//
LPTSTR pEnvVar;
DWORD EnvSize;
EnvSize = GetEnvironmentVariable( FAX_ENVVAR_PRINT_FILE, NULL, 0 );
if (EnvSize) {
pEnvVar = (LPTSTR) MemAllocZ( EnvSize * sizeof(TCHAR) );
if (pEnvVar) {
if (GetEnvironmentVariable( FAX_ENVVAR_PRINT_FILE, pEnvVar, EnvSize )) {
pUserMem->directPrinting = TRUE;
pUserMem->pPrintFile = pEnvVar;
pUserMem->jobType = JOBTYPE_DIRECT;
pDocInfo->lpszOutput = pEnvVar;
return DOCUMENTEVENT_SUCCESS;
}
}
}
//
// Normal fax print job. Present the send fax wizard.
// If the user selected cancel, then return -2 to GDI.
//
if (!DoFirstTimeInitStuff(NULL, TRUE) ||
! SendFaxWizard(pUserMem)) {
SetLastError(ERROR_CANCELLED);
return -2;
}
Assert(pUserMem->pRecipients);
pUserMem->jobType = JOBTYPE_NORMAL;
}
return DOCUMENTEVENT_SUCCESS;
}
VOID
FreeCoverPageFields(
PCOVERPAGEFIELDS pCPFields
)
/*++
Routine Description:
Free up memory used to hold the cover page information
Arguments:
pCPFields - Points to a COVERPAGEFIELDS structure
Return Value:
NONE
--*/
{
if (pCPFields == NULL)
return;
//
// NOTE: We don't need to free the following fields here because they're
// allocated and freed elsewhere and we're only using a copy of the pointer:
// RecName
// RecFaxNumber
// Note
// Subject
//
MemFree(pCPFields->SdrName);
MemFree(pCPFields->SdrFaxNumber);
MemFree(pCPFields->SdrCompany);
MemFree(pCPFields->SdrAddress);
MemFree(pCPFields->SdrTitle);
MemFree(pCPFields->SdrDepartment);
MemFree(pCPFields->SdrOfficeLocation);
MemFree(pCPFields->SdrHomePhone);
MemFree(pCPFields->SdrOfficePhone);
MemFree(pCPFields->NumberOfPages);
MemFree(pCPFields->TimeSent);
MemFree(pCPFields);
}
PCOVERPAGEFIELDS
CollectCoverPageFields(
PUSERMEM pUserMem,
DWORD pageCount
)
/*++
Routine Description:
Collect cover page information into a COVERPAGEFIELDS structure
Arguments:
pUserMem - Pointer to user mode data structure
pageCount - Total number of pages (including cover pages)
Return Value:
Pointer to a COVERPAGEFIELDS structure, NULL if there is an error
--*/
#define FillCoverPageField(field, pValueName) { \
buffer = GetRegistryString(hRegKey, pValueName, TEXT("")); \
if (! IsEmptyString(buffer)) { \
pCPFields->field = DuplicateString(buffer); \
MemFree(buffer); \
} \
}
{
PCOVERPAGEFIELDS pCPFields;
LPTSTR buffer;
HKEY hRegKey;
INT dateTimeLen;
//
// Allocate memory to hold the top level structure
// and open the user info registry key for reading
//
if (! (pCPFields = MemAllocZ(sizeof(COVERPAGEFIELDS))) ||
! (hRegKey = GetUserInfoRegKey(REGKEY_FAX_USERINFO, REG_READWRITE)))
{
FreeCoverPageFields(pCPFields);
return NULL;
}
//
// Read sender information from the registry
//
pCPFields->ThisStructSize = sizeof(COVERPAGEFIELDS);
FillCoverPageField(SdrName, REGVAL_FULLNAME);
FillCoverPageField(SdrCompany, REGVAL_COMPANY);
FillCoverPageField(SdrAddress, REGVAL_ADDRESS);
FillCoverPageField(SdrTitle, REGVAL_TITLE);
FillCoverPageField(SdrDepartment, REGVAL_DEPT);
FillCoverPageField(SdrOfficeLocation, REGVAL_OFFICE);
FillCoverPageField(SdrHomePhone, REGVAL_HOME_PHONE);
FillCoverPageField(SdrOfficePhone, REGVAL_OFFICE_PHONE);
FillCoverPageField(SdrFaxNumber, REGVAL_FAX_NUMBER);
RegCloseKey(hRegKey);
//
// Number of pages and current local system time
//
if (pCPFields->NumberOfPages = MemAllocZ(sizeof(TCHAR) * 16))
wsprintf(pCPFields->NumberOfPages, TEXT("%d"), pageCount);
//
// When the fax was sent
//
dateTimeLen = 128;
if (pCPFields->TimeSent = MemAllocZ(sizeof(TCHAR) * dateTimeLen)) {
LPTSTR p = pCPFields->TimeSent;
INT cch;
GetDateFormat(LOCALE_USER_DEFAULT, 0, NULL, NULL, p, dateTimeLen);
cch = _tcslen(p);
p += cch;
if (++cch < dateTimeLen) {
*p++ = ' ';
dateTimeLen -= cch;
GetTimeFormat(LOCALE_USER_DEFAULT, 0, NULL, NULL, p, dateTimeLen);
}
}
return pCPFields;
}
DWORD
FaxTimeToJobTime(
DWORD faxTime
)
/*++
Routine Description:
Convert fax time to spooler job time:
Fax time is a DWORD whose low-order WORD represents hour value and
high-order WORD represents minute value. Spooler job time is a DWORD
value expressing minutes ellapsed since 12:00 AM GMT.
Arguments:
faxTime - Specifies the fax time to be converted
Return Value:
Spooler job time corresponding to the input fax time
--*/
{
TIME_ZONE_INFORMATION timeZoneInfo;
LONG jobTime;
//
// Convert fax time to minutes pass midnight
//
jobTime = LOWORD(faxTime) * 60 + HIWORD(faxTime);
//
// Take time zone information in account - Add one full
// day to take care of the case where the bias is negative.
//
switch (GetTimeZoneInformation(&timeZoneInfo)) {
case TIME_ZONE_ID_DAYLIGHT:
jobTime += timeZoneInfo.DaylightBias;
case TIME_ZONE_ID_STANDARD:
case TIME_ZONE_ID_UNKNOWN:
jobTime += timeZoneInfo.Bias + MINUTES_PER_DAY;
break;
default:
Error(("GetTimeZoneInformation failed: %d\n", GetLastError()));
break;
}
//
// Make sure the time value is less than one day
//
return jobTime % MINUTES_PER_DAY;
}
PVOID
MyGetJob(
HANDLE hPrinter,
DWORD level,
DWORD jobId
)
/*++
Routine Description:
Wrapper function for spooler API GetJob
Arguments:
hPrinter - Handle to the printer object
level - Level of JOB_INFO structure interested
jobId - Specifies the job ID
Return Value:
Pointer to a JOB_INFO structure, NULL if there is an error
--*/
{
PBYTE pJobInfo = NULL;
DWORD cbNeeded;
if (!GetJob(hPrinter, jobId, level, NULL, 0, &cbNeeded) &&
GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
(pJobInfo = MemAlloc(cbNeeded)) &&
GetJob(hPrinter, jobId, level, pJobInfo, cbNeeded, &cbNeeded))
{
return pJobInfo;
}
Error(("GetJob failed: %d\n", GetLastError()));
MemFree(pJobInfo);
return NULL;
}
BOOL
SetJobInfoAndTime(
HANDLE hPrinter,
DWORD jobId,
LPTSTR pJobParam,
PDMPRIVATE pdmPrivate
)
/*++
Routine Description:
Change the devmode and start/stop times associated with a cover page job
Arguments:
hPrinter - Specifies the printer object
jobId - Specifies the job ID
pJobParam - Specifies the fax job parameters
pdmPrivate - Specifies private devmode information
Return Value:
TRUE if successful, FALSE if there is an error
--*/
{
JOB_INFO_2 *pJobInfo2;
BOOL result = FALSE;
//
// Get the current job information
//
if (pJobInfo2 = MyGetJob(hPrinter, 2, jobId)) {
//
// set the time to send to be now, always
//
Warning(("Fax job parameters: %ws\n", pJobParam));
pJobInfo2->pParameters = pJobParam;
pJobInfo2->Position = JOB_POSITION_UNSPECIFIED;
pJobInfo2->pDevMode = NULL;
pJobInfo2->UntilTime = pJobInfo2->StartTime;
if (! (result = SetJob(hPrinter, jobId, 2, (PBYTE) pJobInfo2, 0))) {
Error(("SetJob failed: %d\n", GetLastError()));
}
MemFree(pJobInfo2);
}
return result;
}
BOOL
ChainFaxJobs(
HANDLE hPrinter,
DWORD parentJobId,
DWORD childJobId
)
/*++
Routine Description:
Tell the spooler to chain up two print jobs
Arguments:
hPrinter - Specifies the printer object
parentJobId - Specifies the job to chain from
childJobId - Specifies the job to chain to
Return Value:
TRUE if successful, FALSE if there is an error
--*/
{
JOB_INFO_3 jobInfo3 = { parentJobId, childJobId };
Warning(("Chaining cover page job to body job: %d => %d\n", parentJobId, childJobId));
return SetJob(hPrinter, parentJobId, 3, (PBYTE) &jobInfo3, 0);
}
LPTSTR
GetJobName(
HANDLE hPrinter,
DWORD jobId
)
/*++
Routine Description:
Return the name of the specified print job
Arguments:
hPrinter - Specifies the printer object
jobId - Specifies the fax body job
Return Value:
Pointer to the job name string, NULL if there is an error
--*/
{
JOB_INFO_1 *pJobInfo1;
LPTSTR pJobName;
//
// Get the information about the specified job and
// return a copy of the job name string
//
if (pJobInfo1 = MyGetJob(hPrinter, 1, jobId)) {
pJobName = DuplicateString(pJobInfo1->pDocument);
MemFree(pJobInfo1);
} else
pJobName = NULL;
return pJobName;
}
LPTSTR
ComposeFaxJobName(
LPTSTR pBodyDocName,
LPTSTR pRecipientName
)
/*++
Routine Description:
Compose the document name string for a cover page job
Arguments:
pBodyDocName - Specifies the name of the body job
pRecipient - Specifies the recipient's name
Return Value:
Pointer to cover page job name string, NULL if there is an error
--*/
#define DOCNAME_FORMAT_STRING TEXT("%s - %s")
{
LPTSTR pCoverJobName;
if (pBodyDocName == NULL) {
//
// If the body job name is NULL somehow, then simply
// use the recipient's name as the cover page job name.
//
pCoverJobName = DuplicateString(pRecipientName);
} else if (pCoverJobName = MemAlloc(SizeOfString(DOCNAME_FORMAT_STRING) +
SizeOfString(pBodyDocName) +
SizeOfString(pRecipientName)))
{
//
// Otherwise, the cover page job name is generated by
// concatenating the recipient's name with the body job name.
//
wsprintf(pCoverJobName, DOCNAME_FORMAT_STRING, pRecipientName, pBodyDocName);
}
return pCoverJobName;
}
LPTSTR
GetBaseNoteFilename(
VOID
)
/*++
Routine Description:
Get the name of base cover page file in system32 directory
Arguments:
argument-name - description of argument
Return Value:
Pointer to name of base cover page file
NULL if there is an error
--*/
#define BASENOTE_FILENAME TEXT("\\basenote.cov")
{
TCHAR systemDir[MAX_PATH];
LPTSTR pBaseNoteName = NULL;
COVDOCINFO covDocInfo;
if (GetSystemDirectory(systemDir, MAX_PATH) &&
(pBaseNoteName = MemAlloc(SizeOfString(systemDir) + SizeOfString(BASENOTE_FILENAME))))
{
_tcscpy(pBaseNoteName, systemDir);
_tcscat(pBaseNoteName, BASENOTE_FILENAME);
Verbose(("Base cover page filename: %ws\n", pBaseNoteName));
if (PrintCoverPage(NULL, NULL, pBaseNoteName, &covDocInfo) ||
! (covDocInfo.Flags & COVFP_NOTE) ||
! (covDocInfo.Flags & COVFP_SUBJECT))
{
Error(("Invalid base cover page file: %ws\n", pBaseNoteName));
MemFree(pBaseNoteName);
pBaseNoteName = NULL;
}
}
return pBaseNoteName;
}
LPTSTR
ComposeFaxJobParameter(
PUSERMEM pUserMem,
PCOVERPAGEFIELDS pCPFields
)
/*++
Routine Description:
Assemble fax job parameters into a single tagged string
Arguments:
pUserMem - Points to user mode memory structure
pCPFields - Points to cover page field information
Return Value:
Pointer to fax job parameter string, NULL if there is an error
--*/
#define NUM_JOBPARAM_TAGS 10
{
//
// Tags used to pass information about fax jobs
//
static LPTSTR faxtagNames[NUM_JOBPARAM_TAGS] = {
FAXTAG_RECIPIENT_NUMBER,
FAXTAG_RECIPIENT_NAME,
FAXTAG_TSID,
FAXTAG_SENDER_NAME,
FAXTAG_SENDER_COMPANY,
FAXTAG_SENDER_DEPT,
FAXTAG_BILLING_CODE,
FAXTAG_EMAIL_NAME,
FAXTAG_WHEN_TO_SEND,
FAXTAG_SEND_AT_TIME
};
LPTSTR faxtagValues[NUM_JOBPARAM_TAGS] = {
pCPFields->RecFaxNumber,
pCPFields->RecName,
pCPFields->SdrFaxNumber,
pCPFields->SdrName,
pCPFields->SdrCompany,
pCPFields->SdrDepartment,
pUserMem->devmode.dmPrivate.billingCode,
pUserMem->devmode.dmPrivate.emailAddress,
NULL,
NULL
};
LPTSTR pJobParam, p;
INT index, size;
TCHAR SendAtTime[16];
//
// create the sendattime string
//
if (pUserMem->devmode.dmPrivate.whenToSend == SENDFAX_AT_CHEAP) {
faxtagValues[8] = TEXT("cheap");
}
if (pUserMem->devmode.dmPrivate.whenToSend == SENDFAX_AT_TIME) {
wsprintf( SendAtTime, TEXT("%02d:%02d"),
pUserMem->devmode.dmPrivate.sendAtTime.Hour,
pUserMem->devmode.dmPrivate.sendAtTime.Minute
);
faxtagValues[8] = TEXT("at");
faxtagValues[9] = SendAtTime;
}
//
// Figure out the total length of the tagged string
//
for (index=size=0; index < NUM_JOBPARAM_TAGS; index++) {
if (faxtagValues[index] && !IsEmptyString(faxtagValues[index]))
size += SizeOfString(faxtagNames[index]) + SizeOfString(faxtagValues[index]);
}
if (size == 0 || (pJobParam = p = MemAlloc(size)) == NULL)
return NULL;
//
// Assemble fax job parameters into a single tagged string
//
for (index=0; index < NUM_JOBPARAM_TAGS; index++) {
if (faxtagValues[index] && !IsEmptyString(faxtagValues[index])) {
_tcscpy(p, faxtagNames[index]);
p += _tcslen(p);
_tcscpy(p, faxtagValues[index]);
p += _tcslen(p);
}
}
return pJobParam;
}
BOOL
DoCoverPageRendering(
HDC hdc,
PUSERMEM pUserMem
)
/*++
Routine Description:
Render a cover page for each recipient
Arguments:
hdc - Handle to the current printer device context
pUserMem - Points to user mode memory structure
Return Value:
TRUE if successful, FALSE otherwise
--*/
{
PCOVERPAGEFIELDS pCPFields;
PRECIPIENT pRecipient;
DOCINFO docinfo;
INT newJobId, lastJobId, cCoverPagesSent;
LPTSTR pBaseNoteName = NULL;
PDMPRIVATE pdmPrivate = &pUserMem->devmode.dmPrivate;
HANDLE hPrinter = pUserMem->hPrinter;
DWORD bodyJobId = pUserMem->jobId;
LPTSTR pBodyDocName, pJobParam;
BOOL sendCoverPage;
DWORD pageCount;
//
// Determine if we need a cover page or not
//
if ((sendCoverPage = pdmPrivate->sendCoverPage) && IsEmptyString(pUserMem->coverPage)) {
Warning(("Missing cover page file\n"));
sendCoverPage = FALSE;
}
//
// Check if we need an extra cover page for rendering note/subject fields
//
pageCount = pUserMem->pageCount;
if (sendCoverPage)
pageCount++;
if (((pUserMem->pSubject && !(pUserMem->noteSubjectFlag & COVFP_SUBJECT)) ||
(pUserMem->pNoteMessage && !(pUserMem->noteSubjectFlag & COVFP_NOTE))) &&
(pBaseNoteName = GetBaseNoteFilename()))
{
pageCount++;
}
//
// Collect cover page information
//
if ((pCPFields = CollectCoverPageFields(pUserMem, pageCount)) == NULL) {
Error(("Couldn't collect cover page information\n"));
return FALSE;
}
//
// Fill out a DOCINFO structure which is passed to StartDoc
//
memset(&docinfo, 0, sizeof(docinfo));
docinfo.cbSize = sizeof(docinfo);
pBodyDocName = GetJobName(hPrinter, bodyJobId);
//
// We assume the fax body job has already been paused
// Use a separate cover page for each recipient
//
lastJobId = cCoverPagesSent = 0;
for (pRecipient=pUserMem->pRecipients; pRecipient; pRecipient=pRecipient->pNext) {
//
// Fill out other fields of cover page information
//
pCPFields->Subject = pUserMem->pSubject;
pCPFields->Note = pUserMem->pNoteMessage;
//
// Get recipient's name and fax number
//
pCPFields->RecName = pRecipient->pName;
pCPFields->RecFaxNumber = pRecipient->pAddress;
//
// Start a cover page job
//
docinfo.lpszDocName = ComposeFaxJobName(pBodyDocName, pRecipient->pName);
pJobParam = ComposeFaxJobParameter(pUserMem, pCPFields);
if ((newJobId = StartDoc(hdc, &docinfo)) > 0) {
BOOL rendered = FALSE;
COVDOCINFO covDocInfo;
//
// Pass fax job parameters using JOB_INFO_2.pParameters field.
//
if (! SetJob(hPrinter, newJobId, 0, NULL, JOB_CONTROL_PAUSE) ||
! SetJobInfoAndTime(hPrinter,
newJobId,
pJobParam ? pJobParam : pCPFields->RecFaxNumber,
pdmPrivate))
{
Error(("Couldn't modify the fax job\n"));
} else if (! sendCoverPage) {
//
// If the user chose not to include cover page,
// the cover page job will be empty
//
rendered = TRUE;
} else if (StartPage(hdc) > 0) {
//
// Call the library function to render the cover page.
//
rendered = PrintCoverPage( hdc, pCPFields, pUserMem->coverPage, &covDocInfo );
if (rendered) {
Error(("PrintCoverPage failed: %d\n", rendered ));
rendered = FALSE;
} else {
rendered = TRUE;
}
EndPage(hdc);
}
//
// Chain the cover page job to the fax body job if no error occured
//
if (rendered && ChainFaxJobs(hPrinter, newJobId, bodyJobId)) {
//
// Check if we need an extra page for note/subject fields
//
if (pBaseNoteName) {
if (StartPage(hdc) > 0) {
DWORD ec;
if (pUserMem->noteSubjectFlag & COVFP_SUBJECT)
pCPFields->Subject = NULL;
if (pUserMem->noteSubjectFlag & COVFP_NOTE)
pCPFields->Note = NULL;
ec = PrintCoverPage(hdc, pCPFields, pBaseNoteName, &covDocInfo);
if (ec) {
Error(("PrintCoverPage failed: %d\n", ec));
}
EndPage(hdc);
} else
Error(("StartPage failed: %d\n", GetLastError()));
}
if (lastJobId != 0)
SetJob(hPrinter, lastJobId, 0, NULL, JOB_CONTROL_RESUME);
lastJobId = newJobId;
EndDoc(hdc);
cCoverPagesSent++;
} else {
AbortDoc(hdc);
newJobId = 0;
}
}
MemFree((PVOID)docinfo.lpszDocName);
MemFree((PVOID)pJobParam);
//
// Indicate to the user about the fact that we failed to render the
// for the current recipient
//
if (newJobId <= 0)
DisplayMessageDialog(NULL, 0, 0, IDS_CPRENDER_FAILED, pRecipient->pName);
}
MemFree(pBaseNoteName);
MemFree(pBodyDocName);
FreeCoverPageFields(pCPFields);
//
// Resume the last cover page job if it's paused and
// delete the fax body job if no cover page jobs were sent
//
if (lastJobId != 0)
SetJob(hPrinter, lastJobId, 0, NULL, JOB_CONTROL_RESUME);
if (cCoverPagesSent > 0)
SetJob(hPrinter, bodyJobId, 0, NULL, JOB_CONTROL_RESUME);
else {
Error(("Fax job deleted due to an error\n"));
SetJob(hPrinter, bodyJobId, 0, NULL, JOB_CONTROL_DELETE);
}
return cCoverPagesSent > 0;
}
INT
DocEventEndDocPost(
HDC hdc,
PUSERMEM pUserMem
)
/*++
Routine Description:
Handle ENDDOCPOST document event
Arguments:
hdc - Specifies the printer device context
pUserMem - Points to the user mode memory structure
Return Value:
Return value for DrvDocumentEvent
--*/
{
INT result = DOCUMENTEVENT_SUCCESS;
switch (pUserMem->jobType) {
case JOBTYPE_NORMAL:
Warning(("Number of pages printed: %d\n", pUserMem->pageCount));
if (! pUserMem->directPrinting) {
HDC hdcCP = NULL;
DWORD dmFlags, dmFields;
SHORT dmPaperSize, dmOrientation;
PDEVMODE pdmPublic;
PDMPRIVATE pdmPrivate;
//
// Generate a cover page for each recipient and associate
// the cover page job with the main body. Create a new DC
// to rendering the cover page instead of using the existing DC.
//
pdmPublic = &pUserMem->devmode.dmPublic;
pdmPrivate = &pUserMem->devmode.dmPrivate;
dmFlags = pdmPrivate->flags;
pdmPrivate->flags |= FAXDM_NO_WIZARD;
if (pUserMem->cpPaperSize) {
dmFields = pdmPublic->dmFields;
pdmPublic->dmFields &= ~(DM_PAPERWIDTH|DM_PAPERLENGTH|DM_FORMNAME);
pdmPublic->dmFields |= DM_PAPERSIZE;
dmPaperSize = pdmPublic->dmPaperSize;
pdmPublic->dmPaperSize = pUserMem->cpPaperSize;
dmOrientation = pdmPublic->dmOrientation;
pdmPublic->dmOrientation = pUserMem->cpOrientation;
}
if (! (hdcCP = CreateDC(NULL,
pUserMem->pPrinterName,
NULL,
(PDEVMODE) &pUserMem->devmode)) ||
! DoCoverPageRendering(hdcCP, pUserMem))
{
result = DOCUMENTEVENT_FAILURE;
}
if (hdcCP != NULL)
DeleteDC(hdcCP);
if (pUserMem->cpPaperSize) {
pdmPublic->dmFields = dmFields;
pdmPublic->dmPaperSize = dmPaperSize;
pdmPublic->dmOrientation = dmOrientation;
}
pdmPrivate->flags = dmFlags;
//
// Free up the list of recipients
//
FreeRecipientList(pUserMem);
}
break;
}
return result;
}
INT
DrvDocumentEvent(
HANDLE hPrinter,
HDC hdc,
INT iEsc,
ULONG cbIn,
PULONG pjIn,
ULONG cbOut,
PULONG pjOut
)
/*++
Routine Description:
Hook into GDI at various points during the output process
Arguments:
hPrinter - Specifies the printer object
hdc - Handle to the printer DC
iEsc - Why this function is called (see notes below)
cbIn - Size of the input buffer
pjIn - Pointer to the input buffer
cbOut - Size of the output buffer
pjOut - Pointer to the output buffer
Return Value:
DOCUMENTEVENT_SUCCESS - success
DOCUMENTEVENT_UNSUPPORTED - iEsc is not supported
DOCUMENTEVENT_FAILURE - an error occured
NOTE:
DOCUMENTEVENT_CREATEDCPRE
input - pointer to a CREATEDCDATA structure
output - pointer to a devmode that's passed to DrvEnablePDEV
return value -
DOCUMENTEVENT_FAILURE causes CreateDC to fail and nothing else is called
DOCUMENTEVENT_CREATEDCPOST
hdc - NULL if if something failed since CREATEDCPRE
input - pointer to the devmode pointer returned by CREATEDCPRE
return value - ignored
DOCUMENTEVENT_RESETDCPRE
input - pointer to the input devmode passed to ResetDC
output - pointer to a devmode that's passed to the kernel driver
return value -
DOCUMENTEVENT_FAILURE causes ResetDC to fail
and CREATEDCPOST will not be called in that case
DOCUMENTEVENT_RESETDCPOST
return value - ignored
DOCUMENTEVENT_STARTDOCPRE
input - pointer to a DOCINFOW structure
return value -
DOCUMENTEVENT_FAILURE causes StartDoc to fail
and DrvStartDoc will not be called in this case
DOCUMENTEVENT_STARTDOCPOST
return value - ignored
DOCUMENTEVENT_STARTPAGE
return value -
DOCUMENTEVENT_FAILURE causes StartPage to fail
and DrvStartPage will not be called in this case
DOCUMENTEVENT_ENDPAGE
return value - ignored and DrvEndPage always called
DOCUMENTEVENT_ENDDOCPRE
return value - ignored and DrvEndDoc always called
DOCUMENTEVENT_ENDDOCPOST
return value - ignored
DOCUMENTEVENT_ABORTDOC
return value - ignored
DOCUMENTEVENT_DELETEDC
return value - ignored
DOCUMENTEVENT_ESCAPE
input - pointer to a ESCAPEDATA structure
cbOut, pjOut - cbOutput and lpszOutData parameters passed to ExtEscape
return value - ignored
DOCUMENTEVENT_SPOOLED
This flag bit is ORed with other iEsc values if the document is
spooled as metafile rather than printed directly to port.
--*/
{
PUSERMEM pUserMem = NULL;
PDEVMODE pDevmode;
INT result = DOCUMENTEVENT_SUCCESS;
Verbose(("Entering DrvDocumentEvent: %d...\n", iEsc));
//
// Metafile spooling on fax jobs is not currently supported
//
Assert((iEsc & DOCUMENTEVENT_SPOOLED) == 0);
//
// Check if the document event requires a device context
//
if (DocEventRequiresDC(iEsc)) {
if (!hdc || !(pUserMem = GetPDEVUserMem(hdc))) {
Error(("Invalid device context: hdc = %x, iEsc = %d\n", hdc, iEsc));
return DOCUMENTEVENT_FAILURE;
}
}
switch (iEsc) {
case DOCUMENTEVENT_CREATEDCPRE:
Assert(cbIn >= sizeof(CREATEDCDATA) && pjIn && cbOut >= sizeof(PDEVMODE) && pjOut);
result = DocEventCreateDCPre(hPrinter, hdc, (PCREATEDCDATA) pjIn, (PDEVMODE *) pjOut);
break;
case DOCUMENTEVENT_CREATEDCPOST:
//
// Handle CREATEDCPOST document event:
// If CreateDC succeeded, then associate the user mode memory structure
// with the device context. Otherwise, free the user mode memory structure.
//
Assert(cbIn >= sizeof(PVOID) && pjIn);
pDevmode = *((PDEVMODE *) pjIn);
Assert(CurrentVersionDevmode(pDevmode));
pUserMem = ((PDRVDEVMODE) pDevmode)->dmPrivate.pUserMem;
Assert(ValidPDEVUserMem(pUserMem));
if (hdc) {
pUserMem->hdc = hdc;
EnterDrvSem();
pUserMem->pNext = gUserMemList;
gUserMemList = pUserMem;
LeaveDrvSem();
} else
FreePDEVUserMem(pUserMem);
break;
case DOCUMENTEVENT_RESETDCPRE:
Verbose(("Document event: RESETDCPRE\n"));
Assert(cbIn >= sizeof(PVOID) && pjIn && cbOut >= sizeof(PDEVMODE) && pjOut);
result = DocEventResetDCPre(hdc, pUserMem, *((PDEVMODE *) pjIn), (PDEVMODE *) pjOut);
break;
case DOCUMENTEVENT_STARTDOCPRE:
//
// if printing a fax attachment then enable direct printing
//
if (pUserMem->hMutex == NULL) {
pUserMem->hMutex = OpenMutex(MUTEX_ALL_ACCESS,FALSE,FAXXP_MUTEX_NAME);
if (pUserMem->hMutex) {
if (WaitForSingleObject( pUserMem->hMutex, 0) == WAIT_OBJECT_0) {
pUserMem->directPrinting = TRUE;
}
else {
CloseHandle( pUserMem->hMutex ) ;
pUserMem->hMutex = NULL;
}
}
}
//
// normal case if we're bringing up the send wizard
//
if (! pUserMem->directPrinting) {
Assert(cbIn >= sizeof(PVOID) && pjIn);
result = DocEventStartDocPre(hdc, pUserMem, *((LPDOCINFO *) pjIn));
}
//
// we're doing direct printing -- check if this is invoked via mapi-spooler
//
else if (pUserMem->hMutex) {
//
// we own the mutex...make sure we can open the shared memory region.
//
pUserMem->pEnvVar = OpenFileMapping(FILE_MAP_ALL_ACCESS,FALSE,FAXXP_MEM_NAME);
if (!pUserMem->pEnvVar) {
ReleaseMutex( pUserMem->hMutex );
CloseHandle( pUserMem->hMutex );
pUserMem->hMutex = NULL;
} else {
//
// we own the mutex and we have the shared memory region open.
//
// check if we are printing to a file or are doing direct printing for
// the mapi spooler.
//
LPTSTR filename;
filename = (LPTSTR)MapViewOfFile(
pUserMem->pEnvVar,
FILE_MAP_WRITE,
0,
0,
0
);
if (!filename) {
Error(("Failed to map a view of the file: %d\n", pUserMem->pEnvVar));
return DOCUMENTEVENT_FAILURE;
}
if (filename && *filename) {
//
// this is really the filename we want to print to.
//
pUserMem->directPrinting = TRUE;
pUserMem->pPrintFile = DuplicateString(filename);
pUserMem->jobType = JOBTYPE_DIRECT;
(*((LPDOCINFO *) pjIn))->lpszOutput = pUserMem->pPrintFile;
}
UnmapViewOfFile( filename );
}
}
break;
case DOCUMENTEVENT_STARTDOCPOST:
if (!pUserMem->directPrinting && pUserMem->jobType == JOBTYPE_NORMAL) {
//
// Job ID is passed in from GDI
//
Assert(cbIn >= sizeof(DWORD) && pjIn);
pUserMem->jobId = *((LPDWORD) pjIn);
//
// Tell spooler to pause the fax body job so that
// we can associate cover pages with it later
//
if (! SetJob(pUserMem->hPrinter, pUserMem->jobId, 0, NULL, JOB_CONTROL_PAUSE)) {
Error(("Couldn't pause fax body job: %d\n", pUserMem->jobId));
return DOCUMENTEVENT_FAILURE;
}
} else if (pUserMem->pEnvVar) {
LPDWORD pJobId;
//
// Job ID is passed in from GDI
//
Assert(cbIn >= sizeof(DWORD) && pjIn);
pUserMem->jobId = *((LPDWORD) pjIn);
if (!pUserMem->pPrintFile) {
//
// Tell spooler to pause the fax job
// so that the mapi fax transport provider
// can chain this job
//
if (! SetJob(pUserMem->hPrinter, pUserMem->jobId, 0, NULL, JOB_CONTROL_PAUSE)) {
Error(("Couldn't pause fax body job: %d\n", pUserMem->jobId));
return DOCUMENTEVENT_FAILURE;
}
}
pJobId = (LPDWORD)MapViewOfFile(
pUserMem->pEnvVar,
FILE_MAP_WRITE,
0,
0,
0
);
if (!pJobId) {
Error(("Failed to map a view of the file: %d\n", pUserMem->jobId));
return DOCUMENTEVENT_FAILURE;
}
*pJobId = (DWORD) pUserMem->jobId;
UnmapViewOfFile( pJobId );
CloseHandle( pUserMem->pEnvVar );
pUserMem->pEnvVar = NULL;
}
break;
case DOCUMENTEVENT_ENDPAGE:
if (! pUserMem->directPrinting)
pUserMem->pageCount++;
break;
case DOCUMENTEVENT_ENDDOCPOST:
if (! pUserMem->directPrinting)
result = DocEventEndDocPost(hdc, pUserMem);
else if (pUserMem->hMutex) {
HANDLE hEvent = NULL;
hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, FAXXP_EVENT_NAME);
if (hEvent) {
SetEvent(hEvent);
CloseHandle(hEvent) ;
}
ReleaseMutex(pUserMem->hMutex);
CloseHandle(pUserMem->hMutex);
pUserMem->hMutex = NULL;
}
break;
case DOCUMENTEVENT_DELETEDC:
EnterDrvSem();
if (pUserMem == gUserMemList)
gUserMemList = gUserMemList->pNext;
else {
PUSERMEM p;
if (p = gUserMemList) {
while (p->pNext && p->pNext != pUserMem)
p = p->pNext;
if (p->pNext != NULL)
p->pNext = pUserMem->pNext;
else
Error(("Orphaned user mode memory structure!!!\n"));
} else
Error(("gUserMemList shouldn't be NULL!!!\n"));
}
LeaveDrvSem();
FreePDEVUserMem(pUserMem);
break;
case DOCUMENTEVENT_ABORTDOC:
case DOCUMENTEVENT_RESETDCPOST:
case DOCUMENTEVENT_STARTPAGE:
case DOCUMENTEVENT_ENDDOCPRE:
break;
case DOCUMENTEVENT_ESCAPE:
default:
Verbose(("Unsupported DrvDocumentEvent escape: %d\n", iEsc));
result = DOCUMENTEVENT_UNSUPPORTED;
break;
}
return result;
}