/*++ Copyright (c) 1996 Microsoft Corporation Module Name: quryprnt.c Abstract: This file handles the DrvQueryPrintEx spooler API Environment: Win32 subsystem, DriverUI module, user mode Revision History: 02/13/97 -davidx- Implement OEM plugin support. 02/08/97 -davidx- Rewrote it to use common data management functions. 02/04/97 -davidx- Reorganize driver UI to separate ps and uni DLLs. 07/17/96 -amandan- Created it. --*/ #include "precomp.h" // // Forward declaration of local functions // BOOL BFormatDQPMessage(PDEVQUERYPRINT_INFO, INT, ...); BOOL BQueryPrintDevmode(PDEVQUERYPRINT_INFO, PCOMMONINFO); BOOL BQueryPrintForm(PDEVQUERYPRINT_INFO, PCOMMONINFO); BOOL DevQueryPrintEx( PDEVQUERYPRINT_INFO pDQPInfo ) /*++ Routine Description: This function checks whether the job can be printed with DEVMODE passed in. This function will use the following criterias to determine whether the job is printable: - get basic printer information - verify input devmode - verify resolution is supported - verify there is no conflicts between printer feature selections - verify form-to-tray assignment Arguments: pDQPInfo - Points to a DEVQUERYPRINT_INFO structure Return Value: TRUE if the job can be printed with the given DEVMODE, otherwise FALSE --*/ { PCOMMONINFO pci; BOOL bResult; if (pDQPInfo == NULL || pDQPInfo->hPrinter == NULL) return BFormatDQPMessage(pDQPInfo, IDS_DQPERR_PARAM); if (pDQPInfo->pDevMode == NULL) return TRUE; if ((pci = PLoadCommonInfo(pDQPInfo->hPrinter, NULL, 0)) == NULL) return BFormatDQPMessage(pDQPInfo, IDS_DQPERR_COMMONINFO); bResult = BQueryPrintDevmode(pDQPInfo, pci) && BQueryPrintForm(pDQPInfo, pci); if (bResult) { PFN_OEMDevQueryPrintEx pfnOEMDevQueryPrintEx; // // call OEMDevQueryPrintEx entrypoint for each plugin, // or until one of them returns FALSE. // FOREACH_OEMPLUGIN_LOOP(pci) if (HAS_COM_INTERFACE(pOemEntry)) { HRESULT hr; hr = HComOEMDevQueryPrintEx(pOemEntry, &pci->oemuiobj, pDQPInfo, pci->pdm, pOemEntry->pOEMDM); if (hr == E_NOTIMPL) continue; if (!(bResult = SUCCEEDED(hr))) break; } else { if ((pfnOEMDevQueryPrintEx = GET_OEM_ENTRYPOINT(pOemEntry, OEMDevQueryPrintEx)) && !pfnOEMDevQueryPrintEx(&pci->oemuiobj, pDQPInfo, pci->pdm, pOemEntry->pOEMDM)) { ERR(("OEMDevQueryPrintEx failed for '%ws': %d\n", CURRENT_OEM_MODULE_NAME(pOemEntry), GetLastError())); bResult = FALSE; break; } } END_OEMPLUGIN_LOOP } VFreeCommonInfo(pci); return bResult; } BOOL BFormatDQPMessage( PDEVQUERYPRINT_INFO pDQPInfo, INT iMsgResId, ... ) /*++ Routine Description: Format DevQueryPrintEx error message Arguments: pDQPInfo - Points to a DEVQUERYPRINT_INFO structure iMsgResId - Error message format specifier (string resource ID) Return Value: FALSE --*/ #define MAX_FORMAT_STRING 256 #define MAX_DQP_MESSAGE 512 { TCHAR awchFormat[MAX_FORMAT_STRING]; TCHAR awchMessage[MAX_DQP_MESSAGE]; INT iLength; va_list arglist; // // Load the format specifier string resource // and use swprintf to format the error message // va_start(arglist, iMsgResId); if (! LoadString(ghInstance, iMsgResId, awchFormat, MAX_FORMAT_STRING)) awchFormat[0] = NUL; iLength = vswprintf(awchMessage, awchFormat, arglist); if (iLength <= 0) { wcscpy(awchMessage, L"Error"); iLength = wcslen(awchMessage); } va_end(arglist); // // Copy the error message string to DQPInfo // iLength += 1; pDQPInfo->cchNeeded = iLength; if (iLength > (INT) pDQPInfo->cchErrorStr) iLength = pDQPInfo->cchErrorStr; if (pDQPInfo->pszErrorStr && iLength) CopyString(pDQPInfo->pszErrorStr, awchMessage, iLength); return FALSE; } BOOL BQueryPrintDevmode( PDEVQUERYPRINT_INFO pDQPInfo, PCOMMONINFO pci ) /*++ Routine Description: Validate devmode information Arguments: pDQPInfo - Points to a DEVQUERYPRINT_INFO structure pci - Points to basic printer info Return Value: TRUE if successful, FALSE if the job should be held --*/ { INT iRealizedRes, iResX, iResY; PFEATURE pFeature; DWORD dwFeatureIndex, dwOptionIndexOld, dwOptionIndexNew; BOOL bUpdateFormField; // // Validate input devmode // Get printer-sticky properties // Merge doc- and printer-sticky printer feature selections // Fix up combined options array with public devmode info // if (! BFillCommonInfoDevmode(pci, NULL, pDQPInfo->pDevMode)) return BFormatDQPMessage(pDQPInfo, IDS_DQPERR_DEVMODE); if (! BFillCommonInfoPrinterData(pci)) return BFormatDQPMessage(pDQPInfo, IDS_DQPERR_PRINTERDATA); if (! BCombineCommonInfoOptionsArray(pci)) return BFormatDQPMessage(pDQPInfo, IDS_DQPERR_MEMORY); VFixOptionsArrayWithDevmode(pci); // // Remember the paper size option parser picked to support the devmode form // if ((pFeature = GET_PREDEFINED_FEATURE(pci->pUIInfo, GID_PAGESIZE)) == NULL) { ASSERT(FALSE); return BFormatDQPMessage(pDQPInfo, IDS_DQPERR_DEVMODE); } dwFeatureIndex = GET_INDEX_FROM_FEATURE(pci->pUIInfo, pFeature); dwOptionIndexOld = pci->pCombinedOptions[dwFeatureIndex].ubCurOptIndex; if (! ResolveUIConflicts( pci->pRawData, pci->pCombinedOptions, MAX_COMBINED_OPTIONS, MODE_DOCANDPRINTER_STICKY|DONT_RESOLVE_CONFLICT)) { return BFormatDQPMessage(pDQPInfo, IDS_DQPERR_OPTSELECT); } dwOptionIndexNew = pci->pCombinedOptions[dwFeatureIndex].ubCurOptIndex; bUpdateFormField = FALSE; if (dwOptionIndexNew != dwOptionIndexOld) { // // Constraint resolving has changed page size selection, so we need // to update devmode's form fields. // bUpdateFormField = TRUE; } else { FORM_INFO_1 *pForm = NULL; // // Unless the form requested by devmode is not supported on the printer, // we still want to show the original form name in upcoming doc-setting UI. // For example, if input devmode requested "Legal", parser maps it to option // "OEM Legal", but both "Legal" and "OEM Legal" will be shown as supported // forms on the printer, then we should still show "Legal" instead of "OEM Legal" // in UI's PageSize list. However, if input devmode requestd "8.5 x 12", which // won't be shown as a supportd form and it's mapped to "OEM Legal", then we should // show "OEM Legal". // // // pdm->dmFormName won't have a valid form name for custom page size (see // BValidateDevmodeFormFields()). VOptionsToDevmodeFields() knows to handle that. // if ((pci->pdm->dmFields & DM_FORMNAME) && (pForm = MyGetForm(pci->hPrinter, pci->pdm->dmFormName, 1)) && !BFormSupportedOnPrinter(pci, pForm, &dwOptionIndexNew)) { bUpdateFormField = TRUE; } MemFree(pForm); } VOptionsToDevmodeFields(pci, bUpdateFormField); if (! BUpdateUIInfo(pci)) return BFormatDQPMessage(pDQPInfo, IDS_DQPERR_COMMONINFO); // // Check if the requested resolution is supported // iRealizedRes = max(pci->pdm->dmPrintQuality, pci->pdm->dmYResolution); iResX = iResY = 0; // // Kludze, there are some cases where apps set dmPrintQuality/dmYResolution // to be one of the DMRES values. We skip the checking for resolution // since Unidrv/Pscript will map them to one of the valid resolution options // at print time // if (pDQPInfo->pDevMode->dmFields & DM_PRINTQUALITY) { iResX = pDQPInfo->pDevMode->dmPrintQuality; if (iResX <= DMRES_DRAFT) return TRUE; } if (pDQPInfo->pDevMode->dmFields & DM_YRESOLUTION) { iResY = pDQPInfo->pDevMode->dmYResolution; if (iResY <= DMRES_DRAFT) return TRUE; } if (max(iResX, iResY) != iRealizedRes) return BFormatDQPMessage(pDQPInfo, IDS_DQPERR_RESOLUTION); return TRUE; } BOOL BQueryPrintForm( PDEVQUERYPRINT_INFO pDQPInfo, PCOMMONINFO pci ) /*++ Routine Description: Check if the requested form and/or tray is available Arguments: pDQPInfo - Points to a DEVQUERYPRINT_INFO structure pci - Points to basic printer info Return Value: TRUE if successful, FALSE if the job should be held --*/ { PUIINFO pUIInfo; PFEATURE pFeature; PPAGESIZE pPageSize; PWSTR pwstrTrayName; FORM_TRAY_TABLE pFormTrayTable; FINDFORMTRAY FindData; WCHAR awchTrayName[CCHBINNAME]; DWORD dwFeatureIndex, dwOptionIndex; BOOL bResult = FALSE; // // Skip it if form name is not specified // if ((pci->pdm->dmFields & DM_FORMNAME) == 0 || pci->pdm->dmFormName[0] == NUL) { return TRUE; } pUIInfo = pci->pUIInfo; if ((pFeature = GET_PREDEFINED_FEATURE(pUIInfo, GID_PAGESIZE)) == NULL) { ASSERT(FALSE); return TRUE; } dwFeatureIndex = GET_INDEX_FROM_FEATURE(pUIInfo, pFeature); dwOptionIndex = pci->pCombinedOptions[dwFeatureIndex].ubCurOptIndex; if ((pPageSize = PGetIndexedOption(pUIInfo, pFeature, dwOptionIndex)) == NULL) { ASSERT(FALSE); return TRUE; } // // For custom page size option, we have left the devmode form fields unchanged. // See function VOptionToDevmodeFields(). // // // We've only shown user forms supported by custom page size in Form-to-Tray table. // if (pPageSize->dwPaperSizeID == DMPAPER_USER || pPageSize->dwPaperSizeID == DMPAPER_CUSTOMSIZE) { FORM_INFO_1 *pForm; // // We already verified the dmFormName field at the beginning. // if (pForm = MyGetForm(pci->hPrinter, pci->pdm->dmFormName, 1)) { // // Built-in and printer forms supported by custom page size option won't show // up in either PageSize list or Form-to-Tray assignment table. So we only // continue to check the From-to-Tray assignment table for user forms supported // by custom page size option. See function BFormSupportedOnPrinter(). // if (pForm->Flags != FORM_USER) { MemFree(pForm); return TRUE; } MemFree(pForm); } } // // Get the specified tray name, if any // pwstrTrayName = NULL; if (pFeature = GET_PREDEFINED_FEATURE(pUIInfo, GID_INPUTSLOT)) { PINPUTSLOT pInputSlot; dwFeatureIndex = GET_INDEX_FROM_FEATURE(pUIInfo, pFeature); dwOptionIndex = pci->pCombinedOptions[dwFeatureIndex].ubCurOptIndex; if ((pInputSlot = PGetIndexedOption(pUIInfo, pFeature, dwOptionIndex)) && (pInputSlot->dwPaperSourceID != DMBIN_FORMSOURCE) && LOAD_STRING_OPTION_NAME(pci, pInputSlot, awchTrayName, CCHBINNAME)) { pwstrTrayName = awchTrayName; } } // // Find out if the requested form/tray pair is // listed in the form-to-tray assignment table. // if (pFormTrayTable = PGetFormTrayTable(pci->hPrinter, NULL)) { RESET_FINDFORMTRAY(pFormTrayTable, &FindData); bResult = BSearchFormTrayTable(pFormTrayTable, pwstrTrayName, pci->pdm->dmFormName, &FindData); MemFree(pFormTrayTable); } if (! bResult) { if (pwstrTrayName != NULL) { return BFormatDQPMessage(pDQPInfo, IDS_DQPERR_FORMTRAY, pci->pdm->dmFormName, pwstrTrayName); } else { return BFormatDQPMessage(pDQPInfo, IDS_DQPERR_FORMTRAY_ANY, pci->pdm->dmFormName); } } return TRUE; }