mirror of
https://github.com/reactos/reactos.git
synced 2025-02-25 01:39:30 +00:00
[LOCALSPL, WINSPOOL]
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
This commit is contained in:
parent
f848e96f26
commit
ba2134dd1e
11 changed files with 1135 additions and 286 deletions
|
@ -61,11 +61,18 @@ AddJobW(HANDLE hPrinter, DWORD Level, PBYTE pData, DWORD cbBuf, PDWORD pcbNeeded
|
||||||
{
|
{
|
||||||
DWORD dwErrorCode;
|
DWORD dwErrorCode;
|
||||||
PADDJOB_INFO_1W pAddJobInfo1;
|
PADDJOB_INFO_1W pAddJobInfo1;
|
||||||
|
PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
|
||||||
|
|
||||||
|
if (!pHandle)
|
||||||
|
{
|
||||||
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
// Do the RPC call
|
// Do the RPC call
|
||||||
RpcTryExcept
|
RpcTryExcept
|
||||||
{
|
{
|
||||||
dwErrorCode = _RpcAddJob(hPrinter, Level, pData, cbBuf, pcbNeeded);
|
dwErrorCode = _RpcAddJob(pHandle->hPrinter, Level, pData, cbBuf, pcbNeeded);
|
||||||
}
|
}
|
||||||
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
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);
|
pAddJobInfo1->Path = (PWSTR)((ULONG_PTR)pAddJobInfo1->Path + (ULONG_PTR)pAddJobInfo1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Cleanup:
|
||||||
SetLastError(dwErrorCode);
|
SetLastError(dwErrorCode);
|
||||||
return (dwErrorCode == ERROR_SUCCESS);
|
return (dwErrorCode == ERROR_SUCCESS);
|
||||||
}
|
}
|
||||||
|
@ -98,11 +106,18 @@ EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pJob
|
||||||
DWORD dwErrorCode;
|
DWORD dwErrorCode;
|
||||||
DWORD i;
|
DWORD i;
|
||||||
PBYTE p = pJob;
|
PBYTE p = pJob;
|
||||||
|
PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
|
||||||
|
|
||||||
|
if (!pHandle)
|
||||||
|
{
|
||||||
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
// Do the RPC call
|
// Do the RPC call
|
||||||
RpcTryExcept
|
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)
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
||||||
{
|
{
|
||||||
|
@ -125,6 +140,7 @@ EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pJob
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Cleanup:
|
||||||
SetLastError(dwErrorCode);
|
SetLastError(dwErrorCode);
|
||||||
return (dwErrorCode == ERROR_SUCCESS);
|
return (dwErrorCode == ERROR_SUCCESS);
|
||||||
}
|
}
|
||||||
|
@ -140,11 +156,18 @@ BOOL WINAPI
|
||||||
GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWORD pcbNeeded)
|
GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWORD pcbNeeded)
|
||||||
{
|
{
|
||||||
DWORD dwErrorCode;
|
DWORD dwErrorCode;
|
||||||
|
PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
|
||||||
|
|
||||||
|
if (!pHandle)
|
||||||
|
{
|
||||||
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
// Do the RPC call
|
// Do the RPC call
|
||||||
RpcTryExcept
|
RpcTryExcept
|
||||||
{
|
{
|
||||||
dwErrorCode = _RpcGetJob(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
|
dwErrorCode = _RpcGetJob(pHandle->hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
|
||||||
}
|
}
|
||||||
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
||||||
{
|
{
|
||||||
|
@ -159,6 +182,7 @@ GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWO
|
||||||
_MarshallUpJobInfo(pJob, Level);
|
_MarshallUpJobInfo(pJob, Level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Cleanup:
|
||||||
SetLastError(dwErrorCode);
|
SetLastError(dwErrorCode);
|
||||||
return (dwErrorCode == ERROR_SUCCESS);
|
return (dwErrorCode == ERROR_SUCCESS);
|
||||||
}
|
}
|
||||||
|
@ -167,19 +191,27 @@ BOOL WINAPI
|
||||||
ScheduleJob(HANDLE hPrinter, DWORD dwJobID)
|
ScheduleJob(HANDLE hPrinter, DWORD dwJobID)
|
||||||
{
|
{
|
||||||
DWORD dwErrorCode;
|
DWORD dwErrorCode;
|
||||||
|
PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
|
||||||
|
|
||||||
|
if (!pHandle)
|
||||||
|
{
|
||||||
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
// Do the RPC call
|
// Do the RPC call
|
||||||
RpcTryExcept
|
RpcTryExcept
|
||||||
{
|
{
|
||||||
dwErrorCode = _RpcScheduleJob(hPrinter, dwJobID);
|
dwErrorCode = _RpcScheduleJob(pHandle->hPrinter, dwJobID);
|
||||||
}
|
}
|
||||||
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
||||||
{
|
{
|
||||||
dwErrorCode = RpcExceptionCode();
|
dwErrorCode = RpcExceptionCode();
|
||||||
ERR("_RpcSetJob failed with exception code %lu!\n", dwErrorCode);
|
ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode);
|
||||||
}
|
}
|
||||||
RpcEndExcept;
|
RpcEndExcept;
|
||||||
|
|
||||||
|
Cleanup:
|
||||||
SetLastError(dwErrorCode);
|
SetLastError(dwErrorCode);
|
||||||
return (dwErrorCode == ERROR_SUCCESS);
|
return (dwErrorCode == ERROR_SUCCESS);
|
||||||
}
|
}
|
||||||
|
@ -195,8 +227,15 @@ BOOL WINAPI
|
||||||
SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command)
|
SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command)
|
||||||
{
|
{
|
||||||
DWORD dwErrorCode;
|
DWORD dwErrorCode;
|
||||||
|
PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
|
||||||
WINSPOOL_JOB_CONTAINER JobContainer;
|
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.
|
// pJobContainer->JobInfo is a union of pointers, so we can just set any element to our BYTE pointer.
|
||||||
JobContainer.Level = Level;
|
JobContainer.Level = Level;
|
||||||
JobContainer.JobInfo.Level1 = (WINSPOOL_JOB_INFO_1*)pJobInfo;
|
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
|
// Do the RPC call
|
||||||
RpcTryExcept
|
RpcTryExcept
|
||||||
{
|
{
|
||||||
dwErrorCode = _RpcSetJob(hPrinter, JobId, &JobContainer, Command);
|
dwErrorCode = _RpcSetJob(pHandle->hPrinter, JobId, &JobContainer, Command);
|
||||||
}
|
}
|
||||||
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
||||||
{
|
{
|
||||||
|
@ -213,6 +252,7 @@ SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command
|
||||||
}
|
}
|
||||||
RpcEndExcept;
|
RpcEndExcept;
|
||||||
|
|
||||||
|
Cleanup:
|
||||||
SetLastError(dwErrorCode);
|
SetLastError(dwErrorCode);
|
||||||
return (dwErrorCode == ERROR_SUCCESS);
|
return (dwErrorCode == ERROR_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
#include "precomp.h"
|
#include "precomp.h"
|
||||||
|
|
||||||
|
// Global Variables
|
||||||
|
HANDLE hProcessHeap;
|
||||||
|
|
||||||
|
|
||||||
handle_t __RPC_USER
|
handle_t __RPC_USER
|
||||||
WINSPOOL_HANDLE_bind(WINSPOOL_HANDLE wszName)
|
WINSPOOL_HANDLE_bind(WINSPOOL_HANDLE wszName)
|
||||||
{
|
{
|
||||||
|
@ -56,13 +60,27 @@ WINSPOOL_HANDLE_unbind(WINSPOOL_HANDLE wszName, handle_t hBinding)
|
||||||
void __RPC_FAR* __RPC_USER
|
void __RPC_FAR* __RPC_USER
|
||||||
midl_user_allocate(SIZE_T len)
|
midl_user_allocate(SIZE_T len)
|
||||||
{
|
{
|
||||||
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
|
return HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __RPC_USER
|
void __RPC_USER
|
||||||
midl_user_free(void __RPC_FAR* ptr)
|
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
|
BOOL WINAPI
|
||||||
|
|
|
@ -19,4 +19,20 @@
|
||||||
#include <wine/debug.h>
|
#include <wine/debug.h>
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL(winspool);
|
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
|
#endif
|
||||||
|
|
|
@ -7,6 +7,94 @@
|
||||||
|
|
||||||
#include "precomp.h"
|
#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
|
BOOL WINAPI
|
||||||
EnumPrintersA(DWORD Flags, LPSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
|
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
|
BOOL WINAPI
|
||||||
EnumPrintersW(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
|
EnumPrintersW(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
|
||||||
{
|
{
|
||||||
BOOL bReturnValue = FALSE;
|
|
||||||
DWORD dwErrorCode;
|
DWORD dwErrorCode;
|
||||||
|
|
||||||
// Do the RPC call
|
// Do the RPC call
|
||||||
RpcTryExcept
|
RpcTryExcept
|
||||||
{
|
{
|
||||||
dwErrorCode = _RpcEnumPrinters(Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
|
dwErrorCode = _RpcEnumPrinters(Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
|
||||||
SetLastError(dwErrorCode);
|
|
||||||
bReturnValue = (dwErrorCode == ERROR_SUCCESS);
|
|
||||||
}
|
}
|
||||||
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
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;
|
RpcEndExcept;
|
||||||
|
|
||||||
return bReturnValue;
|
SetLastError(dwErrorCode);
|
||||||
|
return (dwErrorCode == ERROR_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL WINAPI
|
BOOL WINAPI
|
||||||
ClosePrinter(HANDLE hPrinter)
|
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
|
DWORD WINAPI
|
||||||
|
@ -68,13 +187,92 @@ DocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName, PDEVMODEW pD
|
||||||
BOOL WINAPI
|
BOOL WINAPI
|
||||||
EndDocPrinter(HANDLE hPrinter)
|
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
|
BOOL WINAPI
|
||||||
EndPagePrinter(HANDLE hPrinter)
|
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
|
BOOL WINAPI
|
||||||
|
@ -127,7 +325,7 @@ OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefaul
|
||||||
// Convert pPrinterName to a Unicode string pwszPrinterName
|
// Convert pPrinterName to a Unicode string pwszPrinterName
|
||||||
StringLength = strlen(pPrinterName) + 1;
|
StringLength = strlen(pPrinterName) + 1;
|
||||||
|
|
||||||
pwszPrinterName = HeapAlloc(GetProcessHeap(), 0, StringLength * sizeof(WCHAR));
|
pwszPrinterName = HeapAlloc(hProcessHeap, 0, StringLength * sizeof(WCHAR));
|
||||||
if (!pwszPrinterName)
|
if (!pwszPrinterName)
|
||||||
{
|
{
|
||||||
ERR("HeapAlloc failed for pwszPrinterName with last error %lu!\n", GetLastError());
|
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
|
// Convert pDefault->pDatatype to a Unicode string pwszDatatype that later becomes wDefault.pDatatype
|
||||||
StringLength = strlen(pDefault->pDatatype) + 1;
|
StringLength = strlen(pDefault->pDatatype) + 1;
|
||||||
|
|
||||||
pwszDatatype = HeapAlloc(GetProcessHeap(), 0, StringLength * sizeof(WCHAR));
|
pwszDatatype = HeapAlloc(hProcessHeap, 0, StringLength * sizeof(WCHAR));
|
||||||
if (!pwszDatatype)
|
if (!pwszDatatype)
|
||||||
{
|
{
|
||||||
ERR("HeapAlloc failed for pwszDatatype with last error %lu!\n", GetLastError());
|
ERR("HeapAlloc failed for pwszDatatype with last error %lu!\n", GetLastError());
|
||||||
|
@ -165,13 +363,13 @@ OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefaul
|
||||||
|
|
||||||
Cleanup:
|
Cleanup:
|
||||||
if (wDefault.pDevMode)
|
if (wDefault.pDevMode)
|
||||||
HeapFree(GetProcessHeap(), 0, wDefault.pDevMode);
|
HeapFree(hProcessHeap, 0, wDefault.pDevMode);
|
||||||
|
|
||||||
if (pwszPrinterName)
|
if (pwszPrinterName)
|
||||||
HeapFree(GetProcessHeap(), 0, pwszPrinterName);
|
HeapFree(hProcessHeap, 0, pwszPrinterName);
|
||||||
|
|
||||||
if (pwszDatatype)
|
if (pwszDatatype)
|
||||||
HeapFree(GetProcessHeap(), 0, pwszDatatype);
|
HeapFree(hProcessHeap, 0, pwszDatatype);
|
||||||
|
|
||||||
return bReturnValue;
|
return bReturnValue;
|
||||||
}
|
}
|
||||||
|
@ -179,8 +377,9 @@ Cleanup:
|
||||||
BOOL WINAPI
|
BOOL WINAPI
|
||||||
OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefault)
|
OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefault)
|
||||||
{
|
{
|
||||||
BOOL bReturnValue = FALSE;
|
|
||||||
DWORD dwErrorCode;
|
DWORD dwErrorCode;
|
||||||
|
HANDLE hPrinter;
|
||||||
|
PSPOOLER_HANDLE pHandle;
|
||||||
PWSTR pDatatype = NULL;
|
PWSTR pDatatype = NULL;
|
||||||
WINSPOOL_DEVMODE_CONTAINER DevModeContainer;
|
WINSPOOL_DEVMODE_CONTAINER DevModeContainer;
|
||||||
WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer = NULL;
|
WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer = NULL;
|
||||||
|
@ -199,41 +398,235 @@ OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefau
|
||||||
// Do the RPC call
|
// Do the RPC call
|
||||||
RpcTryExcept
|
RpcTryExcept
|
||||||
{
|
{
|
||||||
dwErrorCode = _RpcOpenPrinter(pPrinterName, phPrinter, pDatatype, pDevModeContainer, AccessRequired);
|
dwErrorCode = _RpcOpenPrinter(pPrinterName, &hPrinter, pDatatype, pDevModeContainer, AccessRequired);
|
||||||
SetLastError(dwErrorCode);
|
|
||||||
bReturnValue = (dwErrorCode == ERROR_SUCCESS);
|
|
||||||
}
|
}
|
||||||
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
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;
|
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
|
BOOL WINAPI
|
||||||
ReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
|
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
|
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
|
BOOL WINAPI
|
||||||
StartPagePrinter(HANDLE hPrinter)
|
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
|
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
|
BOOL WINAPI
|
||||||
|
|
|
@ -10,6 +10,7 @@ list(APPEND SOURCE
|
||||||
ports.c
|
ports.c
|
||||||
precomp.h
|
precomp.h
|
||||||
printers.c
|
printers.c
|
||||||
|
printingthread.c
|
||||||
printprocessors.c
|
printprocessors.c
|
||||||
tools.c)
|
tools.c)
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,27 @@ SKIPLIST GlobalJobList;
|
||||||
static DWORD _dwLastJobID;
|
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
|
* @name _GlobalJobListCompareRoutine
|
||||||
*
|
*
|
||||||
|
@ -81,25 +102,23 @@ _PrinterJobListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL
|
DWORD
|
||||||
GetNextJobID(PDWORD dwJobID)
|
GetJobFilePath(PCWSTR pwszExtension, DWORD dwJobID, PWSTR pwszOutput)
|
||||||
{
|
{
|
||||||
++_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!
|
||||||
|
|
||||||
while (LookupElementSkiplist(&GlobalJobList, &_dwLastJobID, NULL))
|
if (pwszOutput)
|
||||||
{
|
{
|
||||||
// This ID is already taken. Try the next one.
|
CopyMemory(pwszOutput, wszSpoolDirectory, cchSpoolDirectory);
|
||||||
++_dwLastJobID;
|
CopyMemory(&pwszOutput[cchSpoolDirectory], wszPrintersPath, cchPrintersPath);
|
||||||
|
swprintf(&pwszOutput[cchSpoolDirectory + cchPrintersPath], L"%05lu.", dwJobID);
|
||||||
|
CopyMemory(&pwszOutput[cchSpoolDirectory + cchPrintersPath + cchSpoolerFile], pwszExtension, (cchExtension + 1) * sizeof(WCHAR));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IS_VALID_JOB_ID(_dwLastJobID))
|
return (cchSpoolDirectory + cchPrintersPath + cchSpoolerFile + cchExtension + 1) * sizeof(WCHAR);
|
||||||
{
|
|
||||||
ERR("Job ID %lu isn't valid!\n", _dwLastJobID);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
*dwJobID = _dwLastJobID;
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL
|
BOOL
|
||||||
|
@ -107,8 +126,6 @@ InitializeGlobalJobList()
|
||||||
{
|
{
|
||||||
const WCHAR wszPath[] = L"\\PRINTERS\\?????.SHD";
|
const WCHAR wszPath[] = L"\\PRINTERS\\?????.SHD";
|
||||||
const DWORD cchPath = _countof(wszPath) - 1;
|
const DWORD cchPath = _countof(wszPath) - 1;
|
||||||
const DWORD cchFolders = sizeof("\\PRINTERS\\") - 1;
|
|
||||||
const DWORD cchPattern = sizeof("?????") - 1;
|
|
||||||
|
|
||||||
DWORD dwErrorCode;
|
DWORD dwErrorCode;
|
||||||
DWORD dwJobID;
|
DWORD dwJobID;
|
||||||
|
@ -118,7 +135,7 @@ InitializeGlobalJobList()
|
||||||
WCHAR wszFullPath[MAX_PATH];
|
WCHAR wszFullPath[MAX_PATH];
|
||||||
WIN32_FIND_DATAW FindData;
|
WIN32_FIND_DATAW FindData;
|
||||||
|
|
||||||
// This one is incremented in GetNextJobID.
|
// This one is incremented in _GetNextJobID.
|
||||||
_dwLastJobID = 0;
|
_dwLastJobID = 0;
|
||||||
|
|
||||||
// Initialize an empty list for all jobs of all local printers.
|
// Initialize an empty list for all jobs of all local printers.
|
||||||
|
@ -145,6 +162,7 @@ InitializeGlobalJobList()
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Extract the Job ID and verify the file name format at the same time.
|
// 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);
|
dwJobID = wcstoul(FindData.cFileName, &p, 10);
|
||||||
if (!IS_VALID_JOB_ID(dwJobID))
|
if (!IS_VALID_JOB_ID(dwJobID))
|
||||||
continue;
|
continue;
|
||||||
|
@ -153,7 +171,7 @@ InitializeGlobalJobList()
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// This shadow file has a valid name. Construct the full path and try to load it.
|
// 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);
|
pJob = ReadJobShadowFile(wszFullPath);
|
||||||
if (!pJob)
|
if (!pJob)
|
||||||
continue;
|
continue;
|
||||||
|
@ -195,78 +213,31 @@ InitializePrinterJobList(PLOCAL_PRINTER pPrinter)
|
||||||
InitializeSkiplist(&pPrinter->JobList, DllAllocSplMem, _PrinterJobListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
|
InitializeSkiplist(&pPrinter->JobList, DllAllocSplMem, _PrinterJobListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL WINAPI
|
DWORD WINAPI
|
||||||
LocalAddJob(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
|
CreateJob(PLOCAL_PRINTER_HANDLE pPrinterHandle)
|
||||||
{
|
{
|
||||||
const WCHAR wszDoubleBackslash[] = L"\\";
|
const WCHAR wszDoubleBackslash[] = L"\\";
|
||||||
const DWORD cchDoubleBackslash = _countof(wszDoubleBackslash) - 1;
|
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 cchMachineName;
|
||||||
DWORD cchUserName;
|
DWORD cchUserName;
|
||||||
DWORD dwErrorCode;
|
DWORD dwErrorCode;
|
||||||
PBYTE p;
|
|
||||||
PLOCAL_HANDLE pHandle;
|
|
||||||
PLOCAL_JOB pJob;
|
PLOCAL_JOB pJob;
|
||||||
PLOCAL_PRINTER_HANDLE pPrinterHandle;
|
|
||||||
RPC_BINDING_HANDLE hServerBinding = NULL;
|
RPC_BINDING_HANDLE hServerBinding = NULL;
|
||||||
RPC_WSTR pwszBinding = NULL;
|
RPC_WSTR pwszBinding = NULL;
|
||||||
RPC_WSTR pwszMachineName = 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.
|
// Create a new job.
|
||||||
pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
|
pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
|
||||||
if (!pJob)
|
if (!pJob)
|
||||||
{
|
{
|
||||||
dwErrorCode = GetLastError();
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
||||||
ERR("DllAllocSplMem failed with error %lu!\n", dwErrorCode);
|
ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reserve an ID for this job.
|
// Reserve an ID for this job.
|
||||||
if (!GetNextJobID(&pJob->dwJobID))
|
if (!_GetNextJobID(&pJob->dwJobID))
|
||||||
{
|
{
|
||||||
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
|
@ -339,21 +310,18 @@ LocalAddJob(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcb
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a proper ADDJOB_INFO_1W structure.
|
// We were successful!
|
||||||
AddJobInfo1.JobId = pJob->dwJobID;
|
pPrinterHandle->bStartedDoc = TRUE;
|
||||||
AddJobInfo1.Path = (PWSTR)(pData + sizeof(ADDJOB_INFO_1W));
|
pPrinterHandle->pJob = pJob;
|
||||||
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);
|
|
||||||
|
|
||||||
dwErrorCode = ERROR_SUCCESS;
|
dwErrorCode = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// Don't let the cleanup routine free this.
|
||||||
|
pJob = NULL;
|
||||||
|
|
||||||
Cleanup:
|
Cleanup:
|
||||||
|
if (pJob)
|
||||||
|
DllFreeSplMem(pJob);
|
||||||
|
|
||||||
if (pwszMachineName)
|
if (pwszMachineName)
|
||||||
RpcStringFreeW(&pwszMachineName);
|
RpcStringFreeW(&pwszMachineName);
|
||||||
|
|
||||||
|
@ -363,6 +331,72 @@ Cleanup:
|
||||||
if (hServerBinding)
|
if (hServerBinding)
|
||||||
RpcBindingFree(&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);
|
SetLastError(dwErrorCode);
|
||||||
return (dwErrorCode == ERROR_SUCCESS);
|
return (dwErrorCode == ERROR_SUCCESS);
|
||||||
}
|
}
|
||||||
|
@ -842,9 +876,6 @@ Cleanup:
|
||||||
BOOL WINAPI
|
BOOL WINAPI
|
||||||
LocalSetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command)
|
LocalSetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command)
|
||||||
{
|
{
|
||||||
const WCHAR wszFolder[] = L"\\PRINTERS\\";
|
|
||||||
const DWORD cchFolder = _countof(wszFolder) - 1;
|
|
||||||
|
|
||||||
DWORD dwErrorCode;
|
DWORD dwErrorCode;
|
||||||
PLOCAL_HANDLE pHandle;
|
PLOCAL_HANDLE pHandle;
|
||||||
PLOCAL_JOB pJob;
|
PLOCAL_JOB pJob;
|
||||||
|
@ -883,18 +914,34 @@ LocalSetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Com
|
||||||
if (dwErrorCode != ERROR_SUCCESS)
|
if (dwErrorCode != ERROR_SUCCESS)
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
|
|
||||||
// Construct the full path to the shadow file.
|
// If we do spooled printing, the job information is written down into a shadow file.
|
||||||
CopyMemory(wszFullPath, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR));
|
if (!(pPrinterHandle->pPrinter->dwAttributes & PRINTER_ATTRIBUTE_DIRECT))
|
||||||
CopyMemory(&wszFullPath[cchSpoolDirectory], wszFolder, cchFolder * sizeof(WCHAR));
|
{
|
||||||
swprintf(&wszFullPath[cchSpoolDirectory + cchFolder], L"%05lu.SHD", JobId);
|
// Write the job data into the shadow file.
|
||||||
|
GetJobFilePath(L"SHD", JobId, wszFullPath);
|
||||||
// Write the job data into the shadow file.
|
WriteJobShadowFile(wszFullPath, pJob);
|
||||||
WriteJobShadowFile(wszFullPath, pJob);
|
}
|
||||||
|
|
||||||
// Perform an additional command if desired.
|
// Perform an additional command if desired.
|
||||||
if (Command)
|
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;
|
dwErrorCode = ERROR_SUCCESS;
|
||||||
|
@ -1003,18 +1050,15 @@ Cleanup:
|
||||||
BOOL WINAPI
|
BOOL WINAPI
|
||||||
LocalScheduleJob(HANDLE hPrinter, DWORD dwJobID)
|
LocalScheduleJob(HANDLE hPrinter, DWORD dwJobID)
|
||||||
{
|
{
|
||||||
const WCHAR wszFolder[] = L"\\PRINTERS\\";
|
|
||||||
const DWORD cchFolder = _countof(wszFolder) - 1;
|
|
||||||
|
|
||||||
DWORD dwAttributes;
|
DWORD dwAttributes;
|
||||||
DWORD dwErrorCode;
|
DWORD dwErrorCode;
|
||||||
PLOCAL_HANDLE pHandle;
|
HANDLE hThread;
|
||||||
PLOCAL_JOB pJob;
|
PLOCAL_JOB pJob;
|
||||||
|
PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
|
||||||
PLOCAL_PRINTER_HANDLE pPrinterHandle;
|
PLOCAL_PRINTER_HANDLE pPrinterHandle;
|
||||||
WCHAR wszFullPath[MAX_PATH];
|
WCHAR wszFullPath[MAX_PATH];
|
||||||
|
|
||||||
// Check if this is a printer handle.
|
// Check if this is a printer handle.
|
||||||
pHandle = (PLOCAL_HANDLE)hPrinter;
|
|
||||||
if (pHandle->HandleType != HandleType_Printer)
|
if (pHandle->HandleType != HandleType_Printer)
|
||||||
{
|
{
|
||||||
dwErrorCode = ERROR_INVALID_HANDLE;
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
||||||
|
@ -1031,10 +1075,15 @@ LocalScheduleJob(HANDLE hPrinter, DWORD dwJobID)
|
||||||
goto Cleanup;
|
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.
|
// Construct the full path to the spool file.
|
||||||
CopyMemory(wszFullPath, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR));
|
GetJobFilePath(L"SPL", dwJobID, wszFullPath);
|
||||||
CopyMemory(&wszFullPath[cchSpoolDirectory], wszFolder, cchFolder * sizeof(WCHAR));
|
|
||||||
swprintf(&wszFullPath[cchSpoolDirectory + cchFolder], L"%05lu.SPL", dwJobID);
|
|
||||||
|
|
||||||
// Check if it exists.
|
// Check if it exists.
|
||||||
dwAttributes = GetFileAttributesW(wszFullPath);
|
dwAttributes = GetFileAttributesW(wszFullPath);
|
||||||
|
@ -1052,6 +1101,19 @@ LocalScheduleJob(HANDLE hPrinter, DWORD dwJobID)
|
||||||
wcscpy(wcsrchr(wszFullPath, L'.'), L".SHD");
|
wcscpy(wcsrchr(wszFullPath, L'.'), L".SHD");
|
||||||
WriteJobShadowFile(wszFullPath, pJob);
|
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;
|
dwErrorCode = ERROR_SUCCESS;
|
||||||
|
|
||||||
Cleanup:
|
Cleanup:
|
||||||
|
@ -1134,7 +1196,7 @@ ReadJobShadowFile(PCWSTR pwszFilePath)
|
||||||
pJob->dwPriority = pShadowFile->dwPriority;
|
pJob->dwPriority = pShadowFile->dwPriority;
|
||||||
pJob->dwStartTime = pShadowFile->dwStartTime;
|
pJob->dwStartTime = pShadowFile->dwStartTime;
|
||||||
pJob->dwTotalPages = pShadowFile->dwTotalPages;
|
pJob->dwTotalPages = pShadowFile->dwTotalPages;
|
||||||
pJob->dwUntilTime = pShadowFile->dwUntilTime;
|
pJob->dwUntilTime = pShadowFile->dwUntilTime;
|
||||||
pJob->pPrinter = pPrinter;
|
pJob->pPrinter = pPrinter;
|
||||||
pJob->pPrintProcessor = pPrintProcessor;
|
pJob->pPrintProcessor = pPrintProcessor;
|
||||||
pJob->pDevMode = DuplicateDevMode((PDEVMODEW)((ULONG_PTR)pShadowFile + pShadowFile->offDevMode));
|
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));
|
pJob->pwszUserName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offUserName));
|
||||||
CopyMemory(&pJob->stSubmitted, &pShadowFile->stSubmitted, sizeof(SYSTEMTIME));
|
CopyMemory(&pJob->stSubmitted, &pShadowFile->stSubmitted, sizeof(SYSTEMTIME));
|
||||||
|
|
||||||
|
// Jobs read from shadow files were always added using AddJob.
|
||||||
|
pJob->bAddedJob = TRUE;
|
||||||
|
|
||||||
pReturnValue = pJob;
|
pReturnValue = pJob;
|
||||||
|
|
||||||
Cleanup:
|
Cleanup:
|
||||||
|
@ -1303,15 +1368,38 @@ Cleanup:
|
||||||
return bReturnValue;
|
return bReturnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL
|
void
|
||||||
FreeJob(PLOCAL_JOB pJob)
|
FreeJob(PLOCAL_JOB pJob)
|
||||||
{
|
{
|
||||||
////////// TODO /////////
|
PWSTR pwszSHDFile;
|
||||||
/// Add some checks
|
|
||||||
|
// 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->pwszDatatype);
|
||||||
DllFreeSplStr(pJob->pwszDocumentName);
|
DllFreeSplStr(pJob->pwszDocumentName);
|
||||||
DllFreeSplStr(pJob->pwszOutputFile);
|
DllFreeSplStr(pJob->pwszMachineName);
|
||||||
DllFreeSplMem(pJob);
|
DllFreeSplStr(pJob->pwszNotifyName);
|
||||||
|
DllFreeSplStr(pJob->pwszUserName);
|
||||||
|
|
||||||
return TRUE;
|
// Free memory for the optional fields if they are present.
|
||||||
|
if (pJob->pwszOutputFile)
|
||||||
|
DllFreeSplStr(pJob->pwszOutputFile);
|
||||||
|
|
||||||
|
if (pJob->pwszPrintProcessorParameters)
|
||||||
|
DllFreeSplStr(pJob->pwszPrintProcessorParameters);
|
||||||
|
|
||||||
|
if (pJob->pwszStatus)
|
||||||
|
DllFreeSplStr(pJob->pwszStatus);
|
||||||
|
|
||||||
|
// Finally free the job structure itself.
|
||||||
|
DllFreeSplMem(pJob);
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ static const PRINTPROVIDOR _PrintProviderFunctions = {
|
||||||
LocalWritePrinter, // fpWritePrinter
|
LocalWritePrinter, // fpWritePrinter
|
||||||
LocalEndPagePrinter, // fpEndPagePrinter
|
LocalEndPagePrinter, // fpEndPagePrinter
|
||||||
NULL, // fpAbortPrinter
|
NULL, // fpAbortPrinter
|
||||||
NULL, // fpReadPrinter
|
LocalReadPrinter, // fpReadPrinter
|
||||||
LocalEndDocPrinter, // fpEndDocPrinter
|
LocalEndDocPrinter, // fpEndDocPrinter
|
||||||
LocalAddJob, // fpAddJob
|
LocalAddJob, // fpAddJob
|
||||||
LocalScheduleJob, // fpScheduleJob
|
LocalScheduleJob, // fpScheduleJob
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
static LIST_ENTRY _PortList;
|
static LIST_ENTRY _PortList;
|
||||||
|
|
||||||
|
|
||||||
PLOCAL_PRINT_MONITOR
|
PLOCAL_PORT
|
||||||
FindPrintMonitorByPort(PCWSTR pwszName)
|
FindPort(PCWSTR pwszName)
|
||||||
{
|
{
|
||||||
PLIST_ENTRY pEntry;
|
PLIST_ENTRY pEntry;
|
||||||
PLOCAL_PORT pPort;
|
PLOCAL_PORT pPort;
|
||||||
|
@ -22,7 +22,7 @@ FindPrintMonitorByPort(PCWSTR pwszName)
|
||||||
pPort = CONTAINING_RECORD(pEntry, LOCAL_PORT, Entry);
|
pPort = CONTAINING_RECORD(pEntry, LOCAL_PORT, Entry);
|
||||||
|
|
||||||
if (_wcsicmp(pPort->pwszName, pwszName) == 0)
|
if (_wcsicmp(pPort->pwszName, pwszName) == 0)
|
||||||
return pPort->pPrintMonitor;
|
return pPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -49,6 +49,32 @@ typedef LPMONITOREX(WINAPI *PInitializePrintMonitor)(PWSTR);
|
||||||
typedef LPMONITOR2(WINAPI *PInitializePrintMonitor2)(PMONITORINIT, PHANDLE);
|
typedef LPMONITOR2(WINAPI *PInitializePrintMonitor2)(PMONITORINIT, PHANDLE);
|
||||||
|
|
||||||
// Structures
|
// 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.
|
* Describes a Print Processor.
|
||||||
*/
|
*/
|
||||||
|
@ -83,6 +109,7 @@ typedef struct _LOCAL_PRINTER
|
||||||
PWSTR pwszDefaultDatatype;
|
PWSTR pwszDefaultDatatype;
|
||||||
PDEVMODEW pDefaultDevMode;
|
PDEVMODEW pDefaultDevMode;
|
||||||
PLOCAL_PRINT_PROCESSOR pPrintProcessor;
|
PLOCAL_PRINT_PROCESSOR pPrintProcessor;
|
||||||
|
PLOCAL_PORT pPort;
|
||||||
SKIPLIST JobList;
|
SKIPLIST JobList;
|
||||||
}
|
}
|
||||||
LOCAL_PRINTER, *PLOCAL_PRINTER;
|
LOCAL_PRINTER, *PLOCAL_PRINTER;
|
||||||
|
@ -94,26 +121,28 @@ LOCAL_PRINTER, *PLOCAL_PRINTER;
|
||||||
typedef struct _LOCAL_JOB
|
typedef struct _LOCAL_JOB
|
||||||
{
|
{
|
||||||
// This sort key must be the first element for LookupElementSkiplist to work!
|
// 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
|
BOOL bAddedJob : 1; /** Whether AddJob has already been called on this Job. */
|
||||||
PLOCAL_PRINT_PROCESSOR pPrintProcessor; // Associated Print Processor to this Job
|
HANDLE hPrintProcessor; /** Handle returned by OpenPrintProcessor while the Job is printing. */
|
||||||
DWORD dwPriority; // Priority of this Job from MIN_PRIORITY to MAX_PRIORITY, default being DEF_PRIORITY
|
PLOCAL_PRINTER pPrinter; /** Associated Printer to this Job */
|
||||||
SYSTEMTIME stSubmitted; // Time of the submission of this Job
|
PLOCAL_PRINT_PROCESSOR pPrintProcessor; /** Associated Print Processor to this Job */
|
||||||
PWSTR pwszUserName; // User that submitted the Job
|
DWORD dwPriority; /** Priority of this Job from MIN_PRIORITY to MAX_PRIORITY, default being DEF_PRIORITY */
|
||||||
PWSTR pwszNotifyName; // User that shall be notified about the status of the Job
|
SYSTEMTIME stSubmitted; /** Time of the submission of this Job */
|
||||||
PWSTR pwszDocumentName; // Name of the Document that is printed
|
PWSTR pwszUserName; /** User that submitted the Job */
|
||||||
PWSTR pwszDatatype; // Datatype of the Document
|
PWSTR pwszNotifyName; /** User that shall be notified about the status of the Job */
|
||||||
PWSTR pwszOutputFile; // Output File to spool the Job to
|
PWSTR pwszDocumentName; /** Name of the Document that is printed */
|
||||||
PWSTR pwszPrintProcessorParameters; // Optional; Parameters for the chosen Print Processor
|
PWSTR pwszDatatype; /** Datatype of the Document */
|
||||||
PWSTR pwszStatus; // Optional; a Status Message for the Job
|
PWSTR pwszOutputFile; /** Output File to spool the Job to */
|
||||||
DWORD dwTotalPages; // Total pages of the Document
|
PWSTR pwszPrintProcessorParameters; /** Optional; Parameters for the chosen Print Processor */
|
||||||
DWORD dwPagesPrinted; // Number of pages that have already been printed
|
PWSTR pwszStatus; /** Optional; a Status Message for the Job */
|
||||||
DWORD dwStartTime; // Earliest time in minutes since 12:00 AM UTC when this document can be printed
|
DWORD dwTotalPages; /** Total pages of the Document */
|
||||||
DWORD dwUntilTime; // Latest time in minutes since 12:00 AM UTC when this document can be printed
|
DWORD dwPagesPrinted; /** Number of pages that have already been printed */
|
||||||
DWORD dwStatus; // JOB_STATUS_* flags of the Job
|
DWORD dwStartTime; /** Earliest time in minutes since 12:00 AM UTC when this document can be printed */
|
||||||
PWSTR pwszMachineName; // Name of the machine that submitted the Job (prepended with two backslashes)
|
DWORD dwUntilTime; /** Latest time in minutes since 12:00 AM UTC when this document can be printed */
|
||||||
PDEVMODEW pDevMode; // Associated Device Mode to this Job
|
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;
|
LOCAL_JOB, *PLOCAL_JOB;
|
||||||
|
|
||||||
|
@ -126,8 +155,10 @@ LOCAL_JOB, *PLOCAL_JOB;
|
||||||
*/
|
*/
|
||||||
typedef struct _LOCAL_PRINTER_HANDLE
|
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_PRINTER pPrinter;
|
||||||
PLOCAL_JOB pStartedJob;
|
PLOCAL_JOB pJob;
|
||||||
PWSTR pwszDatatype;
|
PWSTR pwszDatatype;
|
||||||
PDEVMODEW pDevMode;
|
PDEVMODEW pDevMode;
|
||||||
}
|
}
|
||||||
|
@ -149,32 +180,6 @@ typedef struct _LOCAL_HANDLE
|
||||||
}
|
}
|
||||||
LOCAL_HANDLE, *PLOCAL_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)
|
* Describes the header of a print job serialized into a shadow file (.SHD)
|
||||||
* Documented in http://www.undocprint.org/formats/winspool/shd
|
* Documented in http://www.undocprint.org/formats/winspool/shd
|
||||||
|
@ -213,10 +218,11 @@ typedef struct _SHD_HEADER
|
||||||
}
|
}
|
||||||
SHD_HEADER, *PSHD_HEADER;
|
SHD_HEADER, *PSHD_HEADER;
|
||||||
|
|
||||||
|
|
||||||
// jobs.c
|
// jobs.c
|
||||||
extern SKIPLIST GlobalJobList;
|
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();
|
BOOL InitializeGlobalJobList();
|
||||||
void InitializePrinterJobList(PLOCAL_PRINTER pPrinter);
|
void InitializePrinterJobList(PLOCAL_PRINTER pPrinter);
|
||||||
BOOL WINAPI LocalAddJob(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded);
|
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);
|
BOOL WINAPI LocalEnumMonitors(PWSTR pName, DWORD Level, PBYTE pMonitors, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned);
|
||||||
|
|
||||||
// ports.c
|
// ports.c
|
||||||
PLOCAL_PRINT_MONITOR FindPrintMonitorByPort(PCWSTR pwszName);
|
PLOCAL_PORT FindPort(PCWSTR pwszName);
|
||||||
BOOL InitializePortList();
|
BOOL InitializePortList();
|
||||||
BOOL WINAPI LocalEnumPorts(PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned);
|
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 InitializePrinterList();
|
||||||
BOOL WINAPI LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned);
|
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 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);
|
DWORD WINAPI LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo);
|
||||||
BOOL WINAPI LocalStartPagePrinter(HANDLE hPrinter);
|
BOOL WINAPI LocalStartPagePrinter(HANDLE hPrinter);
|
||||||
BOOL WINAPI LocalWritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten);
|
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 LocalEndDocPrinter(HANDLE hPrinter);
|
||||||
BOOL WINAPI LocalClosePrinter(HANDLE hPrinter);
|
BOOL WINAPI LocalClosePrinter(HANDLE hPrinter);
|
||||||
|
|
||||||
|
// printingthread.c
|
||||||
|
DWORD WINAPI PrintingThreadProc(PLOCAL_JOB pJob);
|
||||||
|
|
||||||
// printprocessors.c
|
// printprocessors.c
|
||||||
BOOL FindDatatype(const PLOCAL_PRINT_PROCESSOR pPrintProcessor, PCWSTR pwszDatatype);
|
BOOL FindDatatype(const PLOCAL_PRINT_PROCESSOR pPrintProcessor, PCWSTR pwszDatatype);
|
||||||
PLOCAL_PRINT_PROCESSOR FindPrintProcessor(PCWSTR pwszName);
|
PLOCAL_PRINT_PROCESSOR FindPrintProcessor(PCWSTR pwszName);
|
||||||
|
|
|
@ -45,8 +45,10 @@ InitializePrinterList()
|
||||||
DWORD i;
|
DWORD i;
|
||||||
HKEY hKey = NULL;
|
HKEY hKey = NULL;
|
||||||
HKEY hSubKey = NULL;
|
HKEY hSubKey = NULL;
|
||||||
|
PLOCAL_PORT pPort;
|
||||||
PLOCAL_PRINTER pPrinter = NULL;
|
PLOCAL_PRINTER pPrinter = NULL;
|
||||||
PLOCAL_PRINT_PROCESSOR pPrintProcessor;
|
PLOCAL_PRINT_PROCESSOR pPrintProcessor;
|
||||||
|
PWSTR pwszPort = NULL;
|
||||||
PWSTR pwszPrintProcessor = NULL;
|
PWSTR pwszPrintProcessor = NULL;
|
||||||
WCHAR wszPrinterName[MAX_PRINTER_NAME + 1];
|
WCHAR wszPrinterName[MAX_PRINTER_NAME + 1];
|
||||||
|
|
||||||
|
@ -141,6 +143,19 @@ InitializePrinterList()
|
||||||
continue;
|
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.
|
// Create a new LOCAL_PRINTER structure for it.
|
||||||
pPrinter = DllAllocSplMem(sizeof(LOCAL_PRINTER));
|
pPrinter = DllAllocSplMem(sizeof(LOCAL_PRINTER));
|
||||||
if (!pPrinter)
|
if (!pPrinter)
|
||||||
|
@ -488,10 +503,12 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
|
||||||
PWSTR pwszSecondParameter = NULL;
|
PWSTR pwszSecondParameter = NULL;
|
||||||
PLOCAL_JOB pJob;
|
PLOCAL_JOB pJob;
|
||||||
PLOCAL_HANDLE pHandle = NULL;
|
PLOCAL_HANDLE pHandle = NULL;
|
||||||
|
PLOCAL_PORT pPort;
|
||||||
PLOCAL_PRINT_MONITOR pPrintMonitor;
|
PLOCAL_PRINT_MONITOR pPrintMonitor;
|
||||||
PLOCAL_PRINTER pPrinter;
|
PLOCAL_PRINTER pPrinter;
|
||||||
PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL;
|
PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL;
|
||||||
WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
|
WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
|
||||||
|
WCHAR wszFullPath[MAX_PATH];
|
||||||
|
|
||||||
// TODO: lpPrinterName == NULL is supported and means access to the local printer server.
|
// 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.
|
// 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;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*phPrinter = NULL;
|
||||||
|
|
||||||
// Skip any server name in the first parameter.
|
// Skip any server name in the first parameter.
|
||||||
// Does lpPrinterName begin with two backslashes to indicate a server name?
|
// Does lpPrinterName begin with two backslashes to indicate a server name?
|
||||||
if (lpPrinterName[0] == L'\\' && lpPrinterName[1] == L'\\')
|
if (lpPrinterName[0] == L'\\' && lpPrinterName[1] == L'\\')
|
||||||
|
@ -592,14 +611,16 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
|
||||||
// "\\COMPUTERNAME\LPT1:, Port"
|
// "\\COMPUTERNAME\LPT1:, Port"
|
||||||
|
|
||||||
// Look for this port in our Print Monitor Port list.
|
// Look for this port in our Print Monitor Port list.
|
||||||
pPrintMonitor = FindPrintMonitorByPort(pwszFirstParameter);
|
pPort = FindPort(pwszFirstParameter);
|
||||||
if (!pPrintMonitor)
|
if (!pPort)
|
||||||
{
|
{
|
||||||
// The supplied port is unknown to all our Print Monitors.
|
// The supplied port is unknown to all our Print Monitors.
|
||||||
dwErrorCode = ERROR_INVALID_PRINTER_NAME;
|
dwErrorCode = ERROR_INVALID_PRINTER_NAME;
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pPrintMonitor = pPort->pPrintMonitor;
|
||||||
|
|
||||||
// Call the monitor's OpenPort function.
|
// Call the monitor's OpenPort function.
|
||||||
if (pPrintMonitor->bIsLevel2)
|
if (pPrintMonitor->bIsLevel2)
|
||||||
bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnOpenPort(pPrintMonitor->hMonitor, pwszFirstParameter, &hExternalHandle);
|
bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnOpenPort(pPrintMonitor->hMonitor, pwszFirstParameter, &hExternalHandle);
|
||||||
|
@ -649,13 +670,15 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
|
||||||
pwszSecondParameter += 5;
|
pwszSecondParameter += 5;
|
||||||
|
|
||||||
// Look for this port in our Print Monitor Port list.
|
// Look for this port in our Print Monitor Port list.
|
||||||
pPrintMonitor = FindPrintMonitorByPort(pwszSecondParameter);
|
pPort = FindPort(pwszFirstParameter);
|
||||||
if (!pPrintMonitor)
|
if (!pPort)
|
||||||
{
|
{
|
||||||
// The supplied port is unknown to all our Print Monitors.
|
// The supplied port is unknown to all our Print Monitors.
|
||||||
dwErrorCode = ERROR_INVALID_PRINTER_NAME;
|
dwErrorCode = ERROR_INVALID_PRINTER_NAME;
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pPrintMonitor = pPort->pPrintMonitor;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -706,6 +729,7 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pPrinterHandle->hSPLFile = INVALID_HANDLE_VALUE;
|
||||||
pPrinterHandle->pPrinter = pPrinter;
|
pPrinterHandle->pPrinter = pPrinter;
|
||||||
|
|
||||||
// Check if a datatype was given.
|
// Check if a datatype was given.
|
||||||
|
@ -732,7 +756,7 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
|
||||||
else
|
else
|
||||||
pPrinterHandle->pDevMode = DuplicateDevMode(pPrinter->pDefaultDevMode);
|
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)
|
if (pwszSecondParameter)
|
||||||
{
|
{
|
||||||
// The "Job " string has to follow now.
|
// The "Job " string has to follow now.
|
||||||
|
@ -767,7 +791,19 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
|
||||||
goto Cleanup;
|
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.
|
// Return the Printer handle through our general handle.
|
||||||
|
@ -805,32 +841,15 @@ Cleanup:
|
||||||
return (dwErrorCode == ERROR_SUCCESS);
|
return (dwErrorCode == ERROR_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD WINAPI
|
BOOL WINAPI
|
||||||
LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
|
LocalReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
|
||||||
{
|
{
|
||||||
DWORD dwErrorCode;
|
DWORD dwErrorCode;
|
||||||
DWORD dwReturnValue = 0;
|
PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
|
||||||
PDOC_INFO_1W pDocumentInfo1;
|
|
||||||
PLOCAL_HANDLE pHandle;
|
|
||||||
PLOCAL_JOB pJob;
|
|
||||||
PLOCAL_PRINTER_HANDLE pPrinterHandle;
|
PLOCAL_PRINTER_HANDLE pPrinterHandle;
|
||||||
|
|
||||||
// Sanity checks
|
// Sanity checks.
|
||||||
if (!pDocInfo)
|
if (!pHandle || pHandle->HandleType != HandleType_Printer)
|
||||||
{
|
|
||||||
dwErrorCode = ERROR_INVALID_PARAMETER;
|
|
||||||
goto Cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hPrinter)
|
|
||||||
{
|
|
||||||
dwErrorCode = ERROR_INVALID_HANDLE;
|
|
||||||
goto Cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this is a printer handle.
|
|
||||||
pHandle = (PLOCAL_HANDLE)hPrinter;
|
|
||||||
if (pHandle->HandleType != HandleType_Printer)
|
|
||||||
{
|
{
|
||||||
dwErrorCode = ERROR_INVALID_HANDLE;
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
|
@ -838,6 +857,67 @@ LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
|
||||||
|
|
||||||
pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass the parameters to ReadFile.
|
||||||
|
if (!ReadFile(pPrinterHandle->hSPLFile, pBuf, cbBuf, pNoBytesRead, NULL))
|
||||||
|
{
|
||||||
|
dwErrorCode = GetLastError();
|
||||||
|
ERR("ReadFile failed with error %lu!\n", dwErrorCode);
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
dwErrorCode = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
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_PARAMETER;
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pHandle || pHandle->HandleType != HandleType_Printer)
|
||||||
|
{
|
||||||
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
|
||||||
|
|
||||||
|
// pJob may already be occupied if this is a Print Job handle. In this case, StartDocPrinter has to fail.
|
||||||
|
if (pPrinterHandle->pJob)
|
||||||
|
{
|
||||||
|
dwErrorCode = ERROR_INVALID_PARAMETER;
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the validity of the datatype if we got one.
|
||||||
|
if (pDocInfo1->pDatatype && !FindDatatype(pPrinterHandle->pJob->pPrintProcessor, pDocInfo1->pDatatype))
|
||||||
|
{
|
||||||
|
dwErrorCode = ERROR_INVALID_DATATYPE;
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if this is the right document information level.
|
// Check if this is the right document information level.
|
||||||
if (Level != 1)
|
if (Level != 1)
|
||||||
{
|
{
|
||||||
|
@ -845,68 +925,30 @@ LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
pDocumentInfo1 = (PDOC_INFO_1W)pDocInfo;
|
// All requirements are met - create a new job.
|
||||||
|
dwErrorCode = CreateJob(pPrinterHandle);
|
||||||
|
if (dwErrorCode != ERROR_SUCCESS)
|
||||||
|
goto Cleanup;
|
||||||
|
|
||||||
// Create a new job.
|
// Use any given datatype.
|
||||||
pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
|
if (pDocInfo1->pDatatype && !ReallocSplStr(&pPrinterHandle->pJob->pwszDatatype, pDocInfo1->pDatatype))
|
||||||
pJob->pPrinter = pPrinterHandle->pPrinter;
|
|
||||||
pJob->dwPriority = DEF_PRIORITY;
|
|
||||||
|
|
||||||
// Check if a datatype was given.
|
|
||||||
if (pDocumentInfo1->pDatatype)
|
|
||||||
{
|
|
||||||
// Use the datatype if it's valid.
|
|
||||||
if (!FindDatatype(pJob->pPrintProcessor, pDocumentInfo1->pDatatype))
|
|
||||||
{
|
|
||||||
dwErrorCode = ERROR_INVALID_DATATYPE;
|
|
||||||
goto Cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
pJob->pwszDatatype = AllocSplStr(pDocumentInfo1->pDatatype);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Use the printer handle datatype.
|
|
||||||
pJob->pwszDatatype = AllocSplStr(pPrinterHandle->pwszDatatype);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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))
|
|
||||||
{
|
{
|
||||||
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
||||||
|
ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the job to the Global Job List.
|
// Use any given document name.
|
||||||
if (!InsertElementSkiplist(&GlobalJobList, pJob))
|
if (pDocInfo1->pDocName && !ReallocSplStr(&pPrinterHandle->pJob->pwszDocumentName, pDocInfo1->pDocName))
|
||||||
{
|
{
|
||||||
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
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;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the job at the end of the Printer's Job List.
|
// We were successful!
|
||||||
// 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))
|
|
||||||
{
|
|
||||||
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
|
|
||||||
goto Cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
pPrinterHandle->pStartedJob = pJob;
|
|
||||||
dwErrorCode = ERROR_SUCCESS;
|
dwErrorCode = ERROR_SUCCESS;
|
||||||
dwReturnValue = pJob->dwJobID;
|
dwReturnValue = pPrinterHandle->pJob->dwJobID;
|
||||||
|
|
||||||
Cleanup:
|
Cleanup:
|
||||||
SetLastError(dwErrorCode);
|
SetLastError(dwErrorCode);
|
||||||
|
@ -916,48 +958,170 @@ Cleanup:
|
||||||
BOOL WINAPI
|
BOOL WINAPI
|
||||||
LocalStartPagePrinter(HANDLE hPrinter)
|
LocalStartPagePrinter(HANDLE hPrinter)
|
||||||
{
|
{
|
||||||
///////////// TODO /////////////////////
|
DWORD dwErrorCode;
|
||||||
return FALSE;
|
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
|
BOOL WINAPI
|
||||||
LocalWritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
|
LocalWritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
|
||||||
{
|
{
|
||||||
///////////// TODO /////////////////////
|
DWORD dwErrorCode;
|
||||||
return FALSE;
|
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
|
BOOL WINAPI
|
||||||
LocalEndPagePrinter(HANDLE hPrinter)
|
LocalEndPagePrinter(HANDLE hPrinter)
|
||||||
{
|
{
|
||||||
///////////// TODO /////////////////////
|
DWORD dwErrorCode;
|
||||||
return FALSE;
|
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
|
BOOL WINAPI
|
||||||
LocalEndDocPrinter(HANDLE hPrinter)
|
LocalEndDocPrinter(HANDLE hPrinter)
|
||||||
{
|
{
|
||||||
///////////// TODO /////////////////////
|
DWORD dwErrorCode;
|
||||||
return FALSE;
|
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
|
BOOL WINAPI
|
||||||
LocalClosePrinter(HANDLE hPrinter)
|
LocalClosePrinter(HANDLE hPrinter)
|
||||||
{
|
{
|
||||||
PLOCAL_HANDLE pHandle;
|
PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
|
||||||
|
PLOCAL_PRINTER_HANDLE pPrinterHandle;
|
||||||
|
|
||||||
if (!hPrinter)
|
if (!pHandle)
|
||||||
{
|
{
|
||||||
SetLastError(ERROR_INVALID_HANDLE);
|
SetLastError(ERROR_INVALID_HANDLE);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
pHandle = (PLOCAL_HANDLE)hPrinter;
|
if (pHandle->HandleType == HandleType_Port)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
else if (pHandle->HandleType == HandleType_Printer)
|
||||||
|
{
|
||||||
|
pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
|
||||||
|
|
||||||
///////////// TODO /////////////////////
|
// Terminate any started job.
|
||||||
/// Check the handle type, do thoroughful checks on all data fields and clean them.
|
if (pPrinterHandle->pJob)
|
||||||
////////////////////////////////////////
|
FreeJob(pPrinterHandle->pJob);
|
||||||
|
|
||||||
|
// Free memory for the fields.
|
||||||
|
DllFreeSplMem(pPrinterHandle->pDevMode);
|
||||||
|
DllFreeSplStr(pPrinterHandle->pwszDatatype);
|
||||||
|
|
||||||
|
// Free memory for the printer handle itself.
|
||||||
|
DllFreeSplMem(pPrinterHandle);
|
||||||
|
}
|
||||||
|
else if (pHandle->HandleType == HandleType_Xcv)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free memory for the handle itself.
|
||||||
DllFreeSplMem(pHandle);
|
DllFreeSplMem(pHandle);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
119
reactos/win32ss/printing/providers/localspl/printingthread.c
Normal file
119
reactos/win32ss/printing/providers/localspl/printingthread.c
Normal file
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in a new issue