diff --git a/reactos/boot/bootdata/hivesft.inf b/reactos/boot/bootdata/hivesft.inf index 2076f21daf7..217725d3042 100644 --- a/reactos/boot/bootdata/hivesft.inf +++ b/reactos/boot/bootdata/hivesft.inf @@ -432,6 +432,23 @@ HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports","LPT1:",2,"" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports","LPT2:",2,"" HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports","LPT3:",2,"" +; Printing +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers",,0x00000012 +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Dummy Printer On LPT1","Attributes",0x00010001,0 +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Dummy Printer On LPT1","Datatype",,"RAW" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Dummy Printer On LPT1","Description",,"" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Dummy Printer On LPT1","Default DevMode",1,\ +00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ +00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,dc,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ +00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ +00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ +00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00 +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Dummy Printer On LPT1","Location",,"At Home" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Dummy Printer On LPT1","Port",,"LPT1:" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Dummy Printer On LPT1","Print Processor",,"winprint" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Dummy Printer On LPT1","Printer Driver",,"Dummy Printer Driver" +HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\Dummy Printer On LPT1","Status",0x00010001,0 + ; Image File Execution Options (NtGlobalFlag with FLG_SHOW_LDR_SNAPS set for loadlib.exe) HKLM,"Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\loadlib.exe","GlobalFlag",0x00000000,"0x02000000" ;HKLM,"Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\loaddll.exe","GlobalFlag",0x00010001,0x00000002 diff --git a/reactos/boot/bootdata/hivesys.inf b/reactos/boot/bootdata/hivesys.inf index 6a26d6ed113..da5279c02d5 100644 --- a/reactos/boot/bootdata/hivesys.inf +++ b/reactos/boot/bootdata/hivesys.inf @@ -1277,9 +1277,12 @@ HKLM,"SYSTEM\CurrentControlSet\Control\PnP\Pci\CardList","Ali",0x00030003,\ ; Printing +HKLM,"SYSTEM\CurrentControlSet\Control\Print","BeepEnabled",0x00010001,0 HKLM,"SYSTEM\CurrentControlSet\Control\Print","MajorVersion",0x00010001,2 HKLM,"SYSTEM\CurrentControlSet\Control\Print","MinorVersion",0x00010001,0 +HKLM,"SYSTEM\CurrentControlSet\Control\Print","PortThreadPriority",0x00010001,0 HKLM,"SYSTEM\CurrentControlSet\Control\Print","PriorityClass",0x00010001,0 +HKLM,"SYSTEM\CurrentControlSet\Control\Print","SchedulerThreadPriority",0x00010001,0 HKLM,"SYSTEM\CurrentControlSet\Control\Print\Environments",,0x00000010 HKLM,"SYSTEM\CurrentControlSet\Control\Print\Environments\Windows NT x86","Directory",,"W32X86" @@ -1289,22 +1292,6 @@ HKLM,"SYSTEM\CurrentControlSet\Control\Print\Environments\Windows NT x86\Print P HKLM,"SYSTEM\CurrentControlSet\Control\Print\Monitors",,0x00000010 HKLM,"SYSTEM\CurrentControlSet\Control\Print\Monitors\Local Port","Driver",,"localmon.dll" -HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers",,0x00000012 -HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Attributes",0x00010001,0 -HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Datatype",,"RAW" -HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Description",,"" -HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Default DevMode",1,\ -00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ -00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,dc,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ -00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ -00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,\ -00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00 -HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Location",,"At Home" -HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Port",,"LPT1:" -HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Print Processor",,"winprint" -HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Printer Driver",,"Dummy Printer Driver" -HKLM,"SYSTEM\CurrentControlSet\Control\Print\Printers\Dummy Printer On LPT1","Status",0x00010001,0 - HKLM,"SYSTEM\CurrentControlSet\Control\Print\Providers",,0x00000010 @@ -2131,7 +2118,7 @@ HKLM,"SYSTEM\Setup","SystemPartition",0x00000000,"\Device\Harddisk0\Partition1" HKLM,"SYSTEM\Setup","SystemSetupInProgress",0x00010001,0x00000001 ; Debug channels -;HKLM,"SYSTEM\CurrentControlSet\Control\Session Manager\Environment","DEBUGCHANNEL",0x00020000,"+ole,+rpc" +HKLM,"SYSTEM\CurrentControlSet\Control\Session Manager\Environment","DEBUGCHANNEL",0x00020000,"+localspl" ; Winsrv configuration HKLM,"SYSTEM\CurrentControlSet\Control\PriorityControl",,0x00000012 diff --git a/reactos/win32ss/printing/base/spoolss/CMakeLists.txt b/reactos/win32ss/printing/base/spoolss/CMakeLists.txt index 67a7e1f8de3..f7ef3f273d7 100644 --- a/reactos/win32ss/printing/base/spoolss/CMakeLists.txt +++ b/reactos/win32ss/printing/base/spoolss/CMakeLists.txt @@ -9,6 +9,7 @@ list(APPEND SOURCE monitors.c ports.c precomp.h + printerdata.c printers.c printprocessors.c tools.c) diff --git a/reactos/win32ss/printing/base/spoolss/main.c b/reactos/win32ss/printing/base/spoolss/main.c index 673186c8f23..d6ac29cda76 100644 --- a/reactos/win32ss/printing/base/spoolss/main.c +++ b/reactos/win32ss/printing/base/spoolss/main.c @@ -43,7 +43,7 @@ _AddPrintProviderToList(PCWSTR pwszFileName) if (!pPrintProvider) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } @@ -117,7 +117,7 @@ _InitializePrintProviderList() if (!pwszPrintProviderName) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } diff --git a/reactos/win32ss/printing/base/spoolss/memory.c b/reactos/win32ss/printing/base/spoolss/memory.c index 02f6ad1dfd1..66e111f676a 100644 --- a/reactos/win32ss/printing/base/spoolss/memory.c +++ b/reactos/win32ss/printing/base/spoolss/memory.c @@ -69,7 +69,7 @@ AllocSplStr(PCWSTR pwszInput) pwszOutput = HeapAlloc(hProcessHeap, 0, cbInput); if (!pwszOutput) { - ERR("HeapAlloc failed with error %lu!\n", GetLastError()); + ERR("HeapAlloc failed!\n"); return NULL; } @@ -159,7 +159,7 @@ ReallocSplMem(PVOID pOldMem, DWORD cbOld, DWORD cbNew) pNewMem = DllAllocSplMem(cbNew); if (!pNewMem) { - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); return NULL; } diff --git a/reactos/win32ss/printing/base/spoolss/monitors.c b/reactos/win32ss/printing/base/spoolss/monitors.c index 1a7a258857c..e9fbb823a34 100644 --- a/reactos/win32ss/printing/base/spoolss/monitors.c +++ b/reactos/win32ss/printing/base/spoolss/monitors.c @@ -2,7 +2,7 @@ * PROJECT: ReactOS Spooler Router * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation * PURPOSE: Functions related to Print Monitors - * COPYRIGHT: Copyright 2015 Colin Finck + * COPYRIGHT: Copyright 2015-2017 Colin Finck */ #include "precomp.h" @@ -43,6 +43,8 @@ EnumMonitorsW(PWSTR pName, DWORD Level, PBYTE pMonitors, DWORD cbBuf, PDWORD pcb continue; // Call the EnumMonitors function of this Print Provider. + cbNeeded = 0; + dwReturned = 0; bReturnValue = pPrintProvider->PrintProvider.fpEnumMonitors(pName, Level, pCallBuffer, cbCallBuffer, &cbNeeded, &dwReturned); // Add the returned counts to the total values. diff --git a/reactos/win32ss/printing/base/spoolss/ports.c b/reactos/win32ss/printing/base/spoolss/ports.c index 52b0b6c555b..c5df5dfa6de 100644 --- a/reactos/win32ss/printing/base/spoolss/ports.c +++ b/reactos/win32ss/printing/base/spoolss/ports.c @@ -2,7 +2,7 @@ * PROJECT: ReactOS Spooler Router * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation * PURPOSE: Functions related to Ports of the Print Monitors - * COPYRIGHT: Copyright 2015 Colin Finck + * COPYRIGHT: Copyright 2015-2017 Colin Finck */ #include "precomp.h" @@ -39,6 +39,8 @@ EnumPortsW(PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded pPrintProvider = CONTAINING_RECORD(pEntry, SPOOLSS_PRINT_PROVIDER, Entry); // Call the EnumPorts function of this Print Provider. + cbNeeded = 0; + dwReturned = 0; bReturnValue = pPrintProvider->PrintProvider.fpEnumPorts(pName, Level, pCallBuffer, cbCallBuffer, &cbNeeded, &dwReturned); // Add the returned counts to the total values. diff --git a/reactos/win32ss/printing/base/spoolss/printerdata.c b/reactos/win32ss/printing/base/spoolss/printerdata.c new file mode 100644 index 00000000000..0b7665dca3d --- /dev/null +++ b/reactos/win32ss/printing/base/spoolss/printerdata.c @@ -0,0 +1,62 @@ +/* + * PROJECT: ReactOS Spooler Router + * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation + * PURPOSE: Functions related to Printer Configuration Data + * COPYRIGHT: Copyright 2017 Colin Finck + */ + +#include "precomp.h" + +DWORD WINAPI +GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName, LPCWSTR pValueName, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded) +{ + PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hPrinter; + + // Sanity check. + if (!pHandle) + { + // Yes, Windows checks for the handle here and sets the last error to ERROR_INVALID_HANDLE, + // but returns FALSE and not the error code. + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + // Call GetPrinterDataEx of the Print Provider. + return pHandle->pPrintProvider->PrintProvider.fpGetPrinterDataEx(pHandle->hPrinter, pKeyName, pValueName, pType, pData, nSize, pcbNeeded); +} + +DWORD WINAPI +GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded) +{ + // The ReactOS Printing Stack forwards all GetPrinterData calls to GetPrinterDataEx as soon as possible. + // This function may only be called if spoolss.dll is used together with Windows Printing Stack components. + WARN("This function should never be called!\n"); + return GetPrinterDataExW(hPrinter, L"PrinterDriverData", pValueName, pType, pData, nSize, pcbNeeded); +} + +DWORD WINAPI +SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName, LPCWSTR pValueName, DWORD Type, LPBYTE pData, DWORD cbData) +{ + PSPOOLSS_PRINTER_HANDLE pHandle = (PSPOOLSS_PRINTER_HANDLE)hPrinter; + + // Sanity check. + if (!pHandle) + { + // Yes, Windows checks for the handle here and sets the last error to ERROR_INVALID_HANDLE, + // but returns FALSE and not the error code. + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + // Call SetPrinterDataEx of the Print Provider. + return pHandle->pPrintProvider->PrintProvider.fpSetPrinterDataEx(pHandle->hPrinter, pKeyName, pValueName, Type, pData, cbData); +} + +DWORD WINAPI +SetPrinterDataW(HANDLE hPrinter, PWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData) +{ + // The ReactOS Printing Stack forwards all SetPrinterData calls to SetPrinterDataEx as soon as possible. + // This function may only be called if spoolss.dll is used together with Windows Printing Stack components. + WARN("This function should never be called!\n"); + return SetPrinterDataExW(hPrinter, L"PrinterDriverData", pValueName, Type, pData, cbData); +} diff --git a/reactos/win32ss/printing/base/spoolss/printers.c b/reactos/win32ss/printing/base/spoolss/printers.c index ecd64d0b2d4..99d74d24a99 100644 --- a/reactos/win32ss/printing/base/spoolss/printers.c +++ b/reactos/win32ss/printing/base/spoolss/printers.c @@ -93,6 +93,8 @@ EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cb pPrintProvider = CONTAINING_RECORD(pEntry, SPOOLSS_PRINT_PROVIDER, Entry); // Call the EnumPrinters function of this Print Provider. + cbNeeded = 0; + dwReturned = 0; pPrintProvider->PrintProvider.fpEnumPrinters(Flags, Name, Level, pCallBuffer, cbCallBuffer, &cbNeeded, &dwReturned); // Add the returned counts to the total values. @@ -159,13 +161,6 @@ OpenPrinterW(PWSTR pPrinterName, PHANDLE phPrinter, PPRINTER_DEFAULTSW pDefault) PSPOOLSS_PRINTER_HANDLE pHandle; PSPOOLSS_PRINT_PROVIDER pPrintProvider; - // Sanity checks. - if (!pPrinterName || !phPrinter) - { - dwErrorCode = ERROR_INVALID_PARAMETER; - goto Cleanup; - } - // Loop through all Print Providers to find one able to open this Printer. for (pEntry = PrintProviderList.Flink; pEntry != &PrintProviderList; pEntry = pEntry->Flink) { @@ -179,8 +174,8 @@ OpenPrinterW(PWSTR pPrinterName, PHANDLE phPrinter, PPRINTER_DEFAULTSW pDefault) pHandle = DllAllocSplMem(sizeof(SPOOLSS_PRINTER_HANDLE)); if (!pHandle) { - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } @@ -199,11 +194,11 @@ OpenPrinterW(PWSTR pPrinterName, PHANDLE phPrinter, PPRINTER_DEFAULTSW pDefault) } } +Cleanup: // ERROR_INVALID_NAME by the Print Provider is translated to ERROR_INVALID_PRINTER_NAME here, but not in other APIs as far as I know. if (dwErrorCode == ERROR_INVALID_NAME) dwErrorCode = ERROR_INVALID_PRINTER_NAME; -Cleanup: SetLastError(dwErrorCode); return (dwErrorCode == ERROR_SUCCESS); } diff --git a/reactos/win32ss/printing/base/spoolss/spoolss.spec b/reactos/win32ss/printing/base/spoolss/spoolss.spec index 5c0892c7f0d..4696300ca31 100644 --- a/reactos/win32ss/printing/base/spoolss/spoolss.spec +++ b/reactos/win32ss/printing/base/spoolss/spoolss.spec @@ -80,8 +80,8 @@ @ stub GetJobAttributes @ stdcall GetJobW(long long long ptr long ptr) @ stub GetNetworkId -@ stub GetPrinterDataExW -@ stub GetPrinterDataW +@ stdcall GetPrinterDataExW(long wstr wstr ptr ptr long ptr) +@ stdcall GetPrinterDataW(long wstr ptr ptr long ptr) @ stub GetPrinterDriverDirectoryW @ stub GetPrinterDriverExW @ stdcall GetPrinterDriverW(long wstr long ptr long ptr) @@ -139,8 +139,8 @@ @ stub SetFormW @ stdcall SetJobW(long long long ptr long) @ stub SetPortW -@ stub SetPrinterDataExW -@ stub SetPrinterDataW +@ stdcall SetPrinterDataExW(long wstr wstr long ptr long) +@ stdcall SetPrinterDataW(long wstr long ptr long) @ stub SetPrinterW @ stub SplCloseSpoolFileHandle @ stub SplCommitSpoolData diff --git a/reactos/win32ss/printing/base/spoolsv/printerdata.c b/reactos/win32ss/printing/base/spoolsv/printerdata.c index 1ffb0afb0b2..a1937201baf 100644 --- a/reactos/win32ss/printing/base/spoolsv/printerdata.c +++ b/reactos/win32ss/printing/base/spoolsv/printerdata.c @@ -2,7 +2,7 @@ * PROJECT: ReactOS Print Spooler Service * LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation * PURPOSE: Functions related to Printer Configuration Data - * COPYRIGHT: Copyright 2015 Colin Finck + * COPYRIGHT: Copyright 2015-2017 Colin Finck */ #include "precomp.h" @@ -52,27 +52,49 @@ _RpcEnumPrinterDataEx(WINSPOOL_PRINTER_HANDLE hPrinter, const WCHAR* pKeyName, B DWORD _RpcGetPrinterData(WINSPOOL_PRINTER_HANDLE hPrinter, WCHAR* pValueName, DWORD* pType, BYTE* pData, DWORD nSize, DWORD* pcbNeeded) { - UNIMPLEMENTED; - return ERROR_INVALID_FUNCTION; + return _RpcGetPrinterDataEx(hPrinter, L"PrinterDriverData", pValueName, pType, pData, nSize, pcbNeeded); } DWORD _RpcGetPrinterDataEx(WINSPOOL_PRINTER_HANDLE hPrinter, const WCHAR* pKeyName, const WCHAR* pValueName, DWORD* pType, BYTE* pData, DWORD nSize, DWORD* pcbNeeded) { - UNIMPLEMENTED; - return ERROR_INVALID_FUNCTION; + DWORD dwErrorCode; + + dwErrorCode = RpcImpersonateClient(NULL); + if (dwErrorCode != ERROR_SUCCESS) + { + ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode); + return dwErrorCode; + } + + dwErrorCode = GetPrinterDataExW(hPrinter, pKeyName, pValueName, pType, pData, nSize, pcbNeeded); + + RpcRevertToSelf(); + + return dwErrorCode; } DWORD _RpcSetPrinterData(WINSPOOL_PRINTER_HANDLE hPrinter, WCHAR* pValueName, DWORD Type, BYTE* pData, DWORD cbData) { - UNIMPLEMENTED; - return ERROR_INVALID_FUNCTION; + return _RpcSetPrinterDataEx(hPrinter, L"PrinterDriverData", pValueName, Type, pData, cbData); } DWORD _RpcSetPrinterDataEx(WINSPOOL_PRINTER_HANDLE hPrinter, const WCHAR* pKeyName, const WCHAR* pValueName, DWORD Type, BYTE* pData, DWORD cbData) { - UNIMPLEMENTED; - return ERROR_INVALID_FUNCTION; + DWORD dwErrorCode; + + dwErrorCode = RpcImpersonateClient(NULL); + if (dwErrorCode != ERROR_SUCCESS) + { + ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode); + return dwErrorCode; + } + + dwErrorCode = SetPrinterDataExW(hPrinter, pKeyName, pValueName, Type, pData, cbData); + + RpcRevertToSelf(); + + return dwErrorCode; } diff --git a/reactos/win32ss/printing/base/winspool/printerdata.c b/reactos/win32ss/printing/base/winspool/printerdata.c index ec15ac64684..90806422b8f 100644 --- a/reactos/win32ss/printing/base/winspool/printerdata.c +++ b/reactos/win32ss/printing/base/winspool/printerdata.c @@ -2,7 +2,7 @@ * PROJECT: ReactOS Spooler API * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation * PURPOSE: Functions related to Printer Configuration Data - * COPYRIGHT: Copyright 2015 Colin Finck + * COPYRIGHT: Copyright 2015-2017 Colin Finck */ #include "precomp.h" @@ -15,15 +15,327 @@ AdvancedDocumentPropertiesW(HWND hWnd, HANDLE hPrinter, PWSTR pDeviceName, PDEVM } DWORD WINAPI -GetPrinterDataW(HANDLE hPrinter, PWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded) +GetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded) { - UNIMPLEMENTED; - return FALSE; + return GetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, pType, pData, nSize, pcbNeeded); +} + +DWORD WINAPI +GetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName, LPCSTR pValueName, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded) +{ + DWORD cbUnicodeData; + DWORD cch; + DWORD dwReturnValue; + DWORD dwType; + POSVERSIONINFOEXA pInfoA; + POSVERSIONINFOEXW pInfoW; + PVOID pUnicodeData = NULL; + PWSTR pwszKeyName = NULL; + PWSTR pwszValueName = NULL; + + if (pKeyName) + { + // Convert pKeyName to a Unicode string pwszKeyName + cch = strlen(pKeyName); + + pwszKeyName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR)); + if (!pwszKeyName) + { + dwReturnValue = ERROR_NOT_ENOUGH_MEMORY; + ERR("HeapAlloc failed!\n"); + goto Cleanup; + } + + MultiByteToWideChar(CP_ACP, 0, pKeyName, -1, pwszKeyName, cch + 1); + } + + if (pValueName) + { + // Convert pValueName to a Unicode string pwszValueName + cch = strlen(pValueName); + + pwszValueName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR)); + if (!pwszValueName) + { + dwReturnValue = ERROR_NOT_ENOUGH_MEMORY; + ERR("HeapAlloc failed!\n"); + goto Cleanup; + } + + MultiByteToWideChar(CP_ACP, 0, pValueName, -1, pwszValueName, cch + 1); + } + + // We need the data type information, even if no pData was passed. + if (!pType) + pType = &dwType; + + // Call GetPrinterDataExW for the first time. + // If we're lucky, the supplied buffer is already large enough and we don't need to do the expensive RPC call a second time. + dwReturnValue = GetPrinterDataExW(hPrinter, pwszKeyName, pwszValueName, pType, pData, nSize, pcbNeeded); + + // If a critical error occurred, just return it. We cannot do anything else in this case. + if (dwReturnValue != ERROR_SUCCESS && dwReturnValue != ERROR_MORE_DATA) + goto Cleanup; + + // Save the needed buffer size for the Unicode data. We may alter *pcbNeeded for an ANSI buffer size. + cbUnicodeData = *pcbNeeded; + + if (*pType == REG_SZ || *pType == REG_MULTI_SZ || *pType == REG_EXPAND_SZ) + { + // This is a string that needs to be converted from Unicode to ANSI. + // Output the required buffer size for the ANSI string. + *pcbNeeded /= sizeof(WCHAR); + } + else if (*pType == REG_NONE) + { + if (cbUnicodeData == sizeof(OSVERSIONINFOW) && wcsicmp(pwszValueName, SPLREG_OS_VERSION) == 0) + { + // This is a Unicode OSVERSIONINFOW structure that needs to be converted to an ANSI OSVERSIONINFOA. + *pcbNeeded = sizeof(OSVERSIONINFOA); + } + else if (cbUnicodeData == sizeof(OSVERSIONINFOEXW) && wcsicmp(pwszValueName, SPLREG_OS_VERSIONEX) == 0) + { + // This is a Unicode OSVERSIONINFOEXW structure that needs to be converted to an ANSI OSVERSIONINFOEXA. + *pcbNeeded = sizeof(OSVERSIONINFOEXA); + } + else + { + // Other REG_NONE value, nothing to do. + goto Cleanup; + } + } + + // Check if the supplied buffer is large enough for the ANSI data. + if (nSize < *pcbNeeded) + { + dwReturnValue = ERROR_MORE_DATA; + goto Cleanup; + } + + // Allocate a temporary buffer for the Unicode data. + pUnicodeData = HeapAlloc(hProcessHeap, 0, cbUnicodeData); + if (!pUnicodeData) + { + dwReturnValue = ERROR_NOT_ENOUGH_MEMORY; + ERR("HeapAlloc failed!\n"); + goto Cleanup; + } + + if (dwReturnValue == ERROR_SUCCESS) + { + // ERROR_SUCCESS: The buffer is large enough for the ANSI and the Unicode string, + // so the Unicode string has been copied into pData. Copy it to pUnicodeData. + CopyMemory(pUnicodeData, pData, cbUnicodeData); + } + else + { + // ERROR_MORE_DATA: The buffer is large enough for the ANSI string, but not for the Unicode string. + // We have to call GetPrinterDataExW again with the temporary buffer. + dwReturnValue = GetPrinterDataExW(hPrinter, pwszKeyName, pwszValueName, NULL, (PBYTE)pUnicodeData, cbUnicodeData, &cbUnicodeData); + if (dwReturnValue != ERROR_SUCCESS) + goto Cleanup; + } + + if (*pType == REG_SZ || *pType == REG_MULTI_SZ || *pType == REG_EXPAND_SZ) + { + // Convert the Unicode string to ANSI. + WideCharToMultiByte(CP_ACP, 0, (PWSTR)pUnicodeData, -1, (PSTR)pData, *pcbNeeded, NULL, NULL); + } + else + { + // This is a REG_NONE with either OSVERSIONINFOW or OSVERSIONINFOEXW. + // Copy the fields and convert the Unicode CSD Version string to ANSI. + pInfoW = (POSVERSIONINFOEXW)pUnicodeData; + pInfoA = (POSVERSIONINFOEXA)pData; + pInfoA->dwMajorVersion = pInfoW->dwMajorVersion; + pInfoA->dwMinorVersion = pInfoW->dwMinorVersion; + pInfoA->dwBuildNumber = pInfoW->dwBuildNumber; + pInfoA->dwPlatformId = pInfoW->dwPlatformId; + WideCharToMultiByte(CP_ACP, 0, pInfoW->szCSDVersion, -1, pInfoA->szCSDVersion, sizeof(pInfoA->szCSDVersion), NULL, NULL); + + if (cbUnicodeData == sizeof(OSVERSIONINFOW)) + { + pInfoA->dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); + } + else + { + pInfoA->dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXA); + pInfoA->wServicePackMajor = pInfoW->wServicePackMajor; + pInfoA->wServicePackMinor = pInfoW->wServicePackMinor; + pInfoA->wSuiteMask = pInfoW->wSuiteMask; + pInfoA->wProductType = pInfoW->wProductType; + pInfoA->wReserved = pInfoW->wReserved; + } + } + +Cleanup: + if (pwszKeyName) + HeapFree(hProcessHeap, 0, pwszKeyName); + + if (pwszValueName) + HeapFree(hProcessHeap, 0, pwszValueName); + + if (pUnicodeData) + HeapFree(hProcessHeap, 0, pUnicodeData); + + return dwReturnValue; +} + +DWORD WINAPI +GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName, LPCWSTR pValueName, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded) +{ + const WCHAR wszEmptyString[] = L""; + + BYTE DummyData; + DWORD dwErrorCode; + DWORD dwType = REG_NONE; + PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter; + + // Sanity checks + if (!pHandle) + return ERROR_INVALID_HANDLE; + + // Yes, instead of declaring these pointers unique in the IDL file (and perfectly accepting NULL pointers this way), + // Windows does it differently for GetPrinterDataExW and points them to empty variables. + if (!pKeyName) + pKeyName = wszEmptyString; + + if (!pType) + pType = &dwType; + + if (!pData && !nSize) + pData = &DummyData; + + // Do the RPC call + RpcTryExcept + { + dwErrorCode = _RpcGetPrinterDataEx(pHandle->hPrinter, pKeyName, pValueName, pType, pData, nSize, pcbNeeded); + } + RpcExcept(EXCEPTION_EXECUTE_HANDLER) + { + dwErrorCode = RpcExceptionCode(); + } + RpcEndExcept; + + return dwErrorCode; +} + +DWORD WINAPI +GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType, LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded) +{ + return GetPrinterDataExW(hPrinter, L"PrinterDriverData", pValueName, pType, pData, nSize, pcbNeeded); +} + +DWORD WINAPI +SetPrinterDataA(HANDLE hPrinter, PSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData) +{ + return SetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, Type, pData, cbData); +} + +DWORD WINAPI +SetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName, LPCSTR pValueName, DWORD Type, LPBYTE pData, DWORD cbData) +{ + DWORD cch; + DWORD dwReturnValue; + PWSTR pwszKeyName = NULL; + PWSTR pwszValueName = NULL; + PWSTR pUnicodeData = NULL; + + if (pKeyName) + { + // Convert pKeyName to a Unicode string pwszKeyName + cch = strlen(pKeyName); + + pwszKeyName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR)); + if (!pwszKeyName) + { + dwReturnValue = ERROR_NOT_ENOUGH_MEMORY; + ERR("HeapAlloc failed!\n"); + goto Cleanup; + } + + MultiByteToWideChar(CP_ACP, 0, pKeyName, -1, pwszKeyName, cch + 1); + } + + if (pValueName) + { + // Convert pValueName to a Unicode string pwszValueName + cch = strlen(pValueName); + + pwszValueName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR)); + if (!pwszValueName) + { + dwReturnValue = ERROR_NOT_ENOUGH_MEMORY; + ERR("HeapAlloc failed!\n"); + goto Cleanup; + } + + MultiByteToWideChar(CP_ACP, 0, pValueName, -1, pwszValueName, cch + 1); + } + + if (Type == REG_SZ || Type == REG_MULTI_SZ || Type == REG_EXPAND_SZ) + { + // Convert pData to a Unicode string pUnicodeData. + pUnicodeData = HeapAlloc(hProcessHeap, 0, cbData * sizeof(WCHAR)); + if (!pUnicodeData) + { + dwReturnValue = ERROR_NOT_ENOUGH_MEMORY; + ERR("HeapAlloc failed!\n"); + goto Cleanup; + } + + MultiByteToWideChar(CP_ACP, 0, (PCSTR)pData, -1, pUnicodeData, cbData); + + pData = (PBYTE)pUnicodeData; + cbData *= sizeof(WCHAR); + } + + dwReturnValue = SetPrinterDataExW(hPrinter, pwszKeyName, pwszValueName, Type, pData, cbData); + +Cleanup: + if (pwszKeyName) + HeapFree(hProcessHeap, 0, pwszKeyName); + + if (pwszValueName) + HeapFree(hProcessHeap, 0, pwszValueName); + + if (pUnicodeData) + HeapFree(hProcessHeap, 0, pUnicodeData); + + return dwReturnValue; +} + +DWORD WINAPI +SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName, LPCWSTR pValueName, DWORD Type, LPBYTE pData, DWORD cbData) +{ + const WCHAR wszEmptyString[] = L""; + + DWORD dwErrorCode; + PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter; + + // Sanity checks + if (!pHandle) + return ERROR_INVALID_HANDLE; + + if (!pKeyName) + pKeyName = wszEmptyString; + + // Do the RPC call + RpcTryExcept + { + dwErrorCode = _RpcSetPrinterDataEx(pHandle->hPrinter, pKeyName, pValueName, Type, pData, cbData); + } + RpcExcept(EXCEPTION_EXECUTE_HANDLER) + { + dwErrorCode = RpcExceptionCode(); + } + RpcEndExcept; + + return dwErrorCode; } DWORD WINAPI SetPrinterDataW(HANDLE hPrinter, PWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData) { - UNIMPLEMENTED; - return FALSE; + return SetPrinterDataExW(hPrinter, L"PrinterDriverData", pValueName, Type, pData, cbData); } diff --git a/reactos/win32ss/printing/base/winspool/printers.c b/reactos/win32ss/printing/base/winspool/printers.c index 5b95cbc47da..7ab062b8c4d 100644 --- a/reactos/win32ss/printing/base/winspool/printers.c +++ b/reactos/win32ss/printing/base/winspool/printers.c @@ -152,7 +152,7 @@ _StartDocPrinterSpooled(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1, PADDJOB if (!pJobInfo1) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("HeapAlloc failed with error %lu!\n", GetLastError()); + ERR("HeapAlloc failed!\n"); goto Cleanup; } @@ -254,7 +254,6 @@ ClosePrinter(HANDLE hPrinter) Cleanup: SetLastError(dwErrorCode); return (dwErrorCode == ERROR_SUCCESS); - } DWORD WINAPI @@ -438,8 +437,8 @@ GetDefaultPrinterA(LPSTR pszBuffer, LPDWORD pcchBuffer) pwszBuffer = HeapAlloc(hProcessHeap, 0, *pcchBuffer * sizeof(WCHAR)); if (!pwszBuffer) { - dwErrorCode = GetLastError(); - ERR("HeapAlloc failed with error %lu!\n", dwErrorCode); + dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; + ERR("HeapAlloc failed!\n"); goto Cleanup; } } @@ -499,8 +498,8 @@ GetDefaultPrinterW(LPWSTR pszBuffer, LPDWORD pcchBuffer) pwszDevice = HeapAlloc(hProcessHeap, 0, cbNeeded); if (!pwszDevice) { - dwErrorCode = GetLastError(); - ERR("HeapAlloc failed with error %lu!\n", dwErrorCode); + dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; + ERR("HeapAlloc failed!\n"); goto Cleanup; } @@ -621,7 +620,8 @@ OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefaul pwszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR)); if (!pwszPrinterName) { - ERR("HeapAlloc failed for pwszPrinterName with last error %lu!\n", GetLastError()); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + ERR("HeapAlloc failed!\n"); goto Cleanup; } @@ -640,7 +640,8 @@ OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefaul wDefault.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR)); if (!wDefault.pDatatype) { - ERR("HeapAlloc failed for wDefault.pDatatype with last error %lu!\n", GetLastError()); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + ERR("HeapAlloc failed!\n"); goto Cleanup; } @@ -676,6 +677,13 @@ OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefau WINSPOOL_DEVMODE_CONTAINER DevModeContainer = { 0 }; ACCESS_MASK AccessRequired = 0; + // Sanity check + if (!phPrinter) + { + dwErrorCode = ERROR_INVALID_PARAMETER; + goto Cleanup; + } + // Prepare the additional parameters in the format required by _RpcOpenPrinter if (pDefault) { @@ -704,7 +712,7 @@ OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefau if (!pHandle) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("HeapAlloc failed with error %lu!\n", GetLastError()); + ERR("HeapAlloc failed!\n"); goto Cleanup; } @@ -772,7 +780,8 @@ SetDefaultPrinterA(LPCSTR pszPrinter) pwszPrinter = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR)); if (!pwszPrinter) { - ERR("HeapAlloc failed for pwszPrinter with last error %lu!\n", GetLastError()); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + ERR("HeapAlloc failed!\n"); goto Cleanup; } @@ -854,8 +863,8 @@ SetDefaultPrinterW(LPCWSTR pszPrinter) pwszDeviceValueData = HeapAlloc(hProcessHeap, 0, cbDeviceValueData); if (!pwszDeviceValueData) { - dwErrorCode = GetLastError(); - ERR("HeapAlloc failed with error %lu\n", dwErrorCode); + dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; + ERR("HeapAlloc failed!\n"); goto Cleanup; } @@ -936,7 +945,8 @@ StartDocPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo) wDocInfo1.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR)); if (!wDocInfo1.pDatatype) { - ERR("HeapAlloc failed for wDocInfo1.pDatatype with last error %lu!\n", GetLastError()); + dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; + ERR("HeapAlloc failed!\n"); goto Cleanup; } @@ -951,7 +961,8 @@ StartDocPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo) wDocInfo1.pDocName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR)); if (!wDocInfo1.pDocName) { - ERR("HeapAlloc failed for wDocInfo1.pDocName with last error %lu!\n", GetLastError()); + dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; + ERR("HeapAlloc failed!\n"); goto Cleanup; } @@ -966,7 +977,8 @@ StartDocPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo) wDocInfo1.pOutputFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR)); if (!wDocInfo1.pOutputFile) { - ERR("HeapAlloc failed for wDocInfo1.pOutputFile with last error %lu!\n", GetLastError()); + dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; + ERR("HeapAlloc failed!\n"); goto Cleanup; } @@ -1040,7 +1052,7 @@ StartDocPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo) if (!pAddJobInfo1) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("HeapAlloc failed with error %lu!\n", GetLastError()); + ERR("HeapAlloc failed!\n"); goto Cleanup; } diff --git a/reactos/win32ss/printing/base/winspool/printprocessors.c b/reactos/win32ss/printing/base/winspool/printprocessors.c index ba719708dc0..bac7d9ee88e 100644 --- a/reactos/win32ss/printing/base/winspool/printprocessors.c +++ b/reactos/win32ss/printing/base/winspool/printprocessors.c @@ -2,7 +2,7 @@ * PROJECT: ReactOS Spooler API * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation * PURPOSE: Functions related to Print Processors - * COPYRIGHT: Copyright 2015-2016 Colin Finck + * COPYRIGHT: Copyright 2015-2017 Colin Finck */ #include "precomp.h" @@ -129,7 +129,8 @@ GetPrintProcessorDirectoryA(PSTR pName, PSTR pEnvironment, DWORD Level, PBYTE pP pwszName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR)); if (!pwszName) { - ERR("HeapAlloc failed for pwszName with last error %lu!\n", GetLastError()); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + ERR("HeapAlloc failed!\n"); goto Cleanup; } @@ -144,7 +145,8 @@ GetPrintProcessorDirectoryA(PSTR pName, PSTR pEnvironment, DWORD Level, PBYTE pP pwszEnvironment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR)); if (!pwszEnvironment) { - ERR("HeapAlloc failed for pwszEnvironment with last error %lu!\n", GetLastError()); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + ERR("HeapAlloc failed!\n"); goto Cleanup; } @@ -159,7 +161,8 @@ GetPrintProcessorDirectoryA(PSTR pName, PSTR pEnvironment, DWORD Level, PBYTE pP pwszPrintProcessorInfo = HeapAlloc(hProcessHeap, 0, cbBuf); if (!pwszPrintProcessorInfo) { - ERR("HeapAlloc failed for pwszPrintProcessorInfo with last error %lu!\n", GetLastError()); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + ERR("HeapAlloc failed!\n"); goto Cleanup; } } diff --git a/reactos/win32ss/printing/base/winspool/winspool.spec b/reactos/win32ss/printing/base/winspool/winspool.spec index 21140cd8224..1083ebcf0e1 100644 --- a/reactos/win32ss/printing/base/winspool/winspool.spec +++ b/reactos/win32ss/printing/base/winspool/winspool.spec @@ -147,9 +147,9 @@ 246 stdcall GetPrintProcessorDirectoryA(str str long ptr long ptr) 247 stdcall GetPrintProcessorDirectoryW(wstr wstr long ptr long ptr) 248 stdcall GetPrinterA(long long ptr long ptr) -249 stub GetPrinterDataA -250 stub GetPrinterDataExA -251 stub GetPrinterDataExW +249 stdcall GetPrinterDataA(long str ptr ptr long ptr) +250 stdcall GetPrinterDataExA(long str str ptr ptr long ptr) +251 stdcall GetPrinterDataExW(long wstr wstr ptr ptr long ptr) 252 stdcall GetPrinterDataW(long wstr ptr ptr long ptr) 253 stdcall GetPrinterDriverA(long str long ptr long ptr) 254 stdcall -stub GetPrinterDriverDirectoryA(str str long ptr long ptr) @@ -181,9 +181,9 @@ 280 stub SetPortA 281 stub SetPortW 282 stub SetPrinterA -283 stub SetPrinterDataA -284 stub SetPrinterDataExA -285 stub SetPrinterDataExW +283 stdcall SetPrinterDataA(long str long ptr long) +284 stdcall SetPrinterDataExA(long str str long ptr long) +285 stdcall SetPrinterDataExW(long wstr wstr long ptr long) 286 stdcall SetPrinterDataW(long wstr long ptr long) 287 stdcall SetPrinterW(long long ptr long) 288 stub SplDriverUnloadComplete diff --git a/reactos/win32ss/printing/providers/localspl/CMakeLists.txt b/reactos/win32ss/printing/providers/localspl/CMakeLists.txt index 365f84b6637..fb6e988ab93 100644 --- a/reactos/win32ss/printing/providers/localspl/CMakeLists.txt +++ b/reactos/win32ss/printing/providers/localspl/CMakeLists.txt @@ -9,6 +9,7 @@ list(APPEND SOURCE monitors.c ports.c precomp.h + printerdata.c printers.c printingthread.c printprocessors.c @@ -22,6 +23,6 @@ add_library(localspl SHARED set_module_type(localspl win32dll UNICODE) target_link_libraries(localspl skiplist16 wine) -add_importlibs(localspl advapi32 rpcrt4 spoolss msvcrt kernel32 ntdll) +add_importlibs(localspl advapi32 netapi32 rpcrt4 secur32 spoolss msvcrt kernel32 ntdll) add_pch(localspl precomp.h SOURCE) add_cd_file(TARGET localspl DESTINATION reactos/system32 FOR all) diff --git a/reactos/win32ss/printing/providers/localspl/jobs.c b/reactos/win32ss/printing/providers/localspl/jobs.c index 2479b772e7c..69b5982c3d3 100644 --- a/reactos/win32ss/printing/providers/localspl/jobs.c +++ b/reactos/win32ss/printing/providers/localspl/jobs.c @@ -2,7 +2,7 @@ * PROJECT: ReactOS Local Spooler * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation * PURPOSE: Functions for managing print jobs - * COPYRIGHT: Copyright 2015 Colin Finck + * COPYRIGHT: Copyright 2015-2017 Colin Finck */ #include "precomp.h" @@ -135,26 +135,20 @@ _PrinterJobListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct) DWORD GetJobFilePath(PCWSTR pwszExtension, DWORD dwJobID, PWSTR pwszOutput) { - const WCHAR wszPrintersPath[] = L"\\PRINTERS\\"; - const DWORD cchPrintersPath = _countof(wszPrintersPath) - 1; - const DWORD cchSpoolerFile = sizeof("?????.") - 1; - const DWORD cchExtension = sizeof("SPL") - 1; // pwszExtension may be L"SPL" or L"SHD", same length for both! - if (pwszOutput) { - CopyMemory(pwszOutput, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR)); - CopyMemory(&pwszOutput[cchSpoolDirectory], wszPrintersPath, cchPrintersPath * sizeof(WCHAR)); - swprintf(&pwszOutput[cchSpoolDirectory + cchPrintersPath], L"%05lu.", dwJobID); - CopyMemory(&pwszOutput[cchSpoolDirectory + cchPrintersPath + cchSpoolerFile], pwszExtension, (cchExtension + 1) * sizeof(WCHAR)); + CopyMemory(pwszOutput, wszJobDirectory, cchJobDirectory * sizeof(WCHAR)); + swprintf(&pwszOutput[cchJobDirectory], L"\\%05lu.%s", dwJobID, pwszExtension); } - return (cchSpoolDirectory + cchPrintersPath + cchSpoolerFile + cchExtension + 1) * sizeof(WCHAR); + // pwszExtension may be L"SPL" or L"SHD", same length for both! + return (cchJobDirectory + sizeof("\\?????.SPL")) * sizeof(WCHAR); } BOOL InitializeGlobalJobList() { - const WCHAR wszPath[] = L"\\PRINTERS\\?????.SHD"; + const WCHAR wszPath[] = L"\\?????.SHD"; const DWORD cchPath = _countof(wszPath) - 1; DWORD dwErrorCode; @@ -173,8 +167,8 @@ InitializeGlobalJobList() InitializeSkiplist(&GlobalJobList, DllAllocSplMem, _GlobalJobListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem); // Construct the full path search pattern. - CopyMemory(wszFullPath, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR)); - CopyMemory(&wszFullPath[cchSpoolDirectory], wszPath, (cchPath + 1) * sizeof(WCHAR)); + CopyMemory(wszFullPath, wszJobDirectory, cchJobDirectory * sizeof(WCHAR)); + CopyMemory(&wszFullPath[cchJobDirectory], wszPath, (cchPath + 1) * sizeof(WCHAR)); // Use the search pattern to look for unfinished jobs serialized in shadow files (.SHD) hFind = FindFirstFileW(wszFullPath, &FindData); @@ -262,7 +256,7 @@ CreateJob(PLOCAL_PRINTER_HANDLE pPrinterHandle) if (!pJob) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } @@ -1209,7 +1203,7 @@ ReadJobShadowFile(PCWSTR pwszFilePath) pShadowFile = DllAllocSplMem(cbFileSize); if (!pShadowFile) { - ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath); + ERR("DllAllocSplMem failed for file \"%S\"!\n", pwszFilePath); goto Cleanup; } @@ -1249,7 +1243,7 @@ ReadJobShadowFile(PCWSTR pwszFilePath) pJob = DllAllocSplMem(sizeof(LOCAL_JOB)); if (!pJob) { - ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath); + ERR("DllAllocSplMem failed for file \"%S\"!\n", pwszFilePath); goto Cleanup; } @@ -1341,7 +1335,7 @@ WriteJobShadowFile(PWSTR pwszFilePath, const PLOCAL_JOB pJob) pShadowFile = DllAllocSplMem(cbFileSize); if (!pShadowFile) { - ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath); + ERR("DllAllocSplMem failed for file \"%S\"!\n", pwszFilePath); goto Cleanup; } diff --git a/reactos/win32ss/printing/providers/localspl/main.c b/reactos/win32ss/printing/providers/localspl/main.c index 4b2e75a6deb..44b7eea76ef 100644 --- a/reactos/win32ss/printing/providers/localspl/main.c +++ b/reactos/win32ss/printing/providers/localspl/main.c @@ -8,12 +8,20 @@ #include "precomp.h" // Global Variables +HKEY hPrintKey = NULL; +HKEY hPrintersKey = NULL; +WCHAR wszJobDirectory[MAX_PATH]; +DWORD cchJobDirectory; WCHAR wszSpoolDirectory[MAX_PATH]; DWORD cchSpoolDirectory; // Global Constants #include +/** This is what the Spooler of Windows Server 2003 returns (for example using GetPrinterDataExW, SPLREG_MAJOR_VERSION/SPLREG_MINOR_VERSION) */ +const DWORD dwSpoolerMajorVersion = 3; +const DWORD dwSpoolerMinorVersion = 0; + const WCHAR wszDefaultDocumentName[] = L"Local Downlevel Document"; PWSTR wszPrintProviderInfo[3] = { @@ -52,8 +60,8 @@ static const PRINTPROVIDOR _PrintProviderFunctions = { LocalEndDocPrinter, // fpEndDocPrinter LocalAddJob, // fpAddJob LocalScheduleJob, // fpScheduleJob - NULL, // fpGetPrinterData - NULL, // fpSetPrinterData + LocalGetPrinterData, // fpGetPrinterData + LocalSetPrinterData, // fpSetPrinterData NULL, // fpWaitForPrinterChange LocalClosePrinter, // fpClosePrinter NULL, // fpAddForm @@ -89,8 +97,8 @@ static const PRINTPROVIDOR _PrintProviderFunctions = { NULL, // fpClusterSplOpen NULL, // fpClusterSplClose NULL, // fpClusterSplIsAlive - NULL, // fpSetPrinterDataEx - NULL, // fpGetPrinterDataEx + LocalSetPrinterDataEx, // fpSetPrinterDataEx + LocalGetPrinterDataEx, // fpGetPrinterDataEx NULL, // fpEnumPrinterDataEx NULL, // fpEnumPrinterKey NULL, // fpDeletePrinterDataEx @@ -112,17 +120,112 @@ static const PRINTPROVIDOR _PrintProviderFunctions = { NULL, // fpAddDriverCatalog }; -static void -_GetSpoolDirectory() +static BOOL +_InitializeLocalSpooler(void) { + const WCHAR wszPrintersPath[] = L"\\PRINTERS"; + const DWORD cchPrintersPath = _countof(wszPrintersPath) - 1; const WCHAR wszSpoolPath[] = L"\\spool"; const DWORD cchSpoolPath = _countof(wszSpoolPath) - 1; + const WCHAR wszSymbolicLinkValue[] = L"REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Print\\Printers"; + const DWORD cbSymbolicLinkValue = sizeof(wszSymbolicLinkValue) - sizeof(WCHAR); - // Get the system directory and append the "spool" subdirectory. + BOOL bReturnValue = FALSE; + DWORD cbData; + DWORD dwErrorCode; + HKEY hKey; + + // On startup, always create a volatile symbolic link in the registry if it doesn't exist yet. + // "SYSTEM\CurrentControlSet\Control\Print\Printers" -> "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers" + // + // According to https://social.technet.microsoft.com/Forums/windowsserver/en-US/a683ab54-c43c-4ebe-af8f-1f7a65af2a51 + // this is needed when having >900 printers to work around a size limit of the SYSTEM registry hive. + dwErrorCode = (DWORD)RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Print\\Printers", 0, NULL, REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK, KEY_CREATE_LINK | KEY_SET_VALUE, NULL, &hKey, NULL); + if (dwErrorCode == ERROR_SUCCESS) + { + // Note that wszSymbolicLink has to be stored WITHOUT the terminating null character for the symbolic link to work! + // See cbSymbolicLinkValue above. + dwErrorCode = (DWORD)RegSetValueExW(hKey, L"SymbolicLinkValue", 0, REG_LINK, (PBYTE)wszSymbolicLinkValue, cbSymbolicLinkValue); + if (dwErrorCode != ERROR_SUCCESS) + { + ERR("RegSetValueExW failed for the Printers symlink with error %lu!\n", dwErrorCode); + goto Cleanup; + } + } + else if (dwErrorCode != ERROR_ALREADY_EXISTS) + { + ERR("RegCreateKeyExW failed for the Printers symlink with error %lu!\n", dwErrorCode); + goto Cleanup; + } + + // Open some registry keys and leave them open. We need them multiple times throughout the Local Spooler. + dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Print", 0, KEY_ALL_ACCESS, &hPrintKey); + if (dwErrorCode != ERROR_SUCCESS) + { + ERR("RegOpenKeyExW failed for \"Print\" with error %lu!\n", dwErrorCode); + goto Cleanup; + } + + dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Print\\Printers", 0, KEY_ALL_ACCESS, &hPrintersKey); + if (dwErrorCode != ERROR_SUCCESS) + { + ERR("RegOpenKeyExW failed for \"Printers\" with error %lu!\n", dwErrorCode); + goto Cleanup; + } + + // Construct the path to "%SystemRoot%\system32\spool". // Forget about length checks here. If this doesn't fit into MAX_PATH, our OS has more serious problems... cchSpoolDirectory = GetSystemDirectoryW(wszSpoolDirectory, MAX_PATH); CopyMemory(&wszSpoolDirectory[cchSpoolDirectory], wszSpoolPath, (cchSpoolPath + 1) * sizeof(WCHAR)); cchSpoolDirectory += cchSpoolPath; + + // Query the job directory. + cbData = sizeof(wszJobDirectory); + dwErrorCode = (DWORD)RegQueryValueExW(hPrintersKey, SPLREG_DEFAULT_SPOOL_DIRECTORY, NULL, NULL, (PBYTE)wszJobDirectory, &cbData); + if (dwErrorCode == ERROR_SUCCESS) + { + cchJobDirectory = cbData / sizeof(WCHAR) - 1; + } + else if (dwErrorCode == ERROR_FILE_NOT_FOUND) + { + // Use the default "%SystemRoot%\system32\spool\PRINTERS". + CopyMemory(wszJobDirectory, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR)); + CopyMemory(&wszJobDirectory[cchSpoolDirectory], wszPrintersPath, (cchPrintersPath + 1) * sizeof(WCHAR)); + cchJobDirectory = cchSpoolDirectory + cchPrintersPath; + + // Save this for next time. + RegSetValueExW(hPrintersKey, SPLREG_DEFAULT_SPOOL_DIRECTORY, 0, REG_SZ, (PBYTE)wszJobDirectory, (cchJobDirectory + 1) * sizeof(WCHAR)); + } + else + { + ERR("RegQueryValueExW failed for \"%S\" with error %lu!\n", SPLREG_DEFAULT_SPOOL_DIRECTORY, dwErrorCode); + goto Cleanup; + } + + // Initialize all lists. + if (!InitializePrintMonitorList()) + goto Cleanup; + + if (!InitializePortList()) + goto Cleanup; + + if (!InitializePrintProcessorList()) + goto Cleanup; + + if (!InitializePrinterList()) + goto Cleanup; + + if (!InitializeGlobalJobList()) + goto Cleanup; + + // Local Spooler Initialization finished successfully! + bReturnValue = TRUE; + +Cleanup: + if (hKey) + RegCloseKey(hKey); + + return bReturnValue; } BOOL WINAPI @@ -132,13 +235,7 @@ DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(hinstDLL); - _GetSpoolDirectory(); - - return InitializePrintMonitorList() && - InitializePortList() && - InitializePrintProcessorList() && - InitializePrinterList() && - InitializeGlobalJobList(); + return _InitializeLocalSpooler(); default: return TRUE; diff --git a/reactos/win32ss/printing/providers/localspl/monitors.c b/reactos/win32ss/printing/providers/localspl/monitors.c index c3ce99d409f..d9222a0e18d 100644 --- a/reactos/win32ss/printing/providers/localspl/monitors.c +++ b/reactos/win32ss/printing/providers/localspl/monitors.c @@ -2,7 +2,7 @@ * PROJECT: ReactOS Local Spooler * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation * PURPOSE: Functions related to Print Monitors - * COPYRIGHT: Copyright 2015 Colin Finck + * COPYRIGHT: Copyright 2015-2017 Colin Finck */ #include "precomp.h" @@ -103,7 +103,7 @@ InitializePrintMonitorList() if (!pPrintMonitor) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } @@ -112,7 +112,7 @@ InitializePrintMonitorList() if (!pPrintMonitor->pwszName) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } @@ -177,7 +177,7 @@ InitializePrintMonitorList() if (!pwszRegistryPath) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } diff --git a/reactos/win32ss/printing/providers/localspl/ports.c b/reactos/win32ss/printing/providers/localspl/ports.c index 1685a8ab7b2..bb51d9cd4ac 100644 --- a/reactos/win32ss/printing/providers/localspl/ports.c +++ b/reactos/win32ss/printing/providers/localspl/ports.c @@ -2,7 +2,7 @@ * PROJECT: ReactOS Local Spooler * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation * PURPOSE: Functions related to Ports of the Print Monitors - * COPYRIGHT: Copyright 2015 Colin Finck + * COPYRIGHT: Copyright 2015-2017 Colin Finck */ #include "precomp.h" @@ -79,7 +79,7 @@ InitializePortList() if (!pPortInfo1) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } @@ -108,7 +108,7 @@ InitializePortList() if (!pPort) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } @@ -165,6 +165,9 @@ LocalEnumPorts(PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNe pPrintMonitor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_MONITOR, Entry); // Call the EnumPorts function of this Print Monitor. + cbNeeded = 0; + dwReturned = 0; + if (pPrintMonitor->bIsLevel2) bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnEnumPorts(pPrintMonitor->hMonitor, pName, Level, pCallBuffer, cbCallBuffer, &cbNeeded, &dwReturned); else diff --git a/reactos/win32ss/printing/providers/localspl/precomp.h b/reactos/win32ss/printing/providers/localspl/precomp.h index 8419d5aca92..5241e43d8e9 100644 --- a/reactos/win32ss/printing/providers/localspl/precomp.h +++ b/reactos/win32ss/printing/providers/localspl/precomp.h @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #define SKIPLIST_LEVELS 16 @@ -195,6 +197,7 @@ struct _LOCAL_HANDLE enum { HandleType_Port, /** pSpecificHandle is a PLOCAL_PORT_HANDLE. */ HandleType_Printer, /** pSpecificHandle is a PLOCAL_PRINTER_HANDLE. */ + HandleType_PrintServer, /** pSpecificHandle is NULL (no additional information needed for a handle to the Print Server) */ HandleType_Xcv /** pSpecificHandle is a PLOCAL_XCV_HANDLE. */ } HandleType; @@ -256,8 +259,14 @@ BOOL WriteJobShadowFile(PWSTR pwszFilePath, const PLOCAL_JOB pJob); // main.c extern const WCHAR wszCurrentEnvironment[]; extern const DWORD cbCurrentEnvironment; +extern const DWORD dwSpoolerMajorVersion; +extern const DWORD dwSpoolerMinorVersion; extern const WCHAR wszDefaultDocumentName[]; +extern HKEY hPrintKey; +extern HKEY hPrintersKey; extern PWSTR wszPrintProviderInfo[3]; +extern WCHAR wszJobDirectory[MAX_PATH]; +extern DWORD cchJobDirectory; extern WCHAR wszSpoolDirectory[MAX_PATH]; extern DWORD cchSpoolDirectory; @@ -272,6 +281,12 @@ PLOCAL_PORT FindPort(PCWSTR pwszName); BOOL InitializePortList(); BOOL WINAPI LocalEnumPorts(PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned); +// printerdata.c +DWORD WINAPI LocalGetPrinterData(HANDLE hPrinter, PWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded); +DWORD WINAPI LocalGetPrinterDataEx(HANDLE hPrinter, PCWSTR pKeyName, PCWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded); +DWORD WINAPI LocalSetPrinterData(HANDLE hPrinter, PWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData); +DWORD WINAPI LocalSetPrinterDataEx(HANDLE hPrinter, LPCWSTR pKeyName, LPCWSTR pValueName, DWORD Type, LPBYTE pData, DWORD cbData); + // printers.c extern SKIPLIST PrinterList; BOOL InitializePrinterList(); diff --git a/reactos/win32ss/printing/providers/localspl/printerdata.c b/reactos/win32ss/printing/providers/localspl/printerdata.c new file mode 100644 index 00000000000..807509552b2 --- /dev/null +++ b/reactos/win32ss/printing/providers/localspl/printerdata.c @@ -0,0 +1,478 @@ +/* + * PROJECT: ReactOS Local Spooler + * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation + * PURPOSE: Functions related to Printer Configuration Data + * COPYRIGHT: Copyright 2017 Colin Finck + */ + +#include "precomp.h" + +DWORD WINAPI +LocalGetPrinterData(HANDLE hPrinter, PWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded) +{ + // The ReactOS Printing Stack forwards all GetPrinterData calls to GetPrinterDataEx as soon as possible. + // This function may only be called if localspl.dll is used together with Windows Printing Stack components. + WARN("This function should never be called!\n"); + return LocalGetPrinterDataEx(hPrinter, L"PrinterDriverData", pValueName, pType, pData, nSize, pcbNeeded); +} + +static DWORD +_MakePrinterSubKey(PLOCAL_PRINTER_HANDLE pPrinterHandle, PCWSTR pKeyName, PWSTR* ppwszSubKey) +{ + const WCHAR wszBackslash[] = L"\\"; + + size_t cbSubKey; + PWSTR p; + + // Sanity check + if (!pKeyName) + return ERROR_INVALID_PARAMETER; + + // Allocate a buffer for the subkey "PrinterName\KeyName". + cbSubKey = (wcslen(pPrinterHandle->pPrinter->pwszPrinterName) + 1 + wcslen(pKeyName) + 1) * sizeof(WCHAR); + *ppwszSubKey = DllAllocSplMem(cbSubKey); + if (!*ppwszSubKey) + return ERROR_NOT_ENOUGH_MEMORY; + + // Concatenate the subkey. + p = *ppwszSubKey; + StringCbCopyExW(p, cbSubKey, pPrinterHandle->pPrinter->pwszPrinterName, &p, &cbSubKey, 0); + StringCbCopyExW(p, cbSubKey, wszBackslash, &p, &cbSubKey, 0); + StringCbCopyExW(p, cbSubKey, pKeyName, &p, &cbSubKey, 0); + + return ERROR_SUCCESS; +} + +static DWORD +_LocalGetPrinterHandleData(PLOCAL_PRINTER_HANDLE pPrinterHandle, PCWSTR pKeyName, PCWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded) +{ + DWORD dwErrorCode; + HKEY hKey = NULL; + PWSTR pwszSubKey = NULL; + + dwErrorCode = _MakePrinterSubKey(pPrinterHandle, pKeyName, &pwszSubKey); + if (dwErrorCode != ERROR_SUCCESS) + goto Cleanup; + + // Open the subkey. + dwErrorCode = (DWORD)RegOpenKeyExW(hPrintersKey, pwszSubKey, 0, KEY_READ, &hKey); + if (dwErrorCode != ERROR_SUCCESS) + { + ERR("RegOpenKeyExW failed for \"%S\" with error %lu!\n", pwszSubKey, dwErrorCode); + goto Cleanup; + } + + // Query the desired value. + *pcbNeeded = nSize; + dwErrorCode = (DWORD)RegQueryValueExW(hKey, pValueName, NULL, pType, pData, pcbNeeded); + +Cleanup: + if (hKey) + RegCloseKey(hKey); + + if (pwszSubKey) + DllFreeSplMem(pwszSubKey); + + return dwErrorCode; +} + +static DWORD +_LocalGetPrintServerHandleData(PCWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded) +{ + DWORD dwErrorCode; + + if (wcsicmp(pValueName, SPLREG_DEFAULT_SPOOL_DIRECTORY) == 0 || + wcsicmp(pValueName, SPLREG_PORT_THREAD_PRIORITY) == 0 || + wcsicmp(pValueName, SPLREG_SCHEDULER_THREAD_PRIORITY) == 0 || + wcsicmp(pValueName, SPLREG_BEEP_ENABLED) == 0 || + wcsicmp(pValueName, SPLREG_ALLOW_USER_MANAGEFORMS) == 0) + { + *pcbNeeded = nSize; + return (DWORD)RegQueryValueExW(hPrintersKey, pValueName, NULL, pType, pData, pcbNeeded); + } + else if (wcsicmp(pValueName, SPLREG_PORT_THREAD_PRIORITY_DEFAULT) == 0 || + wcsicmp(pValueName, SPLREG_SCHEDULER_THREAD_PRIORITY_DEFAULT) == 0) + { + // Store a DWORD value as REG_NONE. + *pType = REG_NONE; + *pcbNeeded = sizeof(DWORD); + if (nSize < *pcbNeeded) + return ERROR_MORE_DATA; + + // Apparently, these values don't serve a purpose anymore. + *((PDWORD)pData) = 0; + return ERROR_SUCCESS; + } + else if (wcsicmp(pValueName, SPLREG_NET_POPUP) == 0 || + wcsicmp(pValueName, SPLREG_RETRY_POPUP) == 0 || + wcsicmp(pValueName, SPLREG_NET_POPUP_TO_COMPUTER) == 0 || + wcsicmp(pValueName, SPLREG_EVENT_LOG) == 0 || + wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ERROR) == 0 || + wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ENABLED) == 0) + { + HKEY hKey; + + dwErrorCode = (DWORD)RegOpenKeyExW(hPrintKey, L"Providers", 0, KEY_READ, &hKey); + if (dwErrorCode != ERROR_SUCCESS) + { + ERR("RegOpenKeyExW failed for \"Providers\" with error %lu!\n", dwErrorCode); + return dwErrorCode; + } + + *pcbNeeded = nSize; + dwErrorCode = (DWORD)RegQueryValueExW(hKey, pValueName, NULL, pType, pData, pcbNeeded); + RegCloseKey(hKey); + return dwErrorCode; + } + else if (wcsicmp(pValueName, SPLREG_MAJOR_VERSION) == 0) + { + // Store a DWORD value as REG_NONE. + *pType = REG_NONE; + *pcbNeeded = sizeof(DWORD); + if (nSize < *pcbNeeded) + return ERROR_MORE_DATA; + + // Apparently, these values don't serve a purpose anymore. + *((PDWORD)pData) = dwSpoolerMajorVersion; + return ERROR_SUCCESS; + } + else if (wcsicmp(pValueName, SPLREG_MINOR_VERSION) == 0) + { + // Store a DWORD value as REG_NONE. + *pType = REG_NONE; + *pcbNeeded = sizeof(DWORD); + if (nSize < *pcbNeeded) + return ERROR_MORE_DATA; + + // Apparently, these values don't serve a purpose anymore. + *((PDWORD)pData) = dwSpoolerMinorVersion; + return ERROR_SUCCESS; + } + else if (wcsicmp(pValueName, SPLREG_ARCHITECTURE) == 0) + { + // Store a string as REG_NONE with the length of the environment name string. + *pType = REG_NONE; + *pcbNeeded = cbCurrentEnvironment; + if (nSize < *pcbNeeded) + return ERROR_MORE_DATA; + + // Copy the environment name as the output value for SPLREG_ARCHITECTURE. + CopyMemory(pData, wszCurrentEnvironment, cbCurrentEnvironment); + return ERROR_SUCCESS; + } + else if (wcsicmp(pValueName, SPLREG_OS_VERSION) == 0) + { + POSVERSIONINFOW pInfo = (POSVERSIONINFOW)pData; + + // Store the OSVERSIONINFOW structure as REG_NONE. + *pType = REG_NONE; + *pcbNeeded = sizeof(OSVERSIONINFOW); + if (nSize < *pcbNeeded) + return ERROR_MORE_DATA; + + // Return OS version information. + pInfo->dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); + GetVersionExW(pInfo); + return ERROR_SUCCESS; + } + else if (wcsicmp(pValueName, SPLREG_OS_VERSIONEX) == 0) + { + POSVERSIONINFOEXW pInfo = (POSVERSIONINFOEXW)pData; + + // Store the OSVERSIONINFOEXW structure as REG_NONE. + *pType = REG_NONE; + *pcbNeeded = sizeof(OSVERSIONINFOEXW); + if (nSize < *pcbNeeded) + return ERROR_MORE_DATA; + + // Return extended OS version information. + pInfo->dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + GetVersionExW((POSVERSIONINFOW)pInfo); + return ERROR_SUCCESS; + } + else if (wcsicmp(pValueName, SPLREG_DS_PRESENT) == 0) + { + PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pInfo; + + // We want to store a REG_DWORD value. + *pType = REG_DWORD; + *pcbNeeded = sizeof(DWORD); + if (nSize < *pcbNeeded) + return ERROR_MORE_DATA; + + // Get information about the domain membership of this computer. + dwErrorCode = DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE*)&pInfo); + if (dwErrorCode != ERROR_SUCCESS) + { + ERR("DsRoleGetPrimaryDomainInformation failed with error %lu!\n", GetLastError()); + return dwErrorCode; + } + + // Return whether this computer is a workstation or server inside a domain. + *((PDWORD)pData) = (pInfo->MachineRole == DsRole_RoleMemberWorkstation || pInfo->MachineRole == DsRole_RoleMemberServer); + DsRoleFreeMemory(pInfo); + return ERROR_SUCCESS; + } + else if (wcsicmp(pValueName, SPLREG_DS_PRESENT_FOR_USER) == 0) + { + DWORD cch; + PWSTR pwszUserSam; + PWSTR p; + WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1]; + + // We want to store a REG_DWORD value. + *pType = REG_DWORD; + *pcbNeeded = sizeof(DWORD); + if (nSize < *pcbNeeded) + return ERROR_MORE_DATA; + + // Get the local Computer Name. + cch = MAX_COMPUTERNAME_LENGTH + 1; + if (!GetComputerNameW(wszComputerName, &cch)) + { + ERR("GetComputerNameW failed with error %lu!\n", GetLastError()); + return GetLastError(); + } + + // Get the User Name in the SAM format. + // This could either be: + // COMPUTERNAME\User + // DOMAINNAME\User + cch = 0; + GetUserNameExW(NameSamCompatible, NULL, &cch); + dwErrorCode = GetLastError(); + if (dwErrorCode != ERROR_MORE_DATA) + { + ERR("GetUserNameExW failed with error %lu!\n", dwErrorCode); + return dwErrorCode; + } + + pwszUserSam = DllAllocSplMem(cch * sizeof(WCHAR)); + if (!pwszUserSam) + { + dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; + ERR("DllAllocSplMem failed!\n"); + return dwErrorCode; + } + + if (!GetUserNameExW(NameSamCompatible, pwszUserSam, &cch)) + { + dwErrorCode = GetLastError(); + ERR("GetUserNameExW failed with error %lu!\n", dwErrorCode); + DllFreeSplMem(pwszUserSam); + return dwErrorCode; + } + + // Terminate the SAM-formatted User Name at the backslash. + p = wcschr(pwszUserSam, L'\\'); + *p = 0; + + // Compare it with the Computer Name. + // If they differ, this User is part of a domain. + *((PDWORD)pData) = (wcscmp(pwszUserSam, wszComputerName) != 0); + DllFreeSplMem(pwszUserSam); + return ERROR_SUCCESS; + } + else if (wcsicmp(pValueName, SPLREG_REMOTE_FAX) == 0) + { + // Store a DWORD value as REG_NONE. + *pType = REG_NONE; + *pcbNeeded = sizeof(DWORD); + if (nSize < *pcbNeeded) + return ERROR_MORE_DATA; + + // TODO: We don't support any fax service yet, but let's return the same value as Windows Server 2003 here. + *((PDWORD)pData) = 1; + return ERROR_SUCCESS; + } + else if (wcsicmp(pValueName, SPLREG_DNS_MACHINE_NAME) == 0) + { + DWORD cchDnsName = 0; + + // Get the length of the fully-qualified computer DNS name. + GetComputerNameExW(ComputerNameDnsFullyQualified, NULL, &cchDnsName); + dwErrorCode = GetLastError(); + if (dwErrorCode != ERROR_MORE_DATA) + { + ERR("GetComputerNameExW failed with error %lu!\n", dwErrorCode); + return dwErrorCode; + } + + // Check if our supplied buffer is large enough. + *pType = REG_SZ; + *pcbNeeded = cchDnsName * sizeof(WCHAR); + if (nSize < *pcbNeeded) + return ERROR_MORE_DATA; + + // Get the actual DNS name. + if (!GetComputerNameExW(ComputerNameDnsFullyQualified, (PWSTR)pData, &cchDnsName)) + { + dwErrorCode = GetLastError(); + ERR("GetComputerNameExW failed with error %lu!\n", dwErrorCode); + return dwErrorCode; + } + + // Lowercase the output just like Windows does. + _wcslwr((PWSTR)pData); + return ERROR_SUCCESS; + } + else + { + // For all other, unknown settings, we just return ERROR_INVALID_PARAMETER. + // That also includes SPLREG_WEBSHAREMGMT, which is supported in Windows Server 2003 according to the documentation, + // but is actually not! + return ERROR_INVALID_PARAMETER; + } +} + +DWORD WINAPI +LocalGetPrinterDataEx(HANDLE hPrinter, PCWSTR pKeyName, PCWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded) +{ + DWORD dwErrorCode; + DWORD dwTemp; + PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter; + + // Even if GetPrinterDataExW in winspool ensures that the RPC function is never called without a valid pointer for pType, + // it's officially optional. Windows' fpGetPrinterDataEx also works with NULL for pType! + // Ensure here that it is always set to simplify the code later. + if (!pType) + pType = &dwTemp; + + if (!pHandle) + { + dwErrorCode = ERROR_INVALID_HANDLE; + } + else if (!pcbNeeded) + { + dwErrorCode = ERROR_INVALID_PARAMETER; + } + else if (pHandle->HandleType == HandleType_Printer) + { + dwErrorCode = _LocalGetPrinterHandleData(pHandle->pSpecificHandle, pKeyName, pValueName, pType, pData, nSize, pcbNeeded); + } + else if (pHandle->HandleType == HandleType_PrintServer) + { + dwErrorCode = _LocalGetPrintServerHandleData(pValueName, pType, pData, nSize, pcbNeeded); + } + else + { + dwErrorCode = ERROR_INVALID_HANDLE; + } + + SetLastError(dwErrorCode); + return dwErrorCode; +} + +DWORD WINAPI +LocalSetPrinterData(HANDLE hPrinter, PWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData) +{ + // The ReactOS Printing Stack forwards all SetPrinterData calls to SetPrinterDataEx as soon as possible. + // This function may only be called if localspl.dll is used together with Windows Printing Stack components. + WARN("This function should never be called!\n"); + return LocalSetPrinterDataEx(hPrinter, L"PrinterDriverData", pValueName, Type, pData, cbData); +} + +static DWORD +_LocalSetPrinterHandleData(PLOCAL_PRINTER_HANDLE pPrinterHandle, PCWSTR pKeyName, PCWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData) +{ + DWORD dwErrorCode; + HKEY hKey = NULL; + PWSTR pwszSubKey = NULL; + + dwErrorCode = _MakePrinterSubKey(pPrinterHandle, pKeyName, &pwszSubKey); + if (dwErrorCode != ERROR_SUCCESS) + goto Cleanup; + + // Open the subkey. + dwErrorCode = (DWORD)RegOpenKeyExW(hPrintersKey, pwszSubKey, 0, KEY_SET_VALUE, &hKey); + if (dwErrorCode != ERROR_SUCCESS) + { + ERR("RegOpenKeyExW failed for \"%S\" with error %lu!\n", pwszSubKey, dwErrorCode); + goto Cleanup; + } + + // Set the value. + dwErrorCode = (DWORD)RegSetValueExW(hKey, pValueName, 0, Type, pData, cbData); + +Cleanup: + if (hKey) + RegCloseKey(hKey); + + if (pwszSubKey) + DllFreeSplMem(pwszSubKey); + + return dwErrorCode; +} + +static DWORD +_LocalSetPrintServerHandleData(PCWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData) +{ + DWORD dwErrorCode; + + if (wcsicmp(pValueName, SPLREG_DEFAULT_SPOOL_DIRECTORY) == 0 || + wcsicmp(pValueName, SPLREG_PORT_THREAD_PRIORITY) == 0 || + wcsicmp(pValueName, SPLREG_SCHEDULER_THREAD_PRIORITY) == 0 || + wcsicmp(pValueName, SPLREG_BEEP_ENABLED) == 0 || + wcsicmp(pValueName, SPLREG_ALLOW_USER_MANAGEFORMS) == 0) + { + return (DWORD)RegSetValueExW(hPrintersKey, pValueName, 0, Type, pData, cbData); + } + else if (wcsicmp(pValueName, SPLREG_NET_POPUP) == 0 || + wcsicmp(pValueName, SPLREG_RETRY_POPUP) == 0 || + wcsicmp(pValueName, SPLREG_NET_POPUP_TO_COMPUTER) == 0 || + wcsicmp(pValueName, SPLREG_EVENT_LOG) == 0 || + wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ERROR) == 0 || + wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ENABLED) == 0 || + wcsicmp(pValueName, L"NoRemotePrinterDrivers") == 0) + { + HKEY hKey; + + dwErrorCode = (DWORD)RegOpenKeyExW(hPrintKey, L"Providers", 0, KEY_SET_VALUE, &hKey); + if (dwErrorCode != ERROR_SUCCESS) + { + ERR("RegOpenKeyExW failed for \"Providers\" with error %lu!\n", dwErrorCode); + return dwErrorCode; + } + + dwErrorCode = (DWORD)RegSetValueExW(hKey, pValueName, 0, Type, pData, cbData); + RegCloseKey(hKey); + return dwErrorCode; + } + else if (wcsicmp(pValueName, SPLREG_WEBSHAREMGMT) == 0) + { + WARN("Attempting to set WebShareMgmt, which is based on IIS and therefore not supported. Returning fake success!\n"); + return ERROR_SUCCESS; + } + else + { + // For all other, unknown settings, we just return ERROR_INVALID_PARAMETER. + return ERROR_INVALID_PARAMETER; + } +} + +DWORD WINAPI +LocalSetPrinterDataEx(HANDLE hPrinter, PCWSTR pKeyName, PCWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData) +{ + DWORD dwErrorCode; + PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter; + + if (!pHandle) + { + dwErrorCode = ERROR_INVALID_HANDLE; + } + else if (pHandle->HandleType == HandleType_Printer) + { + dwErrorCode = _LocalSetPrinterHandleData(pHandle->pSpecificHandle, pKeyName, pValueName, Type, pData, cbData); + } + else if (pHandle->HandleType == HandleType_PrintServer) + { + dwErrorCode = _LocalSetPrintServerHandleData(pValueName, Type, pData, cbData); + } + else + { + dwErrorCode = ERROR_INVALID_HANDLE; + } + + SetLastError(dwErrorCode); + return dwErrorCode; +} diff --git a/reactos/win32ss/printing/providers/localspl/printers.c b/reactos/win32ss/printing/providers/localspl/printers.c index 413c4605691..83af51b2050 100644 --- a/reactos/win32ss/printing/providers/localspl/printers.c +++ b/reactos/win32ss/printing/providers/localspl/printers.c @@ -106,14 +106,11 @@ _PrinterListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct) BOOL InitializePrinterList() { - const WCHAR wszPrintersKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Printers"; - DWORD cbData; DWORD cchPrinterName; DWORD dwErrorCode; DWORD dwSubKeys; DWORD i; - HKEY hKey = NULL; HKEY hSubKey = NULL; PLOCAL_PORT pPort; PLOCAL_PRINTER pPrinter = NULL; @@ -125,16 +122,8 @@ InitializePrinterList() // Initialize an empty list for our printers. InitializeSkiplist(&PrinterList, DllAllocSplMem, _PrinterListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem); - // Open our printers registry key. Each subkey is a local printer there. - dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszPrintersKey, 0, KEY_READ, &hKey); - if (dwErrorCode != ERROR_SUCCESS) - { - ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode); - goto Cleanup; - } - - // Get the number of subkeys. - dwErrorCode = (DWORD)RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + // Get the number of subkeys of the printers registry key. Each subkey is a local printer there. + dwErrorCode = (DWORD)RegQueryInfoKeyW(hPrintersKey, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); if (dwErrorCode != ERROR_SUCCESS) { ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode); @@ -180,7 +169,7 @@ InitializePrinterList() // Get the name of this printer. cchPrinterName = _countof(wszPrinterName); - dwErrorCode = (DWORD)RegEnumKeyExW(hKey, i, wszPrinterName, &cchPrinterName, NULL, NULL, NULL, NULL); + dwErrorCode = (DWORD)RegEnumKeyExW(hPrintersKey, i, wszPrinterName, &cchPrinterName, NULL, NULL, NULL, NULL); if (dwErrorCode == ERROR_MORE_DATA) { // This printer name exceeds the maximum length and is invalid. @@ -193,7 +182,7 @@ InitializePrinterList() } // Open this Printer's registry key. - dwErrorCode = (DWORD)RegOpenKeyExW(hKey, wszPrinterName, 0, KEY_READ, &hSubKey); + dwErrorCode = (DWORD)RegOpenKeyExW(hPrintersKey, wszPrinterName, 0, KEY_READ, &hSubKey); if (dwErrorCode != ERROR_SUCCESS) { ERR("RegOpenKeyExW failed for Printer \"%S\" with status %lu!\n", wszPrinterName, dwErrorCode); @@ -231,7 +220,7 @@ InitializePrinterList() if (!pPrinter) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } @@ -280,7 +269,7 @@ InitializePrinterList() if (!pPrinter->pDefaultDevMode) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } @@ -351,10 +340,6 @@ Cleanup: if (pwszPrintProcessor) DllFreeSplStr(pwszPrintProcessor); - // Outside the loop - if (hKey) - RegCloseKey(hKey); - SetLastError(dwErrorCode); return (dwErrorCode == ERROR_SUCCESS); } @@ -885,8 +870,8 @@ LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DW WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1] = { 0 }; PLOCAL_PRINTER pPrinter; - ASSERT(pcbNeeded); - ASSERT(pcReturned); + // Do no sanity checks or assertions for pcbNeeded and pcReturned here. + // This is verified and required by localspl_apitest! // Begin counting. *pcbNeeded = 0; @@ -984,6 +969,13 @@ LocalGetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDW PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter; PLOCAL_PRINTER_HANDLE pPrinterHandle; + // Sanity checks. + if (!pHandle) + { + dwErrorCode = ERROR_INVALID_HANDLE; + goto Cleanup; + } + // Check if this is a printer handle. if (pHandle->HandleType != HandleType_Printer) { @@ -1020,41 +1012,358 @@ Cleanup: return (dwErrorCode == ERROR_SUCCESS); } -BOOL WINAPI -LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault) +static DWORD +_LocalOpenPortHandle(PWSTR pwszPortName, PHANDLE phPrinter) { BOOL bReturnValue; - DWORD cchComputerName; - DWORD cchFirstParameter; DWORD dwErrorCode; - DWORD dwJobID; - HANDLE hExternalHandle; - PWSTR p = lpPrinterName; - PWSTR pwszFirstParameter = NULL; - PWSTR pwszSecondParameter = NULL; - PLOCAL_JOB pJob; + HANDLE hPort; PLOCAL_HANDLE pHandle = NULL; PLOCAL_PORT pPort; PLOCAL_PORT_HANDLE pPortHandle = NULL; PLOCAL_PRINT_MONITOR pPrintMonitor; - PLOCAL_PRINTER pPrinter; - PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL; - PLOCAL_XCV_HANDLE pXcvHandle = NULL; - WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1]; - WCHAR wszFullPath[MAX_PATH]; - // TODO: lpPrinterName == NULL is supported and means access to the local printer server. - // Not sure yet if that is passed down to localspl.dll or processed in advance. - - // Sanity checks - if (!lpPrinterName || !phPrinter) + // Look for this port in our Print Monitor Port list. + pPort = FindPort(pwszPortName); + if (!pPort) { - dwErrorCode = ERROR_INVALID_PARAMETER; - goto Cleanup; + // The supplied port is unknown to all our Print Monitors. + dwErrorCode = ERROR_INVALID_NAME; + goto Failure; } + pPrintMonitor = pPort->pPrintMonitor; + + // Call the monitor's OpenPort function. + if (pPrintMonitor->bIsLevel2) + bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnOpenPort(pPrintMonitor->hMonitor, pwszPortName, &hPort); + else + bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnOpenPort(pwszPortName, &hPort); + + if (!bReturnValue) + { + // The OpenPort function failed. Return its last error. + dwErrorCode = GetLastError(); + goto Failure; + } + + // Create a new generic handle. + pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE)); + if (!pHandle) + { + dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; + ERR("DllAllocSplMem failed!\n"); + goto Failure; + } + + // Create a new LOCAL_PORT_HANDLE. + pPortHandle = DllAllocSplMem(sizeof(LOCAL_PORT_HANDLE)); + if (!pPortHandle) + { + dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; + ERR("DllAllocSplMem failed!\n"); + goto Failure; + } + + pPortHandle->hPort = hPort; + pPortHandle->pPort = pPort; + + // Make the generic handle a Port handle. + pHandle->HandleType = HandleType_Port; + pHandle->pSpecificHandle = pPortHandle; + + // Return it. + *phPrinter = (HANDLE)pHandle; + return ERROR_SUCCESS; + +Failure: + if (pHandle) + DllFreeSplMem(pHandle); + + if (pPortHandle) + DllFreeSplMem(pPortHandle); + + return dwErrorCode; +} + +static DWORD +_LocalOpenPrinterHandle(PWSTR pwszPrinterName, PWSTR pwszJobParameter, PHANDLE phPrinter, PPRINTER_DEFAULTSW pDefault) +{ + DWORD dwErrorCode; + DWORD dwJobID; + PLOCAL_HANDLE pHandle = NULL; + PLOCAL_JOB pJob; + PLOCAL_PRINTER pPrinter; + PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL; + WCHAR wszFullPath[MAX_PATH]; + + // Retrieve the printer from the list. + pPrinter = LookupElementSkiplist(&PrinterList, &pwszPrinterName, NULL); + if (!pPrinter) + { + // The printer does not exist. + dwErrorCode = ERROR_INVALID_NAME; + goto Failure; + } + + // Create a new LOCAL_PRINTER_HANDLE. + pPrinterHandle = DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE)); + if (!pPrinterHandle) + { + dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; + ERR("DllAllocSplMem failed!\n"); + goto Failure; + } + + pPrinterHandle->hSPLFile = INVALID_HANDLE_VALUE; + pPrinterHandle->pPrinter = pPrinter; + + // Check if a datatype was given. + if (pDefault && pDefault->pDatatype) + { + // Use the datatype if it's valid. + if (!FindDatatype(pPrinter->pPrintProcessor, pDefault->pDatatype)) + { + dwErrorCode = ERROR_INVALID_DATATYPE; + goto Failure; + } + + pPrinterHandle->pwszDatatype = AllocSplStr(pDefault->pDatatype); + } + else + { + // Use the default datatype. + pPrinterHandle->pwszDatatype = AllocSplStr(pPrinter->pwszDefaultDatatype); + } + + // Check if a DevMode was given, otherwise use the default. + if (pDefault && pDefault->pDevMode) + pPrinterHandle->pDevMode = DuplicateDevMode(pDefault->pDevMode); + else + pPrinterHandle->pDevMode = DuplicateDevMode(pPrinter->pDefaultDevMode); + + // Check if the caller wants a handle to an existing Print Job. + if (pwszJobParameter) + { + // The "Job " string has to follow now. + if (wcsncmp(pwszJobParameter, L"Job ", 4) != 0) + { + dwErrorCode = ERROR_INVALID_NAME; + goto Failure; + } + + // Skip the "Job " string. + pwszJobParameter += 4; + + // Skip even more whitespace. + while (*pwszJobParameter == ' ') + ++pwszJobParameter; + + // Finally extract the desired Job ID. + dwJobID = wcstoul(pwszJobParameter, NULL, 10); + if (!IS_VALID_JOB_ID(dwJobID)) + { + // The user supplied an invalid Job ID. + dwErrorCode = ERROR_INVALID_NAME; + goto Failure; + } + + // Look for this job in the Global Job List. + pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL); + if (!pJob || pJob->pPrinter != pPrinter) + { + // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name. + dwErrorCode = ERROR_INVALID_PRINTER_NAME; + goto Failure; + } + + // Try to open its SPL file. + GetJobFilePath(L"SPL", dwJobID, wszFullPath); + pPrinterHandle->hSPLFile = CreateFileW(wszFullPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (pPrinterHandle->hSPLFile == INVALID_HANDLE_VALUE) + { + dwErrorCode = GetLastError(); + ERR("CreateFileW failed with error %lu for \"%S\"!", dwErrorCode, wszFullPath); + goto Failure; + } + + // Associate the job to our Printer Handle, but don't set bStartedDoc. + // This prevents the caller from doing further StartDocPrinter, WritePrinter, etc. calls on it. + pPrinterHandle->pJob = pJob; + } + + // Make the generic handle a Port handle. + pHandle->HandleType = HandleType_Printer; + pHandle->pSpecificHandle = pPrinterHandle; + + // Return it. + *phPrinter = (HANDLE)pHandle; + return ERROR_SUCCESS; + +Failure: + if (pHandle) + DllFreeSplMem(pHandle); + + if (pPrinterHandle) + { + if (pPrinterHandle->pwszDatatype) + DllFreeSplStr(pPrinterHandle->pwszDatatype); + + if (pPrinterHandle->pDevMode) + DllFreeSplMem(pPrinterHandle->pDevMode); + + DllFreeSplMem(pPrinterHandle); + } + + return dwErrorCode; +} + +static DWORD +_LocalOpenPrintServerHandle(PHANDLE phPrinter) +{ + PLOCAL_HANDLE pHandle; + + // Create a new generic handle. + pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE)); + if (!pHandle) + { + ERR("DllAllocSplMem failed!\n"); + return ERROR_NOT_ENOUGH_MEMORY; + } + + // Make the generic handle a Print Server handle. + pHandle->HandleType = HandleType_PrintServer; + pHandle->pSpecificHandle = NULL; + + // Return it. + *phPrinter = (HANDLE)pHandle; + return ERROR_SUCCESS; +} + +static DWORD +_LocalOpenXcvHandle(PWSTR pwszParameter, PHANDLE phPrinter) +{ + BOOL bReturnValue; + DWORD dwErrorCode; + HANDLE hXcv; + PLOCAL_HANDLE pHandle = NULL; + PLOCAL_PORT pPort; + PLOCAL_PRINT_MONITOR pPrintMonitor; + PLOCAL_XCV_HANDLE pXcvHandle = NULL; + + // Skip the "Xcv" string. + pwszParameter += 3; + + // Is XcvMonitor or XcvPort requested? + if (wcsncmp(pwszParameter, L"Monitor ", 8) == 0) + { + // Skip the "Monitor " string. + pwszParameter += 8; + + // Look for this monitor in our Print Monitor list. + pPrintMonitor = FindPrintMonitor(pwszParameter); + if (!pPrintMonitor) + { + // The caller supplied a non-existing Monitor name. + dwErrorCode = ERROR_INVALID_NAME; + goto Failure; + } + } + else if (wcsncmp(pwszParameter, L"Port ", 5) == 0) + { + // Skip the "Port " string. + pwszParameter += 5; + + // Look for this port in our Print Monitor Port list. + pPort = FindPort(pwszParameter); + if (!pPort) + { + // The supplied port is unknown to all our Print Monitors. + dwErrorCode = ERROR_INVALID_NAME; + goto Failure; + } + + pPrintMonitor = pPort->pPrintMonitor; + } + else + { + dwErrorCode = ERROR_INVALID_NAME; + goto Failure; + } + + // Call the monitor's XcvOpenPort function. + if (pPrintMonitor->bIsLevel2) + bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnXcvOpenPort(pPrintMonitor->hMonitor, pwszParameter, SERVER_EXECUTE, &hXcv); + else + bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnXcvOpenPort(pwszParameter, SERVER_EXECUTE, &hXcv); + + if (!bReturnValue) + { + // The XcvOpenPort function failed. Return its last error. + dwErrorCode = GetLastError(); + goto Failure; + } + + // Create a new generic handle. + pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE)); + if (!pHandle) + { + dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; + ERR("DllAllocSplMem failed!\n"); + goto Failure; + } + + // Create a new LOCAL_XCV_HANDLE. + pXcvHandle = DllAllocSplMem(sizeof(LOCAL_XCV_HANDLE)); + if (!pXcvHandle) + { + dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; + ERR("DllAllocSplMem failed!\n"); + goto Failure; + } + + pXcvHandle->hXcv = hXcv; + pXcvHandle->pPrintMonitor = pPrintMonitor; + + // Make the generic handle a Xcv handle. + pHandle->HandleType = HandleType_Xcv; + pHandle->pSpecificHandle = pXcvHandle; + + // Return it. + *phPrinter = (HANDLE)pHandle; + return ERROR_SUCCESS; + +Failure: + if (pHandle) + DllFreeSplMem(pHandle); + + if (pXcvHandle) + DllFreeSplMem(pXcvHandle); + + return dwErrorCode; +} + +BOOL WINAPI +LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault) +{ + DWORD cchComputerName; + DWORD cchFirstParameter; + DWORD dwErrorCode; + PWSTR p = lpPrinterName; + PWSTR pwszFirstParameter = NULL; + PWSTR pwszSecondParameter; + WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1]; + + ASSERT(phPrinter); *phPrinter = NULL; + if (!lpPrinterName) + { + // The caller wants a Print Server handle and provided a NULL string. + dwErrorCode = _LocalOpenPrintServerHandle(phPrinter); + goto Cleanup; + } + // Skip any server name in the first parameter. // Does lpPrinterName begin with two backslashes to indicate a server name? if (lpPrinterName[0] == L'\\' && lpPrinterName[1] == L'\\') @@ -1062,14 +1371,10 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef // Skip these two backslashes. lpPrinterName += 2; - // Look for the closing backslash. - p = wcschr(lpPrinterName, L'\\'); - if (!p) - { - // We didn't get a proper server name. - dwErrorCode = ERROR_INVALID_PRINTER_NAME; - goto Cleanup; - } + // Look for the terminating null character or closing backslash. + p = lpPrinterName; + while (*p != L'\0' && *p != L'\\') + p++; // Get the local computer name for comparison. cchComputerName = _countof(wszComputerName); @@ -1085,7 +1390,16 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef // This print provider only supports local printers, so both strings have to match. if (p - lpPrinterName != cchComputerName || _wcsnicmp(lpPrinterName, wszComputerName, cchComputerName) != 0) { - dwErrorCode = ERROR_INVALID_PRINTER_NAME; + dwErrorCode = ERROR_INVALID_NAME; + goto Cleanup; + } + + // If lpPrinterName is only "\\COMPUTERNAME" with nothing more, the caller wants a handle to the local Print Server. + if (!*p) + { + // The caller wants a Print Server handle and provided a string like: + // "\\COMPUTERNAME" + dwErrorCode = _LocalOpenPrintServerHandle(phPrinter); goto Cleanup; } @@ -1103,7 +1417,7 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef // We must have at least one parameter. if (!cchFirstParameter && !pwszSecondParameter) { - dwErrorCode = ERROR_INVALID_PRINTER_NAME; + dwErrorCode = ERROR_INVALID_NAME; goto Cleanup; } @@ -1127,61 +1441,13 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef ++pwszSecondParameter; } - // Create a new handle. - pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE)); - if (!pHandle) - { - dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); - goto Cleanup; - } - // Now we can finally check the type of handle actually requested. if (pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Port", 4) == 0) { // The caller wants a port handle and provided a string like: // "LPT1:, Port" // "\\COMPUTERNAME\LPT1:, Port" - - // Look for this port in our Print Monitor Port list. - pPort = FindPort(pwszFirstParameter); - if (!pPort) - { - // The supplied port is unknown to all our Print Monitors. - dwErrorCode = ERROR_INVALID_PRINTER_NAME; - goto Cleanup; - } - - pPrintMonitor = pPort->pPrintMonitor; - - // Call the monitor's OpenPort function. - if (pPrintMonitor->bIsLevel2) - bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnOpenPort(pPrintMonitor->hMonitor, pwszFirstParameter, &hExternalHandle); - else - bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnOpenPort(pwszFirstParameter, &hExternalHandle); - - if (!bReturnValue) - { - // The OpenPort function failed. Return its last error. - dwErrorCode = GetLastError(); - goto Cleanup; - } - - // Create a new LOCAL_PORT_HANDLE. - pPortHandle = DllAllocSplMem(sizeof(LOCAL_PORT_HANDLE)); - if (!pPortHandle) - { - dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); - goto Cleanup; - } - - pPortHandle->hPort = hExternalHandle; - pPortHandle->pPort = pPort; - - // Return the Port handle through our general handle. - pHandle->HandleType = HandleType_Port; - pHandle->pSpecificHandle = pPortHandle; + dwErrorCode = _LocalOpenPortHandle(pwszFirstParameter, phPrinter); } else if (!pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Xcv", 3) == 0) { @@ -1190,75 +1456,7 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef // "\\COMPUTERNAME\, XcvMonitor Local Port" // ", XcvPort LPT1:" // "\\COMPUTERNAME\, XcvPort LPT1:" - - // Skip the "Xcv" string. - pwszSecondParameter += 3; - - // Is XcvMonitor or XcvPort requested? - if (wcsncmp(pwszSecondParameter, L"Monitor ", 8) == 0) - { - // Skip the "Monitor " string. - pwszSecondParameter += 8; - - // Look for this monitor in our Print Monitor list. - pPrintMonitor = FindPrintMonitor(pwszSecondParameter); - if (!pPrintMonitor) - { - // The caller supplied a non-existing Monitor name. - dwErrorCode = ERROR_INVALID_PRINTER_NAME; - goto Cleanup; - } - } - else if (wcsncmp(pwszSecondParameter, L"Port ", 5) == 0) - { - // Skip the "Port " string. - pwszSecondParameter += 5; - - // Look for this port in our Print Monitor Port list. - pPort = FindPort(pwszFirstParameter); - if (!pPort) - { - // The supplied port is unknown to all our Print Monitors. - dwErrorCode = ERROR_INVALID_PRINTER_NAME; - goto Cleanup; - } - - pPrintMonitor = pPort->pPrintMonitor; - } - else - { - dwErrorCode = ERROR_INVALID_PRINTER_NAME; - goto Cleanup; - } - - // Call the monitor's XcvOpenPort function. - if (pPrintMonitor->bIsLevel2) - bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnXcvOpenPort(pPrintMonitor->hMonitor, pwszSecondParameter, SERVER_EXECUTE, &hExternalHandle); - else - bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnXcvOpenPort(pwszSecondParameter, SERVER_EXECUTE, &hExternalHandle); - - if (!bReturnValue) - { - // The XcvOpenPort function failed. Return its last error. - dwErrorCode = GetLastError(); - goto Cleanup; - } - - // Create a new LOCAL_XCV_HANDLE. - pXcvHandle = DllAllocSplMem(sizeof(LOCAL_XCV_HANDLE)); - if (!pXcvHandle) - { - dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); - goto Cleanup; - } - - pXcvHandle->hXcv = hExternalHandle; - pXcvHandle->pPrintMonitor = pPrintMonitor; - - // Return the Xcv handle through our general handle. - pHandle->HandleType = HandleType_Xcv; - pHandle->pSpecificHandle = pXcvHandle; + dwErrorCode = _LocalOpenXcvHandle(pwszSecondParameter, phPrinter); } else { @@ -1267,130 +1465,10 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef // "\\COMPUTERNAME\HP DeskJet" // "HP DeskJet, Job 5" // "\\COMPUTERNAME\HP DeskJet, Job 5" - - // Retrieve the printer from the list. - pPrinter = LookupElementSkiplist(&PrinterList, &pwszFirstParameter, NULL); - if (!pPrinter) - { - // The printer does not exist. - dwErrorCode = ERROR_INVALID_PRINTER_NAME; - goto Cleanup; - } - - // Create a new LOCAL_PRINTER_HANDLE. - pPrinterHandle = DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE)); - if (!pPrinterHandle) - { - dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); - goto Cleanup; - } - - pPrinterHandle->hSPLFile = INVALID_HANDLE_VALUE; - pPrinterHandle->pPrinter = pPrinter; - - // Check if a datatype was given. - if (pDefault && pDefault->pDatatype) - { - // Use the datatype if it's valid. - if (!FindDatatype(pPrinter->pPrintProcessor, pDefault->pDatatype)) - { - dwErrorCode = ERROR_INVALID_DATATYPE; - goto Cleanup; - } - - pPrinterHandle->pwszDatatype = AllocSplStr(pDefault->pDatatype); - } - else - { - // Use the default datatype. - pPrinterHandle->pwszDatatype = AllocSplStr(pPrinter->pwszDefaultDatatype); - } - - // Check if a DevMode was given, otherwise use the default. - if (pDefault && pDefault->pDevMode) - pPrinterHandle->pDevMode = DuplicateDevMode(pDefault->pDevMode); - else - pPrinterHandle->pDevMode = DuplicateDevMode(pPrinter->pDefaultDevMode); - - // Check if the caller wants a handle to an existing Print Job. - if (pwszSecondParameter) - { - // The "Job " string has to follow now. - if (wcsncmp(pwszSecondParameter, L"Job ", 4) != 0) - { - dwErrorCode = ERROR_INVALID_PRINTER_NAME; - goto Cleanup; - } - - // Skip the "Job " string. - pwszSecondParameter += 4; - - // Skip even more whitespace. - while (*pwszSecondParameter == ' ') - ++pwszSecondParameter; - - // Finally extract the desired Job ID. - dwJobID = wcstoul(pwszSecondParameter, NULL, 10); - if (!IS_VALID_JOB_ID(dwJobID)) - { - // The user supplied an invalid Job ID. - dwErrorCode = ERROR_INVALID_PRINTER_NAME; - goto Cleanup; - } - - // Look for this job in the Global Job List. - pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL); - if (!pJob || pJob->pPrinter != pPrinter) - { - // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name. - dwErrorCode = ERROR_INVALID_PRINTER_NAME; - goto Cleanup; - } - - // Try to open its SPL file. - GetJobFilePath(L"SPL", dwJobID, wszFullPath); - pPrinterHandle->hSPLFile = CreateFileW(wszFullPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - if (pPrinterHandle->hSPLFile == INVALID_HANDLE_VALUE) - { - dwErrorCode = GetLastError(); - ERR("CreateFileW failed with error %lu for \"%S\"!", dwErrorCode, wszFullPath); - goto Cleanup; - } - - // Associate the job to our Printer Handle, but don't set bStartedDoc. - // This prevents the caller from doing further StartDocPrinter, WritePrinter, etc. calls on it. - pPrinterHandle->pJob = pJob; - } - - // Return the Printer handle through our general handle. - pHandle->HandleType = HandleType_Printer; - pHandle->pSpecificHandle = pPrinterHandle; + dwErrorCode = _LocalOpenPrinterHandle(pwszFirstParameter, pwszSecondParameter, phPrinter, pDefault); } - // We were successful! Return the handle. - *phPrinter = (HANDLE)pHandle; - dwErrorCode = ERROR_SUCCESS; - - // Don't let the cleanup routines free this. - pHandle = NULL; - pPrinterHandle = NULL; - Cleanup: - if (pHandle) - DllFreeSplMem(pHandle); - - if (pPrinterHandle) - { - if (pPrinterHandle->pwszDatatype) - DllFreeSplStr(pPrinterHandle->pwszDatatype); - - if (pPrinterHandle->pDevMode) - DllFreeSplMem(pPrinterHandle->pDevMode); - - DllFreeSplMem(pPrinterHandle); - } - if (pwszFirstParameter) DllFreeSplMem(pwszFirstParameter); @@ -1778,13 +1856,42 @@ Cleanup: return (dwErrorCode == ERROR_SUCCESS); } +static void +_LocalClosePortHandle(PLOCAL_PORT_HANDLE pPortHandle) +{ + // Call the monitor's ClosePort function. + if (pPortHandle->pPort->pPrintMonitor->bIsLevel2) + ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnClosePort(pPortHandle->hPort); + else + ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnClosePort(pPortHandle->hPort); +} + +static void +_LocalClosePrinterHandle(PLOCAL_PRINTER_HANDLE pPrinterHandle) +{ + // Terminate any started job. + if (pPrinterHandle->pJob) + FreeJob(pPrinterHandle->pJob); + + // Free memory for the fields. + DllFreeSplMem(pPrinterHandle->pDevMode); + DllFreeSplStr(pPrinterHandle->pwszDatatype); +} + +static void +_LocalCloseXcvHandle(PLOCAL_XCV_HANDLE pXcvHandle) +{ + // Call the monitor's XcvClosePort function. + if (pXcvHandle->pPrintMonitor->bIsLevel2) + ((PMONITOR2)pXcvHandle->pPrintMonitor->pMonitor)->pfnXcvClosePort(pXcvHandle->hXcv); + else + ((LPMONITOREX)pXcvHandle->pPrintMonitor->pMonitor)->Monitor.pfnXcvClosePort(pXcvHandle->hXcv); +} + BOOL WINAPI LocalClosePrinter(HANDLE hPrinter) { PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter; - PLOCAL_PORT_HANDLE pPortHandle; - PLOCAL_PRINTER_HANDLE pPrinterHandle; - PLOCAL_XCV_HANDLE pXcvHandle; if (!pHandle) { @@ -1794,39 +1901,25 @@ LocalClosePrinter(HANDLE hPrinter) if (pHandle->HandleType == HandleType_Port) { - pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle; - - // Call the monitor's ClosePort function. - if (pPortHandle->pPort->pPrintMonitor->bIsLevel2) - ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnClosePort(pPortHandle->hPort); - else - ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnClosePort(pPortHandle->hPort); + _LocalClosePortHandle(pHandle->pSpecificHandle); } else if (pHandle->HandleType == HandleType_Printer) { - pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle; - - // Terminate any started job. - if (pPrinterHandle->pJob) - FreeJob(pPrinterHandle->pJob); - - // Free memory for the fields. - DllFreeSplMem(pPrinterHandle->pDevMode); - DllFreeSplStr(pPrinterHandle->pwszDatatype); + _LocalClosePrinterHandle(pHandle->pSpecificHandle); + } + else if (pHandle->HandleType == HandleType_PrintServer) + { + // Nothing to do. } else if (pHandle->HandleType == HandleType_Xcv) { - pXcvHandle = (PLOCAL_XCV_HANDLE)pHandle->pSpecificHandle; - - // Call the monitor's XcvClosePort function. - if (pXcvHandle->pPrintMonitor->bIsLevel2) - ((PMONITOR2)pXcvHandle->pPrintMonitor->pMonitor)->pfnXcvClosePort(pXcvHandle->hXcv); - else - ((LPMONITOREX)pXcvHandle->pPrintMonitor->pMonitor)->Monitor.pfnXcvClosePort(pXcvHandle->hXcv); + _LocalCloseXcvHandle(pHandle->pSpecificHandle); } - // Free memory for the handle and the specific handle. - DllFreeSplMem(pHandle->pSpecificHandle); + // Free memory for the handle and the specific handle (if any). + if (pHandle->pSpecificHandle) + DllFreeSplMem(pHandle->pSpecificHandle); + DllFreeSplMem(pHandle); return TRUE; diff --git a/reactos/win32ss/printing/providers/localspl/printingthread.c b/reactos/win32ss/printing/providers/localspl/printingthread.c index d85826762d7..9197c4a6810 100644 --- a/reactos/win32ss/printing/providers/localspl/printingthread.c +++ b/reactos/win32ss/printing/providers/localspl/printingthread.c @@ -2,7 +2,7 @@ * PROJECT: ReactOS Local Spooler * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation * PURPOSE: Implementation of the Thread that actually performs the printing process - * COPYRIGHT: Copyright 2015 Colin Finck + * COPYRIGHT: Copyright 2015-2017 Colin Finck */ #include "precomp.h" @@ -32,7 +32,7 @@ PrintingThreadProc(PLOCAL_JOB pJob) if (!pwszPrinterPort) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } @@ -71,7 +71,7 @@ PrintingThreadProc(PLOCAL_JOB pJob) if (!pwszPrinterAndJob) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } @@ -100,7 +100,7 @@ PrintingThreadProc(PLOCAL_JOB pJob) if (!pwszSPLFile) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } diff --git a/reactos/win32ss/printing/providers/localspl/printprocessors.c b/reactos/win32ss/printing/providers/localspl/printprocessors.c index 1b994c65b09..1b948da6433 100644 --- a/reactos/win32ss/printing/providers/localspl/printprocessors.c +++ b/reactos/win32ss/printing/providers/localspl/printprocessors.c @@ -2,7 +2,7 @@ * PROJECT: ReactOS Local Spooler * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation * PURPOSE: Functions related to Print Processors - * COPYRIGHT: Copyright 2015-2016 Colin Finck + * COPYRIGHT: Copyright 2015-2017 Colin Finck */ #include "precomp.h" @@ -48,7 +48,8 @@ _OpenEnvironment(PCWSTR pEnvironment, PHKEY hKey) pwszEnvironmentKey = DllAllocSplMem((cchEnvironmentsKey + cchEnvironment + 1) * sizeof(WCHAR)); if (!pwszEnvironmentKey) { - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } @@ -205,7 +206,7 @@ InitializePrintProcessorList() if (!pPrintProcessor) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } @@ -214,7 +215,7 @@ InitializePrintProcessorList() if (!pPrintProcessor->pwszName) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } @@ -311,7 +312,7 @@ InitializePrintProcessorList() if (!pPrintProcessor->pDatatypesInfo1) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } @@ -514,7 +515,7 @@ LocalEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE if (!pwszTemp) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); goto Cleanup; } diff --git a/reactos/win32ss/printing/providers/localspl/tools.c b/reactos/win32ss/printing/providers/localspl/tools.c index 06f3c1bbf0e..a69317ca924 100644 --- a/reactos/win32ss/printing/providers/localspl/tools.c +++ b/reactos/win32ss/printing/providers/localspl/tools.c @@ -2,7 +2,7 @@ * PROJECT: ReactOS Local Spooler * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation * PURPOSE: Various tools - * COPYRIGHT: Copyright 2015 Colin Finck + * COPYRIGHT: Copyright 2015-2017 Colin Finck */ #include "precomp.h" @@ -41,7 +41,7 @@ AllocAndRegQueryWSZ(HKEY hKey, PCWSTR pwszValueName) pwszValue = DllAllocSplMem(cbNeeded); if (!pwszValue) { - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); return NULL; } @@ -66,7 +66,7 @@ DuplicateDevMode(PDEVMODEW pInput) pOutput = DllAllocSplMem(pInput->dmSize + pInput->dmDriverExtra); if (!pOutput) { - ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); + ERR("DllAllocSplMem failed!\n"); return NULL; } diff --git a/rostests/apitests/winspool/CMakeLists.txt b/rostests/apitests/winspool/CMakeLists.txt index 7f393419b6f..803d3d0c896 100644 --- a/rostests/apitests/winspool/CMakeLists.txt +++ b/rostests/apitests/winspool/CMakeLists.txt @@ -4,6 +4,7 @@ list(APPEND SOURCE EnumPrinters.c EnumPrintProcessorDatatypes.c GetDefaultPrinter.c + GetPrinterData.c GetPrintProcessorDirectory.c IsValidDevmode.c OpenPrinter.c diff --git a/rostests/apitests/winspool/GetPrinterData.c b/rostests/apitests/winspool/GetPrinterData.c new file mode 100644 index 00000000000..b8665554d82 --- /dev/null +++ b/rostests/apitests/winspool/GetPrinterData.c @@ -0,0 +1,188 @@ +/* + * PROJECT: ReactOS Print Spooler DLL API Tests + * LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation + * PURPOSE: Tests for GetPrinterData(Ex)A/GetPrinterData(Ex)W/SetPrinterData(Ex)A/SetPrinterData(Ex)W + * COPYRIGHT: Copyright 2017 Colin Finck + */ + +#include + +#define WIN32_NO_STATUS +#include +#include +#include +#include +#include + +/* From printing/include/spoolss.h */ +#define MAX_PRINTER_NAME 220 + +typedef struct _SPLREG_VALUE +{ + PSTR pszName; + PWSTR pwszName; + DWORD dwType; + DWORD cbNeededA; + BOOL bSettable; +} +SPLREG_VALUE, *PSPLREG_VALUE; + +SPLREG_VALUE SplRegValues[] = { +#if 0 + { "DefaultSpoolDirectory", L"DefaultSpoolDirectory", REG_SZ, 0xFFFFFFFF, TRUE }, + { "PortThreadPriorityDefault", L"PortThreadPriorityDefault", REG_NONE, 4, FALSE }, + { "PortThreadPriority", L"PortThreadPriority", REG_DWORD, 4, TRUE }, + { "SchedulerThreadPriorityDefault", L"SchedulerThreadPriorityDefault", REG_NONE, 4, FALSE }, + { "SchedulerThreadPriority", L"SchedulerThreadPriority", REG_DWORD, 4, TRUE }, + { "BeepEnabled", L"BeepEnabled", REG_DWORD, 4, TRUE }, + + /* These fail in Win8, probably removed since NT6: + + { "NetPopup", L"NetPopup", REG_DWORD, 4, TRUE }, + { "RetryPopup", L"RetryPopup", REG_DWORD, 4, TRUE }, + { "NetPopupToComputer", L"NetPopupToComputer", REG_DWORD, 4, TRUE }, + + */ + + { "EventLog", L"EventLog", REG_DWORD, 4, TRUE }, + { "MajorVersion", L"MajorVersion", REG_NONE, 4, FALSE }, + { "MinorVersion", L"MinorVersion", REG_NONE, 4, FALSE }, + { "Architecture", L"Architecture", REG_NONE, 0xFFFFFFFF, FALSE }, + { "OSVersion", L"OSVersion", REG_NONE, sizeof(OSVERSIONINFOA), FALSE }, + { "OSVersionEx", L"OSVersionEx", REG_NONE, sizeof(OSVERSIONINFOEXA), FALSE }, + { "DsPresent", L"DsPresent", REG_DWORD, 4, FALSE }, + { "DsPresentForUser", L"DsPresentForUser", REG_DWORD, 4, FALSE }, +#endif + { "RemoteFax", L"RemoteFax", REG_NONE, 4, FALSE }, + { "RestartJobOnPoolError", L"RestartJobOnPoolError", REG_DWORD, 4, TRUE }, + { "RestartJobOnPoolEnabled", L"RestartJobOnPoolEnabled", REG_DWORD, 4, TRUE }, + { "DNSMachineName", L"DNSMachineName", REG_SZ, 0xFFFFFFFF, FALSE }, + { "AllowUserManageForms", L"AllowUserManageForms", REG_DWORD, 4, TRUE }, + { NULL, NULL, 0, 0, FALSE } +}; + +START_TEST(GetPrinterData) +{ + DWORD cbNeeded; + DWORD cchDefaultPrinter; + DWORD dwReturnCode; + DWORD dwType; + HANDLE hPrinter; + PBYTE pDataA; + PBYTE pDataW; + PSPLREG_VALUE p; + WCHAR wszDefaultPrinter[MAX_PRINTER_NAME + 1]; + + // Don't supply any parameters, this has to fail with ERROR_INVALID_HANDLE! + dwReturnCode = GetPrinterDataExW(NULL, NULL, NULL, NULL, NULL, 0, NULL); + ok(dwReturnCode == ERROR_INVALID_HANDLE, "GetPrinterDataExW returns error %lu!\n", dwReturnCode); + + // Open a handle to the local print server. + if (!OpenPrinterW(NULL, &hPrinter, NULL)) + { + skip("Could not retrieve a handle to the local print server!\n"); + return; + } + + // Now try with valid handle, but leave remaining parameters NULL. + dwReturnCode = GetPrinterDataExW(hPrinter, NULL, NULL, NULL, NULL, 0, NULL); + ok(dwReturnCode == RPC_X_NULL_REF_POINTER, "GetPrinterDataExW returns error %lu!\n", dwReturnCode); + + // Try all valid Print Server data values. + for (p = SplRegValues; p->pszName; p++) + { + // Try the ANSI version of the function. + dwType = 0xDEADBEEF; + dwReturnCode = GetPrinterDataExA(hPrinter, NULL, p->pszName, &dwType, NULL, 0, &cbNeeded); + ok(dwReturnCode == ERROR_MORE_DATA || dwReturnCode == ERROR_FILE_NOT_FOUND, "GetPrinterDataExA returns %lu for \"%s\"!\n", dwReturnCode, p->pszName); + if (dwReturnCode != ERROR_MORE_DATA) + continue; + + ok(dwType == p->dwType, "dwType is %lu for \"%s\"!\n", dwType, p->pszName); + + if (p->cbNeededA < 0xFFFFFFFF) + ok(cbNeeded == p->cbNeededA, "cbNeeded is %lu for \"%s\", but expected %lu!\n", cbNeeded, p->pszName, p->cbNeededA); + else + ok(cbNeeded > 0, "cbNeeded is 0 for \"%s\"!\n", p->pszName); + + pDataA = HeapAlloc(GetProcessHeap(), 0, cbNeeded); + dwReturnCode = GetPrinterDataExA(hPrinter, NULL, p->pszName, NULL, pDataA, cbNeeded, &cbNeeded); + ok(dwReturnCode == ERROR_SUCCESS, "GetPrinterDataExA returns %lu for \"%s\"!\n", dwReturnCode, p->pszName); + + // Try the Unicode version of the function too. + dwType = 0xDEADBEEF; + dwReturnCode = GetPrinterDataExW(hPrinter, NULL, p->pwszName, &dwType, NULL, 0, &cbNeeded); + ok(dwReturnCode == ERROR_MORE_DATA, "GetPrinterDataExW returns %lu for \"%s\"!\n", dwReturnCode, p->pszName); + ok(dwType == p->dwType, "dwType is %lu for \"%s\"!\n", dwType, p->pszName); + + pDataW = HeapAlloc(GetProcessHeap(), 0, cbNeeded); + dwReturnCode = GetPrinterDataExW(hPrinter, NULL, p->pwszName, NULL, pDataW, cbNeeded, &cbNeeded); + ok(dwReturnCode == ERROR_SUCCESS, "GetPrinterDataExW returns %lu for \"%s\"!\n", dwReturnCode, p->pszName); + + // Verify that OSVERSIONINFO structures are correctly returned. + if (strcmp(p->pszName, "OSVersion") == 0) + { + POSVERSIONINFOA pOSVersionInfoA = (POSVERSIONINFOA)pDataA; + POSVERSIONINFOW pOSVersionInfoW = (POSVERSIONINFOW)pDataW; + ok(pOSVersionInfoA->dwOSVersionInfoSize == sizeof(OSVERSIONINFOA), "dwOSVersionInfoSize is %lu!\n", pOSVersionInfoA->dwOSVersionInfoSize); + ok(pOSVersionInfoW->dwOSVersionInfoSize == sizeof(OSVERSIONINFOW), "dwOSVersionInfoSize is %lu!\n", pOSVersionInfoW->dwOSVersionInfoSize); + } + else if (strcmp(p->pszName, "OSVersionEx") == 0) + { + POSVERSIONINFOEXA pOSVersionInfoA = (POSVERSIONINFOEXA)pDataA; + POSVERSIONINFOEXW pOSVersionInfoW = (POSVERSIONINFOEXW)pDataW; + ok(pOSVersionInfoA->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA), "dwOSVersionInfoSize is %lu!\n", pOSVersionInfoA->dwOSVersionInfoSize); + ok(pOSVersionInfoW->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXW), "dwOSVersionInfoSize is %lu!\n", pOSVersionInfoW->dwOSVersionInfoSize); + } + + // Shortly test SetPrinterDataExW by setting the same data we just retrieved. + if (p->bSettable) + { + dwReturnCode = SetPrinterDataExW(hPrinter, NULL, p->pwszName, dwType, pDataW, cbNeeded); + ok(dwReturnCode == ERROR_SUCCESS, "SetPrinterDataExW returns %lu for \"%s\"!\n", dwReturnCode, p->pszName); + } + + HeapFree(GetProcessHeap(), 0, pDataA); + HeapFree(GetProcessHeap(), 0, pDataW); + } + + // Try an invalid one. + dwReturnCode = GetPrinterDataExW(hPrinter, NULL, L"Invalid", NULL, NULL, 0, &cbNeeded); + ok(dwReturnCode == ERROR_INVALID_PARAMETER, "GetPrinterDataExW returns %lu!\n", dwReturnCode); + + ClosePrinter(hPrinter); + + // Open a handle to the default printer. + cchDefaultPrinter = _countof(wszDefaultPrinter); + ok(GetDefaultPrinterW(wszDefaultPrinter, &cchDefaultPrinter), "GetDefaultPrinterW returns FALSE and requires %lu characters!\n", cchDefaultPrinter); + if (!OpenPrinterW(wszDefaultPrinter, &hPrinter, NULL)) + { + skip("Could not retrieve a handle to the default printer!\n"); + return; + } + + // Using NULL or L"" for pKeyName on a Printer handle yields ERROR_INVALID_PARAMETER. + dwReturnCode = GetPrinterDataExW(hPrinter, NULL, L"Name", NULL, NULL, 0, &cbNeeded); + ok(dwReturnCode == ERROR_INVALID_PARAMETER, "GetPrinterDataExW returns %lu!\n", dwReturnCode); + dwReturnCode = GetPrinterDataExW(hPrinter, L"", L"Name", NULL, NULL, 0, &cbNeeded); + ok(dwReturnCode == ERROR_INVALID_PARAMETER, "GetPrinterDataExW returns %lu!\n", dwReturnCode); + + // Using L"\\" allows us to examine the contents of the main printer key anyway. + dwReturnCode = GetPrinterDataExW(hPrinter, L"\\", L"Name", &dwType, NULL, 0, &cbNeeded); + ok(dwReturnCode == ERROR_MORE_DATA, "GetPrinterDataExW returns %lu!\n", dwReturnCode); + ok(dwType == REG_SZ, "dwType is %lu!\n", dwType); + ok(cbNeeded > 0, "cbNeeded is 0!\n"); + + pDataW = HeapAlloc(GetProcessHeap(), 0, cbNeeded); + dwReturnCode = GetPrinterDataExW(hPrinter, L"\\", L"Name", NULL, pDataW, cbNeeded, &cbNeeded); + ok(dwReturnCode == ERROR_SUCCESS, "GetPrinterDataExW returns %lu!\n", dwReturnCode); + + // The following test fails if the default printer is a remote printer. + ok(wcscmp((PWSTR)pDataW, wszDefaultPrinter) == 0, "pDataW is \"%S\", default printer is \"%S\"!\n", (PWSTR)pDataW, wszDefaultPrinter); + + // SetPrinterDataExW should return ERROR_ACCESS_DENIED when attempting to set the Name. + dwReturnCode = SetPrinterDataExW(hPrinter, L"\\", L"Name", REG_SZ, pDataW, cbNeeded); + ok(dwReturnCode == ERROR_ACCESS_DENIED, "SetPrinterDataExW returns %lu!\n", dwReturnCode); + + HeapFree(GetProcessHeap(), 0, pDataW); +} diff --git a/rostests/apitests/winspool/testlist.c b/rostests/apitests/winspool/testlist.c index 74a70fb5c2d..1ab82750556 100644 --- a/rostests/apitests/winspool/testlist.c +++ b/rostests/apitests/winspool/testlist.c @@ -14,6 +14,7 @@ extern void func_ClosePrinter(void); extern void func_EnumPrinters(void); extern void func_EnumPrintProcessorDatatypes(void); extern void func_GetDefaultPrinter(void); +extern void func_GetPrinterData(void); extern void func_GetPrintProcessorDirectoryA(void); extern void func_GetPrintProcessorDirectoryW(void); extern void func_IsValidDevmodeA(void); @@ -27,6 +28,7 @@ const struct test winetest_testlist[] = { "EnumPrinters", func_EnumPrinters }, { "EnumPrintProcessorDatatypes", func_EnumPrintProcessorDatatypes }, { "GetDefaultPrinter", func_GetDefaultPrinter }, + { "GetPrinterData", func_GetPrinterData }, { "GetPrintProcessorDirectoryA", func_GetPrintProcessorDirectoryA }, { "GetPrintProcessorDirectoryW", func_GetPrintProcessorDirectoryW }, { "IsValidDevmodeA", func_IsValidDevmodeA },