diff --git a/reactos/win32ss/printing/base/spoolss/context.c b/reactos/win32ss/printing/base/spoolss/context.c index 5fce2bc0dd9..ca39f5a57f9 100644 --- a/reactos/win32ss/printing/base/spoolss/context.c +++ b/reactos/win32ss/printing/base/spoolss/context.c @@ -21,7 +21,7 @@ ImpersonatePrinterClient(HANDLE hToken) if (!SetThreadToken(NULL, hToken)) { - ERR("SetThreadToken failed with error %u!\n", GetLastError()); + ERR("SetThreadToken failed with error %lu!\n", GetLastError()); CloseHandle(hToken); return FALSE; } @@ -46,14 +46,14 @@ RevertToPrinterSelf() // Retrieve our current impersonation token if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &hToken)) { - ERR("OpenThreadToken failed with error %u!\n", GetLastError()); + ERR("OpenThreadToken failed with error %lu!\n", GetLastError()); return NULL; } // Tell the thread to stop impersonating if (!SetThreadToken(NULL, NULL)) { - ERR("SetThreadToken failed with error %u!\n", GetLastError()); + ERR("SetThreadToken failed with error %lu!\n", GetLastError()); return NULL; } diff --git a/reactos/win32ss/printing/base/spoolss/main.c b/reactos/win32ss/printing/base/spoolss/main.c index b37bd30756c..39a45855209 100644 --- a/reactos/win32ss/printing/base/spoolss/main.c +++ b/reactos/win32ss/printing/base/spoolss/main.c @@ -7,6 +7,9 @@ #include "precomp.h" +PRINTPROVIDOR LocalSplFuncs; + + BOOL WINAPI ClosePrinter(HANDLE hPrinter) { @@ -52,7 +55,32 @@ GetPrintProcessorDirectoryW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBY BOOL WINAPI InitializeRouter(HANDLE SpoolerStatusHandle) { - return FALSE; + HINSTANCE hinstLocalSpl; + PInitializePrintProvidor pfnInitializePrintProvidor; + + // Only initialize localspl.dll for now. + // This function should later look for all available print providers in the registry and initialize all of them. + hinstLocalSpl = LoadLibraryW(L"localspl"); + if (!hinstLocalSpl) + { + ERR("LoadLibraryW for localspl failed with error %lu!\n", GetLastError()); + return FALSE; + } + + pfnInitializePrintProvidor = (PInitializePrintProvidor)GetProcAddress(hinstLocalSpl, "InitializePrintProvidor"); + if (!pfnInitializePrintProvidor) + { + ERR("GetProcAddress failed with error %lu!\n", GetLastError()); + return FALSE; + } + + if (!pfnInitializePrintProvidor(&LocalSplFuncs, sizeof(PRINTPROVIDOR), NULL)) + { + ERR("InitializePrintProvidor failed for localspl with error %lu!\n", GetLastError()); + return FALSE; + } + + return TRUE; } BOOL WINAPI diff --git a/reactos/win32ss/printing/base/spoolss/precomp.h b/reactos/win32ss/printing/base/spoolss/precomp.h index fa442336290..25f43d72505 100644 --- a/reactos/win32ss/printing/base/spoolss/precomp.h +++ b/reactos/win32ss/printing/base/spoolss/precomp.h @@ -12,9 +12,14 @@ #include #include #include +#include #include +#include #include WINE_DEFAULT_DEBUG_CHANNEL(spoolss); +// Function pointer to InitializePrintProvidor of a provider DLL +typedef BOOL (WINAPI *PInitializePrintProvidor)(LPPRINTPROVIDOR, DWORD, LPWSTR); + #endif diff --git a/reactos/win32ss/printing/base/spoolsv/init.c b/reactos/win32ss/printing/base/spoolsv/init.c index 699505fdb84..2cbb6f61cf1 100644 --- a/reactos/win32ss/printing/base/spoolsv/init.c +++ b/reactos/win32ss/printing/base/spoolsv/init.c @@ -10,23 +10,23 @@ DWORD _RpcSpoolerInit() { - DWORD ErrorCode; + DWORD dwErrorCode; // Call SpoolerInit in the security context of the client. // This delay-loads spoolss.dll in the user context and all further calls to functions in spoolss.dll will be done in the user context as well. - ErrorCode = RpcImpersonateClient(NULL); - if (ErrorCode != ERROR_SUCCESS) + dwErrorCode = RpcImpersonateClient(NULL); + if (dwErrorCode != ERROR_SUCCESS) { - ERR("RpcImpersonateClient failed with status %u!\n", ErrorCode); - return ErrorCode; + ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode); + return dwErrorCode; } - ErrorCode = SpoolerInit(); - if (ErrorCode != ERROR_SUCCESS) + dwErrorCode = SpoolerInit(); + if (dwErrorCode != ERROR_SUCCESS) { - ERR("SpoolerInit failed with status %u!\n", ErrorCode); + ERR("SpoolerInit failed with error %lu!\n", dwErrorCode); RpcRevertToSelf(); - return ErrorCode; + return dwErrorCode; } return RpcRevertToSelf(); diff --git a/reactos/win32ss/printing/base/spoolsv/main.c b/reactos/win32ss/printing/base/spoolsv/main.c index 1b00de0edb8..f00abaad051 100644 --- a/reactos/win32ss/printing/base/spoolsv/main.c +++ b/reactos/win32ss/printing/base/spoolsv/main.c @@ -60,18 +60,26 @@ _ServiceMain(DWORD dwArgc, LPWSTR* lpszArgv) // Create a thread for serving RPC requests hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RpcThreadProc, NULL, 0, NULL); - - if (hThread) - { - // We don't need the thread handle. Keeping it open blocks the thread from terminating. - CloseHandle(hThread); - _UpdateServiceStatus(SERVICE_RUNNING, 0); - } - else + if (!hThread) { ERR("CreateThread failed with error %u!\n", GetLastError()); _UpdateServiceStatus(SERVICE_STOPPED, 0); + return; } + + // We don't need the thread handle. Keeping it open blocks the thread from terminating. + CloseHandle(hThread); + + // Initialize the routing layer in spoolss.dll + if (!InitializeRouter(hServiceStatus)) + { + ERR("InitializeRouter failed with error %lu!\n", GetLastError()); + _UpdateServiceStatus(SERVICE_STOPPED, 0); + return; + } + + // We're alive! + _UpdateServiceStatus(SERVICE_RUNNING, 0); } int diff --git a/reactos/win32ss/printing/base/spoolsv/precomp.h b/reactos/win32ss/printing/base/spoolsv/precomp.h index a15b0d98bff..f14c3333b2b 100644 --- a/reactos/win32ss/printing/base/spoolsv/precomp.h +++ b/reactos/win32ss/printing/base/spoolsv/precomp.h @@ -10,16 +10,22 @@ #define WIN32_NO_STATUS #include +#include +#include #include +#include +#include +#include #include #include WINE_DEFAULT_DEBUG_CHANNEL(spoolsv); // rpcserver.c -extern DWORD WINAPI RpcThreadProc(LPVOID lpParameter); +DWORD WINAPI RpcThreadProc(LPVOID lpParameter); // Undocumented spoolss +BOOL WINAPI InitializeRouter(HANDLE SpoolerStatusHandle); DWORD WINAPI SpoolerInit(); #endif diff --git a/reactos/win32ss/printing/base/spoolsv/rpcserver.c b/reactos/win32ss/printing/base/spoolsv/rpcserver.c index 6e57d7fecda..869353d8c8a 100644 --- a/reactos/win32ss/printing/base/spoolsv/rpcserver.c +++ b/reactos/win32ss/printing/base/spoolsv/rpcserver.c @@ -15,21 +15,21 @@ RpcThreadProc(LPVOID lpParameter) Status = RpcServerUseProtseqEpW(L"ncacn_np", 20, L"\\pipe\\spoolss", NULL); if (Status != RPC_S_OK) { - ERR("RpcServerUseProtseqEpW failed with status %u!\n", Status); + ERR("RpcServerUseProtseqEpW failed with status %ld!\n", Status); return 0; } Status = RpcServerRegisterIf(winspool_v1_0_s_ifspec, NULL, NULL); if (Status != RPC_S_OK) { - ERR("RpcServerRegisterIf failed with status %u!\n", Status); + ERR("RpcServerRegisterIf failed with status %ld!\n", Status); return 0; } Status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, 0); if (Status != RPC_S_OK) { - ERR("RpcServerListen() failed with status %u!\n", Status); + ERR("RpcServerListen() failed with status %ld!\n", Status); } return 0; diff --git a/reactos/win32ss/printing/base/winspool/main.c b/reactos/win32ss/printing/base/winspool/main.c index 5e798c351f7..ffe2d3a95d8 100644 --- a/reactos/win32ss/printing/base/winspool/main.c +++ b/reactos/win32ss/printing/base/winspool/main.c @@ -18,7 +18,7 @@ WINSPOOL_HANDLE_bind(WINSPOOL_HANDLE wszName) Status = RpcStringBindingComposeW(NULL, L"ncacn_np", wszName, L"\\pipe\\spoolss", NULL, &wszStringBinding); if (Status != RPC_S_OK) { - ERR("RpcStringBindingComposeW failed with status %u!\n", Status); + ERR("RpcStringBindingComposeW failed with status %ld!\n", Status); return NULL; } @@ -26,7 +26,7 @@ WINSPOOL_HANDLE_bind(WINSPOOL_HANDLE wszName) Status = RpcBindingFromStringBindingW(wszStringBinding, &hBinding); if (Status != RPC_S_OK) { - ERR("RpcBindingFromStringBindingW failed with status %u!\n", Status); + ERR("RpcBindingFromStringBindingW failed with status %ld!\n", Status); return NULL; } @@ -34,7 +34,7 @@ WINSPOOL_HANDLE_bind(WINSPOOL_HANDLE wszName) Status = RpcStringFreeW(&wszStringBinding); if (Status != RPC_S_OK) { - ERR("RpcStringFreeW failed with status %u!\n", Status); + ERR("RpcStringFreeW failed with status %ld!\n", Status); return NULL; } @@ -49,7 +49,7 @@ WINSPOOL_HANDLE_unbind(WINSPOOL_HANDLE wszName, handle_t hBinding) Status = RpcBindingFree(&hBinding); if (Status != RPC_S_OK) { - ERR("RpcBindingFree failed with status %u!\n", Status); + ERR("RpcBindingFree failed with status %ld!\n", Status); } } @@ -119,6 +119,18 @@ EnumPrintersW(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD return FALSE; } +BOOL WINAPI +EnumPrintProcessorDatatypesA(LPSTR pName, LPSTR pPrintProcessorName, DWORD Level, LPBYTE pDatatypes, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned) +{ + return FALSE; +} + +BOOL WINAPI +EnumPrintProcessorDatatypesW(LPWSTR pName, LPWSTR pPrintProcessorName, DWORD Level, LPBYTE pDatatypes, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned) +{ + return FALSE; +} + BOOL WINAPI GetDefaultPrinterA(LPSTR pszBuffer, LPDWORD pcchBuffer) { @@ -178,7 +190,7 @@ OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefaul pwszPrinterName = HeapAlloc(GetProcessHeap(), 0, StringLength * sizeof(WCHAR)); if (!pwszPrinterName) { - ERR("HeapAlloc failed for pwszPrinterName with last error %u!\n", GetLastError()); + ERR("HeapAlloc failed for pwszPrinterName with last error %lu!\n", GetLastError()); goto Cleanup; } @@ -197,7 +209,7 @@ OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefaul pwszDatatype = HeapAlloc(GetProcessHeap(), 0, StringLength * sizeof(WCHAR)); if (!pwszDatatype) { - ERR("HeapAlloc failed for pwszDatatype with last error %u!\n", GetLastError()); + ERR("HeapAlloc failed for pwszDatatype with last error %lu!\n", GetLastError()); goto Cleanup; } @@ -250,14 +262,14 @@ OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefau ErrorCode = _RpcOpenPrinter(pPrinterName, phPrinter, pDatatype, pDevModeContainer, AccessRequired); if (ErrorCode) { - ERR("_RpcOpenPrinter failed with error %u!\n", ErrorCode); + ERR("_RpcOpenPrinter failed with error %lu!\n", ErrorCode); } ReturnValue = (ErrorCode == ERROR_SUCCESS); } RpcExcept(EXCEPTION_EXECUTE_HANDLER) { - ERR("_RpcOpenPrinter failed with exception code %u!\n", RpcExceptionCode()); + ERR("_RpcOpenPrinter failed with exception code %lu!\n", RpcExceptionCode()); } RpcEndExcept; @@ -276,14 +288,14 @@ SpoolerInit() ErrorCode = _RpcSpoolerInit(); if (ErrorCode) { - ERR("_RpcSpoolerInit failed with error %u!\n", ErrorCode); + ERR("_RpcSpoolerInit failed with error %lu!\n", ErrorCode); } ReturnValue = (ErrorCode == ERROR_SUCCESS); } RpcExcept(EXCEPTION_EXECUTE_HANDLER) { - ERR("_RpcSpoolerInit failed with exception code %u!\n", RpcExceptionCode()); + ERR("_RpcSpoolerInit failed with exception code %lu!\n", RpcExceptionCode()); } RpcEndExcept; diff --git a/reactos/win32ss/printing/base/winspool/winspool.spec b/reactos/win32ss/printing/base/winspool/winspool.spec index 665d7856a0b..8b48c113ff5 100644 --- a/reactos/win32ss/printing/base/winspool/winspool.spec +++ b/reactos/win32ss/printing/base/winspool/winspool.spec @@ -91,8 +91,8 @@ @ stub EnumPrinterKeyW @ stdcall EnumPrintersA(long ptr long ptr long ptr ptr) @ stdcall EnumPrintersW(long ptr long ptr long ptr ptr) -@ stub EnumPrintProcessorDatatypesA -@ stub EnumPrintProcessorDatatypesW +@ stdcall EnumPrintProcessorDatatypesA(ptr ptr long ptr long ptr ptr) +@ stdcall EnumPrintProcessorDatatypesW(ptr ptr long ptr long ptr ptr) @ stub EnumPrintProcessorsA @ stub EnumPrintProcessorsW @ stub EXTDEVICEMODE diff --git a/reactos/win32ss/printing/processors/winprint/CMakeLists.txt b/reactos/win32ss/printing/processors/winprint/CMakeLists.txt index 0f120e21075..1f422ec0330 100644 --- a/reactos/win32ss/printing/processors/winprint/CMakeLists.txt +++ b/reactos/win32ss/printing/processors/winprint/CMakeLists.txt @@ -1,5 +1,5 @@ -spec2def(winprint winprint.spec ADD_IMPORTLIB) +spec2def(winprint.dll winprint.spec ADD_IMPORTLIB) list(APPEND SOURCE main.c diff --git a/reactos/win32ss/printing/processors/winprint/main.c b/reactos/win32ss/printing/processors/winprint/main.c index df999e24918..8c279a8cf27 100644 --- a/reactos/win32ss/printing/processors/winprint/main.c +++ b/reactos/win32ss/printing/processors/winprint/main.c @@ -7,3 +7,97 @@ #include "precomp.h" +PCWSTR pwszDatatypes[] = { + L"RAW", + 0 +}; + +/** + * @name EnumPrintProcessorDatatypesW + * + * Obtains an array of all datatypes supported by this Print Processor. + * + * @param pName + * Server Name. Ignored here, because every caller of EnumPrintProcessorDatatypesW is interested in this Print Processor's information. + * + * @param pPrintProcessorName + * Print Processor Name. Ignored here, because every caller of EnumPrintProcessorDatatypesW is interested in this Print Processor's information. + * + * @param Level + * The level of the structure supplied through pDatatypes. This must be 1. + * + * @param pDatatypes + * Pointer to the buffer that receives an array of DATATYPES_INFO_1W structures. + * Can be NULL if you just want to know the required size of the buffer. + * + * @param cbBuf + * Size of the buffer you supplied for pDatatypes, in bytes. + * + * @param pcbNeeded + * Pointer to a variable that receives the required size of the buffer for pDatatypes, in bytes. + * This parameter mustn't be NULL! + * + * @param pcReturned + * Pointer to a variable that receives the number of elements of the DATATYPES_INFO_1W array. + * This parameter mustn't be NULL! + * + * @return + * TRUE if we successfully copied the array into pDatatypes, FALSE otherwise. + * A more specific error code can be obtained through GetLastError. + */ +BOOL WINAPI +EnumPrintProcessorDatatypesW(LPWSTR pName, LPWSTR pPrintProcessorName, DWORD Level, LPBYTE pDatatypes, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned) +{ + DATATYPES_INFO_1W DatatypesInfo1; + DWORD cbDatatype; + PBYTE pCurrentOutputDatatype; + PBYTE pCurrentOutputDatatypeInfo; + PCWSTR* pCurrentDatatype; + + // Sanity checks + if (Level != 1 || !pcbNeeded || !pcReturned) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + // Count the required buffer size and the number of datatypes. + *pcbNeeded = 0; + *pcReturned = 0; + + for (pCurrentDatatype = pwszDatatypes; *pCurrentDatatype; pCurrentDatatype++) + { + cbDatatype = (wcslen(*pCurrentDatatype) + 1) * sizeof(WCHAR); + *pcbNeeded += sizeof(DATATYPES_INFO_1W) + cbDatatype; + *pcReturned++; + } + + // Check if the supplied buffer is large enough. + if (cbBuf < *pcbNeeded) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + // Put the datatype strings right after the last DATATYPES_INFO_1W structure. + pCurrentOutputDatatypeInfo = pDatatypes; + pCurrentOutputDatatype = pDatatypes + *pcReturned * sizeof(DATATYPES_INFO_1W); + + // Copy over all datatypes. + for (pCurrentDatatype = pwszDatatypes; *pCurrentDatatype; pCurrentDatatype++) + { + // Copy the datatype string into the output buffer. + cbDatatype = (wcslen(*pCurrentDatatype) + 1) * sizeof(WCHAR); + CopyMemory(pCurrentOutputDatatype, *pCurrentDatatype, cbDatatype); + + // Fill and copy the DATATYPES_INFO_1W structure belonging to this datatype. + DatatypesInfo1.pName = (PWSTR)pCurrentOutputDatatype; + CopyMemory(pCurrentOutputDatatypeInfo, &DatatypesInfo1, sizeof(DATATYPES_INFO_1W)); + + // Advance to the next DATATYPES_INFO_1W location and string location in the output buffer. + pCurrentOutputDatatype += cbDatatype; + pCurrentOutputDatatypeInfo += sizeof(DATATYPES_INFO_1W); + } + + return TRUE; +} diff --git a/reactos/win32ss/printing/processors/winprint/winprint.spec b/reactos/win32ss/printing/processors/winprint/winprint.spec index 745b1b10b77..947cf947a5a 100644 --- a/reactos/win32ss/printing/processors/winprint/winprint.spec +++ b/reactos/win32ss/printing/processors/winprint/winprint.spec @@ -1,6 +1,6 @@ @ stub ClosePrintProcessor @ stub ControlPrintProcessor -@ stub EnumPrintProcessorDatatypesW +@ stdcall EnumPrintProcessorDatatypesW(ptr ptr long ptr long ptr ptr) @ stub GetPrintProcessorCapabilities @ stub OpenPrintProcessor @ stub PrintDocumentOnPrintProcessor diff --git a/reactos/win32ss/printing/providers/localspl/CMakeLists.txt b/reactos/win32ss/printing/providers/localspl/CMakeLists.txt index 28ad707fe8f..1280f6d4e84 100644 --- a/reactos/win32ss/printing/providers/localspl/CMakeLists.txt +++ b/reactos/win32ss/printing/providers/localspl/CMakeLists.txt @@ -1,9 +1,13 @@ -spec2def(localspl localspl.spec ADD_IMPORTLIB) +spec2def(localspl.dll localspl.spec ADD_IMPORTLIB) list(APPEND SOURCE + jobs.c main.c - precomp.h) + precomp.h + printers.c + printprocessors.c + tools.c) add_library(localspl SHARED ${SOURCE} @@ -13,6 +17,6 @@ add_library(localspl SHARED set_module_type(localspl win32dll UNICODE) target_link_libraries(localspl wine) -add_importlibs(localspl kernel32 msvcrt ntdll) +add_importlibs(localspl advapi32 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 new file mode 100644 index 00000000000..7e36f83677b --- /dev/null +++ b/reactos/win32ss/printing/providers/localspl/jobs.c @@ -0,0 +1,232 @@ +/* + * 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 + */ + +#include "precomp.h" + +LIST_ENTRY LocalJobQueue; + +void +InitializeJobQueue() +{ + const WCHAR wszPath[] = L"\\PRINTERS\\?????.SHD"; + const DWORD cchPath = sizeof(wszPath) / sizeof(WCHAR) - 1; + const DWORD cchFolders = sizeof("\\PRINTERS\\") - 1; + const DWORD cchPattern = sizeof("?????") - 1; + + DWORD dwJobID; + HANDLE hFind; + PLOCAL_JOB pJob; + PWSTR p; + WCHAR wszFullPath[MAX_PATH]; + WIN32_FIND_DATAW FindData; + + // Construct the full path search pattern. + CopyMemory(wszFullPath, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR)); + CopyMemory(&wszFullPath[cchSpoolDirectory], wszPath, (cchPath + 1) * sizeof(WCHAR)); + + // Use the search pattern to look for unfinished jobs serialized in shadow files (.SHD) + hFind = FindFirstFileW(wszFullPath, &FindData); + if (hFind == INVALID_HANDLE_VALUE) + { + // No unfinished jobs found. + return; + } + + do + { + // Skip possible subdirectories. + if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + continue; + + // Extract the Job ID and verify the file name format at the same time. + dwJobID = wcstoul(FindData.cFileName, &p, 10); + if (!IS_VALID_JOB_ID(dwJobID)) + continue; + + if (wcsicmp(p, L".SHD") != 0) + continue; + + // This shadow file has a valid name. Construct the full path and try to load it. + CopyMemory(&wszFullPath[cchSpoolDirectory + cchFolders], FindData.cFileName, cchPattern); + pJob = ReadJobShadowFile(wszFullPath); + if (!pJob) + continue; + + // Add it to the job queue of the respective printer. + InsertTailList(&pJob->Printer->JobQueue, &pJob->Entry); + } + while (FindNextFileW(hFind, &FindData)); + + FindClose(hFind); +} + +PLOCAL_JOB +ReadJobShadowFile(PCWSTR pwszFilePath) +{ + DWORD cbFileSize; + DWORD cbRead; + HANDLE hFile = INVALID_HANDLE_VALUE; + PLOCAL_JOB pJob; + PLOCAL_JOB pReturnValue = NULL; + PLOCAL_PRINTER pPrinter; + PSHD_HEADER pShadowFile = NULL; + PWSTR pwszPrinterName; + + // Try to open the file. + hFile = CreateFileW(pwszFilePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + ERR("CreateFileW failed with error %lu!\n", GetLastError()); + goto Cleanup; + } + + // Get its file size (small enough for a single DWORD) and allocate memory for all of it. + cbFileSize = GetFileSize(hFile, NULL); + pShadowFile = HeapAlloc(hProcessHeap, 0, cbFileSize); + if (!pShadowFile) + { + ERR("HeapAlloc failed with error %lu!\n", GetLastError()); + goto Cleanup; + } + + // Read the entire file. + if (!ReadFile(hFile, pShadowFile, cbFileSize, &cbRead, NULL)) + { + ERR("ReadFile failed with error %lu!\n", GetLastError()); + goto Cleanup; + } + + // Check signature and header size. + if (pShadowFile->dwSignature != SHD_WIN2003_SIGNATURE || pShadowFile->cbHeader != sizeof(SHD_HEADER)) + { + ERR("Signature or Header Size mismatch!\n"); + goto Cleanup; + } + + // Retrieve the associated printer from the table. + pwszPrinterName = (PWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offPrinterName); + pPrinter = RtlLookupElementGenericTable(&PrinterTable, pwszPrinterName); + if (!pPrinter) + { + ERR("This shadow file references a non-existing printer!\n"); + goto Cleanup; + } + + // Create a new job structure and copy over the relevant fields. + pJob = HeapAlloc(hProcessHeap, 0, sizeof(LOCAL_JOB)); + if (!pJob) + { + ERR("HeapAlloc failed with error %lu!\n", GetLastError()); + goto Cleanup; + } + + pJob->dwJobID = pShadowFile->dwJobID; + pJob->Printer = pPrinter; + pJob->pwszDatatype = DuplicateStringW((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offDatatype)); + pJob->pwszDocumentName = DuplicateStringW((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offDocumentName)); + pJob->pwszOutputFile = NULL; + CopyMemory(&pJob->DevMode, (PDEVMODEW)((ULONG_PTR)pShadowFile + pShadowFile->offDevMode), sizeof(DEVMODEW)); + + pReturnValue = pJob; + +Cleanup: + if (pShadowFile) + HeapFree(hProcessHeap, 0, pShadowFile); + + if (hFile != INVALID_HANDLE_VALUE) + CloseHandle(hFile); + + return pReturnValue; +} + +BOOL +WriteJobShadowFile(PCWSTR pwszFilePath, const PLOCAL_JOB pJob) +{ + BOOL bReturnValue = FALSE; + DWORD cbDatatype; + DWORD cbDocumentName; + DWORD cbFileSize; + DWORD cbPrinterName; + DWORD cbWritten; + DWORD dwCurrentOffset; + HANDLE hFile = INVALID_HANDLE_VALUE; + PSHD_HEADER pShadowFile = NULL; + + // Try to open the file. + hFile = CreateFileW(pwszFilePath, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + ERR("CreateFileW failed with error %lu!\n", GetLastError()); + goto Cleanup; + } + + // Compute the total size of the shadow file. + cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR); + cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR); + cbPrinterName = (wcslen(pJob->Printer->pwszPrinterName) + 1) * sizeof(WCHAR); + cbFileSize = sizeof(SHD_HEADER) + cbDatatype + cbDocumentName + cbPrinterName; + + // Allocate memory for it. + pShadowFile = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, cbFileSize); + if (!pShadowFile) + { + ERR("HeapAlloc failed with error %lu!\n", GetLastError()); + goto Cleanup; + } + + // Fill out the shadow file header information. + pShadowFile->dwSignature = SHD_WIN2003_SIGNATURE; + pShadowFile->cbHeader = sizeof(SHD_HEADER); + pShadowFile->dwJobID = pJob->dwJobID; + + // Add the extra values that are stored as offsets in the shadow file. + // The first value begins right after the shadow file header. + dwCurrentOffset = sizeof(SHD_HEADER); + + CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszDatatype, cbDatatype); + pShadowFile->offDatatype = dwCurrentOffset; + dwCurrentOffset += cbDatatype; + + CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszDocumentName, cbDocumentName); + pShadowFile->offDocumentName = dwCurrentOffset; + dwCurrentOffset += cbDocumentName; + + CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->Printer->pwszPrinterName, cbPrinterName); + pShadowFile->offPrinterName = dwCurrentOffset; + dwCurrentOffset += cbPrinterName; + + // Write the file. + if (!WriteFile(hFile, pShadowFile, cbFileSize, &cbWritten, NULL)) + { + ERR("WriteFile failed with error %lu!\n", GetLastError()); + goto Cleanup; + } + + bReturnValue = TRUE; + +Cleanup: + if (pShadowFile) + HeapFree(hProcessHeap, 0, pShadowFile); + + if (hFile != INVALID_HANDLE_VALUE) + CloseHandle(hFile); + + return bReturnValue; +} + +BOOL +FreeJob(PLOCAL_JOB pJob) +{ + ////////// TODO ///////// + /// Add some checks + HeapFree(hProcessHeap, 0, pJob->pwszDatatype); + HeapFree(hProcessHeap, 0, pJob->pwszDocumentName); + HeapFree(hProcessHeap, 0, pJob->pwszOutputFile); + HeapFree(hProcessHeap, 0, pJob); + + return TRUE; +} diff --git a/reactos/win32ss/printing/providers/localspl/main.c b/reactos/win32ss/printing/providers/localspl/main.c index 374c21ad4bf..a0bd9235718 100644 --- a/reactos/win32ss/printing/providers/localspl/main.c +++ b/reactos/win32ss/printing/providers/localspl/main.c @@ -7,8 +7,153 @@ #include "precomp.h" +// Global Variables +HANDLE hProcessHeap; +WCHAR wszSpoolDirectory[MAX_PATH]; +DWORD cchSpoolDirectory; + +// Constants +const WCHAR wszCurrentEnvironment[] = +#if defined(_X86_) + L"Windows NT x86"; +#elif defined(_AMD64_) + L"Windows x64"; +#elif defined(_ARM_) + L"Windows ARM"; +#else + #error Unsupported architecture +#endif + +static const PRINTPROVIDOR PrintProviderFunctions = { + LocalOpenPrinter, // fpOpenPrinter + NULL, // fpSetJob + NULL, // fpGetJob + NULL, // fpEnumJobs + NULL, // fpAddPrinter + NULL, // fpDeletePrinter + NULL, // fpSetPrinter + NULL, // fpGetPrinter + LocalEnumPrinters, // fpEnumPrinters + NULL, // fpAddPrinterDriver + NULL, // fpEnumPrinterDrivers + NULL, // fpGetPrinterDriver + NULL, // fpGetPrinterDriverDirectory + NULL, // fpDeletePrinterDriver + NULL, // fpAddPrintProcessor + LocalEnumPrintProcessors, // fpEnumPrintProcessors + LocalGetPrintProcessorDirectory, // fpGetPrintProcessorDirectory + NULL, // fpDeletePrintProcessor + LocalEnumPrintProcessorDatatypes, // fpEnumPrintProcessorDatatypes + LocalStartDocPrinter, // fpStartDocPrinter + LocalStartPagePrinter, // fpStartPagePrinter + LocalWritePrinter, // fpWritePrinter + LocalEndPagePrinter, // fpEndPagePrinter + NULL, // fpAbortPrinter + NULL, // fpReadPrinter + LocalEndDocPrinter, // fpEndDocPrinter + NULL, // fpAddJob + NULL, // fpScheduleJob + NULL, // fpGetPrinterData + NULL, // fpSetPrinterData + NULL, // fpWaitForPrinterChange + LocalClosePrinter, // fpClosePrinter + NULL, // fpAddForm + NULL, // fpDeleteForm + NULL, // fpGetForm + NULL, // fpSetForm + NULL, // fpEnumForms + NULL, // fpEnumMonitors + NULL, // fpEnumPorts + NULL, // fpAddPort + NULL, // fpConfigurePort + NULL, // fpDeletePort + NULL, // fpCreatePrinterIC + NULL, // fpPlayGdiScriptOnPrinterIC + NULL, // fpDeletePrinterIC + NULL, // fpAddPrinterConnection + NULL, // fpDeletePrinterConnection + NULL, // fpPrinterMessageBox + NULL, // fpAddMonitor + NULL, // fpDeleteMonitor + NULL, // fpResetPrinter + NULL, // fpGetPrinterDriverEx + NULL, // fpFindFirstPrinterChangeNotification + NULL, // fpFindClosePrinterChangeNotification + NULL, // fpAddPortEx + NULL, // fpShutDown + NULL, // fpRefreshPrinterChangeNotification + NULL, // fpOpenPrinterEx + NULL, // fpAddPrinterEx + NULL, // fpSetPort + NULL, // fpEnumPrinterData + NULL, // fpDeletePrinterData + NULL, // fpClusterSplOpen + NULL, // fpClusterSplClose + NULL, // fpClusterSplIsAlive + NULL, // fpSetPrinterDataEx + NULL, // fpGetPrinterDataEx + NULL, // fpEnumPrinterDataEx + NULL, // fpEnumPrinterKey + NULL, // fpDeletePrinterDataEx + NULL, // fpDeletePrinterKey + NULL, // fpSeekPrinter + NULL, // fpDeletePrinterDriverEx + NULL, // fpAddPerMachineConnection + NULL, // fpDeletePerMachineConnection + NULL, // fpEnumPerMachineConnections + NULL, // fpXcvData + NULL, // fpAddPrinterDriverEx + NULL, // fpSplReadPrinter + NULL, // fpDriverUnloadComplete + NULL, // fpGetSpoolFileInfo + NULL, // fpCommitSpoolData + NULL, // fpCloseSpoolFileHandle + NULL, // fpFlushPrinter + NULL, // fpSendRecvBidiData + NULL, // fpAddDriverCatalog +}; + +static void +_GetSpoolDirectory() +{ + const WCHAR wszSpoolPath[] = L"\\spool"; + const DWORD cchSpoolPath = sizeof(wszSpoolPath) / sizeof(WCHAR) - 1; + + // Get the system directory and append the "spool" subdirectory. + // 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; +} + +BOOL WINAPI +DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hinstDLL); + hProcessHeap = GetProcessHeap(); + _GetSpoolDirectory(); + InitializePrintProcessorTable(); + InitializePrinterTable(); + break; + } + + return TRUE; +} + BOOL WINAPI InitializePrintProvidor(LPPRINTPROVIDOR pPrintProvidor, DWORD cbPrintProvidor, LPWSTR pFullRegistryPath) { - return FALSE; + DWORD cbCopy; + + if (cbPrintProvidor < sizeof(PRINTPROVIDOR)) + cbCopy = cbPrintProvidor; + else + cbCopy = sizeof(PRINTPROVIDOR); + + CopyMemory(pPrintProvidor, &PrintProviderFunctions, cbCopy); + + return TRUE; } diff --git a/reactos/win32ss/printing/providers/localspl/precomp.h b/reactos/win32ss/printing/providers/localspl/precomp.h index ab00cc3f30f..6af7e4e1220 100644 --- a/reactos/win32ss/printing/providers/localspl/precomp.h +++ b/reactos/win32ss/printing/providers/localspl/precomp.h @@ -9,14 +9,181 @@ #define _PRECOMP_H #define WIN32_NO_STATUS +#include +#include + #include #include #include #include #include #include +#include #include WINE_DEFAULT_DEBUG_CHANNEL(localspl); +// Macros +#define IS_VALID_JOB_ID(ID) (ID >= 1 && ID <= 99999) + +// Constants +#define MAX_PRINTER_NAME 220 +#define SHD_WIN2003_SIGNATURE 0x4968 + +// Function pointers +typedef BOOL (WINAPI *PClosePrintProcessor)(HANDLE); +typedef BOOL (WINAPI *PControlPrintProcessor)(HANDLE, DWORD); +typedef BOOL (WINAPI *PEnumPrintProcessorDatatypesW)(LPWSTR, LPWSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD); +typedef DWORD (WINAPI *PGetPrintProcessorCapabilities)(LPWSTR, DWORD, LPBYTE, DWORD, LPDWORD); +typedef HANDLE (WINAPI *POpenPrintProcessor)(LPWSTR, PPRINTPROCESSOROPENDATA); +typedef BOOL (WINAPI *PPrintDocumentOnPrintProcessor)(HANDLE, LPWSTR); + +// Structures +/** +* Describes a Print Processor. +*/ +typedef struct _LOCAL_PRINT_PROCESSOR +{ + PWSTR pwszName; + RTL_GENERIC_TABLE DatatypeTable; + PClosePrintProcessor pfnClosePrintProcessor; + PControlPrintProcessor pfnControlPrintProcessor; + PEnumPrintProcessorDatatypesW pfnEnumPrintProcessorDatatypesW; + PGetPrintProcessorCapabilities pfnGetPrintProcessorCapabilities; + POpenPrintProcessor pfnOpenPrintProcessor; + PPrintDocumentOnPrintProcessor pfnPrintDocumentOnPrintProcessor; +} +LOCAL_PRINT_PROCESSOR, *PLOCAL_PRINT_PROCESSOR; + +/** + * Describes a printer and manages its print jobs. + * Created once for every printer at startup. + */ +typedef struct _LOCAL_PRINTER +{ + PWSTR pwszPrinterName; + PWSTR pwszDefaultDatatype; + DEVMODEW DefaultDevMode; + PLOCAL_PRINT_PROCESSOR pPrintProcessor; + LIST_ENTRY JobQueue; +} +LOCAL_PRINTER, *PLOCAL_PRINTER; + +/** + * Describes an entire print job associated to a specific printer through the Printer member. + * Created with every valid call to LocalStartDocPrinter. + */ +typedef struct _LOCAL_JOB +{ + LIST_ENTRY Entry; + PLOCAL_PRINTER Printer; + DWORD dwJobID; + PWSTR pwszDocumentName; + PWSTR pwszDatatype; + PWSTR pwszOutputFile; + DEVMODEW DevMode; +} +LOCAL_JOB, *PLOCAL_JOB; + +/** + * Describes a template for new print jobs for a specific printer. + * Created with every valid call to LocalOpenPrinter. + * + * This is needed, because you can supply defaults in a LocalOpenPrinter call, which affect all subsequent print jobs + * started with the same handle and a call to LocalStartDocPrinter. + */ +typedef struct _LOCAL_PRINTER_HANDLE +{ + PLOCAL_PRINTER Printer; + PLOCAL_JOB StartedJob; + PWSTR pwszDatatype; + DEVMODEW DevMode; +} +LOCAL_PRINTER_HANDLE, *PLOCAL_PRINTER_HANDLE; + +/** + * Describes a handle returned by LocalOpenPrinter. + * Suitable for all things that can be opened through LocalOpenPrinter. + */ +typedef struct _LOCAL_HANDLE +{ + enum { Printer, Monitor, Port } HandleType; + PVOID SpecificHandle; +} +LOCAL_HANDLE, *PLOCAL_HANDLE; + +/** + * Describes the header of a print job serialized into a shadow file (.SHD) + * Documented in http://www.undocprint.org/formats/winspool/shd + * Compatible with Windows Server 2003 + */ +typedef struct _SHD_HEADER +{ + DWORD dwSignature; + DWORD cbHeader; + WORD wStatus; + WORD wUnknown1; + DWORD dwJobID; + DWORD dwPriority; + DWORD offUserName; + DWORD offNotifyName; + DWORD offDocumentName; + DWORD offPort; + DWORD offPrinterName; + DWORD offDriverName; + DWORD offDevMode; + DWORD offPrintProcessor; + DWORD offDatatype; + DWORD dwUnknown2; + SYSTEMTIME stSubmitTime; + DWORD dwStartTime; + DWORD dwUntilTime; + DWORD dwUnknown6; + DWORD dwPageCount; + DWORD cbSecurityDescriptor; + DWORD offSecurityDescriptor; + DWORD dwUnknown3; + DWORD dwUnknown4; + DWORD dwUnknown5; + DWORD offComputerName; + DWORD dwSPLSize; +} +SHD_HEADER, *PSHD_HEADER; + + +// jobs.c +PLOCAL_JOB ReadJobShadowFile(PCWSTR pwszFilePath); +BOOL WriteJobShadowFile(PCWSTR pwszFilePath, const PLOCAL_JOB pJob); + +// main.c +extern const WCHAR wszCurrentEnvironment[]; +extern HANDLE hProcessHeap; +extern WCHAR wszSpoolDirectory[MAX_PATH]; +extern DWORD cchSpoolDirectory; + +// printers.c +extern RTL_GENERIC_TABLE PrinterTable; +void InitializePrinterTable(); +BOOL WINAPI LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned); +BOOL WINAPI LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault); +DWORD WINAPI LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo); +BOOL WINAPI LocalStartPagePrinter(HANDLE hPrinter); +BOOL WINAPI LocalWritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten); +BOOL WINAPI LocalEndPagePrinter(HANDLE hPrinter); +BOOL WINAPI LocalEndDocPrinter(HANDLE hPrinter); +BOOL WINAPI LocalClosePrinter(HANDLE hPrinter); + +// printprocessors.c +extern RTL_GENERIC_TABLE PrintProcessorTable; +void InitializePrintProcessorTable(); +BOOL WINAPI LocalEnumPrintProcessorDatatypes(LPWSTR pName, LPWSTR pPrintProcessorName, DWORD Level, LPBYTE pDatatypes, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned); +BOOL WINAPI LocalEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned); +BOOL WINAPI LocalGetPrintProcessorDirectory(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded); + +// tools.c +PWSTR AllocAndRegQueryWSZ(HKEY hKey, PCWSTR pwszValueName); +PWSTR DuplicateStringW(PCWSTR pwszInput); +PVOID NTAPI GenericTableAllocateRoutine(PRTL_GENERIC_TABLE Table, CLONG ByteSize); +VOID NTAPI GenericTableFreeRoutine(PRTL_GENERIC_TABLE Table, PVOID Buffer); + #endif diff --git a/reactos/win32ss/printing/providers/localspl/printers.c b/reactos/win32ss/printing/providers/localspl/printers.c new file mode 100644 index 00000000000..9149f52fd04 --- /dev/null +++ b/reactos/win32ss/printing/providers/localspl/printers.c @@ -0,0 +1,590 @@ +/* + * PROJECT: ReactOS Local Spooler + * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation + * PURPOSE: Functions related to Printers and printing + * COPYRIGHT: Copyright 2015 Colin Finck + */ + +#include "precomp.h" + +// Global Variables +RTL_GENERIC_TABLE PrinterTable; + + +/** + * @name _PrinterTableCompareRoutine + * + * RTL_GENERIC_COMPARE_ROUTINE for the Printer Table. + * Does a case-insensitive comparison, because e.g. LocalOpenPrinter doesn't match the case when looking for Printers. + */ +static RTL_GENERIC_COMPARE_RESULTS NTAPI +_PrinterTableCompareRoutine(PRTL_GENERIC_TABLE Table, PVOID FirstStruct, PVOID SecondStruct) +{ + PLOCAL_PRINTER A = (PLOCAL_PRINTER)FirstStruct; + PLOCAL_PRINTER B = (PLOCAL_PRINTER)SecondStruct; + + int iResult = wcsicmp(A->pwszPrinterName, B->pwszPrinterName); + + if (iResult < 0) + return GenericLessThan; + else if (iResult > 0) + return GenericGreaterThan; + else + return GenericEqual; +} + +/** + * @name InitializePrinterTable + * + * Initializes a RTL_GENERIC_TABLE of locally available Printers. + * The table is searchable by name and returns information about the printers, including their job queues. + * During this process, the job queues are also initialized. + */ +void +InitializePrinterTable() +{ + const WCHAR wszPrintersKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Printers"; + + DWORD cbDevMode; + DWORD cchPrinterName; + DWORD dwSubKeys; + DWORD i; + HKEY hKey = NULL; + HKEY hSubKey = NULL; + LONG lStatus; + PLOCAL_PRINT_PROCESSOR pPrintProcessor; + PLOCAL_PRINTER pPrinter = NULL; + PWSTR pwszPrintProcessor = NULL; + WCHAR wszPrinterName[MAX_PRINTER_NAME + 1]; + + // Initialize an empty table for our printers. + // We will search it by printer name. + RtlInitializeGenericTable(&PrinterTable, _PrinterTableCompareRoutine, GenericTableAllocateRoutine, GenericTableFreeRoutine, NULL); + + // Open our printers registry key. Each subkey is a local printer there. + lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszPrintersKey, 0, KEY_READ, &hKey); + if (lStatus != ERROR_SUCCESS) + { + ERR("RegOpenKeyExW failed with status %ld!\n", lStatus); + goto Cleanup; + } + + // Get the number of subkeys. + lStatus = RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + if (lStatus != ERROR_SUCCESS) + { + ERR("RegQueryInfoKeyW failed with status %ld!\n", lStatus); + goto Cleanup; + } + + // Loop through all available local printers. + for (i = 0; i < dwSubKeys; i++) + { + // Cleanup tasks from the previous run + if (hSubKey) + { + RegCloseKey(hSubKey); + hSubKey = NULL; + } + + if (pPrinter) + { + if (pPrinter->pwszDefaultDatatype) + HeapFree(hProcessHeap, 0, pPrinter->pwszDefaultDatatype); + + HeapFree(hProcessHeap, 0, pPrinter); + pPrinter = NULL; + } + + if (pwszPrintProcessor) + { + HeapFree(hProcessHeap, 0, pwszPrintProcessor); + pwszPrintProcessor = NULL; + } + + // Get the name of this printer. + cchPrinterName = sizeof(wszPrinterName) / sizeof(WCHAR); + lStatus = RegEnumKeyExW(hKey, i, wszPrinterName, &cchPrinterName, NULL, NULL, NULL, NULL); + if (lStatus == ERROR_MORE_DATA) + { + // This printer name exceeds the maximum length and is invalid. + continue; + } + else if (lStatus != ERROR_SUCCESS) + { + ERR("RegEnumKeyExW failed for iteration %lu with status %ld!\n", i, lStatus); + continue; + } + + // Open this Printer's registry key. + lStatus = RegOpenKeyExW(hKey, wszPrinterName, 0, KEY_READ, &hSubKey); + if (lStatus != ERROR_SUCCESS) + { + ERR("RegOpenKeyExW failed for Printer \"%S\" with status %ld!\n", wszPrinterName, lStatus); + continue; + } + + // Get the Print Processor. + pwszPrintProcessor = AllocAndRegQueryWSZ(hSubKey, L"Print Processor"); + if (!pwszPrintProcessor) + continue; + + // Try to find it in the Print Processor Table. + pPrintProcessor = RtlLookupElementGenericTable(&PrintProcessorTable, pwszPrintProcessor); + if (!pPrintProcessor) + { + ERR("Invalid Print Processor \"%S\" for Printer \"%S\"!\n", pwszPrintProcessor, wszPrinterName); + continue; + } + + // Create a new LOCAL_PRINTER structure for it. + pPrinter = HeapAlloc(hProcessHeap, 0, sizeof(LOCAL_PRINTER)); + if (!pPrinter) + { + ERR("HeapAlloc failed with error %lu!\n", GetLastError()); + goto Cleanup; + } + + pPrinter->pwszPrinterName = DuplicateStringW(wszPrinterName); + pPrinter->pPrintProcessor = pPrintProcessor; + InitializeListHead(&pPrinter->JobQueue); + + // Get the default datatype. + pPrinter->pwszDefaultDatatype = AllocAndRegQueryWSZ(hSubKey, L"Datatype"); + if (!pPrinter->pwszDefaultDatatype) + continue; + + // Verify that it's valid. + if (!RtlLookupElementGenericTable(&pPrintProcessor->DatatypeTable, pPrinter->pwszDefaultDatatype)) + { + ERR("Invalid default datatype \"%S\" for Printer \"%S\"!\n", pPrinter->pwszDefaultDatatype, wszPrinterName); + continue; + } + + // Get the default DevMode. + cbDevMode = sizeof(DEVMODEW); + lStatus = RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, (PBYTE)&pPrinter->DefaultDevMode, &cbDevMode); + if (lStatus != ERROR_SUCCESS || cbDevMode != sizeof(DEVMODEW)) + { + ERR("Couldn't query DevMode for Printer \"%S\", status is %ld, cbDevMode is %lu!\n", wszPrinterName, lStatus, cbDevMode); + continue; + } + + // Add this printer to the printer table. + if (!RtlInsertElementGenericTable(&PrinterTable, pPrinter, sizeof(LOCAL_PRINTER), NULL)) + { + ERR("RtlInsertElementGenericTable failed with error %lu!\n", GetLastError()); + goto Cleanup; + } + + // Don't let the cleanup routines free this. + pPrinter = NULL; + } + +Cleanup: + if (pwszPrintProcessor) + HeapFree(hProcessHeap, 0, pwszPrintProcessor); + + if (pPrinter) + { + if (pPrinter->pwszDefaultDatatype) + HeapFree(hProcessHeap, 0, pPrinter->pwszDefaultDatatype); + + HeapFree(hProcessHeap, 0, pPrinter); + } + + if (hSubKey) + RegCloseKey(hSubKey); + + if (hKey) + RegCloseKey(hKey); +} + + +BOOL WINAPI +LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned) +{ + ///////////// TODO ///////////////////// + return FALSE; +} + +BOOL WINAPI +LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault) +{ + BOOL bReturnValue = ROUTER_UNKNOWN; + DWORD cchComputerName; + DWORD cchPrinterName; + DWORD dwJobID; + PWSTR p = lpPrinterName; + PWSTR pwszPrinterName = NULL; + PLOCAL_JOB pJob; + PLOCAL_HANDLE pHandle; + PLOCAL_PRINTER pPrinter; + PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL; + PLIST_ENTRY pEntry; + WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1]; + + // Sanity checks + if (!lpPrinterName || !phPrinter) + { + SetLastError(ERROR_INVALID_PARAMETER); + goto Cleanup; + } + + // Does lpPrinterName begin with two backslashes to indicate a server name? + if (lpPrinterName[0] == L'\\' && lpPrinterName[1] == L'\\') + { + // 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. + SetLastError(ERROR_INVALID_PRINTER_NAME); + goto Cleanup; + } + + // Null-terminate the string here to enable comparison. + *p = 0; + + // Get the local computer name for comparison. + cchComputerName = sizeof(wszComputerName) / sizeof(WCHAR); + if (!GetComputerNameW(wszComputerName, &cchComputerName)) + { + ERR("GetComputerNameW failed with error %lu!\n", GetLastError()); + goto Cleanup; + } + + // Now compare this with the local computer name and reject if it doesn't match, because this print provider only supports local printers. + if (wcsicmp(lpPrinterName, wszComputerName) != 0) + { + SetLastError(ERROR_INVALID_PRINTER_NAME); + goto Cleanup; + } + + // We have checked the server name and don't need it anymore. + lpPrinterName = p + 1; + } + + // Look for a comma. If it exists, it indicates the end of the printer name. + p = wcschr(lpPrinterName, L','); + if (p) + cchPrinterName = p - lpPrinterName; + else + cchPrinterName = wcslen(lpPrinterName); + + // No printer name and no comma? This is invalid! + if (!cchPrinterName && !p) + { + SetLastError(ERROR_INVALID_PRINTER_NAME); + goto Cleanup; + } + + // Do we have a printer name? + if (cchPrinterName) + { + // Yes, extract it. + pwszPrinterName = HeapAlloc(hProcessHeap, 0, (cchPrinterName + 1) * sizeof(WCHAR)); + CopyMemory(pwszPrinterName, lpPrinterName, cchPrinterName * sizeof(WCHAR)); + pwszPrinterName[cchPrinterName] = 0; + + // Retrieve the associated printer from the table. + pPrinter = RtlLookupElementGenericTable(&PrinterTable, pwszPrinterName); + if (!pPrinter) + { + // The printer does not exist. + SetLastError(ERROR_INVALID_PRINTER_NAME); + goto Cleanup; + } + + // Create a new printer handle. + pPrinterHandle = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, sizeof(LOCAL_PRINTER_HANDLE)); + pPrinterHandle->Printer = pPrinter; + + // Check if a datatype was given. + if (pDefault && pDefault->pDatatype) + { + // Use the datatype if it's valid. + if (!RtlLookupElementGenericTable(&pPrinter->pPrintProcessor->DatatypeTable, pDefault->pDatatype)) + { + SetLastError(ERROR_INVALID_DATATYPE); + goto Cleanup; + } + + pPrinterHandle->pwszDatatype = DuplicateStringW(pDefault->pDatatype); + } + else + { + // Use the default datatype. + pPrinterHandle->pwszDatatype = DuplicateStringW(pPrinter->pwszDefaultDatatype); + } + + // Check if a DevMode was given, otherwise use the default. + if (pDefault && pDefault->pDevMode) + CopyMemory(&pPrinterHandle->DevMode, pDefault->pDevMode, sizeof(DEVMODEW)); + else + CopyMemory(&pPrinterHandle->DevMode, &pPrinter->DefaultDevMode, sizeof(DEVMODEW)); + + // Did we have a comma? Then the user may want a handle to an existing job instead of creating a new job. + if (p) + { + ++p; + + // Skip whitespace. + do + { + ++p; + } + while (*p == ' '); + + // The "Job " string has to follow now. + if (wcscmp(p, L"Job ") != 0) + { + SetLastError(ERROR_INVALID_PRINTER_NAME); + goto Cleanup; + } + + // Skip the "Job " string. + p += sizeof("Job ") - 1; + + // Skip even more whitespace. + while (*p == ' ') + ++p; + + // Finally extract the desired Job ID. + dwJobID = wcstoul(p, NULL, 10); + if (!IS_VALID_JOB_ID(dwJobID)) + { + // The user supplied an invalid Job ID. + SetLastError(ERROR_INVALID_PRINTER_NAME); + goto Cleanup; + } + + // Look for this job in the job queue of the printer. + pEntry = pPrinter->JobQueue.Flink; + + for (;;) + { + if (pEntry == &pPrinter->JobQueue) + { + // We have reached the end of the list without finding the desired Job ID. + SetLastError(ERROR_INVALID_PRINTER_NAME); + goto Cleanup; + } + + // Get our job structure. + pJob = CONTAINING_RECORD(pEntry, LOCAL_JOB, Entry); + + if (pJob->dwJobID == dwJobID) + { + // We have found the desired job. Give the caller a printer handle referencing it. + pPrinterHandle->StartedJob = pJob; + break; + } + + pEntry = pEntry->Flink; + } + } + + // Create a new handle that references a printer. + pHandle = HeapAlloc(hProcessHeap, 0, sizeof(LOCAL_HANDLE)); + pHandle->HandleType = Printer; + pHandle->SpecificHandle = pPrinterHandle; + } + else + { + // No printer name, but we have a comma! + // This may be a request to a XcvMonitor or XcvPort handle. + ++p; + + // Skip whitespace. + do + { + ++p; + } + while (*p == ' '); + + // Check if this is a request to a XcvMonitor. + if (wcscmp(p, L"XcvMonitor ") == 0) + { + // Skip the "XcvMonitor " string. + p += sizeof("XcvMonitor ") - 1; + + ///////////// TODO ///////////////////// + pHandle = HeapAlloc(hProcessHeap, 0, sizeof(LOCAL_HANDLE)); + pHandle->HandleType = Monitor; + //pHandle->SpecificHandle = pMonitorHandle; + } + else if (wcscmp(p, L"XcvPort ") == 0) + { + // Skip the "XcvPort " string. + p += sizeof("XcvPort ") - 1; + + //////////// TODO ////////////////////// + pHandle = HeapAlloc(hProcessHeap, 0, sizeof(LOCAL_HANDLE)); + pHandle->HandleType = Port; + //pHandle->SpecificHandle = pPortHandle; + } + else + { + SetLastError(ERROR_INVALID_PRINTER_NAME); + goto Cleanup; + } + } + + *phPrinter = (HANDLE)pHandle; + bReturnValue = ROUTER_SUCCESS; + + // Don't let the cleanup routines free this. + pPrinterHandle = NULL; + pwszPrinterName = NULL; + +Cleanup: + if (pPrinterHandle) + { + if (pPrinterHandle->pwszDatatype) + HeapFree(hProcessHeap, 0, pPrinterHandle->pwszDatatype); + + HeapFree(hProcessHeap, 0, pPrinterHandle); + } + + if (pwszPrinterName) + HeapFree(hProcessHeap, 0, pwszPrinterName); + + return bReturnValue; +} + +DWORD WINAPI +LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo) +{ + DWORD dwReturnValue = 0; + PDOC_INFO_1W pDocumentInfo1; + PLOCAL_HANDLE pHandle; + PLOCAL_PRINTER_HANDLE pPrinterHandle; + PLOCAL_JOB pJob; + + // Sanity checks + if (!pDocInfo) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + if (!hPrinter) + { + SetLastError(ERROR_INVALID_HANDLE); + return 0; + } + + // Check if this is a printer handle. + pHandle = (PLOCAL_HANDLE)hPrinter; + if (pHandle->HandleType != Printer) + { + SetLastError(ERROR_INVALID_HANDLE); + return 0; + } + + pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->SpecificHandle; + + // Check if this is the right document information level. + if (Level != 1) + { + SetLastError(ERROR_INVALID_LEVEL); + return 0; + } + + pDocumentInfo1 = (PDOC_INFO_1W)pDocInfo; + + // Create a new job. + pJob = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, sizeof(LOCAL_JOB)); + pJob->Printer = pPrinterHandle->Printer; + + // Check if a datatype was given. + if (pDocumentInfo1->pDatatype) + { + // Use the datatype if it's valid. + if (!RtlLookupElementGenericTable(&pJob->Printer->pPrintProcessor->DatatypeTable, pDocumentInfo1->pDatatype)) + { + SetLastError(ERROR_INVALID_DATATYPE); + goto Cleanup; + } + + pJob->pwszDatatype = DuplicateStringW(pDocumentInfo1->pDatatype); + } + else + { + // Use the printer handle datatype. + pJob->pwszDatatype = DuplicateStringW(pPrinterHandle->pwszDatatype); + } + + // Copy over printer defaults. + CopyMemory(&pJob->DevMode, &pPrinterHandle->DevMode, sizeof(DEVMODEW)); + + // Copy over supplied information. + if (pDocumentInfo1->pDocName) + pJob->pwszDocumentName = DuplicateStringW(pDocumentInfo1->pDocName); + + if (pDocumentInfo1->pOutputFile) + pJob->pwszOutputFile = DuplicateStringW(pDocumentInfo1->pOutputFile); + + // Enqueue the job. + ///////////// TODO ///////////////////// + +Cleanup: + if (pJob) + HeapFree(hProcessHeap, 0, pJob); + + return dwReturnValue; +} + +BOOL WINAPI +LocalStartPagePrinter(HANDLE hPrinter) +{ + ///////////// TODO ///////////////////// + return FALSE; +} + +BOOL WINAPI +LocalWritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten) +{ + ///////////// TODO ///////////////////// + return FALSE; +} + +BOOL WINAPI +LocalEndPagePrinter(HANDLE hPrinter) +{ + ///////////// TODO ///////////////////// + return FALSE; +} + +BOOL WINAPI +LocalEndDocPrinter(HANDLE hPrinter) +{ + ///////////// TODO ///////////////////// + return FALSE; +} + +BOOL WINAPI +LocalClosePrinter(HANDLE hPrinter) +{ + PLOCAL_HANDLE pHandle; + + if (!hPrinter) + { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + pHandle = (PLOCAL_HANDLE)hPrinter; + + ///////////// TODO ///////////////////// + /// Check the handle type, do thoroughful checks on all data fields and clean them. + //////////////////////////////////////// + + HeapFree(hProcessHeap, 0, pHandle); + + return TRUE; +} diff --git a/reactos/win32ss/printing/providers/localspl/printprocessors.c b/reactos/win32ss/printing/providers/localspl/printprocessors.c new file mode 100644 index 00000000000..ea29a00e7f3 --- /dev/null +++ b/reactos/win32ss/printing/providers/localspl/printprocessors.c @@ -0,0 +1,705 @@ +/* + * 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 Colin Finck + */ + +#include "precomp.h" + + +// Global Variables +RTL_GENERIC_TABLE PrintProcessorTable; + +/** + * @name _OpenEnvironment + * + * Checks a supplied pEnvironment variable for validity and opens its registry key. + * + * @param pEnvironment + * The pEnvironment variable to check. Can be NULL to use the current environment. + * + * @param hKey + * On success, this variable will contain a HKEY to the opened registry key of the environment. + * You can use it for further tasks and have to close it with RegCloseKey. + * + * @return + * TRUE if the environment is valid and a registry key was opened, FALSE otherwise. + * In case of failure, Last Error will be set to ERROR_INVALID_ENVIRONMENT. + */ +static BOOL +_OpenEnvironment(PCWSTR pEnvironment, PHKEY hKey) +{ + const WCHAR wszEnvironmentsKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Environments\\"; + const DWORD cchEnvironmentsKey = sizeof(wszEnvironmentsKey) / sizeof(WCHAR) - 1; + + BOOL bReturnValue = FALSE; + DWORD cchEnvironment; + LONG lStatus; + PWSTR pwszEnvironmentKey = NULL; + + // Use the current environment if none was supplied. + if (!pEnvironment) + pEnvironment = wszCurrentEnvironment; + + // Construct the registry key of the demanded environment. + cchEnvironment = wcslen(pEnvironment); + pwszEnvironmentKey = HeapAlloc(hProcessHeap, 0, (cchEnvironmentsKey + cchEnvironment + 1) * sizeof(WCHAR)); + if (!pwszEnvironmentKey) + { + ERR("HeapAlloc failed with error %lu!\n", GetLastError()); + goto Cleanup; + } + + CopyMemory(pwszEnvironmentKey, wszEnvironmentsKey, cchEnvironmentsKey * sizeof(WCHAR)); + CopyMemory(&pwszEnvironmentKey[cchEnvironmentsKey], pEnvironment, (cchEnvironment + 1) * sizeof(WCHAR)); + + // Open the registry key. + lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE, pwszEnvironmentKey, 0, KEY_READ, hKey); + if (lStatus == ERROR_FILE_NOT_FOUND) + { + SetLastError(ERROR_INVALID_ENVIRONMENT); + goto Cleanup; + } + else if (lStatus != ERROR_SUCCESS) + { + ERR("RegOpenKeyExW failed with status %ld!\n", lStatus); + goto Cleanup; + } + + bReturnValue = TRUE; + +Cleanup: + if (pwszEnvironmentKey) + HeapFree(hProcessHeap, 0, pwszEnvironmentKey); + + return bReturnValue; +} + +/** + * @name _PrinterTableCompareRoutine + * + * RTL_GENERIC_COMPARE_ROUTINE for the Print Processor Table. + * Does a case-insensitive comparison, because e.g. LocalEnumPrintProcessorDatatypes doesn't match the case when looking for Print Processors. + */ +static RTL_GENERIC_COMPARE_RESULTS NTAPI +_PrintProcessorTableCompareRoutine(PRTL_GENERIC_TABLE Table, PVOID FirstStruct, PVOID SecondStruct) +{ + PLOCAL_PRINT_PROCESSOR A = (PLOCAL_PRINT_PROCESSOR)FirstStruct; + PLOCAL_PRINT_PROCESSOR B = (PLOCAL_PRINT_PROCESSOR)SecondStruct; + + int iResult = wcsicmp(A->pwszName, B->pwszName); + + if (iResult < 0) + return GenericLessThan; + else if (iResult > 0) + return GenericGreaterThan; + else + return GenericEqual; +} + +/** + * @name _DatatypeTableCompareRoutine + * + * RTL_GENERIC_COMPARE_ROUTINE for the Datatype Table. + * Does a case-insensitive comparison, because e.g. LocalOpenPrinter doesn't match the case when looking for Datatypes. + */ +static RTL_GENERIC_COMPARE_RESULTS NTAPI +_DatatypeTableCompareRoutine(PRTL_GENERIC_TABLE Table, PVOID FirstStruct, PVOID SecondStruct) +{ + PWSTR A = (PWSTR)FirstStruct; + PWSTR B = (PWSTR)SecondStruct; + + int iResult = wcsicmp(A, B); + + if (iResult < 0) + return GenericLessThan; + else if (iResult > 0) + return GenericGreaterThan; + else + return GenericEqual; +} + +/** + * @name InitializePrintProcessorTable + * + * Initializes a RTL_GENERIC_TABLE of locally available Print Processors. + * The table is searchable by name and returns pointers to the functions of the loaded Print Processor DLL. + */ +void +InitializePrintProcessorTable() +{ + DWORD cbDatatypes; + DWORD cbFileName; + DWORD cchPrintProcessorPath; + DWORD cchMaxSubKey; + DWORD cchPrintProcessorName; + DWORD dwDatatypes; + DWORD dwSubKeys; + DWORD i; + DWORD j; + HINSTANCE hinstPrintProcessor; + HKEY hKey = NULL; + HKEY hSubKey = NULL; + HKEY hSubSubKey = NULL; + LONG lStatus; + PDATATYPES_INFO_1W pDatatypesInfo1 = NULL; + PLOCAL_PRINT_PROCESSOR pPrintProcessor = NULL; + PWSTR pwszDatatype = NULL; + PWSTR pwszPrintProcessorName = NULL; + WCHAR wszFileName[MAX_PATH]; + WCHAR wszPrintProcessorPath[MAX_PATH]; + + // Initialize an empty table for our Print Processors. + // We will search it by Print Processor name. + RtlInitializeGenericTable(&PrintProcessorTable, _PrintProcessorTableCompareRoutine, GenericTableAllocateRoutine, GenericTableFreeRoutine, NULL); + + // Prepare the path to the Print Processor directory. + if (!LocalGetPrintProcessorDirectory(NULL, NULL, 1, (PBYTE)wszPrintProcessorPath, sizeof(wszPrintProcessorPath), &cchPrintProcessorPath)) + goto Cleanup; + + cchPrintProcessorPath /= sizeof(WCHAR); + wszPrintProcessorPath[cchPrintProcessorPath++] = L'\\'; + + // Open the environment registry key. + if (!_OpenEnvironment(NULL, &hKey)) + goto Cleanup; + + // Open the "Print Processors" subkey. + lStatus = RegOpenKeyExW(hKey, L"Print Processors", 0, KEY_READ, &hSubKey); + if (lStatus != ERROR_SUCCESS) + { + ERR("RegOpenKeyExW failed with status %ld!\n", lStatus); + goto Cleanup; + } + + // Get the number of Print Processors and maximum sub key length. + lStatus = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwSubKeys, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL); + if (lStatus != ERROR_SUCCESS) + { + ERR("RegQueryInfoKeyW failed with status %ld!\n", lStatus); + goto Cleanup; + } + + // Allocate a temporary buffer for the Print Processor names. + pwszPrintProcessorName = HeapAlloc(hProcessHeap, 0, (cchMaxSubKey + 1) * sizeof(WCHAR)); + if (!pwszPrintProcessorName) + { + ERR("HeapAlloc failed with error %lu!\n", GetLastError()); + goto Cleanup; + } + + // Loop through all available local Print Processors. + for (i = 0; i < dwSubKeys; i++) + { + // Cleanup tasks from the previous run + if (hSubSubKey) + { + RegCloseKey(hSubSubKey); + hSubSubKey = NULL; + } + + if (pPrintProcessor) + { + if (pPrintProcessor->pwszName) + HeapFree(hProcessHeap, 0, pPrintProcessor->pwszName); + + HeapFree(hProcessHeap, 0, pPrintProcessor); + pPrintProcessor = NULL; + } + + if (pDatatypesInfo1) + { + HeapFree(hProcessHeap, 0, pDatatypesInfo1); + pDatatypesInfo1 = NULL; + } + + // Get the name of this Print Processor. + cchPrintProcessorName = cchMaxSubKey; + lStatus = RegEnumKeyExW(hSubKey, i, pwszPrintProcessorName, &cchPrintProcessorName, NULL, NULL, NULL, NULL); + if (lStatus != ERROR_SUCCESS) + { + ERR("RegEnumKeyExW failed with status %ld!\n", lStatus); + continue; + } + + // Open this Print Processor's registry key. + lStatus = RegOpenKeyExW(hSubKey, pwszPrintProcessorName, 0, KEY_READ, &hSubSubKey); + if (lStatus != ERROR_SUCCESS) + { + ERR("RegOpenKeyExW failed for Print Processor \"%S\" with status %ld!\n", pwszPrintProcessorName, lStatus); + continue; + } + + // Get the file name of the Print Processor. + cbFileName = sizeof(wszFileName); + lStatus = RegQueryValueExW(hSubSubKey, L"Driver", NULL, NULL, (PBYTE)wszFileName, &cbFileName); + if (lStatus != ERROR_SUCCESS) + { + ERR("RegQueryValueExW failed for Print Processor \"%S\" with status %ld!\n", pwszPrintProcessorName, lStatus); + continue; + } + + // Verify that our buffer is large enough. + if (cchPrintProcessorPath + cbFileName / sizeof(WCHAR) > MAX_PATH) + { + ERR("Print Processor directory \"%S\" for Print Processor \"%S\" is too long!\n", wszFileName, pwszPrintProcessorName); + continue; + } + + // Construct the full path to the Print Processor. + CopyMemory(&wszPrintProcessorPath[cchPrintProcessorPath], wszFileName, cbFileName); + + // Try to load it. + hinstPrintProcessor = LoadLibraryW(wszPrintProcessorPath); + if (lStatus != ERROR_SUCCESS) + { + ERR("LoadLibraryW failed for \"%S\" with error %lu!\n", wszPrintProcessorPath, GetLastError()); + continue; + } + + // Create a new LOCAL_PRINT_PROCESSOR structure for it. + pPrintProcessor = HeapAlloc(hProcessHeap, 0, sizeof(LOCAL_PRINT_PROCESSOR)); + pPrintProcessor->pwszName = DuplicateStringW(pwszPrintProcessorName); + + // Get and verify all its function pointers. + pPrintProcessor->pfnClosePrintProcessor = (PClosePrintProcessor)GetProcAddress(hinstPrintProcessor, "ClosePrintProcessor"); + if (!pPrintProcessor->pfnClosePrintProcessor) + { + ERR("Print Processor \"%S\" exports no ClosePrintProcessor!\n", wszPrintProcessorPath); + continue; + } + + pPrintProcessor->pfnControlPrintProcessor = (PControlPrintProcessor)GetProcAddress(hinstPrintProcessor, "ControlPrintProcessor"); + if (!pPrintProcessor->pfnControlPrintProcessor) + { + ERR("Print Processor \"%S\" exports no ControlPrintProcessor!\n", wszPrintProcessorPath); + continue; + } + + pPrintProcessor->pfnEnumPrintProcessorDatatypesW = (PEnumPrintProcessorDatatypesW)GetProcAddress(hinstPrintProcessor, "EnumPrintProcessorDatatypesW"); + if (!pPrintProcessor->pfnEnumPrintProcessorDatatypesW) + { + ERR("Print Processor \"%S\" exports no EnumPrintProcessorDatatypesW!\n", wszPrintProcessorPath); + continue; + } + + pPrintProcessor->pfnGetPrintProcessorCapabilities = (PGetPrintProcessorCapabilities)GetProcAddress(hinstPrintProcessor, "GetPrintProcessorCapabilities"); + if (!pPrintProcessor->pfnGetPrintProcessorCapabilities) + { + ERR("Print Processor \"%S\" exports no GetPrintProcessorCapabilities!\n", wszPrintProcessorPath); + continue; + } + + pPrintProcessor->pfnOpenPrintProcessor = (POpenPrintProcessor)GetProcAddress(hinstPrintProcessor, "OpenPrintProcessor"); + if (!pPrintProcessor->pfnOpenPrintProcessor) + { + ERR("Print Processor \"%S\" exports no OpenPrintProcessor!\n", wszPrintProcessorPath); + continue; + } + + pPrintProcessor->pfnPrintDocumentOnPrintProcessor = (PPrintDocumentOnPrintProcessor)GetProcAddress(hinstPrintProcessor, "PrintDocumentOnPrintProcessor"); + if (!pPrintProcessor->pfnPrintDocumentOnPrintProcessor) + { + ERR("Print Processor \"%S\" exports no PrintDocumentOnPrintProcessor!\n", wszPrintProcessorPath); + continue; + } + + // Get all supported datatypes. + pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, 0, &cbDatatypes, &dwDatatypes); + pDatatypesInfo1 = HeapAlloc(hProcessHeap, 0, cbDatatypes); + if (!pDatatypesInfo1) + { + ERR("HeapAlloc failed with error %lu!\n", GetLastError()); + goto Cleanup; + } + + if (!pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, (PBYTE)pDatatypesInfo1, cbDatatypes, &cbDatatypes, &dwDatatypes)) + { + ERR("EnumPrintProcessorDatatypesW failed for Print Processor \"%S\" with error %lu!\n", wszPrintProcessorPath, GetLastError()); + continue; + } + + // Add the supported datatypes to the datatype table. + RtlInitializeGenericTable(&pPrintProcessor->DatatypeTable, _DatatypeTableCompareRoutine, GenericTableAllocateRoutine, GenericTableFreeRoutine, NULL); + + for (j = 0; j < dwDatatypes; j++) + { + pwszDatatype = DuplicateStringW(pDatatypesInfo1->pName); + + if (!RtlInsertElementGenericTable(&pPrintProcessor->DatatypeTable, pDatatypesInfo1->pName, sizeof(PWSTR), NULL)) + { + ERR("RtlInsertElementGenericTable failed for iteration %lu with error %lu!\n", j, GetLastError()); + goto Cleanup; + } + + ++pDatatypesInfo1; + } + + // Add the Print Processor to the table. + if (!RtlInsertElementGenericTable(&PrintProcessorTable, pPrintProcessor, sizeof(LOCAL_PRINT_PROCESSOR), NULL)) + { + ERR("RtlInsertElementGenericTable failed for iteration %lu with error %lu!\n", i, GetLastError()); + goto Cleanup; + } + + // Don't let the cleanup routines free this. + pwszDatatype = NULL; + pPrintProcessor = NULL; + } + +Cleanup: + if (pwszDatatype) + HeapFree(hProcessHeap, 0, pwszDatatype); + + if (pDatatypesInfo1) + HeapFree(hProcessHeap, 0, pDatatypesInfo1); + + if (pPrintProcessor) + { + if (pPrintProcessor->pwszName) + HeapFree(hProcessHeap, 0, pPrintProcessor->pwszName); + + HeapFree(hProcessHeap, 0, pPrintProcessor); + } + + if (pwszPrintProcessorName) + HeapFree(hProcessHeap, 0, pwszPrintProcessorName); + + if (hSubSubKey) + RegCloseKey(hSubSubKey); + + if (hSubKey) + RegCloseKey(hSubKey); + + if (hKey) + RegCloseKey(hKey); +} + +/** + * @name LocalEnumPrintProcessorDatatypes + * + * Obtains an array of all datatypes supported by a particular Print Processor. + * Print Provider function for EnumPrintProcessorDatatypesA/EnumPrintProcessorDatatypesW. + * + * @param pName + * Server Name. Ignored here, because every caller of LocalEnumPrintProcessorDatatypes is interested in the local directory. + * + * @param pPrintProcessorName + * The (case-insensitive) name of the Print Processor to query. + * + * @param Level + * The level of the structure supplied through pDatatypes. This must be 1. + * + * @param pDatatypes + * Pointer to the buffer that receives an array of DATATYPES_INFO_1W structures. + * Can be NULL if you just want to know the required size of the buffer. + * + * @param cbBuf + * Size of the buffer you supplied for pDatatypes, in bytes. + * + * @param pcbNeeded + * Pointer to a variable that receives the required size of the buffer for pDatatypes, in bytes. + * This parameter mustn't be NULL! + * + * @param pcReturned + * Pointer to a variable that receives the number of elements of the DATATYPES_INFO_1W array. + * This parameter mustn't be NULL! + * + * @return + * TRUE if we successfully copied the array into pDatatypes, FALSE otherwise. + * A more specific error code can be obtained through GetLastError. + */ +BOOL WINAPI +LocalEnumPrintProcessorDatatypes(LPWSTR pName, LPWSTR pPrintProcessorName, DWORD Level, LPBYTE pDatatypes, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned) +{ + PLOCAL_PRINT_PROCESSOR pPrintProcessor; + + // Sanity checks + if (Level != 1) + { + SetLastError(ERROR_INVALID_LEVEL); + return FALSE; + } + + // Try to find the Print Processor. + pPrintProcessor = RtlLookupElementGenericTable(&PrintProcessorTable, pPrintProcessorName); + if (!pPrintProcessor) + { + SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR); + return FALSE; + } + + // Call its EnumPrintProcessorDatatypesW function. + return pPrintProcessor->pfnEnumPrintProcessorDatatypesW(pName, pPrintProcessorName, Level, pDatatypes, cbBuf, pcbNeeded, pcReturned); +} + +/** + * @name LocalEnumPrintProcessors + * + * Obtains an array of all available Print Processors on this computer. + * Print Provider function for EnumPrintProcessorsA/EnumPrintProcessorsW. + * + * @param pName + * Server Name. Ignored here, because every caller of LocalEnumPrintProcessors is interested in the local directory. + * + * @param pEnvironment + * One of the predefined operating system and architecture "environment" strings (like "Windows NT x86"). + * Alternatively, NULL to output the Print Processor directory of the current environment. + * + * @param Level + * The level of the structure supplied through pPrintProcessorInfo. This must be 1. + * + * @param pPrintProcessorInfo + * Pointer to the buffer that receives an array of PRINTPROCESSOR_INFO_1W structures. + * Can be NULL if you just want to know the required size of the buffer. + * + * @param cbBuf + * Size of the buffer you supplied for pPrintProcessorInfo, in bytes. + * + * @param pcbNeeded + * Pointer to a variable that receives the required size of the buffer for pPrintProcessorInfo, in bytes. + * This parameter mustn't be NULL! + * + * @param pcReturned + * Pointer to a variable that receives the number of elements of the PRINTPROCESSOR_INFO_1W array. + * This parameter mustn't be NULL! + * + * @return + * TRUE if we successfully copied the array into pPrintProcessorInfo, FALSE otherwise. + * A more specific error code can be obtained through GetLastError. + */ +BOOL WINAPI +LocalEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned) +{ + BOOL bReturnValue = FALSE; + DWORD cchMaxSubKey; + DWORD cchPrintProcessor; + DWORD i; + HKEY hKey = NULL; + HKEY hSubKey = NULL; + LONG lStatus; + PBYTE pCurrentOutputPrintProcessor; + PBYTE pCurrentOutputPrintProcessorInfo; + PRINTPROCESSOR_INFO_1W PrintProcessorInfo1; + PWSTR pwszEnvironmentKey = NULL; + PWSTR pwszTemp = NULL; + + // Sanity checks + if (Level != 1) + { + SetLastError(ERROR_INVALID_LEVEL); + goto Cleanup; + } + + if (!pcbNeeded || !pcReturned) + { + // This error must be caught by RPC and returned as RPC_X_NULL_REF_POINTER. + ERR("pcbNeeded or pcReturned is NULL!\n"); + goto Cleanup; + } + + // Verify pEnvironment and open its registry key. + if (!_OpenEnvironment(pEnvironment, &hKey)) + goto Cleanup; + + // Open the "Print Processors" subkey. + lStatus = RegOpenKeyExW(hKey, L"Print Processors", 0, KEY_READ, &hSubKey); + if (lStatus != ERROR_SUCCESS) + { + ERR("RegOpenKeyExW failed with status %ld!\n", lStatus); + goto Cleanup; + } + + // Get the number of Print Processors and maximum sub key length. + lStatus = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, pcReturned, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL); + if (lStatus != ERROR_SUCCESS) + { + ERR("RegQueryInfoKeyW failed with status %ld!\n", lStatus); + goto Cleanup; + } + + // Allocate a temporary buffer to let RegEnumKeyExW succeed. + pwszTemp = HeapAlloc(hProcessHeap, 0, (cchMaxSubKey + 1) * sizeof(WCHAR)); + if (!pwszTemp) + { + ERR("HeapAlloc failed with error %lu!\n", GetLastError()); + goto Cleanup; + } + + // Determine the required size of the output buffer. + *pcbNeeded = 0; + + for (i = 0; i < *pcReturned; i++) + { + // RegEnumKeyExW sucks! Unlike similar API functions, it only returns the actual numbers of characters copied when you supply a buffer large enough. + // So use pwszTemp with its size cchMaxSubKey for this. + cchPrintProcessor = cchMaxSubKey; + lStatus = RegEnumKeyExW(hSubKey, i, pwszTemp, &cchPrintProcessor, NULL, NULL, NULL, NULL); + if (lStatus != ERROR_SUCCESS) + { + ERR("RegEnumKeyExW failed with status %ld!\n", lStatus); + goto Cleanup; + } + + *pcbNeeded += sizeof(PRINTPROCESSOR_INFO_1W) + (cchPrintProcessor + 1) * sizeof(WCHAR); + } + + // Check if the supplied buffer is large enough. + if (cbBuf < *pcbNeeded) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + goto Cleanup; + } + + // Put the Print Processor strings right after the last PRINTPROCESSOR_INFO_1W structure. + pCurrentOutputPrintProcessorInfo = pPrintProcessorInfo; + pCurrentOutputPrintProcessor = pPrintProcessorInfo + *pcReturned * sizeof(PRINTPROCESSOR_INFO_1W); + + // Copy over all Print Processors. + for (i = 0; i < *pcReturned; i++) + { + // This isn't really correct, but doesn't cause any harm, because we've extensively checked the size of the supplied buffer above. + cchPrintProcessor = cchMaxSubKey; + + // Copy the Print Processor name. + lStatus = RegEnumKeyExW(hSubKey, i, (PWSTR)pCurrentOutputPrintProcessor, &cchPrintProcessor, NULL, NULL, NULL, NULL); + if (lStatus != ERROR_SUCCESS) + { + ERR("RegEnumKeyExW failed with status %ld!\n", lStatus); + goto Cleanup; + } + + // Fill and copy the PRINTPROCESSOR_INFO_1W structure belonging to this Print Processor. + PrintProcessorInfo1.pName = (PWSTR)pCurrentOutputPrintProcessor; + CopyMemory(pCurrentOutputPrintProcessorInfo, &PrintProcessorInfo1, sizeof(PRINTPROCESSOR_INFO_1W)); + + // Advance to the next PRINTPROCESSOR_INFO_1W location and string location in the output buffer. + pCurrentOutputPrintProcessor += (cchPrintProcessor + 1) * sizeof(WCHAR); + pCurrentOutputPrintProcessorInfo += sizeof(PRINTPROCESSOR_INFO_1W); + } + + // We've finished successfully! + SetLastError(ERROR_SUCCESS); + bReturnValue = TRUE; + +Cleanup: + if (pwszTemp) + HeapFree(hProcessHeap, 0, pwszTemp); + + if (pwszEnvironmentKey) + HeapFree(hProcessHeap, 0, pwszEnvironmentKey); + + if (hSubKey) + RegCloseKey(hSubKey); + + if (hKey) + RegCloseKey(hKey); + + return bReturnValue; +} + +/** + * @name LocalGetPrintProcessorDirectory + * + * Obtains the path to the local Print Processor directory. + * Print Provider function for GetPrintProcessorDirectoryA/GetPrintProcessorDirectoryW. + * + * @param pName + * Server Name. Ignored here, because every caller of LocalGetPrintProcessorDirectory is interested in the local directory. + * + * @param pEnvironment + * One of the predefined operating system and architecture "environment" strings (like "Windows NT x86"). + * Alternatively, NULL to output the Print Processor directory of the current environment. + * + * @param Level + * The level of the (non-existing) structure supplied through pPrintProcessorInfo. This must be 1. + * + * @param pPrintProcessorInfo + * Pointer to the buffer that receives the full path to the Print Processor directory. + * Can be NULL if you just want to know the required size of the buffer. + * + * @param cbBuf + * Size of the buffer you supplied for pPrintProcessorInfo, in bytes. + * + * @param pcbNeeded + * Pointer to a variable that receives the required size of the buffer for pPrintProcessorInfo, in bytes. + * This parameter mustn't be NULL! + * + * @return + * TRUE if we successfully copied the directory into pPrintProcessorInfo, FALSE otherwise. + * A more specific error code can be obtained through GetLastError. + */ +BOOL WINAPI +LocalGetPrintProcessorDirectory(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded) +{ + const WCHAR wszPath[] = L"\\PRTPROCS\\"; + const DWORD cchPath = sizeof(wszPath) / sizeof(WCHAR) - 1; + + BOOL bReturnValue = FALSE; + DWORD cbDataWritten; + HKEY hKey = NULL; + LONG lStatus; + PWSTR pwszEnvironmentKey = NULL; + + // Sanity checks + if (Level != 1) + { + SetLastError(ERROR_INVALID_LEVEL); + goto Cleanup; + } + + if (!pcbNeeded) + { + // This error must be caught by RPC and returned as RPC_X_NULL_REF_POINTER. + ERR("pcbNeeded is NULL!\n"); + goto Cleanup; + } + + // Verify pEnvironment and open its registry key. + if (!_OpenEnvironment(pEnvironment, &hKey)) + goto Cleanup; + + // Determine the size of the required buffer. + lStatus = RegQueryValueExW(hKey, L"Directory", NULL, NULL, NULL, pcbNeeded); + if (lStatus != ERROR_SUCCESS) + { + ERR("RegQueryValueExW failed with status %ld!\n", lStatus); + goto Cleanup; + } + + *pcbNeeded += cchSpoolDirectory; + *pcbNeeded += cchPath; + + // Is the supplied buffer large enough? + if (cbBuf < *pcbNeeded) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + goto Cleanup; + } + + // Copy the path to the "prtprocs" directory into pPrintProcessorInfo + CopyMemory(pPrintProcessorInfo, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR)); + CopyMemory(&pPrintProcessorInfo[cchSpoolDirectory], wszPath, cchPath * sizeof(WCHAR)); + + // Get the directory name from the registry. + lStatus = RegQueryValueExW(hKey, L"Directory", NULL, NULL, &pPrintProcessorInfo[cchSpoolDirectory + cchPath], &cbDataWritten); + if (lStatus != ERROR_SUCCESS) + { + ERR("RegQueryValueExW failed with status %ld!\n", lStatus); + goto Cleanup; + } + + // We've finished successfully! + SetLastError(ERROR_SUCCESS); + bReturnValue = TRUE; + +Cleanup: + if (pwszEnvironmentKey) + HeapFree(hProcessHeap, 0, pwszEnvironmentKey); + + if (hKey) + RegCloseKey(hKey); + + return bReturnValue; +} diff --git a/reactos/win32ss/printing/providers/localspl/tools.c b/reactos/win32ss/printing/providers/localspl/tools.c new file mode 100644 index 00000000000..fa0b7338d71 --- /dev/null +++ b/reactos/win32ss/printing/providers/localspl/tools.c @@ -0,0 +1,112 @@ +/* + * 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 + */ + +#include "precomp.h" + +/** + * @name AllocAndRegQueryWSZ + * + * Queries a REG_SZ value in the registry, allocates memory for it and returns a buffer containing the value. + * You have to free this buffer using HeapFree. + * + * @param hKey + * HKEY variable of the key opened with RegOpenKeyExW. + * + * @param pwszValueName + * Name of the REG_SZ value to query. + * + * @return + * Pointer to the buffer containing the value or NULL in case of failure. + */ +PWSTR +AllocAndRegQueryWSZ(HKEY hKey, PCWSTR pwszValueName) +{ + DWORD cbNeeded; + LONG lStatus; + PWSTR pwszValue; + + // Determine the size of the required buffer. + lStatus = RegQueryValueExW(hKey, pwszValueName, NULL, NULL, NULL, &cbNeeded); + if (lStatus != ERROR_SUCCESS) + { + ERR("RegQueryValueExW failed with status %ld!\n", lStatus); + return NULL; + } + + // Allocate it. + pwszValue = HeapAlloc(hProcessHeap, 0, cbNeeded); + if (!pwszValue) + { + ERR("HeapAlloc failed with error %lu!\n", GetLastError()); + return NULL; + } + + // Now get the actual value. + lStatus = RegQueryValueExW(hKey, pwszValueName, NULL, NULL, (PBYTE)pwszValue, &cbNeeded); + if (lStatus != ERROR_SUCCESS) + { + ERR("RegQueryValueExW failed with status %ld!\n", lStatus); + HeapFree(hProcessHeap, 0, pwszValue); + return NULL; + } + + return pwszValue; +} + +/** + * @name DuplicateStringW + * + * Allocates memory for a Unicode string and copies the input string into it. + * Equivalent of wcsdup, but the returned buffer must be freed with HeapFree instead of free. + * + * @param pwszInput + * The input string to copy + * + * @return + * Pointer to the copied string or NULL if no memory could be allocated. + */ +PWSTR +DuplicateStringW(PCWSTR pwszInput) +{ + DWORD cchInput; + PWSTR pwszOutput; + + cchInput = wcslen(pwszInput); + + pwszOutput = HeapAlloc(hProcessHeap, 0, (cchInput + 1) * sizeof(WCHAR)); + if (!pwszOutput) + { + ERR("HeapAlloc failed with error %lu!\n", GetLastError()); + return NULL; + } + + CopyMemory(pwszOutput, pwszInput, (cchInput + 1) * sizeof(WCHAR)); + + return pwszOutput; +} + +/** + * @name GenericTableAllocateRoutine + * + * RTL_GENERIC_ALLOCATE_ROUTINE for all our RTL_GENERIC_TABLEs, using HeapAlloc. + */ +PVOID NTAPI +GenericTableAllocateRoutine(PRTL_GENERIC_TABLE Table, CLONG ByteSize) +{ + return HeapAlloc(hProcessHeap, 0, ByteSize); +} + +/** + * @name GenericTableFreeRoutine + * + * RTL_GENERIC_FREE_ROUTINE for all our RTL_GENERIC_TABLEs, using HeapFree. + */ +VOID NTAPI +GenericTableFreeRoutine(PRTL_GENERIC_TABLE Table, PVOID Buffer) +{ + HeapFree(hProcessHeap, 0, Buffer); +} diff --git a/rostests/apitests/CMakeLists.txt b/rostests/apitests/CMakeLists.txt index 5bd8117e370..792a98d319f 100644 --- a/rostests/apitests/CMakeLists.txt +++ b/rostests/apitests/CMakeLists.txt @@ -24,4 +24,5 @@ if(NOT ARCH STREQUAL "amd64") endif() add_subdirectory(winhttp) add_subdirectory(wininet) +add_subdirectory(winspool) add_subdirectory(ws2_32) diff --git a/rostests/apitests/winspool/CMakeLists.txt b/rostests/apitests/winspool/CMakeLists.txt new file mode 100644 index 00000000000..6e4484dcb4a --- /dev/null +++ b/rostests/apitests/winspool/CMakeLists.txt @@ -0,0 +1,14 @@ + +list(APPEND SOURCE + ClosePrinter.c + EnumPrintProcessorDatatypes.c + GetPrintProcessorDirectory.c + OpenPrinter.c + StartDocPrinter.c + testlist.c) + +add_executable(winspool_apitest ${SOURCE}) +target_link_libraries(winspool_apitest wine ${PSEH_LIB}) +set_module_type(winspool_apitest win32cui) +add_importlibs(winspool_apitest winspool msvcrt kernel32 ntdll) +add_cd_file(TARGET winspool_apitest DESTINATION reactos/bin FOR all) diff --git a/rostests/apitests/winspool/ClosePrinter.c b/rostests/apitests/winspool/ClosePrinter.c new file mode 100644 index 00000000000..86865b4be51 --- /dev/null +++ b/rostests/apitests/winspool/ClosePrinter.c @@ -0,0 +1,21 @@ +/* + * PROJECT: ReactOS Print Spooler DLL API Tests + * LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation + * PURPOSE: Tests for ClosePrinter + * COPYRIGHT: Copyright 2015 Colin Finck + */ + +#include + +#define WIN32_NO_STATUS +#include +#include +#include +#include + +START_TEST(ClosePrinter) +{ + SetLastError(0xDEADBEEF); + ok(!ClosePrinter(NULL), "ClosePrinter returns TRUE!\n"); + ok(GetLastError() == ERROR_INVALID_HANDLE, "ClosePrinter returns error %lu!\n", GetLastError()); +} diff --git a/rostests/apitests/winspool/EnumPrintProcessorDatatypes.c b/rostests/apitests/winspool/EnumPrintProcessorDatatypes.c new file mode 100644 index 00000000000..29c7977c98e --- /dev/null +++ b/rostests/apitests/winspool/EnumPrintProcessorDatatypes.c @@ -0,0 +1,71 @@ +/* + * PROJECT: ReactOS Print Spooler DLL API Tests + * LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation + * PURPOSE: Tests for EnumPrintProcessorDatatypesA/EnumPrintProcessorDatatypesW + * COPYRIGHT: Copyright 2015 Colin Finck + */ + +#include + +#define WIN32_NO_STATUS +#include +#include +#include +#include + +START_TEST(EnumPrintProcessorDatatypes) +{ + DWORD cbNeeded; + DWORD dwReturned; + PDATATYPES_INFO_1W pDatatypesInfo1; + + // Try with an invalid level, this needs to be caught first. + SetLastError(0xDEADBEEF); + ok(!EnumPrintProcessorDatatypesW(NULL, NULL, 0, NULL, 0, NULL, NULL), "EnumPrintProcessorDatatypesW returns TRUE!\n"); + ok(GetLastError() == ERROR_INVALID_LEVEL, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError()); + + // Now try with valid level, but no pcbNeeded and no pcReturned. + SetLastError(0xDEADBEEF); + ok(!EnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, 0, NULL, NULL), "EnumPrintProcessorDatatypesW returns TRUE!\n"); + ok(GetLastError() == RPC_X_NULL_REF_POINTER, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError()); + + // Now try with pcbNeeded and pcReturned, but give no Print Processor. + SetLastError(0xDEADBEEF); + ok(!EnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintProcessorDatatypesW returns TRUE!\n"); + ok(GetLastError() == ERROR_UNKNOWN_PRINTPROCESSOR, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError()); + + // Same error has to occur when looking for an invalid Print Processor. + SetLastError(0xDEADBEEF); + ok(!EnumPrintProcessorDatatypesW(NULL, L"invalid", 1, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintProcessorDatatypesW returns TRUE!\n"); + ok(GetLastError() == ERROR_UNKNOWN_PRINTPROCESSOR, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError()); + + // Now get the required buffer size by supplying all information. This needs to fail with ERROR_INSUFFICIENT_BUFFER. + // This also verifies that the function is really doing a case-insensitive lookup. + SetLastError(0xDEADBEEF); + ok(!EnumPrintProcessorDatatypesW(NULL, L"wInPrInT", 1, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintProcessorDatatypesW returns TRUE!\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError()); + ok(cbNeeded > 0, "cbNeeded is 0!\n"); + ok(dwReturned > 0, "dwReturned is 0!\n"); + + // Now provide the demanded size, but no buffer. + SetLastError(0xDEADBEEF); + ok(!EnumPrintProcessorDatatypesW(NULL, L"wInPrInT", 1, NULL, cbNeeded, &cbNeeded, &dwReturned), "EnumPrintProcessorDatatypesW returns TRUE!\n"); + ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError()); + + // This also has to fail the same way when no Print Processor was given at all. + SetLastError(0xDEADBEEF); + ok(!EnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, cbNeeded, &cbNeeded, &dwReturned), "EnumPrintProcessorDatatypesW returns TRUE!\n"); + ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError()); + + // Same error has to occur with a valid Print Processor, but a size too small. + SetLastError(0xDEADBEEF); + ok(!EnumPrintProcessorDatatypesW(NULL, L"wInPrInT", 1, NULL, cbNeeded, &cbNeeded, &dwReturned), "EnumPrintProcessorDatatypesW returns TRUE!\n"); + ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError()); + + // Finally use the function as intended and aim for success! + pDatatypesInfo1 = HeapAlloc(GetProcessHeap(), 0, cbNeeded); + SetLastError(0xDEADBEEF); + ok(EnumPrintProcessorDatatypesW(NULL, L"wInPrInT", 1, (PBYTE)pDatatypesInfo1, cbNeeded, &cbNeeded, &dwReturned), "EnumPrintProcessorDatatypesW returns FALSE!\n"); + ok(GetLastError() == ERROR_SUCCESS, "EnumPrintProcessorDatatypesW returns error %lu!\n", GetLastError()); + HeapFree(GetProcessHeap(), 0, pDatatypesInfo1); +} diff --git a/rostests/apitests/winspool/GetPrintProcessorDirectory.c b/rostests/apitests/winspool/GetPrintProcessorDirectory.c new file mode 100644 index 00000000000..bece06e8bb7 --- /dev/null +++ b/rostests/apitests/winspool/GetPrintProcessorDirectory.c @@ -0,0 +1,58 @@ +/* + * PROJECT: ReactOS Print Spooler DLL API Tests + * LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation + * PURPOSE: Tests for GetPrintProcessorDirectoryA/GetPrintProcessorDirectoryW + * COPYRIGHT: Copyright 2015 Colin Finck + */ + +#include + +#define WIN32_NO_STATUS +#include +#include +#include +#include + +START_TEST(GetPrintProcessorDirectory) +{ + DWORD cbNeeded; + PWSTR pwszBuffer; + + // Try with an invalid level, this needs to be caught first. + SetLastError(0xDEADBEEF); + ok(!GetPrintProcessorDirectoryW(NULL, NULL, 0, NULL, 0, NULL), "GetPrintProcessorDirectoryW returns TRUE!\n"); + ok(GetLastError() == ERROR_INVALID_LEVEL, "GetPrintProcessorDirectoryW returns error %lu!\n", GetLastError()); + + // Now try with valid level, but no pcbNeeded. + SetLastError(0xDEADBEEF); + ok(!GetPrintProcessorDirectoryW(NULL, NULL, 1, NULL, 0, NULL), "GetPrintProcessorDirectoryW returns TRUE!\n"); + ok(GetLastError() == RPC_X_NULL_REF_POINTER, "GetPrintProcessorDirectoryW returns error %lu!\n", GetLastError()); + + // Now get the required buffer size by supplying pcbNeeded. This needs to fail with ERROR_INSUFFICIENT_BUFFER. + SetLastError(0xDEADBEEF); + ok(!GetPrintProcessorDirectoryW(NULL, NULL, 1, NULL, 0, &cbNeeded), "GetPrintProcessorDirectoryW returns TRUE!\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetPrintProcessorDirectoryW returns error %lu!\n", GetLastError()); + ok(cbNeeded > 0, "cbNeeded is 0!\n"); + + // Now provide the demanded size, but no buffer. + SetLastError(0xDEADBEEF); + ok(!GetPrintProcessorDirectoryW(NULL, NULL, 1, NULL, cbNeeded, &cbNeeded), "GetPrintProcessorDirectoryW returns TRUE!\n"); + ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "GetPrintProcessorDirectoryW returns error %lu!\n", GetLastError()); + + // Same error has to occur with a size too small. + SetLastError(0xDEADBEEF); + ok(!GetPrintProcessorDirectoryW(NULL, NULL, 1, NULL, 1, &cbNeeded), "GetPrintProcessorDirectoryW returns TRUE!\n"); + ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "GetPrintProcessorDirectoryW returns error %lu!\n", GetLastError()); + + // Try with an invalid environment as well. + SetLastError(0xDEADBEEF); + ok(!GetPrintProcessorDirectoryW(NULL, L"invalid", 1, NULL, 0, &cbNeeded), "GetPrintProcessorDirectoryW returns TRUE!\n"); + ok(GetLastError() == ERROR_INVALID_ENVIRONMENT, "GetPrintProcessorDirectoryW returns error %lu!\n", GetLastError()); + + // Finally use the function as intended and aim for success! + pwszBuffer = HeapAlloc(GetProcessHeap(), 0, cbNeeded); + SetLastError(0xDEADBEEF); + ok(GetPrintProcessorDirectoryW(NULL, NULL, 1, (PBYTE)pwszBuffer, cbNeeded, &cbNeeded), "GetPrintProcessorDirectoryW returns FALSE!\n"); + ok(GetLastError() == ERROR_SUCCESS, "GetPrintProcessorDirectoryW returns error %lu!\n", GetLastError()); + HeapFree(GetProcessHeap(), 0, pwszBuffer); +} diff --git a/rostests/apitests/winspool/OpenPrinter.c b/rostests/apitests/winspool/OpenPrinter.c new file mode 100644 index 00000000000..7049edf6903 --- /dev/null +++ b/rostests/apitests/winspool/OpenPrinter.c @@ -0,0 +1,27 @@ +/* + * PROJECT: ReactOS Print Spooler DLL API Tests + * LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation + * PURPOSE: Tests for OpenPrinterA/OpenPrinterW + * COPYRIGHT: Copyright 2015 Colin Finck + */ + +#include + +#define WIN32_NO_STATUS +#include +#include +#include +#include + +START_TEST(OpenPrinter) +{ + HANDLE hPrinter; + + SetLastError(0xDEADBEEF); + ok(!OpenPrinterW(NULL, NULL, NULL), "OpenPrinterW returns TRUE!\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "OpenPrinterW returns error %lu!\n", GetLastError()); + + SetLastError(0xDEADBEEF); + ok(!OpenPrinterW(NULL, &hPrinter, NULL), "OpenPrinterW returns TRUE!\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "OpenPrinterW returns error %lu!\n", GetLastError()); +} diff --git a/rostests/apitests/winspool/StartDocPrinter.c b/rostests/apitests/winspool/StartDocPrinter.c new file mode 100644 index 00000000000..24af7dd7920 --- /dev/null +++ b/rostests/apitests/winspool/StartDocPrinter.c @@ -0,0 +1,42 @@ +/* + * PROJECT: ReactOS Print Spooler DLL API Tests + * LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation + * PURPOSE: Tests for StartDocPrinterA/StartDocPrinterW + * COPYRIGHT: Copyright 2015 Colin Finck + */ + +#include + +#define WIN32_NO_STATUS +#include +#include +#include +#include + +START_TEST(StartDocPrinter) +{ + DWORD dwResult; + DOCINFOW docInfo = { 0 }; + + SetLastError(0xDEADBEEF); + dwResult = StartDocPrinterW(NULL, 0, NULL); + ok(dwResult == 0, "StartDocPrinterW returns %lu!\n", dwResult); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "StartDocPrinter returns error %lu!\n", GetLastError()); + + SetLastError(0xDEADBEEF); + dwResult = StartDocPrinterW(NULL, 1, NULL); + ok(dwResult == 0, "StartDocPrinterW returns %lu!\n", dwResult); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "StartDocPrinter returns error %lu!\n", GetLastError()); + + SetLastError(0xDEADBEEF); + dwResult = StartDocPrinterW(NULL, 0, (LPBYTE)&docInfo); + ok(dwResult == 0, "StartDocPrinterW returns %lu!\n", dwResult); + ok(GetLastError() == ERROR_INVALID_HANDLE, "StartDocPrinter returns error %lu!\n", GetLastError()); + + SetLastError(0xDEADBEEF); + dwResult = StartDocPrinterW(NULL, 1, (LPBYTE)&docInfo); + ok(dwResult == 0, "StartDocPrinterW returns %lu!\n", dwResult); + ok(GetLastError() == ERROR_INVALID_HANDLE, "StartDocPrinter returns error %lu!\n", GetLastError()); + + /// ERROR_INVALID_LEVEL with correct handle but invalid Level +} diff --git a/rostests/apitests/winspool/testlist.c b/rostests/apitests/winspool/testlist.c new file mode 100644 index 00000000000..1193c3a7382 --- /dev/null +++ b/rostests/apitests/winspool/testlist.c @@ -0,0 +1,28 @@ +/* + * PROJECT: ReactOS Print Spooler DLL API Tests + * LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation + * PURPOSE: Test list + * COPYRIGHT: Copyright 2015 Colin Finck + */ + +#define __ROS_LONG64__ + +#define STANDALONE +#include + +extern void func_ClosePrinter(void); +extern void func_EnumPrintProcessorDatatypes(void); +extern void func_GetPrintProcessorDirectory(void); +extern void func_OpenPrinter(void); +extern void func_StartDocPrinter(void); + +const struct test winetest_testlist[] = +{ + { "ClosePrinter", func_ClosePrinter }, + { "EnumPrintProcessorDatatypes", func_EnumPrintProcessorDatatypes }, + { "GetPrintProcessorDirectory", func_GetPrintProcessorDirectory }, + { "OpenPrinter", func_OpenPrinter }, + { "StartDocPrinter", func_StartDocPrinter }, + + { 0, 0 } +};