/*++ 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 #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; }