[LOCALSPL]
authorColin Finck <colin@reactos.org>
Fri, 19 Jun 2015 15:21:30 +0000 (15:21 +0000)
committerColin Finck <colin@reactos.org>
Fri, 19 Jun 2015 15:21:30 +0000 (15:21 +0000)
- Get rid of the Generic Tables entirely and make use of the new Skiplist for the Global Job List, the Printer's Job List and the Printer List.
  Use a doubly linked-list for the Print Processors (there won't be many) and just save the information returned from the Print Processor's EnumPrintProcessorDatatypesW instead of putting it in another structure.
- Implement LocalAddJob and LocalGetJob (with full JOB_INFO_1W and JOB_INFO_2W support)
  This makes use of the element index in the new Skiplist implementation to retrieve the position of the job in the list.
- Do some changes to LocalStartDocPrinter, though this will make use of LocalAddJob in the future.
- Make some global variables static again.

svn path=/branches/colins-printing-for-freedom/; revision=68195

reactos/win32ss/printing/providers/localspl/CMakeLists.txt
reactos/win32ss/printing/providers/localspl/jobs.c
reactos/win32ss/printing/providers/localspl/main.c
reactos/win32ss/printing/providers/localspl/precomp.h
reactos/win32ss/printing/providers/localspl/printers.c
reactos/win32ss/printing/providers/localspl/printprocessors.c
reactos/win32ss/printing/providers/localspl/tools.c

index 11a52a8..bf2a542 100644 (file)
@@ -1,6 +1,8 @@
 
 spec2def(localspl.dll localspl.spec ADD_IMPORTLIB)
 
+include_directories(${REACTOS_SOURCE_DIR}/lib/skiplist)
+
 list(APPEND SOURCE
     jobs.c
     main.c
@@ -16,7 +18,7 @@ add_library(localspl SHARED
     ${CMAKE_CURRENT_BINARY_DIR}/localspl.def)
 
 set_module_type(localspl win32dll UNICODE)
-target_link_libraries(localspl wine)
-add_importlibs(localspl advapi32 spoolss msvcrt kernel32 ntdll)
+target_link_libraries(localspl skiplist16 wine)
+add_importlibs(localspl advapi32 rpcrt4 spoolss msvcrt kernel32 ntdll)
 add_pch(localspl precomp.h SOURCE)
 add_cd_file(TARGET localspl DESTINATION reactos/system32 FOR all)
index 7cce869..7fa5753 100644 (file)
 
 #include "precomp.h"
 
-LIST_ENTRY LocalJobQueue;
+// Global Variables
+SKIPLIST GlobalJobList;
+
+// Local Variables
+static DWORD _dwLastJobID;
+
+
+/**
+ * @name _GlobalJobListCompareRoutine
+ *
+ * SKIPLIST_COMPARE_ROUTINE for the Global Job List.
+ * We need the Global Job List to check whether a Job ID is already in use. Consequently, this list is sorted by ID.
+ */
+static int WINAPI
+_GlobalJobListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
+{
+    PLOCAL_JOB A = (PLOCAL_JOB)FirstStruct;
+    PLOCAL_JOB B = (PLOCAL_JOB)SecondStruct;
+
+    return A->dwJobID - B->dwJobID;
+}
+
+/**
+ * @name _PrinterJobListCompareRoutine
+ *
+ * SKIPLIST_COMPARE_ROUTINE for the each Printer's Job List.
+ * Jobs in this list are sorted in the desired order of processing.
+ */
+static int WINAPI
+_PrinterJobListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
+{
+    PLOCAL_JOB A = (PLOCAL_JOB)FirstStruct;
+    PLOCAL_JOB B = (PLOCAL_JOB)SecondStruct;
+    int iComparison;
+    FILETIME ftSubmittedA;
+    FILETIME ftSubmittedB;
+    ULARGE_INTEGER uliSubmittedA;
+    ULARGE_INTEGER uliSubmittedB;
+    ULONGLONG ullResult;
+
+    // First compare the priorities to determine the order.
+    // The job with a higher priority shall come first.
+    iComparison = A->dwPriority - B->dwPriority;
+    if (iComparison != 0)
+        return iComparison;
+
+    // Both have the same priority, so go by creation time.
+    // Comparison is done using the MSDN-recommended way for comparing SYSTEMTIMEs.
+    if (!SystemTimeToFileTime(&A->stSubmitted, &ftSubmittedA))
+    {
+        ERR("SystemTimeToFileTime failed for A with error %lu!\n", GetLastError());
+        return 0;
+    }
+
+    if (!SystemTimeToFileTime(&B->stSubmitted, &ftSubmittedB))
+    {
+        ERR("SystemTimeToFileTime failed for B with error %lu!\n", GetLastError());
+        return 0;
+    }
+
+    uliSubmittedA.LowPart = ftSubmittedA.dwLowDateTime;
+    uliSubmittedA.HighPart = ftSubmittedA.dwHighDateTime;
+    uliSubmittedB.LowPart = ftSubmittedB.dwLowDateTime;
+    uliSubmittedB.HighPart = ftSubmittedB.dwHighDateTime;
+    ullResult = uliSubmittedA.QuadPart - uliSubmittedB.QuadPart;
+
+    if (ullResult < 0)
+        return -1;
+    else if (ullResult > 0)
+        return 1;
+
+    return 0;
+}
+
+BOOL
+GetNextJobID(PDWORD dwJobID)
+{
+    ++_dwLastJobID;
+
+    while (LookupElementSkiplist(&GlobalJobList, &_dwLastJobID, NULL))
+    {
+        // This ID is already taken. Try the next one.
+        ++_dwLastJobID;
+    }
+
+    if (!IS_VALID_JOB_ID(_dwLastJobID))
+    {
+        ERR("Job ID %lu isn't valid!\n", _dwLastJobID);
+        return FALSE;
+    }
+
+    *dwJobID = _dwLastJobID;
+    return TRUE;
+}
 
 void
-InitializeJobQueue()
+InitializeGlobalJobList()
 {
     const WCHAR wszPath[] = L"\\PRINTERS\\?????.SHD";
     const DWORD cchPath = _countof(wszPath) - 1;
@@ -19,11 +112,18 @@ InitializeJobQueue()
 
     DWORD dwJobID;
     HANDLE hFind;
-    PLOCAL_JOB pJob;
+    PLOCAL_JOB pJob = NULL;
     PWSTR p;
     WCHAR wszFullPath[MAX_PATH];
     WIN32_FIND_DATAW FindData;
 
+    // This one is incremented in GetNextJobID.
+    _dwLastJobID = 0;
+
+    // Initialize an empty list for all jobs of all local printers.
+    // We will search it by Job ID (supply a pointer to a DWORD in LookupElementSkiplist).
+    InitializeSkiplist(&GlobalJobList, DllAllocSplMem, _GlobalJobListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
+
     // Construct the full path search pattern.
     CopyMemory(wszFullPath, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR));
     CopyMemory(&wszFullPath[cchSpoolDirectory], wszPath, (cchPath + 1) * sizeof(WCHAR));
@@ -33,7 +133,7 @@ InitializeJobQueue()
     if (hFind == INVALID_HANDLE_VALUE)
     {
         // No unfinished jobs found.
-        return;
+        goto Cleanup;
     }
 
     do
@@ -56,12 +156,368 @@ InitializeJobQueue()
         if (!pJob)
             continue;
 
-        // Add it to the job queue of the respective printer.
-        InsertTailList(&pJob->Printer->JobQueue, &pJob->Entry);
+        // Add it to the Global Job List.
+        if (!InsertElementSkiplist(&GlobalJobList, pJob))
+        {
+            ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
+            goto Cleanup;
+        }
+
+        // Add it to the Printer's Job List.
+        if (!InsertElementSkiplist(&pJob->Printer->JobList, pJob))
+        {
+            ERR("InsertElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
+            goto Cleanup;
+        }
     }
     while (FindNextFileW(hFind, &FindData));
 
-    FindClose(hFind);
+Cleanup:
+    // Outside the loop
+    if (hFind)
+        FindClose(hFind);
+}
+
+void
+InitializePrinterJobList(PLOCAL_PRINTER pPrinter)
+{
+    // Initialize an empty list for this printer's jobs.
+    // This one is only for sorting the jobs. If you need to lookup a job, search the GlobalJobList by Job ID.
+    InitializeSkiplist(&pPrinter->JobList, DllAllocSplMem, _PrinterJobListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
+}
+
+BOOL WINAPI
+LocalAddJob(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
+{
+    const WCHAR wszDoubleBackslash[] = L"\\";
+    const DWORD cchDoubleBackslash = _countof(wszDoubleBackslash) - 1;
+    const WCHAR wszPrintersPath[] = L"\\PRINTERS\\";
+    const DWORD cchPrintersPath = _countof(wszPrintersPath) - 1;
+    const DWORD cchSpl = _countof("?????.SPL") - 1;
+
+    ADDJOB_INFO_1W AddJobInfo1;
+    BOOL bReturnValue = FALSE;
+    DWORD cchMachineName;
+    DWORD cchUserName;
+    PBYTE p;
+    PLOCAL_HANDLE pHandle;
+    PLOCAL_JOB pJob;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+    RPC_BINDING_HANDLE hServerBinding = NULL;
+    RPC_STATUS Status;
+    RPC_WSTR pwszBinding = NULL;
+    RPC_WSTR pwszMachineName = NULL;
+
+    // Check if this is a printer handle.
+    pHandle = (PLOCAL_HANDLE)hPrinter;
+    if (pHandle->HandleType != Printer)
+        goto Cleanup;
+
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->SpecificHandle;
+
+    // Check if this is the right structure level.
+    if (Level != 1)
+        goto Cleanup;
+
+    // FIXME: This needs to fail if the printer is set to do direct printing.
+
+    // Check if the supplied buffer is large enough.
+    *pcbNeeded = sizeof(ADDJOB_INFO_1W) + (cchSpoolDirectory + cchPrintersPath + cchSpl + 1) * sizeof(WCHAR);
+    if (cbBuf < *pcbNeeded)
+    {
+        SetLastError(ERROR_INSUFFICIENT_BUFFER);
+        goto Cleanup;
+    }
+
+    // Create a new job.
+    pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
+    if (!pJob)
+    {
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Reserve an ID for this job.
+    if (!GetNextJobID(&pJob->dwJobID))
+        goto Cleanup;
+
+    // Copy over defaults to the LOCAL_JOB structure.
+    pJob->Printer = pPrinterHandle->Printer;
+    pJob->dwPriority = DEF_PRIORITY;
+    pJob->pwszDatatype = AllocSplStr(pPrinterHandle->pwszDatatype);
+    pJob->pwszDocumentName = AllocSplStr(wszDefaultDocumentName);
+    CopyMemory(&pJob->DevMode, &pPrinterHandle->DevMode, sizeof(DEVMODEW));
+    GetSystemTime(&pJob->stSubmitted);
+
+    // Get the user name for the Job.
+    cchUserName = UNLEN + 1;
+    pJob->pwszUserName = DllAllocSplMem(cchUserName * sizeof(WCHAR));
+    if (!GetUserNameW(pJob->pwszUserName, &cchUserName))
+    {
+        ERR("GetUserNameW failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // FIXME: For now, pwszNotifyName equals pwszUserName.
+    pJob->pwszNotifyName = AllocSplStr(pJob->pwszUserName);
+
+    // Get the name of the machine that submitted the Job over RPC.
+    Status = RpcBindingServerFromClient(NULL, &hServerBinding);
+    if (Status != RPC_S_OK)
+    {
+        ERR("RpcBindingServerFromClient failed with status %lu!\n", Status);
+        goto Cleanup;
+    }
+
+    Status = RpcBindingToStringBindingW(hServerBinding, &pwszBinding);
+    if (Status != RPC_S_OK)
+    {
+        ERR("RpcBindingToStringBindingW failed with status %lu!\n", Status);
+        goto Cleanup;
+    }
+
+    Status = RpcStringBindingParseW(pwszBinding, NULL, NULL, &pwszMachineName, NULL, NULL);
+    if (Status != RPC_S_OK)
+    {
+        ERR("RpcStringBindingParseW failed with status %lu!\n", Status);
+        goto Cleanup;
+    }
+
+    cchMachineName = wcslen(pwszMachineName);
+    pJob->pwszMachineName = DllAllocSplMem((cchMachineName + cchDoubleBackslash + 1) * sizeof(WCHAR));
+    CopyMemory(pJob->pwszMachineName, wszDoubleBackslash, cchDoubleBackslash * sizeof(WCHAR));
+    CopyMemory(pJob->pwszMachineName + cchDoubleBackslash, pwszMachineName, (cchMachineName + 1) * sizeof(WCHAR));
+
+    // Add the job to the Global Job List.
+    if (!InsertElementSkiplist(&GlobalJobList, pJob))
+    {
+        ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
+        goto Cleanup;
+    }
+
+    // Add the job at the end of the Printer's Job List.
+    // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
+    if (!InsertTailElementSkiplist(&pJob->Printer->JobList, pJob))
+    {
+        ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
+        goto Cleanup;
+    }
+
+    // Return a proper ADDJOB_INFO_1W structure.
+    AddJobInfo1.JobId = pJob->dwJobID;
+    AddJobInfo1.Path = (PWSTR)(pData + sizeof(ADDJOB_INFO_1W));
+    p = pData;
+    CopyMemory(p, &AddJobInfo1, sizeof(ADDJOB_INFO_1W));
+    p += sizeof(ADDJOB_INFO_1W);
+    CopyMemory(p, wszSpoolDirectory, cchSpoolDirectory);
+    p += cchSpoolDirectory;
+    CopyMemory(p, wszPrintersPath, cchPrintersPath);
+    p += cchPrintersPath;
+    swprintf((PWSTR)p, L"%05lu.SPL", pJob->dwJobID);
+
+    bReturnValue = TRUE;
+
+Cleanup:
+    if (pwszMachineName)
+        RpcStringFreeW(&pwszMachineName);
+
+    if (pwszBinding)
+        RpcStringFreeW(&pwszBinding);
+
+    if (hServerBinding)
+        RpcBindingFree(&hServerBinding);
+
+    return bReturnValue;
+}
+
+
+static BOOL
+_LocalGetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PBYTE pOutput, DWORD cbBuf, PDWORD pcbNeeded)
+{
+    DWORD cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
+    DWORD cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
+    DWORD cbMachineName = (wcslen(pJob->pwszMachineName) + 1) * sizeof(WCHAR);
+    DWORD cbPrinterName = (wcslen(pJob->Printer->pwszPrinterName) + 1) * sizeof(WCHAR);
+    DWORD cbUserName = (wcslen(pJob->pwszUserName) + 1) * sizeof(WCHAR);
+    JOB_INFO_1W JobInfo1 = { 0 };
+    PBYTE pString;
+
+    // Check if the supplied buffer is large enough.
+    *pcbNeeded = sizeof(JOB_INFO_1W) + cbDatatype + cbDocumentName + cbMachineName + cbPrinterName + cbUserName;
+    if (cbBuf < *pcbNeeded)
+    {
+        SetLastError(ERROR_INSUFFICIENT_BUFFER);
+        return FALSE;
+    }
+
+    // Put the strings right after the JOB_INFO_1W structure.
+    pString = pOutput + sizeof(JOB_INFO_1W);
+
+    JobInfo1.pDatatype = (PWSTR)pString;
+    CopyMemory(pString, pJob->pwszDatatype, cbDatatype);
+    pString += cbDatatype;
+
+    JobInfo1.pDocument = (PWSTR)pString;
+    CopyMemory(pString, pJob->pwszDocumentName, cbDocumentName);
+    pString += cbDocumentName;
+
+    JobInfo1.pMachineName = (PWSTR)pString;
+    CopyMemory(pString, pJob->pwszMachineName, cbMachineName);
+    pString += cbMachineName;
+
+    JobInfo1.pPrinterName = (PWSTR)pString;
+    CopyMemory(pString, pJob->Printer->pwszPrinterName, cbPrinterName);
+    pString += cbPrinterName;
+
+    JobInfo1.pUserName = (PWSTR)pString;
+    CopyMemory(pString, pJob->pwszUserName, cbUserName);
+    pString += cbUserName;
+
+    // Fill the structure and copy it as well.
+    JobInfo1.JobId = pJob->dwJobID;
+    JobInfo1.Priority = pJob->dwPriority;
+    JobInfo1.Status = pJob->dwStatus;
+    JobInfo1.TotalPages = pJob->dwTotalPages;
+    CopyMemory(&JobInfo1.Submitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
+    CopyMemory(pOutput, &JobInfo1, sizeof(JOB_INFO_1W));
+
+    return TRUE;
+}
+
+static BOOL
+_LocalGetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PBYTE pOutput, DWORD cbBuf, PDWORD pcbNeeded)
+{
+    DWORD cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
+    DWORD cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
+    DWORD cbDriverName = (wcslen(pJob->Printer->pwszPrinterDriver) + 1) * sizeof(WCHAR);
+    DWORD cbMachineName = (wcslen(pJob->pwszMachineName) + 1) * sizeof(WCHAR);
+    DWORD cbNotifyName = (wcslen(pJob->pwszNotifyName) + 1) * sizeof(WCHAR);
+    DWORD cbPrinterName = (wcslen(pJob->Printer->pwszPrinterName) + 1) * sizeof(WCHAR);
+    DWORD cbPrintProcessor = (wcslen(pJob->Printer->pPrintProcessor->pwszName) + 1) * sizeof(WCHAR);
+    DWORD cbUserName = (wcslen(pJob->pwszUserName) + 1) * sizeof(WCHAR);
+    FILETIME ftNow;
+    FILETIME ftSubmitted;
+    JOB_INFO_2W JobInfo2 = { 0 };
+    PBYTE pString;
+    ULARGE_INTEGER uliNow;
+    ULARGE_INTEGER uliSubmitted;
+
+    // Check if the supplied buffer is large enough.
+    *pcbNeeded = sizeof(JOB_INFO_2W) + cbDatatype + sizeof(DEVMODEW) + cbDocumentName + cbDriverName + cbMachineName + cbNotifyName + cbPrinterName + cbPrintProcessor + cbUserName;
+    if (cbBuf < *pcbNeeded)
+    {
+        SetLastError(ERROR_INSUFFICIENT_BUFFER);
+        return FALSE;
+    }
+
+    // Put the strings right after the JOB_INFO_2W structure.
+    pString = pOutput + sizeof(JOB_INFO_2W);
+
+    JobInfo2.pDatatype = (PWSTR)pString;
+    CopyMemory(pString, pJob->pwszDatatype, cbDatatype);
+    pString += cbDatatype;
+
+    JobInfo2.pDevMode = (PDEVMODEW)pString;
+    CopyMemory(pString, &pJob->DevMode, sizeof(DEVMODEW));
+    pString += sizeof(DEVMODEW);
+
+    JobInfo2.pDocument = (PWSTR)pString;
+    CopyMemory(pString, pJob->pwszDocumentName, cbDocumentName);
+    pString += cbDocumentName;
+
+    JobInfo2.pDriverName = (PWSTR)pString;
+    CopyMemory(pString, pJob->Printer->pwszPrinterDriver, cbDriverName);
+    pString += cbDriverName;
+
+    JobInfo2.pMachineName = (PWSTR)pString;
+    CopyMemory(pString, pJob->pwszMachineName, cbMachineName);
+    pString += cbMachineName;
+
+    JobInfo2.pNotifyName = (PWSTR)pString;
+    CopyMemory(pString, pJob->pwszNotifyName, cbNotifyName);
+    pString += cbNotifyName;
+
+    JobInfo2.pPrinterName = (PWSTR)pString;
+    CopyMemory(pString, pJob->Printer->pwszPrinterName, cbPrinterName);
+    pString += cbPrinterName;
+
+    JobInfo2.pPrintProcessor = (PWSTR)pString;
+    CopyMemory(pString, pJob->Printer->pPrintProcessor->pwszName, cbPrintProcessor);
+    pString += cbPrintProcessor;
+
+    JobInfo2.pUserName = (PWSTR)pString;
+    CopyMemory(pString, pJob->pwszUserName, cbUserName);
+    pString += cbUserName;
+
+    // Time in JOB_INFO_2W is the number of milliseconds elapsed since the job was submitted. Calculate this time.
+    if (!SystemTimeToFileTime(&pJob->stSubmitted, &ftSubmitted))
+    {
+        ERR("SystemTimeToFileTime failed with error %lu!\n", GetLastError());
+        return FALSE;
+    }
+
+    GetSystemTimeAsFileTime(&ftNow);
+    uliSubmitted.LowPart = ftSubmitted.dwLowDateTime;
+    uliSubmitted.HighPart = ftSubmitted.dwHighDateTime;
+    uliNow.LowPart = ftNow.dwLowDateTime;
+    uliNow.HighPart = ftNow.dwHighDateTime;
+    JobInfo2.Time = (DWORD)((uliNow.QuadPart - uliSubmitted.QuadPart) / 10000);
+
+    // Position in JOB_INFO_2W is the 1-based index of the job in the processing queue.
+    // Retrieve this through the element index of the job in the Printer's Job List.
+    if (!LookupElementSkiplist(&pJob->Printer->JobList, pJob, &JobInfo2.Position))
+    {
+        ERR("pJob could not be located in the Printer's Job List!\n");
+        return FALSE;
+    }
+
+    // Make the index 1-based.
+    ++JobInfo2.Position;
+
+    // Fill the rest of the structure.
+    JobInfo2.JobId = pJob->dwJobID;
+    JobInfo2.PagesPrinted = pJob->dwPagesPrinted;
+    JobInfo2.Priority = pJob->dwPriority;
+    JobInfo2.StartTime = pJob->dwStartTime;
+    JobInfo2.Status = pJob->dwStatus;
+    JobInfo2.TotalPages = pJob->dwTotalPages;
+    JobInfo2.UntilTime = pJob->dwUntilTime;
+    CopyMemory(&JobInfo2.Submitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
+
+    // Finally copy the structure to the output pointer.
+    CopyMemory(pOutput, &JobInfo2, sizeof(JOB_INFO_2W));
+    return TRUE;
+}
+
+BOOL WINAPI
+LocalGetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pOutput, DWORD cbBuf, LPDWORD pcbNeeded)
+{
+    PLOCAL_HANDLE pHandle;
+    PLOCAL_JOB pJob;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+
+    // Check if this is a printer handle.
+    pHandle = (PLOCAL_HANDLE)hPrinter;
+    if (pHandle->HandleType != Printer)
+        return FALSE;
+
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->SpecificHandle;
+
+    // Get the desired job.
+    pJob = LookupElementSkiplist(&GlobalJobList, &JobId, NULL);
+    if (!pJob || pJob->Printer != pPrinterHandle->Printer)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    // The function behaves differently for each level.
+    if (Level == 1)
+        return _LocalGetJobLevel1(pPrinterHandle, pJob, pOutput, cbBuf, pcbNeeded);
+    else if (Level == 2)
+        return _LocalGetJobLevel2(pPrinterHandle, pJob, pOutput, cbBuf, pcbNeeded);
+
+    return FALSE;
 }
 
 PLOCAL_JOB
@@ -80,7 +536,7 @@ ReadJobShadowFile(PCWSTR pwszFilePath)
     hFile = CreateFileW(pwszFilePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
     if (hFile == INVALID_HANDLE_VALUE)
     {
-        ERR("CreateFileW failed with error %lu!\n", GetLastError());
+        ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
         goto Cleanup;
     }
 
@@ -89,30 +545,30 @@ ReadJobShadowFile(PCWSTR pwszFilePath)
     pShadowFile = DllAllocSplMem(cbFileSize);
     if (!pShadowFile)
     {
-        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        ERR("DllAllocSplMem failed with error %lufor file \"%S\"!\n", GetLastError(), pwszFilePath);
         goto Cleanup;
     }
 
     // Read the entire file.
     if (!ReadFile(hFile, pShadowFile, cbFileSize, &cbRead, NULL))
     {
-        ERR("ReadFile failed with error %lu!\n", GetLastError());
+        ERR("ReadFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
         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");
+        ERR("Signature or Header Size mismatch for file \"%S\"!\n", pwszFilePath);
         goto Cleanup;
     }
 
-    // Retrieve the associated printer from the table.
+    // Retrieve the associated printer from the list.
     pwszPrinterName = (PWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offPrinterName);
-    pPrinter = RtlLookupElementGenericTable(&PrinterTable, pwszPrinterName);
+    pPrinter = LookupElementSkiplist(&PrinterList, &pwszPrinterName, NULL);
     if (!pPrinter)
     {
-        ERR("This shadow file references a non-existing printer!\n");
+        ERR("Shadow file \"%S\" references a non-existing printer!\n", pwszFilePath);
         goto Cleanup;
     }
 
@@ -120,15 +576,18 @@ ReadJobShadowFile(PCWSTR pwszFilePath)
     pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
     if (!pJob)
     {
-        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
         goto Cleanup;
     }
 
     pJob->dwJobID = pShadowFile->dwJobID;
+    pJob->dwTotalPages = pShadowFile->dwTotalPages;
+    pJob->dwPriority = pShadowFile->dwPriority;
     pJob->Printer = pPrinter;
     pJob->pwszDatatype = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offDatatype));
     pJob->pwszDocumentName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offDocumentName));
     pJob->pwszOutputFile = NULL;
+    CopyMemory(&pJob->stSubmitted, &pShadowFile->stSubmitted, sizeof(SYSTEMTIME));
     CopyMemory(&pJob->DevMode, (PDEVMODEW)((ULONG_PTR)pShadowFile + pShadowFile->offDevMode), sizeof(DEVMODEW));
 
     pReturnValue = pJob;
@@ -160,33 +619,42 @@ WriteJobShadowFile(PCWSTR pwszFilePath, const PLOCAL_JOB pJob)
     hFile = CreateFileW(pwszFilePath, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, NULL);
     if (hFile == INVALID_HANDLE_VALUE)
     {
-        ERR("CreateFileW failed with error %lu!\n", GetLastError());
+        ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
         goto Cleanup;
     }
 
     // Compute the total size of the shadow file.
+    cbPrinterName = (wcslen(pJob->Printer->pwszPrinterName) + 1) * sizeof(WCHAR);
     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;
+    cbFileSize = sizeof(SHD_HEADER) + cbPrinterName + cbDatatype + cbDocumentName + sizeof(DEVMODEW);
 
     // Allocate memory for it.
     pShadowFile = DllAllocSplMem(cbFileSize);
     if (!pShadowFile)
     {
-        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
         goto Cleanup;
     }
 
     // Fill out the shadow file header information.
     pShadowFile->dwSignature = SHD_WIN2003_SIGNATURE;
     pShadowFile->cbHeader = sizeof(SHD_HEADER);
+
+    // Copy the values.
     pShadowFile->dwJobID = pJob->dwJobID;
+    pShadowFile->dwTotalPages = pJob->dwTotalPages;
+    pShadowFile->dwPriority = pJob->dwPriority;
+    CopyMemory(&pShadowFile->stSubmitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
 
     // 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->Printer->pwszPrinterName, cbPrinterName);
+    pShadowFile->offPrinterName = dwCurrentOffset;
+    dwCurrentOffset += cbPrinterName;
+
     CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszDatatype, cbDatatype);
     pShadowFile->offDatatype = dwCurrentOffset;
     dwCurrentOffset += cbDatatype;
@@ -195,14 +663,14 @@ WriteJobShadowFile(PCWSTR pwszFilePath, const PLOCAL_JOB pJob)
     pShadowFile->offDocumentName = dwCurrentOffset;
     dwCurrentOffset += cbDocumentName;
 
-    CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->Printer->pwszPrinterName, cbPrinterName);
-    pShadowFile->offPrinterName = dwCurrentOffset;
-    dwCurrentOffset += cbPrinterName;
+    CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, &pJob->DevMode, sizeof(DEVMODEW));
+    pShadowFile->offDevMode = dwCurrentOffset;
+    dwCurrentOffset += sizeof(DEVMODEW);
 
     // Write the file.
     if (!WriteFile(hFile, pShadowFile, cbFileSize, &cbWritten, NULL))
     {
-        ERR("WriteFile failed with error %lu!\n", GetLastError());
+        ERR("WriteFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
         goto Cleanup;
     }
 
index 2565b1c..4ede442 100644 (file)
@@ -11,7 +11,7 @@
 WCHAR wszSpoolDirectory[MAX_PATH];
 DWORD cchSpoolDirectory;
 
-// Constants
+// Global Constants
 const WCHAR wszCurrentEnvironment[] =
 #if defined(_X86_)
     L"Windows NT x86";
@@ -23,16 +23,19 @@ const WCHAR wszCurrentEnvironment[] =
     #error Unsupported architecture
 #endif
 
+const WCHAR wszDefaultDocumentName[] = L"Local Downlevel Document";
+
 const WCHAR* wszPrintProviderInfo[3] = {
     L"Windows NT Local Print Providor",     // Name
     L"Windows NT Local Printers",           // Description
     L"Locally connected Printers"           // Comment
 };
 
-static const PRINTPROVIDOR PrintProviderFunctions = {
+// Local Constants
+static const PRINTPROVIDOR _PrintProviderFunctions = {
     LocalOpenPrinter,                           // fpOpenPrinter
     NULL,                                       // fpSetJob
-    NULL,                                       // fpGetJob
+    LocalGetJob,                                // fpGetJob
     NULL,                                       // fpEnumJobs
     NULL,                                       // fpAddPrinter
     NULL,                                       // fpDeletePrinter
@@ -56,7 +59,7 @@ static const PRINTPROVIDOR PrintProviderFunctions = {
     NULL,                                       // fpAbortPrinter
     NULL,                                       // fpReadPrinter
     LocalEndDocPrinter,                         // fpEndDocPrinter
-    NULL,                                       // fpAddJob
+    LocalAddJob,                                // fpAddJob
     NULL,                                       // fpScheduleJob
     NULL,                                       // fpGetPrinterData
     NULL,                                       // fpSetPrinterData
@@ -139,8 +142,9 @@ DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
         case DLL_PROCESS_ATTACH:
             DisableThreadLibraryCalls(hinstDLL);
             _GetSpoolDirectory();
-            InitializePrintProcessorTable();
-            InitializePrinterTable();
+            InitializePrintProcessorList();
+            InitializePrinterList();
+            InitializeGlobalJobList();
             break;
     }
 
@@ -150,7 +154,7 @@ DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
 BOOL WINAPI
 InitializePrintProvidor(LPPRINTPROVIDOR pPrintProvidor, DWORD cbPrintProvidor, LPWSTR pFullRegistryPath)
 {
-    CopyMemory(pPrintProvidor, &PrintProviderFunctions, min(cbPrintProvidor, sizeof(PRINTPROVIDOR)));
+    CopyMemory(pPrintProvidor, &_PrintProviderFunctions, min(cbPrintProvidor, sizeof(PRINTPROVIDOR)));
 
     return TRUE;
 }
index cb08315..d45edfb 100644 (file)
@@ -13,6 +13,8 @@
 #include <stdlib.h>
 #include <wchar.h>
 
+#include <lmcons.h>
+#include <rpc.h>
 #include <windef.h>
 #include <winbase.h>
 #include <wingdi.h>
@@ -21,6 +23,8 @@
 #include <winsplp.h>
 #include <ndk/rtlfuncs.h>
 
+#define SKIPLIST_LEVELS 16
+#include <skiplist.h>
 #include <spoolss.h>
 
 #include <wine/debug.h>
@@ -47,8 +51,10 @@ typedef BOOL (WINAPI *PPrintDocumentOnPrintProcessor)(HANDLE, LPWSTR);
 */
 typedef struct _LOCAL_PRINT_PROCESSOR
 {
+    LIST_ENTRY Entry;
     PWSTR pwszName;
-    RTL_GENERIC_TABLE DatatypeTable;
+    PDATATYPES_INFO_1W pDatatypesInfo1;
+    DWORD dwDatatypeCount;
     PClosePrintProcessor pfnClosePrintProcessor;
     PControlPrintProcessor pfnControlPrintProcessor;
     PEnumPrintProcessorDatatypesW pfnEnumPrintProcessorDatatypesW;
@@ -64,13 +70,15 @@ LOCAL_PRINT_PROCESSOR, *PLOCAL_PRINT_PROCESSOR;
  */
 typedef struct _LOCAL_PRINTER
 {
+    // This sort key must be the first element for LookupElementSkiplist to work!
     PWSTR pwszPrinterName;
+
     PWSTR pwszPrinterDriver;
     PWSTR pwszDescription;
     PWSTR pwszDefaultDatatype;
     DEVMODEW DefaultDevMode;
     PLOCAL_PRINT_PROCESSOR pPrintProcessor;
-    LIST_ENTRY JobQueue;
+    SKIPLIST JobList;
 }
 LOCAL_PRINTER, *PLOCAL_PRINTER;
 
@@ -80,13 +88,24 @@ LOCAL_PRINTER, *PLOCAL_PRINTER;
  */
 typedef struct _LOCAL_JOB
 {
-    LIST_ENTRY Entry;
-    PLOCAL_PRINTER Printer;
-    DWORD dwJobID;
-    PWSTR pwszDocumentName;
-    PWSTR pwszDatatype;
-    PWSTR pwszOutputFile;
-    DEVMODEW DevMode;
+    // This sort key must be the first element for LookupElementSkiplist to work!
+    DWORD dwJobID;                      // Internal and external ID of this Job
+
+    PLOCAL_PRINTER Printer;             // Associated Printer to this Job
+    DWORD dwPriority;                   // Priority of this Job from MIN_PRIORITY to MAX_PRIORITY, default being DEF_PRIORITY
+    SYSTEMTIME stSubmitted;             // Time of the submission of this Job
+    PWSTR pwszUserName;                 // User that submitted the Job
+    PWSTR pwszNotifyName;               // User that shall be notified about the status of the Job
+    PWSTR pwszDocumentName;             // Name of the Document that is printed
+    PWSTR pwszDatatype;                 // Datatype of the Document
+    PWSTR pwszOutputFile;               // Output File to spool the Job to
+    DWORD dwTotalPages;                 // Total pages of the Document
+    DWORD dwPagesPrinted;               // Number of pages that have already been printed
+    DWORD dwStartTime;                  // Earliest time in minutes since 12:00 AM UTC when this document can be printed
+    DWORD dwUntilTime;                  // Latest time in minutes since 12:00 AM UTC when this document can be printed
+    DWORD dwStatus;                     // JOB_STATUS_* flags of the Job
+    PWSTR pwszMachineName;              // Name of the machine that submitted the Job (prepended with two backslashes)
+    DEVMODEW DevMode;                   // Associated Device Mode to this Job
 }
 LOCAL_JOB, *PLOCAL_JOB;
 
@@ -140,35 +159,42 @@ typedef struct _SHD_HEADER
     DWORD offPrintProcessor;
     DWORD offDatatype;
     DWORD dwUnknown2;
-    SYSTEMTIME stSubmitTime;
+    SYSTEMTIME stSubmitted;
     DWORD dwStartTime;
     DWORD dwUntilTime;
     DWORD dwUnknown6;
-    DWORD dwPageCount;
+    DWORD dwTotalPages;
     DWORD cbSecurityDescriptor;
     DWORD offSecurityDescriptor;
     DWORD dwUnknown3;
     DWORD dwUnknown4;
     DWORD dwUnknown5;
-    DWORD offComputerName;
+    DWORD offMachineName;
     DWORD dwSPLSize;
 }
 SHD_HEADER, *PSHD_HEADER;
 
 
 // jobs.c
+extern SKIPLIST GlobalJobList;
+BOOL GetNextJobID(PDWORD dwJobID);
+void InitializeGlobalJobList();
+void InitializePrinterJobList(PLOCAL_PRINTER pPrinter);
+BOOL WINAPI LocalAddJob(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded);
+BOOL WINAPI LocalGetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pOutput, DWORD cbBuf, LPDWORD pcbNeeded);
 PLOCAL_JOB ReadJobShadowFile(PCWSTR pwszFilePath);
 BOOL WriteJobShadowFile(PCWSTR pwszFilePath, const PLOCAL_JOB pJob);
 
 // main.c
 extern const WCHAR wszCurrentEnvironment[];
+extern const WCHAR wszDefaultDocumentName[];
 extern const WCHAR* wszPrintProviderInfo[3];
 extern WCHAR wszSpoolDirectory[MAX_PATH];
 extern DWORD cchSpoolDirectory;
 
 // printers.c
-extern RTL_GENERIC_TABLE PrinterTable;
-void InitializePrinterTable();
+extern SKIPLIST PrinterList;
+void InitializePrinterList();
 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);
@@ -179,15 +205,14 @@ BOOL WINAPI LocalEndDocPrinter(HANDLE hPrinter);
 BOOL WINAPI LocalClosePrinter(HANDLE hPrinter);
 
 // printprocessors.c
-extern RTL_GENERIC_TABLE PrintProcessorTable;
-void InitializePrintProcessorTable();
+BOOL FindDatatype(PLOCAL_PRINT_PROCESSOR pPrintProcessor, PWSTR pwszDatatype);
+PLOCAL_PRINT_PROCESSOR FindPrintProcessor(PWSTR pwszName);
+void InitializePrintProcessorList();
 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);
-PVOID NTAPI GenericTableAllocateRoutine(PRTL_GENERIC_TABLE Table, CLONG ByteSize);
-VOID NTAPI GenericTableFreeRoutine(PRTL_GENERIC_TABLE Table, PVOID Buffer);
 
 #endif
index 4d8891c..8a8d616 100644 (file)
@@ -8,40 +8,33 @@
 #include "precomp.h"
 
 // Global Variables
-RTL_GENERIC_TABLE PrinterTable;
+SKIPLIST PrinterList;
 
 
 /**
- * @name _PrinterTableCompareRoutine
+ * @name _PrinterListCompareRoutine
  *
- * RTL_GENERIC_COMPARE_ROUTINE for the Printer Table.
+ * SKIPLIST_COMPARE_ROUTINE for the Printer List.
  * 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)
+static int WINAPI
+_PrinterListCompareRoutine(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;
+    return wcsicmp(A->pwszPrinterName, B->pwszPrinterName);
 }
 
 /**
- * @name InitializePrinterTable
+ * @name InitializePrinterList
  *
- * 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.
+ * Initializes a list of locally available Printers.
+ * The list 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()
+InitializePrinterList()
 {
     const WCHAR wszPrintersKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Printers";
 
@@ -52,14 +45,13 @@ InitializePrinterTable()
     HKEY hKey = NULL;
     HKEY hSubKey = NULL;
     LONG lStatus;
-    PLOCAL_PRINT_PROCESSOR pPrintProcessor;
     PLOCAL_PRINTER pPrinter = NULL;
+    PLOCAL_PRINT_PROCESSOR pPrintProcessor;
     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);
+    // Initialize an empty list for our printers.
+    InitializeSkiplist(&PrinterList, DllAllocSplMem, _PrinterListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
 
     // Open our printers registry key. Each subkey is a local printer there.
     lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszPrintersKey, 0, KEY_READ, &hKey);
@@ -92,6 +84,15 @@ InitializePrinterTable()
             if (pPrinter->pwszDefaultDatatype)
                 DllFreeSplStr(pPrinter->pwszDefaultDatatype);
 
+            if (pPrinter->pwszDescription)
+                DllFreeSplStr(pPrinter->pwszDescription);
+
+            if (pPrinter->pwszPrinterDriver)
+                DllFreeSplStr(pPrinter->pwszPrinterDriver);
+
+            if (pPrinter->pwszPrinterName)
+                DllFreeSplStr(pPrinter->pwszPrinterName);
+
             DllFreeSplMem(pPrinter);
             pPrinter = NULL;
         }
@@ -129,8 +130,8 @@ InitializePrinterTable()
         if (!pwszPrintProcessor)
             continue;
 
-        // Try to find it in the Print Processor Table.
-        pPrintProcessor = RtlLookupElementGenericTable(&PrintProcessorTable, pwszPrintProcessor);
+        // Try to find it in the Print Processor List.
+        pPrintProcessor = FindPrintProcessor(pwszPrintProcessor);
         if (!pPrintProcessor)
         {
             ERR("Invalid Print Processor \"%S\" for Printer \"%S\"!\n", pwszPrintProcessor, wszPrinterName);
@@ -147,7 +148,7 @@ InitializePrinterTable()
 
         pPrinter->pwszPrinterName = AllocSplStr(wszPrinterName);
         pPrinter->pPrintProcessor = pPrintProcessor;
-        InitializeListHead(&pPrinter->JobQueue);
+        InitializePrinterJobList(pPrinter);
 
         // Get the printer driver.
         pPrinter->pwszPrinterDriver = AllocAndRegQueryWSZ(hSubKey, L"Printer Driver");
@@ -165,7 +166,7 @@ InitializePrinterTable()
             continue;
 
         // Verify that it's valid.
-        if (!RtlLookupElementGenericTable(&pPrintProcessor->DatatypeTable, pPrinter->pwszDefaultDatatype))
+        if (!FindDatatype(pPrintProcessor, pPrinter->pwszDefaultDatatype))
         {
             ERR("Invalid default datatype \"%S\" for Printer \"%S\"!\n", pPrinter->pwszDefaultDatatype, wszPrinterName);
             continue;
@@ -176,14 +177,14 @@ InitializePrinterTable()
         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);
+            ERR("Couldn't query a valid 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))
+        // Add this printer to the printer list.
+        if (!InsertElementSkiplist(&PrinterList, pPrinter))
         {
-            ERR("RtlInsertElementGenericTable failed with error %lu!\n", GetLastError());
+            ERR("InsertElementSkiplist failed for Printer \"%S\"!\n", pPrinter->pwszPrinterName);
             goto Cleanup;
         }
 
@@ -192,20 +193,31 @@ InitializePrinterTable()
     }
 
 Cleanup:
-    if (pwszPrintProcessor)
-        DllFreeSplStr(pwszPrintProcessor);
+    // Inside the loop
+    if (hSubKey)
+        RegCloseKey(hSubKey);
 
     if (pPrinter)
     {
         if (pPrinter->pwszDefaultDatatype)
             DllFreeSplStr(pPrinter->pwszDefaultDatatype);
 
+        if (pPrinter->pwszDescription)
+            DllFreeSplStr(pPrinter->pwszDescription);
+
+        if (pPrinter->pwszPrinterDriver)
+            DllFreeSplStr(pPrinter->pwszPrinterDriver);
+
+        if (pPrinter->pwszPrinterName)
+            DllFreeSplStr(pPrinter->pwszPrinterName);
+
         DllFreeSplMem(pPrinter);
     }
 
-    if (hSubKey)
-        RegCloseKey(hSubKey);
+    if (pwszPrintProcessor)
+        DllFreeSplStr(pwszPrintProcessor);
 
+    // Outside the loop
     if (hKey)
         RegCloseKey(hKey);
 }
@@ -223,9 +235,9 @@ _LocalEnumPrintersLevel1(DWORD Flags, LPWSTR Name, LPBYTE pPrinterEnum, DWORD cb
     DWORD i;
     PBYTE pPrinterInfo;
     PBYTE pPrinterString;
+    PSKIPLIST_NODE pNode;
     PLOCAL_PRINTER pPrinter;
     PRINTER_INFO_1W PrinterInfo1;
-    PVOID pRestartKey = NULL;
     WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1];
 
     DWORD dwOffsets[] = {
@@ -302,8 +314,10 @@ _LocalEnumPrintersLevel1(DWORD Flags, LPWSTR Name, LPBYTE pPrinterEnum, DWORD cb
     }
 
     // Count the required buffer size and the number of printers.
-    for (pPrinter = RtlEnumerateGenericTableWithoutSplaying(&PrinterTable, &pRestartKey); pPrinter; pPrinter = RtlEnumerateGenericTableWithoutSplaying(&PrinterTable, &pRestartKey))
+    for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
     {
+        pPrinter = (PLOCAL_PRINTER)pNode->Element;
+
         // This looks wrong, but is totally right. PRINTER_INFO_1W has three members pName, pComment and pDescription.
         // But pComment equals the "Description" registry value while pDescription is concatenated out of pName and pComment.
         // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
@@ -328,8 +342,10 @@ _LocalEnumPrintersLevel1(DWORD Flags, LPWSTR Name, LPBYTE pPrinterEnum, DWORD cb
     pPrinterString = pPrinterEnum + *pcReturned * sizeof(PRINTER_INFO_1W);
 
     // Copy over the printer information.
-    for (pPrinter = RtlEnumerateGenericTableWithoutSplaying(&PrinterTable, &pRestartKey); pPrinter; pPrinter = RtlEnumerateGenericTableWithoutSplaying(&PrinterTable, &pRestartKey))
+    for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
     {
+        pPrinter = (PLOCAL_PRINTER)pNode->Element;
+
         // FIXME: As for now, the Flags member returns no information.
         PrinterInfo1.Flags = 0;
 
@@ -409,7 +425,6 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
     PLOCAL_HANDLE pHandle;
     PLOCAL_PRINTER pPrinter;
     PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL;
-    PLIST_ENTRY pEntry;
     WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
 
     // Sanity checks
@@ -478,8 +493,8 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
         CopyMemory(pwszPrinterName, lpPrinterName, cchPrinterName * sizeof(WCHAR));
         pwszPrinterName[cchPrinterName] = 0;
 
-        // Retrieve the associated printer from the table.
-        pPrinter = RtlLookupElementGenericTable(&PrinterTable, pwszPrinterName);
+        // Retrieve the associated printer from the list.
+        pPrinter = LookupElementSkiplist(&PrinterList, &pwszPrinterName, NULL);
         if (!pPrinter)
         {
             // The printer does not exist.
@@ -495,7 +510,7 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
         if (pDefault && pDefault->pDatatype)
         {
             // Use the datatype if it's valid.
-            if (!RtlLookupElementGenericTable(&pPrinter->pPrintProcessor->DatatypeTable, pDefault->pDatatype))
+            if (!FindDatatype(pPrinter->pPrintProcessor, pDefault->pDatatype))
             {
                 SetLastError(ERROR_INVALID_DATATYPE);
                 goto Cleanup;
@@ -550,30 +565,16 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
                 goto Cleanup;
             }
 
-            // Look for this job in the job queue of the printer.
-            pEntry = pPrinter->JobQueue.Flink;
-
-            for (;;)
+            // Look for this job in the Global Job List.
+            pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL);
+            if (!pJob || pJob->Printer != pPrinter)
             {
-                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;
+                // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
+                SetLastError(ERROR_INVALID_PRINTER_NAME);
+                goto Cleanup;
             }
+
+            pPrinterHandle->StartedJob = pJob;
         }
 
         // Create a new handle that references a printer.
@@ -647,11 +648,10 @@ Cleanup:
 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;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
 
     // Sanity checks
     if (!pDocInfo)
@@ -688,15 +688,16 @@ LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
     // Create a new job.
     pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
     pJob->Printer = pPrinterHandle->Printer;
+    pJob->dwPriority = DEF_PRIORITY;
 
     // Check if a datatype was given.
     if (pDocumentInfo1->pDatatype)
     {
         // Use the datatype if it's valid.
-        if (!RtlLookupElementGenericTable(&pJob->Printer->pPrintProcessor->DatatypeTable, pDocumentInfo1->pDatatype))
+        if (!FindDatatype(pJob->Printer->pPrintProcessor, pDocumentInfo1->pDatatype))
         {
             SetLastError(ERROR_INVALID_DATATYPE);
-            goto Cleanup;
+            return 0;
         }
 
         pJob->pwszDatatype = AllocSplStr(pDocumentInfo1->pDatatype);
@@ -717,14 +718,27 @@ LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
     if (pDocumentInfo1->pOutputFile)
         pJob->pwszOutputFile = AllocSplStr(pDocumentInfo1->pOutputFile);
 
-    // Enqueue the job.
-    ///////////// TODO /////////////////////
+    // Get an ID for the new job.
+    if (!GetNextJobID(&pJob->dwJobID))
+        return 0;
 
-Cleanup:
-    if (pJob)
-        DllFreeSplMem(pJob);
+    // Add the job to the Global Job List.
+    if (!InsertElementSkiplist(&GlobalJobList, pJob))
+    {
+        ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
+        return 0;
+    }
+
+    // Add the job at the end of the Printer's Job List.
+    // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
+    if (!InsertTailElementSkiplist(&pJob->Printer->JobList, pJob))
+    {
+        ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
+        return 0;
+    }
 
-    return dwReturnValue;
+    pPrinterHandle->StartedJob = pJob;
+    return pJob->dwJobID;
 }
 
 BOOL WINAPI
index 602dd83..a8b9a89 100644 (file)
@@ -8,8 +8,8 @@
 #include "precomp.h"
 
 
-// Global Variables
-RTL_GENERIC_TABLE PrintProcessorTable;
+// Local Variables
+static LIST_ENTRY _PrintProcessorList;
 
 /**
  * @name _OpenEnvironment
@@ -76,83 +76,67 @@ Cleanup:
     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)
+BOOL
+FindDatatype(PLOCAL_PRINT_PROCESSOR pPrintProcessor, PWSTR pwszDatatype)
 {
-    PLOCAL_PRINT_PROCESSOR A = (PLOCAL_PRINT_PROCESSOR)FirstStruct;
-    PLOCAL_PRINT_PROCESSOR B = (PLOCAL_PRINT_PROCESSOR)SecondStruct;
+    DWORD i;
+    PDATATYPES_INFO_1W pCurrentDatatype = pPrintProcessor->pDatatypesInfo1;
 
-    int iResult = wcsicmp(A->pwszName, B->pwszName);
+    for (i = 0; i < pPrintProcessor->dwDatatypeCount; i++)
+    {
+        if (wcsicmp(pCurrentDatatype->pName, pwszDatatype) == 0)
+            return TRUE;
 
-    if (iResult < 0)
-        return GenericLessThan;
-    else if (iResult > 0)
-        return GenericGreaterThan;
-    else
-        return GenericEqual;
+        ++pCurrentDatatype;
+    }
+
+    return FALSE;
 }
 
-/**
- * @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)
+PLOCAL_PRINT_PROCESSOR
+FindPrintProcessor(PWSTR pwszName)
 {
-    PWSTR A = (PWSTR)FirstStruct;
-    PWSTR B = (PWSTR)SecondStruct;
+    PLIST_ENTRY pEntry;
+    PLOCAL_PRINT_PROCESSOR pPrintProcessor;
+
+    for (pEntry = _PrintProcessorList.Flink; pEntry; pEntry = pEntry->Flink)
+    {
+        pPrintProcessor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_PROCESSOR, Entry);
 
-    int iResult = wcsicmp(A, B);
+        if (wcsicmp(pPrintProcessor->pwszName, pwszName) == 0)
+            return pPrintProcessor;
+    }
 
-    if (iResult < 0)
-        return GenericLessThan;
-    else if (iResult > 0)
-        return GenericGreaterThan;
-    else
-        return GenericEqual;
+    return NULL;
 }
 
 /**
- * @name InitializePrintProcessorTable
+ * @name InitializePrintProcessorList
  *
- * 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.
+ * Initializes a singly linked list of locally available Print Processors.
  */
 void
-InitializePrintProcessorTable()
+InitializePrintProcessorList()
 {
     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);
+    // Initialize an empty list for our Print Processors.
+    InitializeListHead(&_PrintProcessorList);
     
     // Prepare the path to the Print Processor directory.
     if (!LocalGetPrintProcessorDirectory(NULL, NULL, 1, (PBYTE)wszPrintProcessorPath, sizeof(wszPrintProcessorPath), &cchPrintProcessorPath))
@@ -204,16 +188,13 @@ InitializePrintProcessorTable()
             if (pPrintProcessor->pwszName)
                 DllFreeSplStr(pPrintProcessor->pwszName);
 
+            if (pPrintProcessor->pDatatypesInfo1)
+                DllFreeSplMem(pPrintProcessor->pDatatypesInfo1);
+
             DllFreeSplMem(pPrintProcessor);
             pPrintProcessor = NULL;
         }
 
-        if (pDatatypesInfo1)
-        {
-            DllFreeSplMem(pDatatypesInfo1);
-            pDatatypesInfo1 = NULL;
-        }
-
         // Get the name of this Print Processor.
         cchPrintProcessorName = cchMaxSubKey;
         lStatus = RegEnumKeyExW(hSubKey, i, pwszPrintProcessorName, &cchPrintProcessorName, NULL, NULL, NULL, NULL);
@@ -306,69 +287,47 @@ InitializePrintProcessorTable()
         }
 
         // Get all supported datatypes.
