From 3077c0e43e08bc98f801718a86d354adc269a963 Mon Sep 17 00:00:00 2001 From: Doug Lyons Date: Tue, 25 Feb 2020 01:02:46 -0600 Subject: [PATCH] [WINSPOOL] Implement DocumentPropertiesA including DEVMODE conversions (#2339) Co-authored-by: Doug Lyons Co-authored-by: Colin Finck --- win32ss/printing/base/winspool/devmode.c | 79 ++++++++++++++++++++++ win32ss/printing/base/winspool/precomp.h | 5 ++ win32ss/printing/base/winspool/printers.c | 80 ++++++++++++++++++++++- 3 files changed, 162 insertions(+), 2 deletions(-) diff --git a/win32ss/printing/base/winspool/devmode.c b/win32ss/printing/base/winspool/devmode.c index b75932a313f..c97f0e9ff58 100644 --- a/win32ss/printing/base/winspool/devmode.c +++ b/win32ss/printing/base/winspool/devmode.c @@ -233,3 +233,82 @@ Failure: SetLastError(ERROR_INVALID_DATA); return FALSE; } + +void RosConvertAnsiDevModeToUnicodeDevmode(PDEVMODEA pDevModeInput, PDEVMODEW pDevModeOutput) +{ + // FIXME: This function should become ConvertAnsiDevModeToUnicodeDevmode when its parameters are known! + + // Check if a pDevModeInput and pDevModeOutput are both not NULL. + if (!pDevModeInput || !pDevModeOutput) + return; + + pDevModeOutput = GdiConvertToDevmodeW(pDevModeInput); +} + +// Internal counterpart to GdiConvertToDevmodeW from gdi32 +static __inline DEVMODEA* +_ConvertToDevmodeA(const DEVMODEW *dmW) +{ + DEVMODEA *dmA; + WORD dmA_size, dmW_size; + size_t BytesToCopy; + + dmW_size = dmW->dmSize; + + /* this is the minimal dmSize that XP accepts */ + if (dmW_size < FIELD_OFFSET(DEVMODEW, dmFields)) + return NULL; + + // Guard against callers that set dmSize incorrectly. + if (dmW_size > sizeof(DEVMODEW)) + dmW_size = sizeof(DEVMODEW); + + // dmA_size must become dmW_size without the additional 1 byte per character for each Unicode string (dmDeviceName and dmFormName). + dmA_size = dmW_size - CCHDEVICENAME; + if (dmW_size >= FIELD_OFFSET(DEVMODEW, dmFormName) + CCHFORMNAME * sizeof(WCHAR)) + dmA_size -= CCHFORMNAME; + + // Allocate the required bytes, that is dmSize for the ANSI DEVMODEA structure plus any extra bytes requested through dmDriverExtra. + dmA = HeapAlloc(GetProcessHeap(), 0, dmA_size + dmW->dmDriverExtra); + if (!dmA) return NULL; + + // Every valid DEVMODEW has a dmDeviceName, which we convert to ANSI here. + WideCharToMultiByte(CP_ACP, 0, dmW->dmDeviceName, -1, (LPSTR)dmA->dmDeviceName, CCHDEVICENAME, NULL, NULL); + + // Copy everything up to dmFormName or the remaining dmW_size, whatever is smaller. + BytesToCopy = min(FIELD_OFFSET(DEVMODEW, dmFormName) - FIELD_OFFSET(DEVMODEW, dmSpecVersion), dmW_size - CCHDEVICENAME * sizeof(WCHAR)); + memcpy(&dmA->dmSpecVersion, &dmW->dmSpecVersion, BytesToCopy); + + // Handle dmFormName if the input DEVMODEW is large enough to contain one. + if (dmW_size >= FIELD_OFFSET(DEVMODEW, dmFormName) + CCHFORMNAME * sizeof(WCHAR)) + { + if (dmW->dmFields & DM_FORMNAME) + WideCharToMultiByte(CP_ACP, 0, dmW->dmFormName, -1, (LPSTR)dmA->dmFormName, CCHFORMNAME, NULL, NULL); + else + dmA->dmFormName[0] = 0; + + // Copy the remaining fields. + if (dmW_size > FIELD_OFFSET(DEVMODEW, dmLogPixels)) + memcpy(&dmA->dmLogPixels, &dmW->dmLogPixels, dmW_size - FIELD_OFFSET(DEVMODEW, dmLogPixels)); + } + + // Append dmDriverExtra if required. + if (dmW->dmDriverExtra) + memcpy((char *)dmA + dmA_size, (const char *)dmW + dmW_size, dmW->dmDriverExtra); + + // Set the corrected dmSize and we are done. + dmA->dmSize = dmA_size; + + return dmA; +} + +void RosConvertUnicodeDevModeToAnsiDevmode(PDEVMODEW pDevModeInput, PDEVMODEA pDevModeOutput) +{ + // FIXME: This function should become ConvertUnicodeDevModeToAnsiDevmode when its parameters are known! + + // Check if a pDevModeInput and pDevModeOutput are both not NULL. + if (!pDevModeInput || !pDevModeOutput) + return; + + pDevModeOutput = _ConvertToDevmodeA(pDevModeInput); +} diff --git a/win32ss/printing/base/winspool/precomp.h b/win32ss/printing/base/winspool/precomp.h index cae8bd320e8..58eed626d02 100644 --- a/win32ss/printing/base/winspool/precomp.h +++ b/win32ss/printing/base/winspool/precomp.h @@ -44,4 +44,9 @@ extern HANDLE hProcessHeap; // utils.c extern BOOL UnicodeToAnsiInPlace(PWSTR pwszField); +// devmode.c +extern void RosConvertAnsiDevModeToUnicodeDevmode(PDEVMODEA pDevModeInput, PDEVMODEW pDevModeOutput); + +extern void RosConvertUnicodeDevModeToAnsiDevmode(PDEVMODEW pDevModeInput, PDEVMODEA pDevModeOutput); + #endif diff --git a/win32ss/printing/base/winspool/printers.c b/win32ss/printing/base/winspool/printers.c index f6616068442..4180dfb3d1c 100644 --- a/win32ss/printing/base/winspool/printers.c +++ b/win32ss/printing/base/winspool/printers.c @@ -205,9 +205,85 @@ DocumentEvent( HANDLE hPrinter, HDC hdc, int iEsc, ULONG cbIn, PVOID pvIn, ULONG LONG WINAPI DocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName, PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput, DWORD fMode) { + PWSTR pwszDeviceName = NULL; + PDEVMODEW pdmwInput = NULL; + PDEVMODEW pdmwOutput = NULL; + BOOL bReturnValue = -1; + DWORD cch; + TRACE("DocumentPropertiesA(%p, %p, %s, %p, %p, %lu)\n", hWnd, hPrinter, pDeviceName, pDevModeOutput, pDevModeInput, fMode); - UNIMPLEMENTED; - return -1; + + if (pDeviceName) + { + // Convert pName to a Unicode string pwszDeviceName. + cch = strlen(pDeviceName); + + pwszDeviceName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR)); + if (!pwszDeviceName) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + ERR("HeapAlloc failed!\n"); + goto Cleanup; + } + + MultiByteToWideChar(CP_ACP, 0, pDeviceName, -1, pwszDeviceName, cch + 1); + } + + if (pDevModeInput) + { + // Create working buffer for input to DocumentPropertiesW. + pdmwInput = HeapAlloc(hProcessHeap, 0, sizeof(DEVMODEW)); + if (!pdmwInput) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + ERR("HeapAlloc failed!\n"); + goto Cleanup; + } + RosConvertAnsiDevModeToUnicodeDevmode(pDevModeInput, pdmwInput); + } + + if (pDevModeOutput) + { + // Create working buffer for output from DocumentPropertiesW. + pdmwOutput = HeapAlloc(hProcessHeap, 0, sizeof(DEVMODEW)); + if (!pdmwOutput) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + ERR("HeapAlloc failed!\n"); + goto Cleanup; + } + } + + bReturnValue = DocumentPropertiesW(hWnd, hPrinter, pwszDeviceName, pdmwOutput, pdmwInput, fMode); + TRACE("bReturnValue from DocumentPropertiesW is '%ld'.\n", bReturnValue); + + if (pwszDeviceName) + { + HeapFree(hProcessHeap, 0, pwszDeviceName); + } + + if (bReturnValue < 0) + { + TRACE("DocumentPropertiesW failed!\n"); + goto Cleanup; + } + + if (pdmwOutput) + { + RosConvertUnicodeDevModeToAnsiDevmode(pdmwOutput, pDevModeOutput); + } + +Cleanup: + if(pwszDeviceName) + HeapFree(hProcessHeap, 0, pwszDeviceName); + + if (pdmwInput) + HeapFree(hProcessHeap, 0, pdmwInput); + + if (pdmwOutput) + HeapFree(hProcessHeap, 0, pdmwOutput); + + return bReturnValue; } static PRINTER_INFO_9W * get_devmodeW(HANDLE hprn)