if (!SetThreadToken(NULL, hToken))
{
- ERR("SetThreadToken failed with error %u!\n", GetLastError());
+ ERR("SetThreadToken failed with error %lu!\n", GetLastError());
CloseHandle(hToken);
return FALSE;
}
// 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;
}
#include "precomp.h"
+PRINTPROVIDOR LocalSplFuncs;
+
+
BOOL WINAPI
ClosePrinter(HANDLE hPrinter)
{
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
#include <windef.h>
#include <winbase.h>
#include <wingdi.h>
+#include <winreg.h>
#include <winspool.h>
+#include <winsplp.h>
#include <wine/debug.h>
WINE_DEFAULT_DEBUG_CHANNEL(spoolss);
+// Function pointer to InitializePrintProvidor of a provider DLL
+typedef BOOL (WINAPI *PInitializePrintProvidor)(LPPRINTPROVIDOR, DWORD, LPWSTR);
+
#endif
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();
// Create a thread for serving RPC requests
hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RpcThreadProc, NULL, 0, NULL);
-
- if (hThread)
+ if (!hThread)
{
- // We don't need the thread handle. Keeping it open blocks the thread from terminating.
- CloseHandle(hThread);
- _UpdateServiceStatus(SERVICE_RUNNING, 0);
+ ERR("CreateThread failed with error %u!\n", GetLastError());
+ _UpdateServiceStatus(SERVICE_STOPPED, 0);
+ return;
}
- else
+
+ // 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("CreateThread failed with error %u!\n", GetLastError());
+ ERR("InitializeRouter failed with error %lu!\n", GetLastError());
_UpdateServiceStatus(SERVICE_STOPPED, 0);
+ return;
}
+
+ // We're alive!
+ _UpdateServiceStatus(SERVICE_RUNNING, 0);
}
int
#define WIN32_NO_STATUS
#include <windef.h>
+#include <winbase.h>
+#include <winreg.h>
#include <winsvc.h>
+#include <wingdi.h>
+#include <winspool.h>
+#include <winsplp.h>
#include <winspool_s.h>
#include <wine/debug.h>
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
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;
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;
}
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;
}
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;
}
Status = RpcBindingFree(&hBinding);
if (Status != RPC_S_OK)
{
- ERR("RpcBindingFree failed with status %u!\n", Status);
+ ERR("RpcBindingFree failed with status %ld!\n", Status);
}
}
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)
{
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;
}
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;
}
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;
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;
@ 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
-spec2def(winprint winprint.spec ADD_IMPORTLIB)
+spec2def(winprint.dll winprint.spec ADD_IMPORTLIB)
list(APPEND SOURCE
main.c
#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;
+}
@ stub ClosePrintProcessor
@ stub ControlPrintProcessor
-@ stub EnumPrintProcessorDatatypesW
+@ stdcall EnumPrintProcessorDatatypesW(ptr ptr long ptr long ptr ptr)
@ stub GetPrintProcessorCapabilities
@ stub OpenPrintProcessor
@ stub PrintDocumentOnPrintProcessor
-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}
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)
--- /dev/null
+/*
+ * 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 <colin@reactos.org>
+ */
+
+#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;
+}
#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;
}
#define _PRECOMP_H
#define WIN32_NO_STATUS
+#include <limits.h>
+#include <wchar.h>
+
#include <windef.h>
#include <winbase.h>
#include <wingdi.h>
#include <winreg.h>
#include <winspool.h>
#include <winsplp.h>
+#include <ndk/rtlfuncs.h>
#include <wine/debug.h>
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
--- /dev/null
+/*
+ * 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 <colin@reactos.org>
+ */
+
+#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;
+}
--- /dev/null
+/*
+ * 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 <colin@reactos.org>
+ */
+
+#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;
+}
--- /dev/null
+/*
+ * 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 <colin@reactos.org>
+ */
+
+#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);
+}
endif()
add_subdirectory(winhttp)
add_subdirectory(wininet)
+add_subdirectory(winspool)
add_subdirectory(ws2_32)
--- /dev/null
+
+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)
--- /dev/null
+/*
+ * 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 <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winspool.h>
+
+START_TEST(ClosePrinter)
+{
+ SetLastError(0xDEADBEEF);
+ ok(!ClosePrinter(NULL), "ClosePrinter returns TRUE!\n");
+ ok(GetLastError() == ERROR_INVALID_HANDLE, "ClosePrinter returns error %lu!\n", GetLastError());
+}
--- /dev/null
+/*
+ * 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 <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winspool.h>
+
+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);
+}
--- /dev/null
+/*
+ * 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 <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winspool.h>
+
+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);
+}
--- /dev/null
+/*
+ * 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 <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winspool.h>
+
+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());
+}
--- /dev/null
+/*
+ * 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 <colin@reactos.org>
+ */
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winspool.h>
+
+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
+}
--- /dev/null
+/*
+ * 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 <colin@reactos.org>
+ */
+
+#define __ROS_LONG64__
+
+#define STANDALONE
+#include <apitest.h>
+
+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 }
+};