GreEscape DEVESC_QUERYJOBPROPERTIES queries the information that is within the printer's job properties structure. This structure is known only to the printer presentation driver and this escape is the only way for an application to look inside the structure.
Since DEVESC_QUERYJOBPROPERTIES is new, applications will call GreEscape DEVESC_QUERYESCSUPPORT to determine if you support this DevEscape.
Note that the DevEscape path has a handle to a device context (HDC) that should already have printer property information associated with the HDC. Job property information may change or may be dependent upon the printer property information in the HDC. In that case, if an application passes job properties for a different device or for a different spooler printer name, then misleading information will be returned.
Remember to test the input job properties to make sure that they are valid. This includes passing tests such as whether they contain your driver's signature string, whether the device name is yours, and whether values are within the proper ranges (only landscape or portrait). If these tests fail, then fail the call.
LONG ENGENTRY Escape (HDC hdc, LONG lEscape, LONG cInCount, PBYTE pInData, PLONG pcOutCount, PBYTE pOutData, PDDC pddc, ULONG ulFunction) { switch (lEscape) { case DEVESC_QUERYJOBPROPERTIES: { INT cbQueries = cInCount; PBYTE pbQueries = pInData; PDJP_ITEM pIQuery = (PDJP_ITEM)pbQueries; BOOL fError = FALSE; PBYTE pbNext; INT iNumQueries; PQUERYTUPLE pTuples = NULL, pTupleCur; if (GRE_234 > globals.ulGreVersion) { assertstring ("Not supported on pre-DAX engine!\n"); lrc = DEVESC_NOTIMPLEMENTED; break; } pGoodDrivData = ReturnDriverData (pddc->pdb, pdi, pddc->pdb->hmcbHeap, pDrivData, pddc->pdb->pszPrinterName, TRUE, pDevice, pDriver); assertF (pGoodDrivData); /* Is the size of the input driver data is not the same as ** the size of our good data? */ if (pDrivData->cb != pGoodDrivData->cb) lrc = DEV_PROP_BUF_TOO_SMALL; /* Or, if the two don't compare, then fail the call. */ if (0 != memcmp (pDrivData, pGoodDrivData, pGoodDrivData->cb)) lrc = DEV_INV_INP_JOBPROPERTIES; // Output buffer must be at least one item in size if (sizeof (DJP_ITEM) > cbQueries) { ulrc = DEV_BAD_PARAMETERS; break; } // Step 1. Count the number of queries iNumQueries = 0; pIQuery = (PDJP_ITEM)pbQueries; while (pIQuery->ulProperty != DJP_NONE) { iNumQueries++; pIQuery = DJP_NEXT_STRUCTP (pIQuery); } iNumQueries++; // Add an EOL marker // Step 2. Allocate and copy the query types pTuples = (PQUERYTUPLE)GplMemoryAlloc (globals.pvSharedHeap, iNumQueries * sizeof (QUERYTUPLE)); assertF (pTuples); if (!pTuples) { ulrc = DEV_ERROR; GplErrSetError (PMERR_INSUFFICIENT_MEMORY); break; } /* Since we will be overwriting the input list to the output list, we ** need to copy the original input list into an array of tuples ** (the only thing that matters) */ pIQuery = (PDJP_ITEM)pbQueries; pTupleCur = pTuples; while (pIQuery->ulProperty != DJP_NONE) { pTupleCur->ulProperty = pIQuery->ulProperty; pTupleCur->lType = pIQuery->lType; pIQuery = DJP_NEXT_STRUCTP (pIQuery); pTupleCur++; } // And copy the EOL pTupleCur->ulProperty = DJP_NONE; pTupleCur->lType = DJP_NONE; // Step 3. Fill in the output list pIQuery = (PDJP_ITEM)pbQueries; pTupleCur = pTuples; while (0 < iNumQueries) { BOOL fCurError = FALSE; pIQuery->ulProperty = pTupleCur->ulProperty; pIQuery->lType = pTupleCur->lType; pIQuery->ulNumReturned = 0; if (sizeof (DJP_ITEM) > cbQueries) { fError = DEV_PROP_BUF_TOO_SMALL; assertstring ("Not enough space left!\n"); break; } DBPRINTF (("Query '%s'/%d. Query type '%s'/%d\n", pszProperty (pTupleCur->ulProperty), pTupleCur->ulProperty, pszType (pTupleCur->lType), pTupleCur->lType)); if (pTupleCur->lType == DJP_CURRENT) { pIQuery->ulNumReturned = 1; // Assume a simple type... complex types will override it pbNext = (PBYTE)(pIQuery + 1); switch (pTupleCur->ulProperty) { case DJP_SJ_ORIENTATION: if (ORIENTATION_PORTRAIT == pJobProp->ulOrientation) pIQuery->ulValue = DJP_ORI_PORTRAIT; else pIQuery->ulValue = DJP_ORI_LANDSCAPE; break; case DJP_CJ_RESOLUTION: { PDJPT_RESOLUTION pRes = DJP_ELEMENTP (*pIQuery, DJPT_RESOLUTION); PRESINFO pResInfo; DJPT_RESOLUTION Res; LONG lRet; pResInfo = GetpResFromResID (pDriver, pJobProp->ulDefResID); if (pResInfo) { Res.usXResolution = pResInfo->ulXRes; Res.usYResolution = pResInfo->ulYRes; *pRes++ = Res; pbNext = (PBYTE)pRes; } else { fCurError = TRUE; } break; } case DJP_SJ_BITSPERPEL: case DJP_SJ_COLOR: { PPRINTMODE pPrintMode; pPrintMode = GetpPrintModeFromID (pDriver, pJobProp->ulDefPrintModeID); if (pPrintMode) { if (DJP_SJ_BITSPERPEL == pTupleCur->ulProperty) { pIQuery->ulValue = pPrintMode->usLogBitCount; } else { if (1 == pPrintMode->usBitsPerPel) pIQuery->ulValue = DJP_CLR_MONOCHROME; else pIQuery->ulValue = DJP_CLR_COLOR; } } else { fCurError = TRUE; } break; } case DJP_CJ_FORM: case DJP_SJ_PAPERSIZE: { PDJPT_FORM pDJPForm = DJP_ELEMENTP (*pIQuery, DJPT_FORM); FORMINFO2 FormInfo; ULONG ulDefaultFormID, ulDefaultTrayID, ulDefaultMediaID; LONG lRet; GetIDsFromConnID (pDriver, pDevice, pJobProp->ulDefConnID, &ulDefaultTrayID, &ulDefaultFormID, &ulDefaultMediaID); if (DJP_CJ_FORM == pTupleCur->ulProperty) { FillFormInfo (pDJPForm, ulDefaultTrayID, ulDefaultFormID, ulDefaultMediaID, pdb, pJobProp->ulDefResID); pDJPForm++; pbNext = (PBYTE)pDJPForm; } else { lRet = GetDefaultFormInfo (pdb, ulDefaultFormID, pJobProp->ulDefResID, &FormInfo); if (lRet) lRet = FormInfo.ulDJPid; else lRet = DJP_PSI_NONE; if (DJP_PSI_NONE == lRet) fCurError = TRUE; else pIQuery->ulValue = lRet; } break; } case DJP_SJ_COPIES: pIQuery->ulValue = pJobProp->ulCopies; break; case DJP_NONE: // Nothing to do! pIQuery->ulValue = DJP_NONE; break; case DJP_SJ_PRINTQUALITY: case DJP_SJ_TRAYTYPE: case DJP_SJ_MEDIA: case DJP_SJ_MEDIA_COLOR: case DJP_CJ_MIXEDFORMS: case DJP_SJ_FONTDOWNLOADING: case DJP_SJ_DUPLEX: case DJP_SJ_COLLATE: case DJP_SJ_FEED: case DJP_SJ_SCALING: case DJP_SJ_FORMFEEDCONTROL: case DJP_SJ_N_UP: default: fCurError = TRUE; DBPRINTF (("Unknow query '%s' = %d!\n", pszProperty (pTupleCur->ulProperty), pTupleCur->ulProperty)); break; } } else if (pTupleCur->lType == DJP_ALL) { /* No assumptions are made here. pbNext must be set up by each ** case statement */ switch (pTupleCur->ulProperty) { case DJP_SJ_ORIENTATION: { PDJPT_ORIENTATION pTmp = DJP_ELEMENTP (*pIQuery, DJPT_ORIENTATION); *pTmp++ = DJP_ORI_PORTRAIT; *pTmp++ = DJP_ORI_LANDSCAPE; pIQuery->ulNumReturned = 2; pbNext = (PBYTE)pTmp; break; } case DJP_CJ_RESOLUTION: { PDJPT_RESOLUTION pRes = DJP_ELEMENTP (*pIQuery, DJPT_RESOLUTION); PRESINFO pResInfo = pDriver->pRES; ULONG ulNumDefined = pDriver->ulNumRes; PULONG pulResDevice = pDevice->pulRES; ULONG ulNumDevice = pDevice->usNumRes; DJPT_RESOLUTION Res; INT iNumReturned = 0; LONG lRet; BOOL fFound; register INT i, j; for (i = 0; i < ulNumDevice; i++) { fFound = FALSE; for (j = 0; j < ulNumDefined; j++) { if (pResInfo[j].ulResID == *pulResDevice) { fFound = TRUE; Res.usXResolution = pResInfo[j].ulXRes; Res.usYResolution = pResInfo[j].ulYRes; *pRes++ = Res; pbNext = (PBYTE)pRes; } } if (!fFound) { assertstring ("ResInfo not found!\n"); } else { iNumReturned++; } pulResDevice++; } pIQuery->ulNumReturned = iNumReturned; if (0 == iNumReturned) { pbNext = (PBYTE)(pIQuery + 1); fCurError = TRUE; } break; } case DJP_SJ_BITSPERPEL: case DJP_SJ_COLOR: { PPRINTMODE pPrintMode = pDriver->pPrintModes; ULONG ulNumDefined = pDriver->ulNumPrintModes; PULONG pulPrintModes = pDevice->pulPrintModes; ULONG ulNumDevice = pDevice->usNumPrintModes; PDJPT_BITSPERPEL pTmp = DJP_ELEMENTP (*pIQuery, DJPT_BITSPERPEL); INT iNumReturned = 0; BOOL fFoundMono = FALSE, fFoundColor = FALSE; register INT i, j; for (i = 0; i < ulNumDevice; i++) { for (j = 0; j < ulNumDefined; j++) { if (pPrintMode[j].ulPrintModeID == pulPrintModes[i]) { if (DJP_SJ_BITSPERPEL == pTupleCur->ulProperty) { *pTmp++ = pPrintMode[j].usLogBitCount; iNumReturned++; } else { /* Only return 1 instance for each no matter how ** many print modes there are. */ if (1 == pPrintMode[j].usBitsPerPel && !fFoundMono ) { fFoundMono = TRUE; *pTmp++ = DJP_CLR_MONOCHROME; iNumReturned++; } else if (!fFoundColor) { fFoundColor = TRUE; *pTmp++ = DJP_CLR_COLOR; iNumReturned++; } } } } pulPrintModes++; } pIQuery->ulNumReturned = iNumReturned; if (0 == iNumReturned) { pbNext = (PBYTE)(pIQuery + 1); fCurError = TRUE; } else pbNext = (PBYTE)pTmp; break; } case DJP_SJ_PAPERSIZE: { ULONG ulNumDefined = pDevice->ulNumForms; PFORMINFO2 pFormInfo = pDevice->pFORMS; PDJPT_PAPERSIZE pSize = DJP_ELEMENTP (*pIQuery, DJPT_PAPERSIZE); INT iNumReturned = 0; register INT i; for (i = 0; i < ulNumDefined; i++) { LONG lRet; lRet = pFormInfo[i].ulDJPid; if (DJP_PSI_NONE == lRet) { fCurError = TRUE; } else { iNumReturned++; *pSize++ = lRet; pbNext = (PBYTE)pSize; } } pIQuery->ulNumReturned = iNumReturned; if (0 == iNumReturned) { pbNext = (PBYTE)(pIQuery + 1); fCurError = TRUE; } break; } case DJP_CJ_FORM: { PDJPT_FORM pDJPForm = DJP_ELEMENTP (*pIQuery, DJPT_FORM); PUSERCONNECT pUserConn; ULONG ulFormID, ulTrayID, ulMediaID; INT iNumReturned = 0; register INT i; iNumReturned = 0; for (i = 0; i < pDevice->usNumConnects; i++) { GetIDsFromConnID (pDriver, pDevice, pDevice->pulCONNECTS[i], &ulTrayID, &ulFormID, &ulMediaID); FillFormInfo (pDJPForm, ulTrayID, ulFormID, ulMediaID, pdb, pJobProp->ulDefResID); pDJPForm++; pbNext = (PBYTE)pDJPForm; iNumReturned++; } // get pointer to first user-defined form connection pUserConn = pDevice->pUserDefData->pUserCONNECTS; // as long as we have user-defined form connections while (pUserConn) { FillFormInfo (pDJPForm, pUserConn->fcUser.ulTrayID, pUserConn->fcUser.ulFormID, pUserConn->fcUser.ulMediaID, pdb, pJobProp->ulDefResID); pDJPForm++; pbNext = (PBYTE)pDJPForm; iNumReturned++; // Move to the next connection pUserConn = pUserConn->pNextConnect; } pIQuery->ulNumReturned = iNumReturned; if (0 == iNumReturned) { pbNext = (PBYTE)(pIQuery + 1); fCurError = TRUE; } break; } case DJP_SJ_COPIES: { pIQuery->lType = DJP_ERROR_NOT_ENUM; pIQuery->ulNumReturned = 0; pIQuery->ulValue = DJP_NONE; pbNext = (PBYTE)(pIQuery + 1); fError = TRUE; break; } case DJP_NONE: { // Nothing to do! pIQuery->ulNumReturned = 0; pIQuery->ulValue = DJP_NONE; pbNext = (PBYTE)(pIQuery + 1); break; } case DJP_SJ_PRINTQUALITY: case DJP_SJ_TRAYTYPE: case DJP_SJ_MEDIA: case DJP_SJ_MEDIA_COLOR: case DJP_CJ_MIXEDFORMS: case DJP_SJ_FONTDOWNLOADING: case DJP_SJ_DUPLEX: case DJP_SJ_COLLATE: case DJP_SJ_FEED: case DJP_SJ_SCALING: case DJP_SJ_FORMFEEDCONTROL: case DJP_SJ_N_UP: default: fCurError = TRUE; pbNext = (PBYTE)(pIQuery + 1); DBPRINTF (("Unknow query '%s' = %d!\n", pszProperty (pTupleCur->ulProperty), pTupleCur->ulProperty)); break; } } else if (DJP_NONE != pTupleCur->lType) { fCurError = TRUE; assertstring ("Unknown query type!\n"); } // We now know the final size pIQuery->cb = pbNext - (PBYTE)pIQuery; /* @TBD We need some way to stop overwriting memory */ assertT (cbQueries < pIQuery->cb); cbQueries -= pIQuery->cb; if (fCurError) { pIQuery->ulNumReturned = 0; pIQuery->ulValue = DJP_NONE; pIQuery->lType = DJP_ERROR_NOT_SUPPORTED; fError = TRUE; } // Move to the next query item pIQuery = DJP_NEXT_STRUCTP (pIQuery); iNumQueries--; pTupleCur++; } // Clean up if (pTuples) GplMemoryFree (pTuples); if (fError) { GplErrSetWarning (PMERR_DATATYPE_ENTRY_INVALID); ulrc = DEV_WARNING; } else { // good result ulrc = DEV_OK; } break; } } return lrc; }