Author: cfinck
Date: Fri Jun 19 15:21:30 2015
New Revision: 68195

URL: http://svn.reactos.org/svn/reactos?rev=68195&view=rev
Log:
[LOCALSPL]
- 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.

Modified:
    
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/CMakeLists.txt
    
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/jobs.c
    
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/main.c
    
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/precomp.h
    
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/printers.c
    
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/printprocessors.c
    
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/tools.c

Modified: 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/CMakeLists.txt
URL: 
http://svn.reactos.org/svn/reactos/branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/CMakeLists.txt?rev=68195&r1=68194&r2=68195&view=diff
==============================================================================
--- 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/CMakeLists.txt
     [iso-8859-1] (original)
+++ 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/CMakeLists.txt
     [iso-8859-1] Fri Jun 19 15:21:30 2015
@@ -1,5 +1,7 @@
 
 spec2def(localspl.dll localspl.spec ADD_IMPORTLIB)
+
+include_directories(${REACTOS_SOURCE_DIR}/lib/skiplist)
 
 list(APPEND SOURCE
     jobs.c
@@ -16,7 +18,7 @@
     ${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)

Modified: 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/jobs.c
URL: 
http://svn.reactos.org/svn/reactos/branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/jobs.c?rev=68195&r1=68194&r2=68195&view=diff
==============================================================================
--- 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/jobs.c
     [iso-8859-1] (original)
+++ 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/jobs.c
     [iso-8859-1] Fri Jun 19 15:21:30 2015
@@ -7,10 +7,103 @@
 
 #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 @@
 
     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 @@
     if (hFind == INVALID_HANDLE_VALUE)
     {
         // No unfinished jobs found.
-        return;
+        goto Cleanup;
     }
 
     do
@@ -56,12 +156,368 @@
         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 @@
     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 @@
     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");
-        goto Cleanup;
-    }
-
-    // Retrieve the associated printer from the table.
+        ERR("Signature or Header Size mismatch for file \"%S\"!\n", 
pwszFilePath);
+        goto Cleanup;
+    }
+
+    // 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 @@
     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 @@
     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 @@
     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;
     }
 

Modified: 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/main.c
URL: 
http://svn.reactos.org/svn/reactos/branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/main.c?rev=68195&r1=68194&r2=68195&view=diff
==============================================================================
--- 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/main.c
     [iso-8859-1] (original)
+++ 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/main.c
     [iso-8859-1] Fri Jun 19 15:21:30 2015
@@ -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 @@
     #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 @@
     NULL,                                       // fpAbortPrinter
     NULL,                                       // fpReadPrinter
     LocalEndDocPrinter,                         // fpEndDocPrinter
-    NULL,                                       // fpAddJob
+    LocalAddJob,                                // fpAddJob
     NULL,                                       // fpScheduleJob
     NULL,                                       // fpGetPrinterData
     NULL,                                       // fpSetPrinterData
@@ -139,8 +142,9 @@
         case DLL_PROCESS_ATTACH:
             DisableThreadLibraryCalls(hinstDLL);
             _GetSpoolDirectory();
-            InitializePrintProcessorTable();
-            InitializePrinterTable();
+            InitializePrintProcessorList();
+            InitializePrinterList();
+            InitializeGlobalJobList();
             break;
     }
 
@@ -150,7 +154,7 @@
 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;
 }

Modified: 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/precomp.h
URL: 
http://svn.reactos.org/svn/reactos/branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/precomp.h?rev=68195&r1=68194&r2=68195&view=diff
==============================================================================
--- 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/precomp.h
  [iso-8859-1] (original)
+++ 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/precomp.h
  [iso-8859-1] Fri Jun 19 15:21:30 2015
@@ -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 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 @@
  */
 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 @@
  */
 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 @@
     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 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

Modified: 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/printers.c
URL: 
http://svn.reactos.org/svn/reactos/branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/printers.c?rev=68195&r1=68194&r2=68195&view=diff
==============================================================================
--- 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/printers.c
 [iso-8859-1] (original)
+++ 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/printers.c
 [iso-8859-1] Fri Jun 19 15:21:30 2015
@@ -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 @@
     HKEY hKey = NULL;
     HKEY hSubKey = NULL;
     LONG lStatus;
+    PLOCAL_PRINTER pPrinter = NULL;
     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);
+    // 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);
@@ -91,6 +83,15 @@
         {
             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 @@
         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 @@
 
         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 @@
             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 @@
         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());
+            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 list.
+        if (!InsertElementSkiplist(&PrinterList, pPrinter))
+        {
+            ERR("InsertElementSkiplist failed for Printer \"%S\"!\n", 
pPrinter->pwszPrinterName);
             goto Cleanup;
         }
 
