[LOCALSPL, WINSPOOL]
authorColin Finck <colin@reactos.org>
Thu, 16 Jul 2015 15:03:47 +0000 (15:03 +0000)
committerColin Finck <colin@reactos.org>
Thu, 16 Jul 2015 15:03:47 +0000 (15:03 +0000)
Partially implement the whole StartDocPrinter, StartPagePrinter, ReadPrinter, WritePrinter, EndPagePrinter, EndDocPrinter, ClosePrinter group of functions.
They behave very differently based on whether spooled printing is enabled, whether it's a local or remote call, etc. Most information was gained by observing callchains under Windows.

So far, only the spooled path is implemented, the others need more investigation first.
Many other TODOs remain as well, see the comments.

Also make some more comments Doxygen-aware :)

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

reactos/win32ss/printing/base/winspool/jobs.c
reactos/win32ss/printing/base/winspool/main.c
reactos/win32ss/printing/base/winspool/precomp.h
reactos/win32ss/printing/base/winspool/printers.c
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/ports.c
reactos/win32ss/printing/providers/localspl/precomp.h
reactos/win32ss/printing/providers/localspl/printers.c
reactos/win32ss/printing/providers/localspl/printingthread.c [new file with mode: 0644]

index f5931ef..bc6dfa0 100644 (file)
@@ -61,11 +61,18 @@ AddJobW(HANDLE hPrinter, DWORD Level, PBYTE pData, DWORD cbBuf, PDWORD pcbNeeded
 {
     DWORD dwErrorCode;
     PADDJOB_INFO_1W pAddJobInfo1;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
 
     // Do the RPC call
     RpcTryExcept
     {
-        dwErrorCode = _RpcAddJob(hPrinter, Level, pData, cbBuf, pcbNeeded);
+        dwErrorCode = _RpcAddJob(pHandle->hPrinter, Level, pData, cbBuf, pcbNeeded);
     }
     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
     {
@@ -81,6 +88,7 @@ AddJobW(HANDLE hPrinter, DWORD Level, PBYTE pData, DWORD cbBuf, PDWORD pcbNeeded
         pAddJobInfo1->Path = (PWSTR)((ULONG_PTR)pAddJobInfo1->Path + (ULONG_PTR)pAddJobInfo1);
     }
 
+Cleanup:
     SetLastError(dwErrorCode);
     return (dwErrorCode == ERROR_SUCCESS);
 }
@@ -98,11 +106,18 @@ EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pJob
     DWORD dwErrorCode;
     DWORD i;
     PBYTE p = pJob;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
 
     // Do the RPC call
     RpcTryExcept
     {
-        dwErrorCode = _RpcEnumJobs(hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned);
+        dwErrorCode = _RpcEnumJobs(pHandle->hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned);
     }
     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
     {
@@ -125,6 +140,7 @@ EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pJob
         }
     }
 
+Cleanup:
     SetLastError(dwErrorCode);
     return (dwErrorCode == ERROR_SUCCESS);
 }
@@ -140,11 +156,18 @@ BOOL WINAPI
 GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWORD pcbNeeded)
 {
     DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
 
     // Do the RPC call
     RpcTryExcept
     {
-        dwErrorCode = _RpcGetJob(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
+        dwErrorCode = _RpcGetJob(pHandle->hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
     }
     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
     {
@@ -159,6 +182,7 @@ GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWO
         _MarshallUpJobInfo(pJob, Level);
     }
 
+Cleanup:
     SetLastError(dwErrorCode);
     return (dwErrorCode == ERROR_SUCCESS);
 }
@@ -167,19 +191,27 @@ BOOL WINAPI
 ScheduleJob(HANDLE hPrinter, DWORD dwJobID)
 {
     DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
 
     // Do the RPC call
     RpcTryExcept
     {
-        dwErrorCode = _RpcScheduleJob(hPrinter, dwJobID);
+        dwErrorCode = _RpcScheduleJob(pHandle->hPrinter, dwJobID);
     }
     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
     {
         dwErrorCode = RpcExceptionCode();
-        ERR("_RpcSetJob failed with exception code %lu!\n", dwErrorCode);
+        ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode);
     }
     RpcEndExcept;
 
+Cleanup:
     SetLastError(dwErrorCode);
     return (dwErrorCode == ERROR_SUCCESS);
 }
@@ -195,8 +227,15 @@ BOOL WINAPI
 SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command)
 {
     DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
     WINSPOOL_JOB_CONTAINER JobContainer;
 
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
     // pJobContainer->JobInfo is a union of pointers, so we can just set any element to our BYTE pointer.
     JobContainer.Level = Level;
     JobContainer.JobInfo.Level1 = (WINSPOOL_JOB_INFO_1*)pJobInfo;
@@ -204,7 +243,7 @@ SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command
     // Do the RPC call
     RpcTryExcept
     {
-        dwErrorCode = _RpcSetJob(hPrinter, JobId, &JobContainer, Command);
+        dwErrorCode = _RpcSetJob(pHandle->hPrinter, JobId, &JobContainer, Command);
     }
     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
     {
@@ -213,6 +252,7 @@ SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command
     }
     RpcEndExcept;
 
+Cleanup:
     SetLastError(dwErrorCode);
     return (dwErrorCode == ERROR_SUCCESS);
 }
index 624c177..4ef8e96 100644 (file)
@@ -7,6 +7,10 @@
 
 #include "precomp.h"
 
+// Global Variables
+HANDLE hProcessHeap;
+
+
 handle_t __RPC_USER
 WINSPOOL_HANDLE_bind(WINSPOOL_HANDLE wszName)
 {
@@ -56,13 +60,27 @@ WINSPOOL_HANDLE_unbind(WINSPOOL_HANDLE wszName, handle_t hBinding)
 void __RPC_FAR* __RPC_USER
 midl_user_allocate(SIZE_T len)
 {
-    return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
+    return HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, len);
 }
 
 void __RPC_USER
 midl_user_free(void __RPC_FAR* ptr)
 {
-    HeapFree(GetProcessHeap(), 0, ptr);
+    HeapFree(hProcessHeap, 0, ptr);
+}
+
+BOOL WINAPI
+DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+    switch (fdwReason)
+    {
+        case DLL_PROCESS_ATTACH:
+            DisableThreadLibraryCalls(hinstDLL);
+                       hProcessHeap = GetProcessHeap();
+            break;
+    }
+
+    return TRUE;
 }
 
 BOOL WINAPI
index 5455c62..df5588a 100644 (file)
 #include <wine/debug.h>
 WINE_DEFAULT_DEBUG_CHANNEL(winspool);
 
+// Structures
+/*
+ * Describes a handle returned by OpenPrinterW.
+ */
+typedef struct _SPOOLER_HANDLE
+{
+    BOOL bStartedDoc : 1;
+    DWORD dwJobID;
+    HANDLE hPrinter;
+    HANDLE hSPLFile;
+}
+SPOOLER_HANDLE, *PSPOOLER_HANDLE;
+
+// main.c
+extern HANDLE hProcessHeap;
+
 #endif
index 11a1cf7..c4b95ea 100644 (file)
@@ -7,6 +7,94 @@
 
 #include "precomp.h"
 
