520 lines
13 KiB
C
520 lines
13 KiB
C
/*++
|
||
|
||
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;
|
||
}
|
||
|