@@ -192,20 +193,31 @@
     }
 
 Cleanup:
+    // 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 (pwszPrintProcessor)
         DllFreeSplStr(pwszPrintProcessor);
 
-    if (pPrinter)
-    {
-        if (pPrinter->pwszDefaultDatatype)
-            DllFreeSplStr(pPrinter->pwszDefaultDatatype);
-
-        DllFreeSplMem(pPrinter);
-    }
-
-    if (hSubKey)
-        RegCloseKey(hSubKey);
-
+    // Outside the loop
     if (hKey)
         RegCloseKey(hKey);
 }
@@ -223,9 +235,9 @@
     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 @@
     }
 
     // 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 @@
     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 @@
     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 @@
         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 @@
         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 @@
                 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 @@
 DWORD WINAPI
 LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
 {
-    DWORD dwReturnValue = 0;
     PDOC_INFO_1W pDocumentInfo1;
     PLOCAL_HANDLE pHandle;
+    PLOCAL_JOB pJob;
     PLOCAL_PRINTER_HANDLE pPrinterHandle;
-    PLOCAL_JOB pJob;
 
     // Sanity checks
     if (!pDocInfo)
@@ -688,15 +688,16 @@
     // 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 @@
     if (pDocumentInfo1->pOutputFile)
         pJob->pwszOutputFile = AllocSplStr(pDocumentInfo1->pOutputFile);
 
-    // Enqueue the job.
-    ///////////// TODO /////////////////////
-
-Cleanup:
-    if (pJob)
-        DllFreeSplMem(pJob);
-
-    return dwReturnValue;
+    // Get an ID for the new job.
+    if (!GetNextJobID(&pJob->dwJobID))
+        return 0;
+
+    // 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;
+    }
+
+    pPrinterHandle->StartedJob = pJob;
+    return pJob->dwJobID;
 }
 
 BOOL WINAPI

Modified: 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/printprocessors.c
URL: 
http://svn.reactos.org/svn/reactos/branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/printprocessors.c?rev=68195&r1=68194&r2=68195&view=diff
==============================================================================
--- 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/printprocessors.c
  [iso-8859-1] (original)
+++ 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/printprocessors.c
  [iso-8859-1] Fri Jun 19 15:21:30 2015
@@ -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 @@
     return bReturnValue;
 }
 
+BOOL
+FindDatatype(PLOCAL_PRINT_PROCESSOR pPrintProcessor, PWSTR pwszDatatype)
+{
+    DWORD i;
+    PDATATYPES_INFO_1W pCurrentDatatype = pPrintProcessor->pDatatypesInfo1;
+
+    for (i = 0; i < pPrintProcessor->dwDatatypeCount; i++)
+    {
+        if (wcsicmp(pCurrentDatatype->pName, pwszDatatype) == 0)
+            return TRUE;
+
+        ++pCurrentDatatype;
+    }
+
+    return FALSE;
+}
+
+PLOCAL_PRINT_PROCESSOR
+FindPrintProcessor(PWSTR pwszName)
+{
+    PLIST_ENTRY pEntry;
+    PLOCAL_PRINT_PROCESSOR pPrintProcessor;
+
+    for (pEntry = _PrintProcessorList.Flink; pEntry; pEntry = pEntry->Flink)
+    {
+        pPrintProcessor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_PROCESSOR, 
Entry);
+
+        if (wcsicmp(pPrintProcessor->pwszName, pwszName) == 0)
+            return pPrintProcessor;
+    }
+
+    return NULL;
+}
+
 /**
- * @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.
+ * @name InitializePrintProcessorList
+ *
+ * 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,14 +188,11 @@
             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.
@@ -306,68 +287,46 @@
         }
 
         // 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 @@
     }
 
     // Try to find the Print Processor.
-    pPrintProcessor = RtlLookupElementGenericTable(&PrintProcessorTable, 
pPrintProcessorName);
+    pPrintProcessor = FindPrintProcessor(pPrintProcessorName);
     if (!pPrintProcessor)
     {
         SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
@@ -499,6 +458,7 @@
     }
 
     // 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;
 

Modified: 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/tools.c
URL: 
http://svn.reactos.org/svn/reactos/branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/tools.c?rev=68195&r1=68194&r2=68195&view=diff
==============================================================================
--- 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/tools.c
    [iso-8859-1] (original)
+++ 
branches/colins-printing-for-freedom/reactos/win32ss/printing/providers/localspl/tools.c
    [iso-8859-1] Fri Jun 19 15:21:30 2015
@@ -56,25 +56,3 @@
 
     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);
-}


Reply via email to