-        pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, 0, &cbDatatypes, &dwDatatypes);
-        pDatatypesInfo1 = DllAllocSplMem(cbDatatypes);
-        if (!pDatatypesInfo1)
+        pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, 0, &cbDatatypes, &pPrintProcessor->dwDatatypeCount);
+        pPrintProcessor->pDatatypesInfo1 = DllAllocSplMem(cbDatatypes);
+        if (!pPrintProcessor->pDatatypesInfo1)
         {
             ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
             goto Cleanup;
         }
 
-        if (!pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, (PBYTE)pDatatypesInfo1, cbDatatypes, &cbDatatypes, &dwDatatypes))
+        if (!pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, (PBYTE)pPrintProcessor->pDatatypesInfo1, cbDatatypes, &cbDatatypes, &pPrintProcessor->dwDatatypeCount))
         {
             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 = AllocSplStr(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;
-        }
+        // Add the Print Processor to the list.
+        InsertTailList(&_PrintProcessorList, &pPrintProcessor->Entry);
 
         // Don't let the cleanup routines free this.
-        pwszDatatype = NULL;
         pPrintProcessor = NULL;
     }
 
 Cleanup:
-    if (pwszDatatype)
-        DllFreeSplStr(pwszDatatype);
-
-    if (pDatatypesInfo1)
-        DllFreeSplMem(pDatatypesInfo1);
+    // Inside the loop
+    if (hSubSubKey)
+        RegCloseKey(hSubSubKey);
 
     if (pPrintProcessor)
     {
         if (pPrintProcessor->pwszName)
             DllFreeSplStr(pPrintProcessor->pwszName);
 
+        if (pPrintProcessor->pDatatypesInfo1)
+            DllFreeSplMem(pPrintProcessor->pDatatypesInfo1);
+
         DllFreeSplMem(pPrintProcessor);
     }
 
