/*++ Copyright (c) 1996 Microsoft Corporation Module Name: dm.c Abstract: Devmode related functions used by the driver UI. Environment: Win32 subsystem, DriverUI module, user mode Revision History: 02/05/97 -davidx- Rewrote it to support OEM plugins among other things. 07/17/96 -amandan- Created it. --*/ #include "precomp.h" // // This is the devmode version 320 (DM_SPECVERSION) // #define DM_SPECVERSION320 0x0320 #define DM_SPECVERSION400 0x0400 #define DM_SPECVERSION401 0x0401 #define DM_SPECVER_BASE DM_SPECVERSION320 #define CCHDEVICENAME320 32 #define CCHFORMNAME320 32 typedef struct _DEVMODE320 { WCHAR dmDeviceName[CCHDEVICENAME320]; WORD dmSpecVersion; WORD dmDriverVersion; WORD dmSize; WORD dmDriverExtra; DWORD dmFields; short dmOrientation; short dmPaperSize; short dmPaperLength; short dmPaperWidth; short dmScale; short dmCopies; short dmDefaultSource; short dmPrintQuality; short dmColor; short dmDuplex; short dmYResolution; short dmTTOption; short dmCollate; WCHAR dmFormName[CCHFORMNAME320]; WORD dmLogPixels; DWORD dmBitsPerPel; DWORD dmPelsWidth; DWORD dmPelsHeight; DWORD dmDisplayFlags; DWORD dmDisplayFrequency; } DEVMODE320, *PDEVMODE320; typedef struct _DMEXTRA400 { DWORD dmICMMethod; DWORD dmICMIntent; DWORD dmMediaType; DWORD dmDitherType; DWORD dmICCManufacturer; DWORD dmICCModel; } DMEXTRA400; typedef struct _DMEXTRA401 { DWORD dmPanningWidth; DWORD dmPanningHeight; } DMEXTRA401; #define DM_SIZE320 sizeof(DEVMODE320) #define DM_SIZE400 (DM_SIZE320 + sizeof(DMEXTRA400)) #define DM_SIZE401 (DM_SIZE400 + sizeof(DMEXTRA401)) VOID VPatchPublicDevmodeVersion( IN OUT PDEVMODE pdm ) /*++ Routine Description: Patch dmSpecVersion field of the input devmode based on its dmSize information Arguments: pdm - Specifies a devmode to be version-checked Return Value: NONE --*/ { ASSERT(pdm != NULL); // // Check against known devmode sizes // switch (pdm->dmSize) { case DM_SIZE320: pdm->dmSpecVersion = DM_SPECVERSION320; break; case DM_SIZE400: pdm->dmSpecVersion = DM_SPECVERSION400; break; case DM_SIZE401: pdm->dmSpecVersion = DM_SPECVERSION401; break; } } VOID VSimpleConvertDevmode( IN PDEVMODE pdmIn, IN OUT PDEVMODE pdmOut ) /*++ Routine Description: Simple-minded devmode conversion function. Arguments: pdmIn - Points to an input devmode pdmOut - Points to an initialized and valid output devmode Return Value: NONE Notes: This function only relies on values of these 4 fields in pdmOut: dmSpecVersion dmDriverVersion dmSize dmDriverExtra All other fields in pdmOut are ignored and zero-filled before any memory copy occurs. --*/ { WORD wSpecVersion, wDriverVersion; WORD wSize, wDriverExtra; ASSERT(pdmIn != NULL && pdmOut != NULL); // // Copy public devmode fields // wSpecVersion = pdmOut->dmSpecVersion; wDriverVersion = pdmOut->dmDriverVersion; wSize = pdmOut->dmSize; wDriverExtra = pdmOut->dmDriverExtra; ZeroMemory(pdmOut, wSize+wDriverExtra); CopyMemory(pdmOut, pdmIn, min(wSize, pdmIn->dmSize)); pdmOut->dmSpecVersion = wSpecVersion; pdmOut->dmDriverVersion = wDriverVersion; pdmOut->dmSize = wSize; pdmOut->dmDriverExtra = wDriverExtra; // // Copy private devmode fields // CopyMemory((PBYTE) pdmOut + pdmOut->dmSize, (PBYTE) pdmIn + pdmIn->dmSize, min(wDriverExtra, pdmIn->dmDriverExtra)); VPatchPublicDevmodeVersion(pdmOut); } /*++ Routine Name: VSmartConvertDevmode Routine Description: Smart devmode conversion function for CDM_CONVERT. It strictly obeys the pdmOut's devmode framework (public, fixed-size core private, each plugin devmode), and do the best to convert data from pdmIn into that framework. It guarantees that pdmIn's data from a certain section only goes into the same section in pdmOut, i.e. pdmIn's core private devmode data won't overrun into pdmOut's plugin devmode section. Compared with VSimpleConvertDevmode, this function doesn't change the size of any private devmode section in the original pdmOut. This includes the sizes of: fixed-size core private devmode and each individual OEM plugin devmode. Arguments: pdmIn - Points to an input devmode pdmOut - Points to an initialized and valid output devmode Return Value: NONE Note: These size/version fields are preserved in pdmOut: dmSpecVersion dmDriverVersion dmSize dmDriverExtra wSize wOEMExtra wVer each individual OEM plugin's OEM_DMEXTRAHEADER dwSize dwSignature dwVersion --*/ VOID VSmartConvertDevmode( IN PDEVMODE pdmIn, IN OUT PDEVMODE pdmOut ) { PDRIVEREXTRA pdmPrivIn, pdmPrivOut; WORD wSpecVersion, wDriverVersion; WORD wSize, wDriverExtra; WORD wCoreFixIn, wOEMExtraIn; WORD wCoreFixOut, wOEMExtraOut, wVerOut; BOOL bMSdm500In = FALSE, bMSdm500Out = FALSE; ASSERT(pdmIn != NULL && pdmOut != NULL); // // First let's determine the version of pdmIn/pdmOut. // pdmPrivIn = (PDRIVEREXTRA)GET_DRIVER_PRIVATE_DEVMODE(pdmIn); pdmPrivOut = (PDRIVEREXTRA)GET_DRIVER_PRIVATE_DEVMODE(pdmOut); if (pdmIn->dmDriverVersion >= gDriverDMInfo.dmDriverVersion500 && pdmIn->dmDriverExtra >= gDriverDMInfo.dmDriverExtra500 && pdmPrivIn->dwSignature == gdwDriverDMSignature) { wCoreFixIn = pdmPrivIn->wSize; wOEMExtraIn = pdmPrivIn->wOEMExtra; if ((wCoreFixIn >= gDriverDMInfo.dmDriverExtra500) && ((wCoreFixIn + wOEMExtraIn) <= pdmIn->dmDriverExtra)) { bMSdm500In = TRUE; } } if (pdmOut->dmDriverVersion >= gDriverDMInfo.dmDriverVersion500 && pdmOut->dmDriverExtra >= gDriverDMInfo.dmDriverExtra500 && pdmPrivOut->dwSignature == gdwDriverDMSignature) { wCoreFixOut = pdmPrivOut->wSize; wOEMExtraOut = pdmPrivOut->wOEMExtra; if ((wCoreFixOut >= gDriverDMInfo.dmDriverExtra500) && ((wCoreFixOut + wOEMExtraOut) <= pdmOut->dmDriverExtra)) { wVerOut = pdmPrivOut->wVer; bMSdm500Out = TRUE; } } if (!bMSdm500In || !bMSdm500Out) { // // For unknown devmode or MS pre-v5 devmode, there is no // complexity caused by plugin devmodes, so we will just // call the simple convert function. // VSimpleConvertDevmode(pdmIn, pdmOut); return; } // // Copy public devmode fields // wSpecVersion = pdmOut->dmSpecVersion; wDriverVersion = pdmOut->dmDriverVersion; wSize = pdmOut->dmSize; wDriverExtra = pdmOut->dmDriverExtra; ZeroMemory(pdmOut, wSize); CopyMemory(pdmOut, pdmIn, min(wSize, pdmIn->dmSize)); pdmOut->dmSpecVersion = wSpecVersion; pdmOut->dmDriverVersion = wDriverVersion; pdmOut->dmSize = wSize; pdmOut->dmDriverExtra = wDriverExtra; VPatchPublicDevmodeVersion(pdmOut); // // Copy private devmode fields section by section // // 1. First copy the fixed-size core section // ZeroMemory(pdmPrivOut, wCoreFixOut); CopyMemory(pdmPrivOut, pdmPrivIn, min(wCoreFixIn, wCoreFixOut)); // // Restore the size/version fields in core private devmode of pdmOut // pdmPrivOut->wSize = wCoreFixOut; pdmPrivOut->wOEMExtra = wOEMExtraOut; pdmPrivOut->wVer = wVerOut; // // 2. Then copy any OEM plugin devmodes // // If pdmOut has no plugin devmodes, then we have no room to copy pdmIn's. // // If pdmIn has no plugin devmode, then we have nothing to copy, so we will // just leave pdmOut's unchanged. // // So we only have work to do if both pdmIn and pdmOut have plugin devmodes. // if (wOEMExtraIn > 0 && wOEMExtraOut > 0) { POEM_DMEXTRAHEADER pOemDMIn, pOemDMOut; pOemDMIn = (POEM_DMEXTRAHEADER) ((PBYTE)pdmPrivIn + wCoreFixIn); pOemDMOut = (POEM_DMEXTRAHEADER) ((PBYTE)pdmPrivOut + wCoreFixOut); // // Make sure both in and out plugin devmodes blocks are valid before // we do the conversion. Otherwise, we will leave pdmOut plugin devmodes // unchanged. // if (bIsValidPluginDevmodes(pOemDMIn, (LONG)wOEMExtraIn) && bIsValidPluginDevmodes(pOemDMOut, (LONG)wOEMExtraOut)) { LONG cbInSize = (LONG)wOEMExtraIn; LONG cbOutSize = (LONG)wOEMExtraOut; while (cbInSize > 0 && cbOutSize > 0) { OEM_DMEXTRAHEADER OemDMHdrIn, OemDMHdrOut; // // Copy headers into local buffers // CopyMemory(&OemDMHdrIn, pOemDMIn, sizeof(OEM_DMEXTRAHEADER)); CopyMemory(&OemDMHdrOut, pOemDMOut, sizeof(OEM_DMEXTRAHEADER)); if (OemDMHdrIn.dwSize > sizeof(OEM_DMEXTRAHEADER) && OemDMHdrOut.dwSize > sizeof(OEM_DMEXTRAHEADER)) { // // Zero-fill, then copy over the plugin devmode portion after // the header structure. Notice that the header structure in // pOemDMOut is unchanged. // ZeroMemory((PBYTE)pOemDMOut + sizeof(OEM_DMEXTRAHEADER), OemDMHdrOut.dwSize - sizeof(OEM_DMEXTRAHEADER)); CopyMemory((PBYTE)pOemDMOut + sizeof(OEM_DMEXTRAHEADER), (PBYTE)pOemDMIn + sizeof(OEM_DMEXTRAHEADER), min(OemDMHdrOut.dwSize - sizeof(OEM_DMEXTRAHEADER), OemDMHdrIn.dwSize - sizeof(OEM_DMEXTRAHEADER))); } cbInSize -= OemDMHdrIn.dwSize; pOemDMIn = (POEM_DMEXTRAHEADER) ((PBYTE)pOemDMIn + OemDMHdrIn.dwSize); cbOutSize -= OemDMHdrOut.dwSize; pOemDMOut = (POEM_DMEXTRAHEADER) ((PBYTE)pOemDMOut + OemDMHdrOut.dwSize); } } } } BOOL BConvertDevmodeOut( IN PDEVMODE pdmSrc, IN PDEVMODE pdmIn, OUT PDEVMODE pdmOut ) /*++ Routine Description: This function copy a source devmode to an output devmode buffer. It should be called by the driver just before the driver returns to the caller of DrvDocumentPropertySheets. Arguments: pdmSrc - pointer to current version of src DEVMODE pdmIn - pointer to input devmode passed in by the app pdmOut - pointer to output buffer passed in by the app Return Value: TRUE for success FALSE for failure. Note: pdmOut is only the output buffer allocated by the application. It doesn't necessarily contain any valid devmode content, so we should not look at any of its fields. --*/ { if (pdmOut == NULL) { RIP(("Output buffer is NULL.\n")); return FALSE; } // // Without an input devmode, we'll have to assume the output // devmode is big enough to hold our current devmode. // if (pdmIn == NULL) { CopyMemory(pdmOut, pdmSrc, pdmSrc->dmSize + pdmSrc->dmDriverExtra); return TRUE; } // // If an input devmode is provided, we make sure we don't copy // anything larger than the input devmode to the output devmode buffer. // So the private devmode size dmDriverExtra of pdmOut can only shrink // (when Src private devmode size is smaller), but it will never grow. // // This is really dumb because we may end up chopping off tail end of // public and private devmode fields. But it's neccesary to work with // ill-behaving apps out there. // if (pdmIn->dmSize < pdmSrc->dmSize) { pdmOut->dmSpecVersion = pdmIn->dmSpecVersion; pdmOut->dmSize = pdmIn->dmSize; } else { pdmOut->dmSpecVersion = pdmSrc->dmSpecVersion; pdmOut->dmSize = pdmSrc->dmSize; } if (pdmIn->dmDriverExtra < pdmSrc->dmDriverExtra) { pdmOut->dmDriverVersion = pdmIn->dmDriverVersion; pdmOut->dmDriverExtra = pdmIn->dmDriverExtra; } else { pdmOut->dmDriverVersion = pdmSrc->dmDriverVersion; pdmOut->dmDriverExtra = pdmSrc->dmDriverExtra; } VSimpleConvertDevmode(pdmSrc, pdmOut); return TRUE; } BOOL DrvConvertDevMode( LPTSTR pPrinterName, PDEVMODE pdmIn, PDEVMODE pdmOut, PLONG pcbNeeded, DWORD fMode ) /*++ Routine Description: This function convert the devmode from previous version. Arguments: pPrinterName - pointer to printer name pdmIn - input devmode pdmOut - output devmode pcbNeeded - size of output buffer on input size of output devmode on output fMode - specifies functions to perform Return Value: TRUE for success and FALSE for failure --*/ { PCOMMONINFO pci; DWORD dwSize, dwError; VERBOSE(("DrvConvertDevMode: fMode = 0x%x\n", fMode)); // // Sanity check: make sure pcbNeeded parameter is not NULL // if (pcbNeeded == NULL) { RIP(("pcbNeeded is NULL.\n")); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } switch (fMode) { case CDM_CONVERT: // // Convert input devmode to output devmode // Note: OEM plugins are not involved here because // they can only convert input devmode to current // version, not between any versions. // if (pdmIn == NULL || pdmOut == NULL || *pcbNeeded < pdmOut->dmSize + pdmOut->dmDriverExtra) { break; } VSmartConvertDevmode(pdmIn, pdmOut); *pcbNeeded = pdmOut->dmSize + pdmOut->dmDriverExtra; return TRUE; case CDM_CONVERT351: // // Convert input devmode to 3.51 version devmode // First check if the caller provided buffer is large enough // dwSize = DM_SIZE320 + gDriverDMInfo.dmDriverExtra351; if (*pcbNeeded < (LONG) dwSize || pdmOut == NULL) { *pcbNeeded = dwSize; SetLastError(ERROR_INSUFFICIENT_BUFFER); return FALSE; } // // Do the conversion from input devmode to 3.51 devmode // pdmOut->dmSpecVersion = DM_SPECVERSION320; pdmOut->dmSize = DM_SIZE320; pdmOut->dmDriverVersion = gDriverDMInfo.dmDriverVersion351; pdmOut->dmDriverExtra = gDriverDMInfo.dmDriverExtra351; VSimpleConvertDevmode(pdmIn, pdmOut); *pcbNeeded = dwSize; return TRUE; case CDM_DRIVER_DEFAULT: // // Get the driver default devmode. // We need to open a handle to the printer // and then load basic driver information. // dwError = ERROR_GEN_FAILURE; pci = PLoadCommonInfo(NULL, pPrinterName, FLAG_OPENPRINTER_NORMAL|FLAG_OPEN_CONDITIONAL); if (pci && BCalcTotalOEMDMSize(pci->hPrinter, pci->pOemPlugins, &dwSize)) { dwSize += sizeof(DEVMODE) + gDriverDMInfo.dmDriverExtra; // // Check if the output buffer is big enough // if (*pcbNeeded < (LONG) dwSize || pdmOut == NULL) dwError = ERROR_INSUFFICIENT_BUFFER; else if (BFillCommonInfoDevmode(pci, NULL, NULL)) { // // Get the driver default devmode and // copy it to the output buffer // CopyMemory(pdmOut, pci->pdm, dwSize); dwError = NO_ERROR; } *pcbNeeded = dwSize; } VFreeCommonInfo(pci); SetLastError(dwError); return (dwError == NO_ERROR); default: ERR(("Invalid fMode in DrvConvertDevMode: %d\n", fMode)); break; } SetLastError(ERROR_INVALID_PARAMETER); return FALSE; }