+static DWORD
+_StartDocPrinterSpooled(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1, PADDJOB_INFO_1W pAddJobInfo1)
+{
+    DWORD cbNeeded;
+    DWORD dwErrorCode;
+    PJOB_INFO_1W pJobInfo1 = NULL;
+
+    // Create the spool file.
+    pHandle->hSPLFile = CreateFileW(pAddJobInfo1->Path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
+    if (pHandle->hSPLFile == INVALID_HANDLE_VALUE)
+    {
+        dwErrorCode = GetLastError();
+        ERR("CreateFileW failed for \"%S\" with error %lu!\n", pAddJobInfo1->Path, dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Get the size of the job information.
+    GetJobW(pHandle->hPrinter, pAddJobInfo1->JobId, 1, NULL, 0, &cbNeeded);
+    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+    {
+        dwErrorCode = GetLastError();
+        ERR("GetJobW failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Allocate enough memory for the returned job information.
+    pJobInfo1 = HeapAlloc(hProcessHeap, 0, cbNeeded);
+    if (!pJobInfo1)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("HeapAlloc failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    // Get the job information.
+    if (!GetJobW(pHandle->hPrinter, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, cbNeeded, &cbNeeded))
+    {
+        dwErrorCode = GetLastError();
+        ERR("GetJobW failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Add our document information.
+    pJobInfo1->pDatatype = pDocInfo1->pDatatype;
+    pJobInfo1->pDocument = pDocInfo1->pDocName;
+
+    // Set the new job information.
+    if (!SetJobW(pHandle->hPrinter, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, 0))
+    {
+        dwErrorCode = GetLastError();
+        ERR("SetJobW failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // We were successful!
+    pHandle->dwJobID = pAddJobInfo1->JobId;
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    if (pJobInfo1)
+        HeapFree(hProcessHeap, 0, pJobInfo1);
+
+    return dwErrorCode;
+}
+
+static DWORD
+_StartDocPrinterWithRPC(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1)
+{
+    DWORD dwErrorCode;
+    WINSPOOL_DOC_INFO_CONTAINER DocInfoContainer;
+
+    DocInfoContainer.Level = 1;
+    DocInfoContainer.DocInfo.pDocInfo1 = (WINSPOOL_DOC_INFO_1*)pDocInfo1;
+
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcStartDocPrinter(pHandle->hPrinter, &DocInfoContainer, &pHandle->dwJobID);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcStartDocPrinter failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+    return dwErrorCode;
+}
+
 BOOL WINAPI
 EnumPrintersA(DWORD Flags, LPSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
 {
@@ -16,29 +104,60 @@ EnumPrintersA(DWORD Flags, LPSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD c
 BOOL WINAPI
 EnumPrintersW(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
 {
-    BOOL bReturnValue = FALSE;
     DWORD dwErrorCode;
 
     // Do the RPC call
     RpcTryExcept
     {
         dwErrorCode = _RpcEnumPrinters(Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
-        SetLastError(dwErrorCode);
-        bReturnValue = (dwErrorCode == ERROR_SUCCESS);
     }
     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
     {
-        ERR("_RpcEnumPrinters failed with exception code %lu!\n", RpcExceptionCode());
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcEnumPrinters failed with exception code %lu!\n", dwErrorCode);
     }
     RpcEndExcept;
 
-    return bReturnValue;
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
 }
 
 BOOL WINAPI
 ClosePrinter(HANDLE hPrinter)
 {
-    return FALSE;
+    DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Do the RPC call.
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcClosePrinter(pHandle->hPrinter);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcClosePrinter failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+    // Close any open file handle.
+    if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
+        CloseHandle(pHandle->hSPLFile);
+
+    // Free the memory for the handle.
+    HeapFree(hProcessHeap, 0, pHandle);
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+
 }
 
 DWORD WINAPI
@@ -68,13 +187,92 @@ DocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName, PDEVMODEW pD
 BOOL WINAPI
 EndDocPrinter(HANDLE hPrinter)
 {
-    return FALSE;
+    DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
+    {
+        // For spooled jobs, the document is finished by calling _RpcScheduleJob.
+        RpcTryExcept
+        {
+            dwErrorCode = _RpcScheduleJob(pHandle->hPrinter, pHandle->dwJobID);
+        }
+        RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+        {
+            dwErrorCode = RpcExceptionCode();
+            ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode);
+        }
+        RpcEndExcept;
+
+        // Close the spool file handle.
+        CloseHandle(pHandle->hSPLFile);
+    }
+    else
+    {
+        // In all other cases, just call _RpcEndDocPrinter.
+        RpcTryExcept
+        {
+            dwErrorCode = _RpcEndDocPrinter(pHandle->hPrinter);
+        }
+        RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+        {
+            dwErrorCode = RpcExceptionCode();
+            ERR("_RpcEndDocPrinter failed with exception code %lu!\n", dwErrorCode);
+        }
+        RpcEndExcept;
+    }
+
+    // A new document can now be started again.
+    pHandle->bStartedDoc = FALSE;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
 }
 
 BOOL WINAPI
 EndPagePrinter(HANDLE hPrinter)
 {
-    return FALSE;
+    DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
+    {
+        // For spooled jobs, we don't need to do anything.
+        dwErrorCode = ERROR_SUCCESS;
+    }
+    else
+    {
+        // In all other cases, just call _RpcEndPagePrinter.
+        RpcTryExcept
+        {
+            dwErrorCode = _RpcEndPagePrinter(pHandle->hPrinter);
+        }
+        RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+        {
+            dwErrorCode = RpcExceptionCode();
+            ERR("_RpcEndPagePrinter failed with exception code %lu!\n", dwErrorCode);
+        }
+        RpcEndExcept;
+    }
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
 }
 
 BOOL WINAPI
@@ -127,7 +325,7 @@ OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefaul
         // Convert pPrinterName to a Unicode string pwszPrinterName
         StringLength = strlen(pPrinterName) + 1;
 
-        pwszPrinterName = HeapAlloc(GetProcessHeap(), 0, StringLength * sizeof(WCHAR));
+        pwszPrinterName = HeapAlloc(hProcessHeap, 0, StringLength * sizeof(WCHAR));
         if (!pwszPrinterName)
         {
             ERR("HeapAlloc failed for pwszPrinterName with last error %lu!\n", GetLastError());
@@ -146,7 +344,7 @@ OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefaul
             // Convert pDefault->pDatatype to a Unicode string pwszDatatype that later becomes wDefault.pDatatype
             StringLength = strlen(pDefault->pDatatype) + 1;
 
-            pwszDatatype = HeapAlloc(GetProcessHeap(), 0, StringLength * sizeof(WCHAR));
+            pwszDatatype = HeapAlloc(hProcessHeap, 0, StringLength * sizeof(WCHAR));
             if (!pwszDatatype)
             {
                 ERR("HeapAlloc failed for pwszDatatype with last error %lu!\n", GetLastError());
@@ -165,13 +363,13 @@ OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefaul
 
 Cleanup:
     if (wDefault.pDevMode)
-        HeapFree(GetProcessHeap(), 0, wDefault.pDevMode);
+        HeapFree(hProcessHeap, 0, wDefault.pDevMode);
 
     if (pwszPrinterName)
-        HeapFree(GetProcessHeap(), 0, pwszPrinterName);
+        HeapFree(hProcessHeap, 0, pwszPrinterName);
 
     if (pwszDatatype)
-        HeapFree(GetProcessHeap(), 0, pwszDatatype);
+        HeapFree(hProcessHeap, 0, pwszDatatype);
 
     return bReturnValue;
 }
@@ -179,8 +377,9 @@ Cleanup:
 BOOL WINAPI
 OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefault)
 {
-    BOOL bReturnValue = FALSE;
     DWORD dwErrorCode;
+    HANDLE hPrinter;
+    PSPOOLER_HANDLE pHandle;
     PWSTR pDatatype = NULL;
     WINSPOOL_DEVMODE_CONTAINER DevModeContainer;
     WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer = NULL;
@@ -199,41 +398,235 @@ OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefau
     // Do the RPC call
     RpcTryExcept
     {
-        dwErrorCode = _RpcOpenPrinter(pPrinterName, phPrinter, pDatatype, pDevModeContainer, AccessRequired);
-        SetLastError(dwErrorCode);
-        bReturnValue = (dwErrorCode == ERROR_SUCCESS);
+        dwErrorCode = _RpcOpenPrinter(pPrinterName, &hPrinter, pDatatype, pDevModeContainer, AccessRequired);
     }
     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
     {
-        ERR("_RpcOpenPrinter failed with exception code %lu!\n", RpcExceptionCode());
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcOpenPrinter failed with exception code %lu!\n", dwErrorCode);
     }
     RpcEndExcept;
 
-    return bReturnValue;
+    if (dwErrorCode == ERROR_SUCCESS)
+    {
+        // Create a new SPOOLER_HANDLE structure.
+        pHandle = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, sizeof(SPOOLER_HANDLE));
+        if (!pHandle)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("HeapAlloc failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        pHandle->hPrinter = hPrinter;
+        pHandle->hSPLFile = INVALID_HANDLE_VALUE;
+
+        // Return it as phPrinter.
+        *phPrinter = (HANDLE)pHandle;
+    }
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
 }
 
 BOOL WINAPI
 ReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
 {
-    return FALSE;
+    DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcReadPrinter(pHandle->hPrinter, pBuf, cbBuf, pNoBytesRead);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcReadPrinter failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
 }
 
 DWORD WINAPI
-StartDocPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
+StartDocPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
 {
-    return 0;
+    DWORD cbAddJobInfo1;
+    DWORD cbNeeded;
+    DWORD dwErrorCode;
+    PADDJOB_INFO_1W pAddJobInfo1 = NULL;
+    PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    if (!pDocInfo1)
+    {
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
+    }
+
+    if (Level != 1)
+    {
+        dwErrorCode = ERROR_INVALID_LEVEL;
+        goto Cleanup;
+    }
+
+    if (pHandle->bStartedDoc)
+    {
+        dwErrorCode = ERROR_INVALID_PRINTER_STATE;
+        goto Cleanup;
+    }
+
+    // Check if we want to redirect output into a file.
+    if (pDocInfo1->pOutputFile)
+    {
+        // Do a StartDocPrinter RPC call in this case.
+        dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
+    }
+    else
+    {
+        // Allocate memory for the ADDJOB_INFO_1W structure and a path.
+        cbAddJobInfo1 = sizeof(ADDJOB_INFO_1W) + MAX_PATH * sizeof(WCHAR);
+        pAddJobInfo1 = HeapAlloc(hProcessHeap, 0, cbAddJobInfo1);
+        if (!pAddJobInfo1)
+        {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+            ERR("HeapAlloc failed with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
+        // Try to add a new job.
+        // This only succeeds if the printer is set to do spooled printing.
+        if (AddJobW(pHandle->hPrinter, 1, (PBYTE)pAddJobInfo1, cbAddJobInfo1, &cbNeeded))
+        {
+            // Do spooled printing.
+            dwErrorCode = _StartDocPrinterSpooled(pHandle, pDocInfo1, pAddJobInfo1);
+        }
+        else if (GetLastError() == ERROR_INVALID_ACCESS)
+        {
+            // ERROR_INVALID_ACCESS is returned when the printer is set to do direct printing.
+            // In this case, we do a StartDocPrinter RPC call.
+            dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
+        }
+        else
+        {
+            dwErrorCode = GetLastError();
+            ERR("AddJobW failed with error %lu!\n", dwErrorCode);
+            goto Cleanup;
+        }
+    }
+
+    if (dwErrorCode == ERROR_SUCCESS)
+        pHandle->bStartedDoc = TRUE;
+
+Cleanup:
+    if (pAddJobInfo1)
+        HeapFree(hProcessHeap, 0, pAddJobInfo1);
+
+    SetLastError(dwErrorCode);
+    return pHandle->dwJobID;
 }
 
 BOOL WINAPI
 StartPagePrinter(HANDLE hPrinter)
 {
-    return FALSE;
+    DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Do the RPC call
+    RpcTryExcept
+    {
+        dwErrorCode = _RpcStartPagePrinter(pHandle->hPrinter);
+    }
+    RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+    {
+        dwErrorCode = RpcExceptionCode();
+        ERR("_RpcStartPagePrinter failed with exception code %lu!\n", dwErrorCode);
+    }
+    RpcEndExcept;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
 }
 
 BOOL WINAPI
-WritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
+WritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
 {
-    return FALSE;
+    DWORD dwErrorCode;
+    PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    if (!pHandle->bStartedDoc)
+    {
+        dwErrorCode = ERROR_SPL_NO_STARTDOC;
+        goto Cleanup;
+    }
+
+    if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
+    {
+        // Write to the spool file. This doesn't need an RPC request.
+        if (!WriteFile(pHandle->hSPLFile, pBuf, cbBuf, pcWritten, NULL))
+        {
+            dwErrorCode = GetLastError();
+            ERR("WriteFile failed with error %lu!\n", dwErrorCode);
+            goto Cleanup;
+        }
+
+        dwErrorCode = ERROR_SUCCESS;
+    }
+    else
+    {
+        // TODO: This case (for direct printing or remote printing) has bad performance if multiple small-sized WritePrinter calls are performed.
+        // We may increase performance by writing into a buffer and only doing a single RPC call when the buffer is full.
+
+        // Do the RPC call
+        RpcTryExcept
+        {
+            dwErrorCode = _RpcWritePrinter(pHandle->hPrinter, pBuf, cbBuf, pcWritten);
+        }
+        RpcExcept(EXCEPTION_EXECUTE_HANDLER)
+        {
+            dwErrorCode = RpcExceptionCode();
+            ERR("_RpcWritePrinter failed with exception code %lu!\n", dwErrorCode);
+        }
+        RpcEndExcept;
+    }
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
 }
 
 BOOL WINAPI
index 78d9600..c1c68d1 100644 (file)
@@ -10,6 +10,7 @@ list(APPEND SOURCE
     ports.c
     precomp.h
     printers.c
+    printingthread.c
     printprocessors.c
     tools.c)
 
index b080bf9..2fa4099 100644 (file)
@@ -14,6 +14,27 @@ SKIPLIST GlobalJobList;
 static DWORD _dwLastJobID;
 
 
+static 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;
+}
+
 /**
  * @name _GlobalJobListCompareRoutine
  *
@@ -81,25 +102,23 @@ _PrinterJobListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
     return 0;
 }
 
-BOOL
-GetNextJobID(PDWORD dwJobID)
+DWORD
+GetJobFilePath(PCWSTR pwszExtension, DWORD dwJobID, PWSTR pwszOutput)
 {
-    ++_dwLastJobID;
-
-    while (LookupElementSkiplist(&GlobalJobList, &_dwLastJobID, NULL))
-    {
-        // This ID is already taken. Try the next one.
-        ++_dwLastJobID;
-    }
+    const WCHAR wszPrintersPath[] = L"\\PRINTERS\\";
+    const DWORD cchPrintersPath = _countof(wszPrintersPath) - 1;
+    const DWORD cchSpoolerFile = sizeof("?????.") - 1;
+    const DWORD cchExtension = sizeof("SPL") - 1;                   // pwszExtension may be L"SPL" or L"SHD", same length for both!
 
-    if (!IS_VALID_JOB_ID(_dwLastJobID))
+    if (pwszOutput)
     {
-        ERR("Job ID %lu isn't valid!\n", _dwLastJobID);
-        return FALSE;
+        CopyMemory(pwszOutput, wszSpoolDirectory, cchSpoolDirectory);
+        CopyMemory(&pwszOutput[cchSpoolDirectory], wszPrintersPath, cchPrintersPath);
+        swprintf(&pwszOutput[cchSpoolDirectory + cchPrintersPath], L"%05lu.", dwJobID);
+        CopyMemory(&pwszOutput[cchSpoolDirectory + cchPrintersPath + cchSpoolerFile], pwszExtension, (cchExtension + 1) * sizeof(WCHAR));
     }
 
-    *dwJobID = _dwLastJobID;
-    return TRUE;
+    return (cchSpoolDirectory + cchPrintersPath + cchSpoolerFile + cchExtension + 1) * sizeof(WCHAR);
 }
 
 BOOL
@@ -107,8 +126,6 @@ InitializeGlobalJobList()
 {
     const WCHAR wszPath[] = L"\\PRINTERS\\?????.SHD";
     const DWORD cchPath = _countof(wszPath) - 1;
-    const DWORD cchFolders = sizeof("\\PRINTERS\\") - 1;
-    const DWORD cchPattern = sizeof("?????") - 1;
 
     DWORD dwErrorCode;
     DWORD dwJobID;
@@ -118,7 +135,7 @@ InitializeGlobalJobList()
     WCHAR wszFullPath[MAX_PATH];
     WIN32_FIND_DATAW FindData;
 
-    // This one is incremented in GetNextJobID.
+    // This one is incremented in _GetNextJobID.
     _dwLastJobID = 0;
 
     // Initialize an empty list for all jobs of all local printers.
@@ -145,6 +162,7 @@ InitializeGlobalJobList()
             continue;
 
         // Extract the Job ID and verify the file name format at the same time.
+        // This includes all valid names (like "00005.SHD") and excludes invalid ones (like "10ABC.SHD").
         dwJobID = wcstoul(FindData.cFileName, &p, 10);
         if (!IS_VALID_JOB_ID(dwJobID))
             continue;
@@ -153,7 +171,7 @@ InitializeGlobalJobList()
             continue;
 
         // This shadow file has a valid name. Construct the full path and try to load it.
-        CopyMemory(&wszFullPath[cchSpoolDirectory + cchFolders], FindData.cFileName, cchPattern);
+        GetJobFilePath(L"SHD", dwJobID, wszFullPath);
         pJob = ReadJobShadowFile(wszFullPath);
         if (!pJob)
             continue;
@@ -195,78 +213,31 @@ InitializePrinterJobList(PLOCAL_PRINTER pPrinter)
     InitializeSkiplist(&pPrinter->JobList, DllAllocSplMem, _PrinterJobListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
 }
 
-BOOL WINAPI
-LocalAddJob(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
+DWORD WINAPI
+CreateJob(PLOCAL_PRINTER_HANDLE pPrinterHandle)
 {
     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;
     DWORD cchMachineName;
     DWORD cchUserName;
     DWORD dwErrorCode;
-    PBYTE p;
-    PLOCAL_HANDLE pHandle;
     PLOCAL_JOB pJob;
-    PLOCAL_PRINTER_HANDLE pPrinterHandle;
     RPC_BINDING_HANDLE hServerBinding = NULL;
     RPC_WSTR pwszBinding = NULL;
     RPC_WSTR pwszMachineName = NULL;
 
-    // Check if this is a printer handle.
-    pHandle = (PLOCAL_HANDLE)hPrinter;
-    if (pHandle->HandleType != HandleType_Printer)
-    {
-        dwErrorCode = ERROR_INVALID_HANDLE;
-        goto Cleanup;
-    }
-
-    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
-
-    // This handle must not have started a job yet!
-    if (pPrinterHandle->pStartedJob)
-    {
-        dwErrorCode = ERROR_INVALID_HANDLE;
-        goto Cleanup;
-    }
-
-    // Check if this is the right structure level.
-    if (Level != 1)
-    {
-        dwErrorCode = ERROR_INVALID_LEVEL;
-        goto Cleanup;
-    }
-
-    // Check if the printer is set to do direct printing.
-    // The Job List isn't used in this case.
-    if (pPrinterHandle->pPrinter->dwAttributes & PRINTER_ATTRIBUTE_DIRECT)
-    {
-        dwErrorCode = ERROR_INVALID_ACCESS;
-        goto Cleanup;
-    }
-
-    // Check if the supplied buffer is large enough.
-    *pcbNeeded = sizeof(ADDJOB_INFO_1W) + (cchSpoolDirectory + cchPrintersPath + cchSpl + 1) * sizeof(WCHAR);
-    if (cbBuf < *pcbNeeded)
-    {
-        dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
-        goto Cleanup;
-    }
-
     // Create a new job.
     pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
     if (!pJob)
     {
-        dwErrorCode = GetLastError();
-        ERR("DllAllocSplMem failed with error %lu!\n", dwErrorCode);
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
         goto Cleanup;
     }
 
     // Reserve an ID for this job.
-    if (!GetNextJobID(&pJob->dwJobID))
+    if (!_GetNextJobID(&pJob->dwJobID))
     {
         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
         goto Cleanup;
@@ -339,21 +310,18 @@ LocalAddJob(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcb
         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);
-
+    // We were successful!
+    pPrinterHandle->bStartedDoc = TRUE;
+    pPrinterHandle->pJob = pJob;
     dwErrorCode = ERROR_SUCCESS;
 
+    // Don't let the cleanup routine free this.
+    pJob = NULL;
+
 Cleanup:
+    if (pJob)
+        DllFreeSplMem(pJob);
+
     if (pwszMachineName)
         RpcStringFreeW(&pwszMachineName);
 
@@ -363,6 +331,72 @@ Cleanup:
     if (hServerBinding)
         RpcBindingFree(&hServerBinding);
 
+    return dwErrorCode;
+}
+
+BOOL WINAPI
+LocalAddJob(HANDLE hPrinter, DWORD Level, PBYTE pData, DWORD cbBuf, PDWORD pcbNeeded)
+{
+    ADDJOB_INFO_1W AddJobInfo1;
+    DWORD dwErrorCode;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+
+    // Check if this is a printer handle.
+    if (pHandle->HandleType != HandleType_Printer)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+    // This handle must not have started a job yet!
+    if (pPrinterHandle->pJob)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // Check if this is the right structure level.
+    if (Level != 1)
+    {
+        dwErrorCode = ERROR_INVALID_LEVEL;
+        goto Cleanup;
+    }
+
+    // Check if the printer is set to do direct printing.
+    // The Job List isn't used in this case.
+    if (pPrinterHandle->pPrinter->dwAttributes & PRINTER_ATTRIBUTE_DIRECT)
+    {
+        dwErrorCode = ERROR_INVALID_ACCESS;
+        goto Cleanup;
+    }
+
+    // Check if the supplied buffer is large enough.
+    *pcbNeeded = sizeof(ADDJOB_INFO_1W) + GetJobFilePath(L"SPL", 0, NULL);
+    if (cbBuf < *pcbNeeded)
+    {
+        dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+        goto Cleanup;
+    }
+
+    // All requirements are met - create a new job.
+    dwErrorCode = CreateJob(pPrinterHandle);
+    if (dwErrorCode != ERROR_SUCCESS)
+        goto Cleanup;
+
+    // Mark that this job was started with AddJob (so that it can be scheduled for printing with ScheduleJob).
+    pPrinterHandle->pJob->bAddedJob = TRUE;
+
+    // Return a proper ADDJOB_INFO_1W structure.
+    AddJobInfo1.JobId = pPrinterHandle->pJob->dwJobID;
+    AddJobInfo1.Path = (PWSTR)(pData + sizeof(ADDJOB_INFO_1W));
+
+    CopyMemory(pData, &AddJobInfo1, sizeof(ADDJOB_INFO_1W));
+    GetJobFilePath(L"SPL", AddJobInfo1.JobId, AddJobInfo1.Path);
+
+Cleanup:
     SetLastError(dwErrorCode);
     return (dwErrorCode == ERROR_SUCCESS);
 }
@@ -842,9 +876,6 @@ Cleanup:
 BOOL WINAPI
 LocalSetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command)
 {
-    const WCHAR wszFolder[] = L"\\PRINTERS\\";
-    const DWORD cchFolder = _countof(wszFolder) - 1;
-
     DWORD dwErrorCode;
     PLOCAL_HANDLE pHandle;
     PLOCAL_JOB pJob;
@@ -883,18 +914,34 @@ LocalSetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Com
     if (dwErrorCode != ERROR_SUCCESS)
         goto Cleanup;
 
-    // Construct the full path to the shadow file.
-    CopyMemory(wszFullPath, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR));
-    CopyMemory(&wszFullPath[cchSpoolDirectory], wszFolder, cchFolder * sizeof(WCHAR));
-    swprintf(&wszFullPath[cchSpoolDirectory + cchFolder], L"%05lu.SHD", JobId);
-
-    // Write the job data into the shadow file.
-    WriteJobShadowFile(wszFullPath, pJob);
+    // If we do spooled printing, the job information is written down into a shadow file.
+    if (!(pPrinterHandle->pPrinter->dwAttributes & PRINTER_ATTRIBUTE_DIRECT))
+    {
+        // Write the job data into the shadow file.
+        GetJobFilePath(L"SHD", JobId, wszFullPath);
+        WriteJobShadowFile(wszFullPath, pJob);
+    }
 
     // Perform an additional command if desired.
     if (Command)
     {
-        // TODO
+        if (Command == JOB_CONTROL_SENT_TO_PRINTER)
+        {
+            // This indicates the end of the Print Job.
+
+            // Cancel the Job at the Print Processor.
+            if (pJob->hPrintProcessor)
+                pJob->pPrintProcessor->pfnControlPrintProcessor(pJob->hPrintProcessor, JOB_CONTROL_CANCEL);
+
+            FreeJob(pJob);
+
+            // TODO: All open handles associated with the job need to be invalidated.
+            // This certainly needs handle tracking...
+        }
+        else
+        {
+            ERR("Unimplemented SetJob Command: %lu!\n", Command);
+        }
     }
 
     dwErrorCode = ERROR_SUCCESS;
@@ -1003,18 +1050,15 @@ Cleanup:
 BOOL WINAPI
 LocalScheduleJob(HANDLE hPrinter, DWORD dwJobID)
 {
-    const WCHAR wszFolder[] = L"\\PRINTERS\\";
-    const DWORD cchFolder = _countof(wszFolder) - 1;
-
     DWORD dwAttributes;
     DWORD dwErrorCode;
-    PLOCAL_HANDLE pHandle;
+    HANDLE hThread;
     PLOCAL_JOB pJob;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
     PLOCAL_PRINTER_HANDLE pPrinterHandle;
     WCHAR wszFullPath[MAX_PATH];
 
     // Check if this is a printer handle.
-    pHandle = (PLOCAL_HANDLE)hPrinter;
     if (pHandle->HandleType != HandleType_Printer)
     {
         dwErrorCode = ERROR_INVALID_HANDLE;
@@ -1031,10 +1075,15 @@ LocalScheduleJob(HANDLE hPrinter, DWORD dwJobID)
         goto Cleanup;
     }
 
+    // Check if this Job was started with AddJob.
+    if (!pJob->bAddedJob)
+    {
+        dwErrorCode = ERROR_SPL_NO_ADDJOB;
+        goto Cleanup;
+    }
+
     // Construct the full path to the spool file.
-    CopyMemory(wszFullPath, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR));
-    CopyMemory(&wszFullPath[cchSpoolDirectory], wszFolder, cchFolder * sizeof(WCHAR));
-    swprintf(&wszFullPath[cchSpoolDirectory + cchFolder], L"%05lu.SPL", dwJobID);
+    GetJobFilePath(L"SPL", dwJobID, wszFullPath);
 
     // Check if it exists.
     dwAttributes = GetFileAttributesW(wszFullPath);
@@ -1052,6 +1101,19 @@ LocalScheduleJob(HANDLE hPrinter, DWORD dwJobID)
     wcscpy(wcsrchr(wszFullPath, L'.'), L".SHD");
     WriteJobShadowFile(wszFullPath, pJob);
 
+    // Create the thread for performing the printing process.
+    hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PrintingThreadProc, pJob, 0, NULL);
+    if (!hThread)
+    {
+        dwErrorCode = GetLastError();
+        ERR("CreateThread failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // We don't need the thread handle. Keeping it open blocks the thread from terminating.
+    CloseHandle(hThread);
+
+    // ScheduleJob has done its job. The rest happens inside the thread.
     dwErrorCode = ERROR_SUCCESS;
 
 Cleanup:
@@ -1134,7 +1196,7 @@ ReadJobShadowFile(PCWSTR pwszFilePath)
     pJob->dwPriority = pShadowFile->dwPriority;
     pJob->dwStartTime = pShadowFile->dwStartTime;
     pJob->dwTotalPages = pShadowFile->dwTotalPages;
-    pJob->dwUntilTime = pShadowFile->dwUntilTime;    
+    pJob->dwUntilTime = pShadowFile->dwUntilTime;
     pJob->pPrinter = pPrinter;
     pJob->pPrintProcessor = pPrintProcessor;
     pJob->pDevMode = DuplicateDevMode((PDEVMODEW)((ULONG_PTR)pShadowFile + pShadowFile->offDevMode));
@@ -1149,6 +1211,9 @@ ReadJobShadowFile(PCWSTR pwszFilePath)
     pJob->pwszUserName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offUserName));
     CopyMemory(&pJob->stSubmitted, &pShadowFile->stSubmitted, sizeof(SYSTEMTIME));
 
+    // Jobs read from shadow files were always added using AddJob.
+    pJob->bAddedJob = TRUE;
+
     pReturnValue = pJob;
 
 Cleanup:
@@ -1303,15 +1368,38 @@ Cleanup:
     return bReturnValue;
 }
 
-BOOL
+void
 FreeJob(PLOCAL_JOB pJob)
 {
-    ////////// TODO /////////
-    /// Add some checks
+    PWSTR pwszSHDFile;
+    
+    // Remove the Job from both Job Lists.
+    DeleteElementSkiplist(&pJob->pPrinter->JobList, pJob);
+    DeleteElementSkiplist(&GlobalJobList, pJob);
+
+    // Try to delete the corresponding .SHD file.
+    pwszSHDFile = DllAllocSplMem(GetJobFilePath(L"SHD", 0, NULL));
+    if (pwszSHDFile && GetJobFilePath(L"SHD", pJob->dwJobID, pwszSHDFile))
+        DeleteFileW(pwszSHDFile);
+
+    // Free memory for the mandatory fields.
+    DllFreeSplMem(pJob->pDevMode);
     DllFreeSplStr(pJob->pwszDatatype);
     DllFreeSplStr(pJob->pwszDocumentName);
-    DllFreeSplStr(pJob->pwszOutputFile);
-    DllFreeSplMem(pJob);
+    DllFreeSplStr(pJob->pwszMachineName);
+    DllFreeSplStr(pJob->pwszNotifyName);
+    DllFreeSplStr(pJob->pwszUserName);
+
+    // Free memory for the optional fields if they are present.
+    if (pJob->pwszOutputFile)
+        DllFreeSplStr(pJob->pwszOutputFile);
+    
+    if (pJob->pwszPrintProcessorParameters)
+        DllFreeSplStr(pJob->pwszPrintProcessorParameters);
 
-    return TRUE;
+    if (pJob->pwszStatus)
+        DllFreeSplStr(pJob->pwszStatus);
+
+    // Finally free the job structure itself.
+    DllFreeSplMem(pJob);
 }
index ae0b16e..98b4d17 100644 (file)
@@ -59,7 +59,7 @@ static const PRINTPROVIDOR _PrintProviderFunctions = {
     LocalWritePrinter,                          // fpWritePrinter
     LocalEndPagePrinter,                        // fpEndPagePrinter
     NULL,                                       // fpAbortPrinter
-    NULL,                                       // fpReadPrinter
+    LocalReadPrinter,                           // fpReadPrinter
     LocalEndDocPrinter,                         // fpEndDocPrinter
     LocalAddJob,                                // fpAddJob
     LocalScheduleJob,                           // fpScheduleJob
index 1c5ed38..b4d9a5d 100644 (file)
@@ -11,8 +11,8 @@
 static LIST_ENTRY _PortList;
 
 
-PLOCAL_PRINT_MONITOR
-FindPrintMonitorByPort(PCWSTR pwszName)
+PLOCAL_PORT
+FindPort(PCWSTR pwszName)
 {
     PLIST_ENTRY pEntry;
     PLOCAL_PORT pPort;
@@ -22,7 +22,7 @@ FindPrintMonitorByPort(PCWSTR pwszName)
         pPort = CONTAINING_RECORD(pEntry, LOCAL_PORT, Entry);
 
         if (_wcsicmp(pPort->pwszName, pwszName) == 0)
-            return pPort->pPrintMonitor;
+            return pPort;
     }
 
     return NULL;
index 65b65d3..1bf6883 100644 (file)
@@ -49,6 +49,32 @@ typedef LPMONITOREX(WINAPI *PInitializePrintMonitor)(PWSTR);
 typedef LPMONITOR2(WINAPI *PInitializePrintMonitor2)(PMONITORINIT, PHANDLE);
 
 // Structures
+/**
+ * Describes a Print Monitor.
+ */
+typedef struct _LOCAL_PRINT_MONITOR
+{
+    LIST_ENTRY Entry;
+    PWSTR pwszName;                     /** Name of the Print Monitor as read from the registry. */
+    PWSTR pwszFileName;                 /** DLL File Name of the Print Monitor. */
+    BOOL bIsLevel2;                     /** Whether this Print Monitor supplies an InitializePrintMonitor2 API (preferred) instead of InitializePrintMonitor. */
+    PVOID pMonitor;                     /** For bIsLevel2 == TRUE:  LPMONITOR2 pointer returned by InitializePrintMonitor2.
+                                            For bIsLevel2 == FALSE: LPMONITOREX pointer returned by InitializePrintMonitor. */
+    HANDLE hMonitor;                    /** Only used when bIsLevel2 == TRUE: Handle returned by InitializePrintMonitor2. */
+}
+LOCAL_PRINT_MONITOR, *PLOCAL_PRINT_MONITOR;
+
+/**
+ * Describes a Port handled by a Print Monitor.
+ */
+typedef struct _LOCAL_PORT
+{
+    LIST_ENTRY Entry;
+    PWSTR pwszName;                         /** The name of the port (including the trailing colon). */
+    PLOCAL_PRINT_MONITOR pPrintMonitor;     /** The Print Monitor handling this port. */
+}
+LOCAL_PORT, *PLOCAL_PORT;
+
 /**
  * Describes a Print Processor.
  */
@@ -83,6 +109,7 @@ typedef struct _LOCAL_PRINTER
     PWSTR pwszDefaultDatatype;
     PDEVMODEW pDefaultDevMode;
     PLOCAL_PRINT_PROCESSOR pPrintProcessor;
+    PLOCAL_PORT pPort;
     SKIPLIST JobList;
 }
 LOCAL_PRINTER, *PLOCAL_PRINTER;
@@ -94,26 +121,28 @@ LOCAL_PRINTER, *PLOCAL_PRINTER;
 typedef struct _LOCAL_JOB
 {
     // This sort key must be the first element for LookupElementSkiplist to work!
-    DWORD dwJobID;                              // Internal and external ID of this Job
+    DWORD dwJobID;                              /** Internal and external ID of this Job */
 
-    PLOCAL_PRINTER pPrinter;                    // Associated Printer to this Job
-    PLOCAL_PRINT_PROCESSOR pPrintProcessor;     // Associated Print Processor 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
-    PWSTR pwszPrintProcessorParameters;         // Optional; Parameters for the chosen Print Processor
-    PWSTR pwszStatus;                           // Optional; a Status Message for the Job
-    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)
-    PDEVMODEW pDevMode;                         // Associated Device Mode to this Job
+    BOOL bAddedJob : 1;                         /** Whether AddJob has already been called on this Job. */
+    HANDLE hPrintProcessor;                     /** Handle returned by OpenPrintProcessor while the Job is printing. */
+    PLOCAL_PRINTER pPrinter;                    /** Associated Printer to this Job */
+    PLOCAL_PRINT_PROCESSOR pPrintProcessor;     /** Associated Print Processor 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 */
+    PWSTR pwszPrintProcessorParameters;         /** Optional; Parameters for the chosen Print Processor */
+    PWSTR pwszStatus;                           /** Optional; a Status Message for the Job */
+    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) */
+    PDEVMODEW pDevMode;                         /** Associated Device Mode to this Job */
 }
 LOCAL_JOB, *PLOCAL_JOB;
 
@@ -126,8 +155,10 @@ LOCAL_JOB, *PLOCAL_JOB;
  */
 typedef struct _LOCAL_PRINTER_HANDLE
 {
+    BOOL bStartedDoc : 1;                       /** Whether StartDocPrinter has already been called. */
+    HANDLE hSPLFile;                            /** Handle to an opened SPL file for Printer Job handles. */
     PLOCAL_PRINTER pPrinter;
-    PLOCAL_JOB pStartedJob;
+    PLOCAL_JOB pJob;
     PWSTR pwszDatatype;
     PDEVMODEW pDevMode;
 }
@@ -149,32 +180,6 @@ typedef struct _LOCAL_HANDLE
 }
 LOCAL_HANDLE, *PLOCAL_HANDLE;
 
-/**
- * Describes a Print Monitor.
- */
-typedef struct _LOCAL_PRINT_MONITOR
-{
-    LIST_ENTRY Entry;
-    PWSTR pwszName;                     /** Name of the Print Monitor as read from the registry. */
-    PWSTR pwszFileName;                 /** DLL File Name of the Print Monitor. */
-    BOOL bIsLevel2;                     /** Whether this Print Monitor supplies an InitializePrintMonitor2 API (preferred) instead of InitializePrintMonitor. */
-    PVOID pMonitor;                     /** For bIsLevel2 == TRUE:  LPMONITOR2 pointer returned by InitializePrintMonitor2.
-                                            For bIsLevel2 == FALSE: LPMONITOREX pointer returned by InitializePrintMonitor. */
-    HANDLE hMonitor;                    /** Only used when bIsLevel2 == TRUE: Handle returned by InitializePrintMonitor2. */
-}
-LOCAL_PRINT_MONITOR, *PLOCAL_PRINT_MONITOR;
-
-/**
- * Describes a Port handled by a Print Monitor.
- */
-typedef struct _LOCAL_PORT
-{
-    LIST_ENTRY Entry;
-    PWSTR pwszName;                         /** The name of the port (including the trailing colon). */
-    PLOCAL_PRINT_MONITOR pPrintMonitor;     /** The Print Monitor handling this port. */
-}
-LOCAL_PORT, *PLOCAL_PORT;
-
 /**
  * Describes the header of a print job serialized into a shadow file (.SHD)
  * Documented in http://www.undocprint.org/formats/winspool/shd
@@ -213,10 +218,11 @@ typedef struct _SHD_HEADER
 }
 SHD_HEADER, *PSHD_HEADER;
 
-
 // jobs.c
 extern SKIPLIST GlobalJobList;
-BOOL GetNextJobID(PDWORD dwJobID);
+DWORD WINAPI CreateJob(PLOCAL_PRINTER_HANDLE pPrinterHandle);
+void FreeJob(PLOCAL_JOB pJob);
+DWORD GetJobFilePath(PCWSTR pwszExtension, DWORD dwJobID, PWSTR pwszOutput);
 BOOL InitializeGlobalJobList();
 void InitializePrinterJobList(PLOCAL_PRINTER pPrinter);
 BOOL WINAPI LocalAddJob(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded);
@@ -242,7 +248,7 @@ BOOL InitializePrintMonitorList();
 BOOL WINAPI LocalEnumMonitors(PWSTR pName, DWORD Level, PBYTE pMonitors, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned);
 
 // ports.c
-PLOCAL_PRINT_MONITOR FindPrintMonitorByPort(PCWSTR pwszName);
+PLOCAL_PORT FindPort(PCWSTR pwszName);
 BOOL InitializePortList();
 BOOL WINAPI LocalEnumPorts(PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned);
 
@@ -251,6 +257,7 @@ extern SKIPLIST PrinterList;
 BOOL 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);
+BOOL WINAPI LocalReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead);
 DWORD WINAPI LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo);
 BOOL WINAPI LocalStartPagePrinter(HANDLE hPrinter);
 BOOL WINAPI LocalWritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten);
@@ -258,6 +265,9 @@ BOOL WINAPI LocalEndPagePrinter(HANDLE hPrinter);
 BOOL WINAPI LocalEndDocPrinter(HANDLE hPrinter);
 BOOL WINAPI LocalClosePrinter(HANDLE hPrinter);
 
+// printingthread.c
+DWORD WINAPI PrintingThreadProc(PLOCAL_JOB pJob);
+
 // printprocessors.c
 BOOL FindDatatype(const PLOCAL_PRINT_PROCESSOR pPrintProcessor, PCWSTR pwszDatatype);
 PLOCAL_PRINT_PROCESSOR FindPrintProcessor(PCWSTR pwszName);
index cf3f36c..655cbb1 100644 (file)
@@ -45,8 +45,10 @@ InitializePrinterList()
     DWORD i;
     HKEY hKey = NULL;
     HKEY hSubKey = NULL;
+    PLOCAL_PORT pPort;
     PLOCAL_PRINTER pPrinter = NULL;
     PLOCAL_PRINT_PROCESSOR pPrintProcessor;
+    PWSTR pwszPort = NULL;
     PWSTR pwszPrintProcessor = NULL;
     WCHAR wszPrinterName[MAX_PRINTER_NAME + 1];
 
@@ -141,6 +143,19 @@ InitializePrinterList()
             continue;
         }
 
+        // Get the Port.
+        pwszPort = AllocAndRegQueryWSZ(hSubKey, L"Port");
+        if (!pwszPort)
+            continue;
+
+        // Try to find it in the Port List.
+        pPort = FindPort(pwszPort);
+        if (!pPort)
+        {
+            ERR("Invalid Port \"%S\" for Printer \"%S\"!\n", pwszPort, wszPrinterName);
+            continue;
+        }
+
         // Create a new LOCAL_PRINTER structure for it.
         pPrinter = DllAllocSplMem(sizeof(LOCAL_PRINTER));
         if (!pPrinter)
@@ -488,10 +503,12 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
     PWSTR pwszSecondParameter = NULL;
     PLOCAL_JOB pJob;
     PLOCAL_HANDLE pHandle = NULL;
+    PLOCAL_PORT pPort;
     PLOCAL_PRINT_MONITOR pPrintMonitor;
     PLOCAL_PRINTER pPrinter;
     PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL;
     WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
+    WCHAR wszFullPath[MAX_PATH];
 
     // TODO: lpPrinterName == NULL is supported and means access to the local printer server.
     // Not sure yet if that is passed down to localspl.dll or processed in advance.
@@ -503,6 +520,8 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
         goto Cleanup;
     }
 
+    *phPrinter = NULL;
+
     // Skip any server name in the first parameter.
     // Does lpPrinterName begin with two backslashes to indicate a server name?
     if (lpPrinterName[0] == L'\\' && lpPrinterName[1] == L'\\')
@@ -592,14 +611,16 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
         //    "\\COMPUTERNAME\LPT1:, Port"
         
         // Look for this port in our Print Monitor Port list.
-        pPrintMonitor = FindPrintMonitorByPort(pwszFirstParameter);
-        if (!pPrintMonitor)
+        pPort = FindPort(pwszFirstParameter);
+        if (!pPort)
         {
             // The supplied port is unknown to all our Print Monitors.
             dwErrorCode = ERROR_INVALID_PRINTER_NAME;
             goto Cleanup;
         }
 
+        pPrintMonitor = pPort->pPrintMonitor;
+
         // Call the monitor's OpenPort function.
         if (pPrintMonitor->bIsLevel2)
             bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnOpenPort(pPrintMonitor->hMonitor, pwszFirstParameter, &hExternalHandle);
@@ -649,13 +670,15 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
             pwszSecondParameter += 5;
 
             // Look for this port in our Print Monitor Port list.
-            pPrintMonitor = FindPrintMonitorByPort(pwszSecondParameter);
-            if (!pPrintMonitor)
+            pPort = FindPort(pwszFirstParameter);
+            if (!pPort)
             {
                 // The supplied port is unknown to all our Print Monitors.
                 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
                 goto Cleanup;
             }
+
+            pPrintMonitor = pPort->pPrintMonitor;
         }
         else
         {
@@ -706,6 +729,7 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
             goto Cleanup;
         }
 
+        pPrinterHandle->hSPLFile = INVALID_HANDLE_VALUE;
         pPrinterHandle->pPrinter = pPrinter;
 
         // Check if a datatype was given.
@@ -732,7 +756,7 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
         else
             pPrinterHandle->pDevMode = DuplicateDevMode(pPrinter->pDefaultDevMode);
 
-        // Check if the caller wants a handle to an existing job.
+        // Check if the caller wants a handle to an existing Print Job.
         if (pwszSecondParameter)
         {
             // The "Job " string has to follow now.
@@ -767,7 +791,19 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
                 goto Cleanup;
             }
 
-            pPrinterHandle->pStartedJob = pJob;
+            // Try to open its SPL file.
+            GetJobFilePath(L"SPL", dwJobID, wszFullPath);
+            pPrinterHandle->hSPLFile = CreateFileW(wszFullPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+            if (pPrinterHandle->hSPLFile == INVALID_HANDLE_VALUE)
+            {
+                dwErrorCode = GetLastError();
+                ERR("CreateFileW failed with error %lu for \"%S\"!", dwErrorCode, wszFullPath);
+                goto Cleanup;
+            }
+
+            // Associate the job to our Printer Handle, but don't set bStartedDoc.
+            // This prevents the caller from doing further StartDocPrinter, WritePrinter, etc. calls on it.
+            pPrinterHandle->pJob = pJob;
         }
 
         // Return the Printer handle through our general handle.
@@ -805,108 +841,114 @@ Cleanup:
     return (dwErrorCode == ERROR_SUCCESS);
 }
 
-DWORD WINAPI
-LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
+BOOL WINAPI
+LocalReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
 {
     DWORD dwErrorCode;
-    DWORD dwReturnValue = 0;
-    PDOC_INFO_1W pDocumentInfo1;
-    PLOCAL_HANDLE pHandle;
-    PLOCAL_JOB pJob;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
     PLOCAL_PRINTER_HANDLE pPrinterHandle;
 
-    // Sanity checks
-    if (!pDocInfo)
+    // Sanity checks.
+    if (!pHandle || pHandle->HandleType != HandleType_Printer)
     {
-        dwErrorCode = ERROR_INVALID_PARAMETER;
+        dwErrorCode = ERROR_INVALID_HANDLE;
         goto Cleanup;
     }
 
-    if (!hPrinter)
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+    // ReadPrinter needs an opened SPL file to work.
+    // This only works if a Printer Job Handle was requested in OpenPrinter.
+    if (pPrinterHandle->hSPLFile == INVALID_HANDLE_VALUE)
     {
         dwErrorCode = ERROR_INVALID_HANDLE;
         goto Cleanup;
     }
 
-    // Check if this is a printer handle.
-    pHandle = (PLOCAL_HANDLE)hPrinter;
-    if (pHandle->HandleType != HandleType_Printer)
+    // Pass the parameters to ReadFile.
+    if (!ReadFile(pPrinterHandle->hSPLFile, pBuf, cbBuf, pNoBytesRead, NULL))
     {
-        dwErrorCode = ERROR_INVALID_HANDLE;
+        dwErrorCode = GetLastError();
+        ERR("ReadFile failed with error %lu!\n", dwErrorCode);
         goto Cleanup;
     }
 
-    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+    dwErrorCode = ERROR_SUCCESS;
 
-    // Check if this is the right document information level.
-    if (Level != 1)
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
+}
+
+DWORD WINAPI
+LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
+{
+    DWORD dwErrorCode;
+    DWORD dwReturnValue = 0;
+    PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+
+    // Sanity checks.
+    if (!pDocInfo1)
     {
-        dwErrorCode = ERROR_INVALID_LEVEL;
+        dwErrorCode = ERROR_INVALID_PARAMETER;
         goto Cleanup;
     }
 
-    pDocumentInfo1 = (PDOC_INFO_1W)pDocInfo;
+    if (!pHandle || pHandle->HandleType != HandleType_Printer)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
 
-    // Create a new job.
-    pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
-    pJob->pPrinter = pPrinterHandle->pPrinter;
-    pJob->dwPriority = DEF_PRIORITY;
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
 
-    // Check if a datatype was given.
-    if (pDocumentInfo1->pDatatype)
+    // pJob may already be occupied if this is a Print Job handle. In this case, StartDocPrinter has to fail.
+    if (pPrinterHandle->pJob)
     {
-        // Use the datatype if it's valid.
-        if (!FindDatatype(pJob->pPrintProcessor, pDocumentInfo1->pDatatype))
-        {
-            dwErrorCode = ERROR_INVALID_DATATYPE;
-            goto Cleanup;
-        }
-
-        pJob->pwszDatatype = AllocSplStr(pDocumentInfo1->pDatatype);
+        dwErrorCode = ERROR_INVALID_PARAMETER;
+        goto Cleanup;
     }
-    else
+
+    // Check the validity of the datatype if we got one.
+    if (pDocInfo1->pDatatype && !FindDatatype(pPrinterHandle->pJob->pPrintProcessor, pDocInfo1->pDatatype))
     {
-        // Use the printer handle datatype.
-        pJob->pwszDatatype = AllocSplStr(pPrinterHandle->pwszDatatype);
+        dwErrorCode = ERROR_INVALID_DATATYPE;
+        goto Cleanup;
     }
 
-    // Copy over printer defaults.
-    pJob->pDevMode = DuplicateDevMode(pPrinterHandle->pDevMode);
-
-    // Copy over supplied information.
-    if (pDocumentInfo1->pDocName)
-        pJob->pwszDocumentName = AllocSplStr(pDocumentInfo1->pDocName);
-
-    if (pDocumentInfo1->pOutputFile)
-        pJob->pwszOutputFile = AllocSplStr(pDocumentInfo1->pOutputFile);
-
-    // Get an ID for the new job.
-    if (!GetNextJobID(&pJob->dwJobID))
+    // Check if this is the right document information level.
+    if (Level != 1)
     {
-        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        dwErrorCode = ERROR_INVALID_LEVEL;
         goto Cleanup;
     }
 
-    // Add the job to the Global Job List.
-    if (!InsertElementSkiplist(&GlobalJobList, pJob))
+    // All requirements are met - create a new job.
+    dwErrorCode = CreateJob(pPrinterHandle);
+    if (dwErrorCode != ERROR_SUCCESS)
+        goto Cleanup;
+
+    // Use any given datatype.
+    if (pDocInfo1->pDatatype && !ReallocSplStr(&pPrinterHandle->pJob->pwszDatatype, pDocInfo1->pDatatype))
     {
         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-        ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
+        ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
         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->pPrinter->JobList, pJob))
+    // Use any given document name.
+    if (pDocInfo1->pDocName && !ReallocSplStr(&pPrinterHandle->pJob->pwszDocumentName, pDocInfo1->pDocName))
     {
         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
-        ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
+        ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
         goto Cleanup;
     }
 
-    pPrinterHandle->pStartedJob = pJob;
+    // We were successful!
     dwErrorCode = ERROR_SUCCESS;
-    dwReturnValue = pJob->dwJobID;
+    dwReturnValue = pPrinterHandle->pJob->dwJobID;
 
 Cleanup:
     SetLastError(dwErrorCode);
@@ -916,48 +958,170 @@ Cleanup:
 BOOL WINAPI
 LocalStartPagePrinter(HANDLE hPrinter)
 {
-    ///////////// TODO /////////////////////
-    return FALSE;
+    DWORD dwErrorCode;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+
+    // Sanity checks.
+    if (!pHandle || pHandle->HandleType != HandleType_Printer)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+    // We require StartDocPrinter or AddJob to be called first.
+    if (!pPrinterHandle->bStartedDoc)
+    {
+        dwErrorCode = ERROR_SPL_NO_STARTDOC;
+        goto Cleanup;
+    }
+
+    // Increase the page count.
+    ++pPrinterHandle->pJob->dwTotalPages;
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
 }
 
 BOOL WINAPI
 LocalWritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
 {
-    ///////////// TODO /////////////////////
-    return FALSE;
+    DWORD dwErrorCode;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+
+    // Sanity checks.
+    if (!pHandle || pHandle->HandleType != HandleType_Printer)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+    // We require StartDocPrinter or AddJob to be called first.
+    if (!pPrinterHandle->bStartedDoc)
+    {
+        dwErrorCode = ERROR_SPL_NO_STARTDOC;
+        goto Cleanup;
+    }
+
+    // TODO: This function is only called when doing non-spooled printing.
+    // This needs to be investigated further. We can't just use pPrinterHandle->hSPLFile here, because that's currently reserved for Printer Job handles (see LocalReadPrinter).
+#if 0
+    // Pass the parameters to WriteFile.
+    if (!WriteFile(SOME_SPOOL_FILE_HANDLE, pBuf, cbBuf, pcWritten, NULL))
+    {
+        dwErrorCode = GetLastError();
+        ERR("WriteFile failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+#endif
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
 }
 
 BOOL WINAPI
 LocalEndPagePrinter(HANDLE hPrinter)
 {
-    ///////////// TODO /////////////////////
-    return FALSE;
+    DWORD dwErrorCode;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+
+    // Sanity checks.
+    if (!pHandle || pHandle->HandleType != HandleType_Printer)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    // This function doesn't do anything else for now.
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
 }
 
 BOOL WINAPI
 LocalEndDocPrinter(HANDLE hPrinter)
 {
-    ///////////// TODO /////////////////////
-    return FALSE;
+    DWORD dwErrorCode;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
+
+    // Sanity checks.
+    if (!pHandle || pHandle->HandleType != HandleType_Printer)
+    {
+        dwErrorCode = ERROR_INVALID_HANDLE;
+        goto Cleanup;
+    }
+
+    pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+    // We require StartDocPrinter or AddJob to be called first.
+    if (!pPrinterHandle->bStartedDoc)
+    {
+        dwErrorCode = ERROR_SPL_NO_STARTDOC;
+        goto Cleanup;
+    }
+
+    // TODO: Something like ScheduleJob
+
+    // Finish the job.
+    pPrinterHandle->bStartedDoc = FALSE;
+    pPrinterHandle->pJob = NULL;
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
 }
 
 BOOL WINAPI
 LocalClosePrinter(HANDLE hPrinter)
 {
-    PLOCAL_HANDLE pHandle;
+    PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+    PLOCAL_PRINTER_HANDLE pPrinterHandle;
 
-    if (!hPrinter)
+    if (!pHandle)
     {
         SetLastError(ERROR_INVALID_HANDLE);
         return FALSE;
     }
 
-    pHandle = (PLOCAL_HANDLE)hPrinter;
+    if (pHandle->HandleType == HandleType_Port)
+    {
+        // TODO
+    }
+    else if (pHandle->HandleType == HandleType_Printer)
+    {
+        pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+        // Terminate any started job.
+        if (pPrinterHandle->pJob)
+            FreeJob(pPrinterHandle->pJob);
+
+        // Free memory for the fields.
+        DllFreeSplMem(pPrinterHandle->pDevMode);
+        DllFreeSplStr(pPrinterHandle->pwszDatatype);
 
-    ///////////// TODO /////////////////////
-    /// Check the handle type, do thoroughful checks on all data fields and clean them.
-    ////////////////////////////////////////
+        // Free memory for the printer handle itself.
+        DllFreeSplMem(pPrinterHandle);
+    }
+    else if (pHandle->HandleType == HandleType_Xcv)
+    {
+        // TODO
+    }
 
+    // Free memory for the handle itself.
     DllFreeSplMem(pHandle);
 
     return TRUE;
diff --git a/reactos/win32ss/printing/providers/localspl/printingthread.c b/reactos/win32ss/printing/providers/localspl/printingthread.c
new file mode 100644 (file)
index 0000000..7771d73
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * PROJECT:     ReactOS Local Spooler
+ * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Implementation of the Thread that actually performs the printing process
+ * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+DWORD WINAPI
+PrintingThreadProc(PLOCAL_JOB pJob)
+{
+    const DWORD cchMaxJobIdDigits = 5;              // Job ID is limited to 5 decimal digits, see IS_VALID_JOB_ID
+    const WCHAR wszJobAppendix[] = L", Job ";
+    const DWORD cchJobAppendix = _countof(wszJobAppendix) - 1;
+    const WCHAR wszPortAppendix[] = L", Port";
+
+    DWORD cchPortName;
+    DWORD cchPrinterName;
+    DWORD dwErrorCode;
+    HANDLE hPrintProcessor = NULL;
+    PLOCAL_PRINT_PROCESSOR pPrintProcessor = pJob->pPrintProcessor;
+    PRINTPROCESSOROPENDATA OpenData;
+    PWSTR pwszPrinterAndJob = NULL;
+    PWSTR pwszPrinterPort = NULL;
+    PWSTR pwszSPLFile = NULL;
+
+    // Prepare the pPrinterName parameter.
+    // This is the string for LocalOpenPrinter to open a port (e.g. "LPT1:, Port").
+    cchPortName = wcslen(pJob->pPrinter->pPort->pwszName);
+    pwszPrinterPort = DllAllocSplMem(cchPortName * sizeof(WCHAR) + sizeof(wszPortAppendix));
+    if (!pwszPrinterPort)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    CopyMemory(pwszPrinterPort, pJob->pPrinter->pPort->pwszName, cchPortName * sizeof(WCHAR));
+    CopyMemory(&pwszPrinterPort[cchPortName], wszPortAppendix, sizeof(wszPortAppendix));
+
+    // Prepare the pPrintProcessorOpenData parameter.
+    OpenData.JobId = pJob->dwJobID;
+    OpenData.pDatatype = pJob->pwszDatatype;
+    OpenData.pDevMode = pJob->pDevMode;
+    OpenData.pDocumentName = pJob->pwszDocumentName;
+    OpenData.pOutputFile = NULL;
+    OpenData.pParameters = pJob->pwszPrintProcessorParameters;
+    OpenData.pPrinterName = pJob->pPrinter->pwszPrinterName;
+
+    // Open a handle to the Print Processor.
+    hPrintProcessor = pPrintProcessor->pfnOpenPrintProcessor(pwszPrinterPort, &OpenData);
+    if (!hPrintProcessor)
+    {
+        dwErrorCode = GetLastError();
+        ERR("OpenPrintProcessor failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Let other functions use the Print Processor as well while it's opened.
+    pJob->hPrintProcessor = hPrintProcessor;
+
+    // Prepare the pDocumentName parameter.
+    cchPrinterName = wcslen(OpenData.pPrinterName);
+    pwszPrinterAndJob = DllAllocSplMem((cchPrinterName + cchJobAppendix + cchMaxJobIdDigits + 1) * sizeof(WCHAR));
+    if (!pwszPrinterAndJob)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    CopyMemory(pwszPrinterAndJob, OpenData.pPrinterName, cchPrinterName * sizeof(WCHAR));
+    CopyMemory(&pwszPrinterAndJob[cchPrinterName], wszJobAppendix, cchJobAppendix * sizeof(WCHAR));
+    _ultow(OpenData.JobId, &pwszPrinterAndJob[cchPrinterName + cchJobAppendix], 10);
+
+    // Print the document.
+    // Note that pJob is freed after this function, so we may not access it anymore.
+    if (!pPrintProcessor->pfnPrintDocumentOnPrintProcessor(hPrintProcessor, pwszPrinterAndJob))
+    {
+        dwErrorCode = GetLastError();
+        ERR("PrintDocumentOnPrintProcessor failed with error %lu!\n", dwErrorCode);
+        goto Cleanup;
+    }
+
+    // Close the Print Processor.
+    pPrintProcessor->pfnClosePrintProcessor(hPrintProcessor);
+    hPrintProcessor = NULL;
+
+    // Delete the spool file.
+    pwszSPLFile = DllAllocSplMem(GetJobFilePath(L"SPL", 0, NULL));
+    if (!pwszSPLFile)
+    {
+        dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
+        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        goto Cleanup;
+    }
+
+    GetJobFilePath(L"SPL", OpenData.JobId, pwszSPLFile);
+    DeleteFileW(pwszSPLFile);
+
+    // We were successful!
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    if (hPrintProcessor)
+        pPrintProcessor->pfnClosePrintProcessor(hPrintProcessor);
+
+    if (pwszPrinterPort)
+        DllFreeSplMem(pwszPrinterPort);
+
+    if (pwszPrinterAndJob)
+        DllFreeSplMem(pwszPrinterAndJob);
+
+    if (pwszSPLFile)
+        DllFreeSplMem(pwszSPLFile);
+
+    return dwErrorCode;
+}