+    // Outside the loop
     if (pwszPrintProcessorName)
         DllFreeSplStr(pwszPrintProcessorName);
 
-    if (hSubSubKey)
-        RegCloseKey(hSubSubKey);
-
     if (hSubKey)
         RegCloseKey(hSubKey);
 
@@ -423,7 +382,7 @@ LocalEnumPrintProcessorDatatypes(LPWSTR pName, LPWSTR pPrintProcessorName, DWORD
     }
 
     // Try to find the Print Processor.
-    pPrintProcessor = RtlLookupElementGenericTable(&PrintProcessorTable, pPrintProcessorName);
+    pPrintProcessor = FindPrintProcessor(pPrintProcessorName);
     if (!pPrintProcessor)
     {
         SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
@@ -499,6 +458,7 @@ LocalEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE
     }
 
     // Verify pEnvironment and open its registry key.
+    // We use the registry and not the PrintProcessorList here, because the caller may request information about a different environment.
     if (!_OpenEnvironment(pEnvironment, &hKey))
         goto Cleanup;
 
index 10515db..ef6ef50 100644 (file)
@@ -56,25 +56,3 @@ AllocAndRegQueryWSZ(HKEY hKey, PCWSTR pwszValueName)
 
     return pwszValue;
 }
-
-/**
- * @name GenericTableAllocateRoutine
- *
- * RTL_GENERIC_ALLOCATE_ROUTINE for all our RTL_GENERIC_TABLEs, using DllAllocSplMem.
- */
-PVOID NTAPI
-GenericTableAllocateRoutine(PRTL_GENERIC_TABLE Table, CLONG ByteSize)
-{
-    return DllAllocSplMem(ByteSize);
-}
-
-/**
- * @name GenericTableFreeRoutine
- *
- * RTL_GENERIC_FREE_ROUTINE for all our RTL_GENERIC_TABLEs, using DllFreeSplMem.
- */
-VOID NTAPI
-GenericTableFreeRoutine(PRTL_GENERIC_TABLE Table, PVOID Buffer)
-{
-    DllFreeSplMem(Buffer);
-}