2 * PROJECT: ReactOS Local Spooler
3 * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation
4 * PURPOSE: Functions for managing print jobs
5 * COPYRIGHT: Copyright 2015 Colin Finck <colin@reactos.org>
11 SKIPLIST GlobalJobList
;
14 static DWORD _dwLastJobID
;
20 * Returns whether two strings are equal.
21 * Unlike wcscmp, this function also works with NULL strings.
24 * First string to compare.
27 * Second string to compare.
30 * TRUE if the strings are equal, FALSE if they differ.
33 _EqualStrings(PCWSTR pwszA
, PCWSTR pwszB
)
44 return (wcscmp(pwszA
, pwszB
) == 0);
48 _GetNextJobID(PDWORD dwJobID
)
52 while (LookupElementSkiplist(&GlobalJobList
, &_dwLastJobID
, NULL
))
54 // This ID is already taken. Try the next one.
58 if (!IS_VALID_JOB_ID(_dwLastJobID
))
60 ERR("Job ID %lu isn't valid!\n", _dwLastJobID
);
64 *dwJobID
= _dwLastJobID
;
69 * @name _GlobalJobListCompareRoutine
71 * SKIPLIST_COMPARE_ROUTINE for the Global Job List.
72 * We need the Global Job List to check whether a Job ID is already in use. Consequently, this list is sorted by ID.
75 _GlobalJobListCompareRoutine(PVOID FirstStruct
, PVOID SecondStruct
)
77 PLOCAL_JOB A
= (PLOCAL_JOB
)FirstStruct
;
78 PLOCAL_JOB B
= (PLOCAL_JOB
)SecondStruct
;
80 return A
->dwJobID
- B
->dwJobID
;
84 * @name _PrinterJobListCompareRoutine
86 * SKIPLIST_COMPARE_ROUTINE for the each Printer's Job List.
87 * Jobs in this list are sorted in the desired order of processing.
90 _PrinterJobListCompareRoutine(PVOID FirstStruct
, PVOID SecondStruct
)
92 PLOCAL_JOB A
= (PLOCAL_JOB
)FirstStruct
;
93 PLOCAL_JOB B
= (PLOCAL_JOB
)SecondStruct
;
95 FILETIME ftSubmittedA
;
96 FILETIME ftSubmittedB
;
97 ULARGE_INTEGER uliSubmittedA
;
98 ULARGE_INTEGER uliSubmittedB
;
101 // First compare the priorities to determine the order.
102 // The job with a higher priority shall come first.
103 iComparison
= A
->dwPriority
- B
->dwPriority
;
104 if (iComparison
!= 0)
107 // Both have the same priority, so go by creation time.
108 // Comparison is done using the MSDN-recommended way for comparing SYSTEMTIMEs.
109 if (!SystemTimeToFileTime(&A
->stSubmitted
, &ftSubmittedA
))
111 ERR("SystemTimeToFileTime failed for A with error %lu!\n", GetLastError());
115 if (!SystemTimeToFileTime(&B
->stSubmitted
, &ftSubmittedB
))
117 ERR("SystemTimeToFileTime failed for B with error %lu!\n", GetLastError());
121 uliSubmittedA
.LowPart
= ftSubmittedA
.dwLowDateTime
;
122 uliSubmittedA
.HighPart
= ftSubmittedA
.dwHighDateTime
;
123 uliSubmittedB
.LowPart
= ftSubmittedB
.dwLowDateTime
;
124 uliSubmittedB
.HighPart
= ftSubmittedB
.dwHighDateTime
;
125 ullResult
= uliSubmittedA
.QuadPart
- uliSubmittedB
.QuadPart
;
129 else if (ullResult
> 0)
136 GetJobFilePath(PCWSTR pwszExtension
, DWORD dwJobID
, PWSTR pwszOutput
)
138 const WCHAR wszPrintersPath
[] = L
"\\PRINTERS\\";
139 const DWORD cchPrintersPath
= _countof(wszPrintersPath
) - 1;
140 const DWORD cchSpoolerFile
= sizeof("?????.") - 1;
141 const DWORD cchExtension
= sizeof("SPL") - 1; // pwszExtension may be L"SPL" or L"SHD", same length for both!
145 CopyMemory(pwszOutput
, wszSpoolDirectory
, cchSpoolDirectory
* sizeof(WCHAR
));
146 CopyMemory(&pwszOutput
[cchSpoolDirectory
], wszPrintersPath
, cchPrintersPath
* sizeof(WCHAR
));
147 swprintf(&pwszOutput
[cchSpoolDirectory
+ cchPrintersPath
], L
"%05lu.", dwJobID
);
148 CopyMemory(&pwszOutput
[cchSpoolDirectory
+ cchPrintersPath
+ cchSpoolerFile
], pwszExtension
, (cchExtension
+ 1) * sizeof(WCHAR
));
151 return (cchSpoolDirectory
+ cchPrintersPath
+ cchSpoolerFile
+ cchExtension
+ 1) * sizeof(WCHAR
);
155 InitializeGlobalJobList()
157 const WCHAR wszPath
[] = L
"\\PRINTERS\\?????.SHD";
158 const DWORD cchPath
= _countof(wszPath
) - 1;
163 PLOCAL_JOB pJob
= NULL
;
165 WCHAR wszFullPath
[MAX_PATH
];
166 WIN32_FIND_DATAW FindData
;
168 // This one is incremented in _GetNextJobID.
171 // Initialize an empty list for all jobs of all local printers.
172 // We will search it by Job ID (supply a pointer to a DWORD in LookupElementSkiplist).
173 InitializeSkiplist(&GlobalJobList
, DllAllocSplMem
, _GlobalJobListCompareRoutine
, (PSKIPLIST_FREE_ROUTINE
)DllFreeSplMem
);
175 // Construct the full path search pattern.
176 CopyMemory(wszFullPath
, wszSpoolDirectory
, cchSpoolDirectory
* sizeof(WCHAR
));
177 CopyMemory(&wszFullPath
[cchSpoolDirectory
], wszPath
, (cchPath
+ 1) * sizeof(WCHAR
));
179 // Use the search pattern to look for unfinished jobs serialized in shadow files (.SHD)
180 hFind
= FindFirstFileW(wszFullPath
, &FindData
);
181 if (hFind
== INVALID_HANDLE_VALUE
)
183 // No unfinished jobs found.
184 dwErrorCode
= ERROR_SUCCESS
;
190 // Skip possible subdirectories.
191 if (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
194 // Extract the Job ID and verify the file name format at the same time.
195 // This includes all valid names (like "00005.SHD") and excludes invalid ones (like "10ABC.SHD").
196 dwJobID
= wcstoul(FindData
.cFileName
, &p
, 10);
197 if (!IS_VALID_JOB_ID(dwJobID
))
200 if (wcsicmp(p
, L
".SHD") != 0)
203 // This shadow file has a valid name. Construct the full path and try to load it.
204 GetJobFilePath(L
"SHD", dwJobID
, wszFullPath
);
205 pJob
= ReadJobShadowFile(wszFullPath
);
209 // Add it to the Global Job List.
210 if (!InsertElementSkiplist(&GlobalJobList
, pJob
))
212 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
213 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob
->dwJobID
);
217 // Add it to the Printer's Job List.
218 if (!InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
))
220 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
221 ERR("InsertElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob
->dwJobID
);
225 while (FindNextFileW(hFind
, &FindData
));
227 dwErrorCode
= ERROR_SUCCESS
;
234 SetLastError(dwErrorCode
);
235 return (dwErrorCode
== ERROR_SUCCESS
);
239 InitializePrinterJobList(PLOCAL_PRINTER pPrinter
)
241 // Initialize an empty list for this printer's jobs.
242 // This one is only for sorting the jobs. If you need to lookup a job, search the GlobalJobList by Job ID.
243 InitializeSkiplist(&pPrinter
->JobList
, DllAllocSplMem
, _PrinterJobListCompareRoutine
, (PSKIPLIST_FREE_ROUTINE
)DllFreeSplMem
);
247 CreateJob(PLOCAL_PRINTER_HANDLE pPrinterHandle
)
249 const WCHAR wszDoubleBackslash
[] = L
"\\";
250 const DWORD cchDoubleBackslash
= _countof(wszDoubleBackslash
) - 1;
252 DWORD cchMachineName
;
256 RPC_BINDING_HANDLE hServerBinding
= NULL
;
257 RPC_WSTR pwszBinding
= NULL
;
258 RPC_WSTR pwszMachineName
= NULL
;
261 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
264 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
265 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
269 // Reserve an ID for this job.
270 if (!_GetNextJobID(&pJob
->dwJobID
))
272 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
276 // Copy over defaults to the LOCAL_JOB structure.
277 pJob
->pPrinter
= pPrinterHandle
->pPrinter
;
278 pJob
->pPrintProcessor
= pPrinterHandle
->pPrinter
->pPrintProcessor
;
279 pJob
->dwPriority
= DEF_PRIORITY
;
280 pJob
->dwStatus
= JOB_STATUS_SPOOLING
;
281 pJob
->pwszDatatype
= AllocSplStr(pPrinterHandle
->pwszDatatype
);
282 pJob
->pwszDocumentName
= AllocSplStr(wszDefaultDocumentName
);
283 pJob
->pDevMode
= DuplicateDevMode(pPrinterHandle
->pDevMode
);
284 GetSystemTime(&pJob
->stSubmitted
);
286 // Get the user name for the Job.
287 cchUserName
= UNLEN
+ 1;
288 pJob
->pwszUserName
= DllAllocSplMem(cchUserName
* sizeof(WCHAR
));
289 if (!GetUserNameW(pJob
->pwszUserName
, &cchUserName
))
291 dwErrorCode
= GetLastError();
292 ERR("GetUserNameW failed with error %lu!\n", dwErrorCode
);
296 // FIXME: For now, pwszNotifyName equals pwszUserName.
297 pJob
->pwszNotifyName
= AllocSplStr(pJob
->pwszUserName
);
299 // Get the name of the machine that submitted the Job over RPC.
300 dwErrorCode
= RpcBindingServerFromClient(NULL
, &hServerBinding
);
301 if (dwErrorCode
!= RPC_S_OK
)
303 ERR("RpcBindingServerFromClient failed with status %lu!\n", dwErrorCode
);
307 dwErrorCode
= RpcBindingToStringBindingW(hServerBinding
, &pwszBinding
);
308 if (dwErrorCode
!= RPC_S_OK
)
310 ERR("RpcBindingToStringBindingW failed with status %lu!\n", dwErrorCode
);
314 dwErrorCode
= RpcStringBindingParseW(pwszBinding
, NULL
, NULL
, &pwszMachineName
, NULL
, NULL
);
315 if (dwErrorCode
!= RPC_S_OK
)
317 ERR("RpcStringBindingParseW failed with status %lu!\n", dwErrorCode
);
321 cchMachineName
= wcslen(pwszMachineName
);
322 pJob
->pwszMachineName
= DllAllocSplMem((cchMachineName
+ cchDoubleBackslash
+ 1) * sizeof(WCHAR
));
323 CopyMemory(pJob
->pwszMachineName
, wszDoubleBackslash
, cchDoubleBackslash
* sizeof(WCHAR
));
324 CopyMemory(&pJob
->pwszMachineName
[cchDoubleBackslash
], pwszMachineName
, (cchMachineName
+ 1) * sizeof(WCHAR
));
326 // Add the job to the Global Job List.
327 if (!InsertElementSkiplist(&GlobalJobList
, pJob
))
329 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
330 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob
->dwJobID
);
334 // Add the job at the end of the Printer's Job List.
335 // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
336 if (!InsertTailElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
))
338 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
339 ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob
->dwJobID
);
343 // We were successful!
344 pPrinterHandle
->bStartedDoc
= TRUE
;
345 pPrinterHandle
->pJob
= pJob
;
346 dwErrorCode
= ERROR_SUCCESS
;
348 // Don't let the cleanup routine free this.
356 RpcStringFreeW(&pwszMachineName
);
359 RpcStringFreeW(&pwszBinding
);
362 RpcBindingFree(&hServerBinding
);
368 LocalAddJob(HANDLE hPrinter
, DWORD Level
, PBYTE pData
, DWORD cbBuf
, PDWORD pcbNeeded
)
370 ADDJOB_INFO_1W AddJobInfo1
;
372 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
373 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
375 // Check if this is a printer handle.
376 if (pHandle
->HandleType
!= HandleType_Printer
)
378 dwErrorCode
= ERROR_INVALID_HANDLE
;
382 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
384 // This handle must not have started a job yet!
385 if (pPrinterHandle
->pJob
)
387 dwErrorCode
= ERROR_INVALID_HANDLE
;
391 // Check if this is the right structure level.
394 dwErrorCode
= ERROR_INVALID_LEVEL
;
398 // Check if the printer is set to do direct printing.
399 // The Job List isn't used in this case.
400 if (pPrinterHandle
->pPrinter
->dwAttributes
& PRINTER_ATTRIBUTE_DIRECT
)
402 dwErrorCode
= ERROR_INVALID_ACCESS
;
406 // Check if the supplied buffer is large enough.
407 *pcbNeeded
= sizeof(ADDJOB_INFO_1W
) + GetJobFilePath(L
"SPL", 0, NULL
);
408 if (cbBuf
< *pcbNeeded
)
410 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
414 // All requirements are met - create a new job.
415 dwErrorCode
= CreateJob(pPrinterHandle
);
416 if (dwErrorCode
!= ERROR_SUCCESS
)
419 // Mark that this job was started with AddJob (so that it can be scheduled for printing with ScheduleJob).
420 pPrinterHandle
->pJob
->bAddedJob
= TRUE
;
422 // Return a proper ADDJOB_INFO_1W structure.
423 AddJobInfo1
.JobId
= pPrinterHandle
->pJob
->dwJobID
;
424 AddJobInfo1
.Path
= (PWSTR
)(pData
+ sizeof(ADDJOB_INFO_1W
));
426 CopyMemory(pData
, &AddJobInfo1
, sizeof(ADDJOB_INFO_1W
));
427 GetJobFilePath(L
"SPL", AddJobInfo1
.JobId
, AddJobInfo1
.Path
);
430 SetLastError(dwErrorCode
);
431 return (dwErrorCode
== ERROR_SUCCESS
);
436 _LocalGetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PBYTE
* ppStart
, PBYTE
* ppEnd
, DWORD cbBuf
, PDWORD pcbNeeded
)
438 DWORD cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
439 DWORD cbDocumentName
= 0;
440 DWORD cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
441 DWORD cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
443 DWORD cbUserName
= 0;
445 JOB_INFO_1W JobInfo1
= { 0 };
447 // Calculate the lengths of the optional values.
448 if (pJob
->pwszDocumentName
)
449 cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
451 if (pJob
->pwszStatus
)
452 cbStatus
= (wcslen(pJob
->pwszStatus
) + 1) * sizeof(WCHAR
);
454 if (pJob
->pwszUserName
)
455 cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
457 // Check if the supplied buffer is large enough.
458 *pcbNeeded
+= sizeof(JOB_INFO_1W
) + cbDatatype
+ cbDocumentName
+ cbMachineName
+ cbPrinterName
+ cbStatus
+ cbUserName
;
459 if (cbBuf
< *pcbNeeded
)
461 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
465 // Put the strings at the end of the buffer.
466 *ppEnd
-= cbDatatype
;
467 JobInfo1
.pDatatype
= (PWSTR
)*ppEnd
;
468 CopyMemory(*ppEnd
, pJob
->pwszDatatype
, cbDatatype
);
470 *ppEnd
-= cbMachineName
;
471 JobInfo1
.pMachineName
= (PWSTR
)*ppEnd
;
472 CopyMemory(*ppEnd
, pJob
->pwszMachineName
, cbMachineName
);
474 *ppEnd
-= cbPrinterName
;
475 JobInfo1
.pPrinterName
= (PWSTR
)*ppEnd
;
476 CopyMemory(*ppEnd
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
478 // Copy the optional values.
481 *ppEnd
-= cbDocumentName
;
482 JobInfo1
.pDocument
= (PWSTR
)*ppEnd
;
483 CopyMemory(*ppEnd
, pJob
->pwszDocumentName
, cbDocumentName
);
489 JobInfo1
.pStatus
= (PWSTR
)*ppEnd
;
490 CopyMemory(*ppEnd
, pJob
->pwszStatus
, cbStatus
);
495 *ppEnd
-= cbUserName
;
496 JobInfo1
.pUserName
= (PWSTR
)*ppEnd
;
497 CopyMemory(*ppEnd
, pJob
->pwszUserName
, cbUserName
);
500 // Fill the rest of the structure.
501 JobInfo1
.JobId
= pJob
->dwJobID
;
502 JobInfo1
.Priority
= pJob
->dwPriority
;
503 JobInfo1
.Status
= pJob
->dwStatus
;
504 JobInfo1
.TotalPages
= pJob
->dwTotalPages
;
505 CopyMemory(&JobInfo1
.Submitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
507 // Finally copy the structure to the output pointer.
508 CopyMemory(*ppStart
, &JobInfo1
, sizeof(JOB_INFO_1W
));
509 *ppStart
+= sizeof(JOB_INFO_1W
);
510 dwErrorCode
= ERROR_SUCCESS
;
517 _LocalGetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PBYTE
* ppStart
, PBYTE
* ppEnd
, DWORD cbBuf
, PDWORD pcbNeeded
)
519 DWORD cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
520 DWORD cbDevMode
= pJob
->pDevMode
->dmSize
+ pJob
->pDevMode
->dmDriverExtra
;
521 DWORD cbDocumentName
= 0;
522 DWORD cbDriverName
= (wcslen(pJob
->pPrinter
->pwszPrinterDriver
) + 1) * sizeof(WCHAR
);
523 DWORD cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
524 DWORD cbNotifyName
= 0;
525 DWORD cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
526 DWORD cbPrintProcessor
= (wcslen(pJob
->pPrintProcessor
->pwszName
) + 1) * sizeof(WCHAR
);
527 DWORD cbPrintProcessorParameters
= 0;
529 DWORD cbUserName
= 0;
532 FILETIME ftSubmitted
;
533 JOB_INFO_2W JobInfo2
= { 0 };
534 ULARGE_INTEGER uliNow
;
535 ULARGE_INTEGER uliSubmitted
;
537 // Calculate the lengths of the optional values.
538 if (pJob
->pwszDocumentName
)
539 cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
541 if (pJob
->pwszNotifyName
)
542 cbNotifyName
= (wcslen(pJob
->pwszNotifyName
) + 1) * sizeof(WCHAR
);
544 if (pJob
->pwszPrintProcessorParameters
)
545 cbPrintProcessorParameters
= (wcslen(pJob
->pwszPrintProcessorParameters
) + 1) * sizeof(WCHAR
);
547 if (pJob
->pwszStatus
)
548 cbStatus
= (wcslen(pJob
->pwszStatus
) + 1) * sizeof(WCHAR
);
550 if (pJob
->pwszUserName
)
551 cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
553 // Check if the supplied buffer is large enough.
554 *pcbNeeded
+= sizeof(JOB_INFO_2W
) + cbDatatype
+ cbDevMode
+ cbDocumentName
+ cbDriverName
+ cbMachineName
+ cbNotifyName
+ cbPrinterName
+ cbPrintProcessor
+ cbPrintProcessorParameters
+ cbStatus
+ cbUserName
;
555 if (cbBuf
< *pcbNeeded
)
557 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
561 // Put the strings at the end of the buffer.
562 *ppEnd
-= cbDatatype
;
563 JobInfo2
.pDatatype
= (PWSTR
)*ppEnd
;
564 CopyMemory(*ppEnd
, pJob
->pwszDatatype
, cbDatatype
);
567 JobInfo2
.pDevMode
= (PDEVMODEW
)*ppEnd
;
568 CopyMemory(*ppEnd
, pJob
->pDevMode
, cbDevMode
);
570 *ppEnd
-= cbDriverName
;
571 JobInfo2
.pDriverName
= (PWSTR
)*ppEnd
;
572 CopyMemory(*ppEnd
, pJob
->pPrinter
->pwszPrinterDriver
, cbDriverName
);
574 *ppEnd
-= cbMachineName
;
575 JobInfo2
.pMachineName
= (PWSTR
)*ppEnd
;
576 CopyMemory(*ppEnd
, pJob
->pwszMachineName
, cbMachineName
);
578 *ppEnd
-= cbPrinterName
;
579 JobInfo2
.pPrinterName
= (PWSTR
)*ppEnd
;
580 CopyMemory(*ppEnd
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
582 *ppEnd
-= cbPrintProcessor
;
583 JobInfo2
.pPrintProcessor
= (PWSTR
)*ppEnd
;
584 CopyMemory(*ppEnd
, pJob
->pPrintProcessor
->pwszName
, cbPrintProcessor
);
586 // Copy the optional values.
589 *ppEnd
-= cbDocumentName
;
590 JobInfo2
.pDocument
= (PWSTR
)*ppEnd
;
591 CopyMemory(*ppEnd
, pJob
->pwszDocumentName
, cbDocumentName
);
596 *ppEnd
-= cbNotifyName
;
597 JobInfo2
.pNotifyName
= (PWSTR
)*ppEnd
;
598 CopyMemory(*ppEnd
, pJob
->pwszNotifyName
, cbNotifyName
);
601 if (cbPrintProcessorParameters
)
603 *ppEnd
-= cbPrintProcessorParameters
;
604 JobInfo2
.pParameters
= (PWSTR
)*ppEnd
;
605 CopyMemory(*ppEnd
, pJob
->pwszPrintProcessorParameters
, cbPrintProcessorParameters
);
611 JobInfo2
.pStatus
= (PWSTR
)*ppEnd
;
612 CopyMemory(*ppEnd
, pJob
->pwszStatus
, cbStatus
);
617 *ppEnd
-= cbUserName
;
618 JobInfo2
.pUserName
= (PWSTR
)*ppEnd
;
619 CopyMemory(*ppEnd
, pJob
->pwszUserName
, cbUserName
);
622 // Time in JOB_INFO_2W is the number of milliseconds elapsed since the job was submitted. Calculate this time.
623 if (!SystemTimeToFileTime(&pJob
->stSubmitted
, &ftSubmitted
))
625 ERR("SystemTimeToFileTime failed with error %lu!\n", GetLastError());
629 GetSystemTimeAsFileTime(&ftNow
);
630 uliSubmitted
.LowPart
= ftSubmitted
.dwLowDateTime
;
631 uliSubmitted
.HighPart
= ftSubmitted
.dwHighDateTime
;
632 uliNow
.LowPart
= ftNow
.dwLowDateTime
;
633 uliNow
.HighPart
= ftNow
.dwHighDateTime
;
634 JobInfo2
.Time
= (DWORD
)((uliNow
.QuadPart
- uliSubmitted
.QuadPart
) / 10000);
636 // Position in JOB_INFO_2W is the 1-based index of the job in the processing queue.
637 // Retrieve this through the element index of the job in the Printer's Job List.
638 if (!LookupElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
, &JobInfo2
.Position
))
640 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
641 ERR("pJob could not be located in the Printer's Job List!\n");
645 // Make the index 1-based.
648 // Fill the rest of the structure.
649 JobInfo2
.JobId
= pJob
->dwJobID
;
650 JobInfo2
.PagesPrinted
= pJob
->dwPagesPrinted
;
651 JobInfo2
.Priority
= pJob
->dwPriority
;
652 JobInfo2
.StartTime
= pJob
->dwStartTime
;
653 JobInfo2
.Status
= pJob
->dwStatus
;
654 JobInfo2
.TotalPages
= pJob
->dwTotalPages
;
655 JobInfo2
.UntilTime
= pJob
->dwUntilTime
;
656 CopyMemory(&JobInfo2
.Submitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
658 // Finally copy the structure to the output pointer.
659 CopyMemory(*ppStart
, &JobInfo2
, sizeof(JOB_INFO_2W
));
660 *ppStart
+= sizeof(JOB_INFO_2W
);
661 dwErrorCode
= ERROR_SUCCESS
;
668 LocalGetJob(HANDLE hPrinter
, DWORD JobId
, DWORD Level
, PBYTE pStart
, DWORD cbBuf
, LPDWORD pcbNeeded
)
671 PBYTE pEnd
= &pStart
[cbBuf
];
672 PLOCAL_HANDLE pHandle
;
674 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
676 // Check if this is a printer handle.
677 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
678 if (pHandle
->HandleType
!= HandleType_Printer
)
680 dwErrorCode
= ERROR_INVALID_HANDLE
;
684 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
686 // Get the desired job.
687 pJob
= LookupElementSkiplist(&GlobalJobList
, &JobId
, NULL
);
688 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
690 dwErrorCode
= ERROR_INVALID_PARAMETER
;
697 // The function behaves differently for each level.
699 dwErrorCode
= _LocalGetJobLevel1(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
701 dwErrorCode
= _LocalGetJobLevel2(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
703 dwErrorCode
= ERROR_INVALID_LEVEL
;
706 SetLastError(dwErrorCode
);
707 return (dwErrorCode
== ERROR_SUCCESS
);
711 _LocalSetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PJOB_INFO_1W pJobInfo
)
715 // First check the validity of the input before changing anything.
716 if (!FindDatatype(pJob
->pPrintProcessor
, pJobInfo
->pDatatype
))
718 dwErrorCode
= ERROR_INVALID_DATATYPE
;
722 // Check if the datatype has changed.
723 if (!_EqualStrings(pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
725 // Use the new value.
726 if (!ReallocSplStr(&pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
728 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
729 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
734 // Check if the document name has changed. An empty string is permitted here!
735 if (!_EqualStrings(pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
737 // Use the new value.
738 if (!ReallocSplStr(&pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
740 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
741 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
746 // Check if the status message has changed. An empty string is permitted here!
747 if (!_EqualStrings(pJob
->pwszStatus
, pJobInfo
->pStatus
))
749 // Use the new value.
750 if (!ReallocSplStr(&pJob
->pwszStatus
, pJobInfo
->pStatus
))
752 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
753 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
758 // Check if the user name has changed. An empty string is permitted here!
759 if (!_EqualStrings(pJob
->pwszUserName
, pJobInfo
->pUserName
) != 0)
761 // The new user name doesn't need to exist, so no additional verification is required.
763 // Use the new value.
764 if (!ReallocSplStr(&pJob
->pwszUserName
, pJobInfo
->pUserName
))
766 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
767 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
772 // Check if the priority has changed.
773 if (pJob
->dwPriority
!= pJobInfo
->Priority
&& IS_VALID_PRIORITY(pJobInfo
->Priority
))
775 // Set the new priority.
776 pJob
->dwPriority
= pJobInfo
->Priority
;
778 // Remove and reinsert the job in the Printer's Job List.
779 // The Compare function will be used to find the right position now considering the new priority.
780 DeleteElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
781 InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
784 // Check if the status flags have changed.
785 if (pJob
->dwStatus
!= pJobInfo
->Status
)
787 // Only add status flags that make sense.
788 if (pJobInfo
->Status
& JOB_STATUS_PAUSED
)
789 pJob
->dwStatus
|= JOB_STATUS_PAUSED
;
791 if (pJobInfo
->Status
& JOB_STATUS_ERROR
)
792 pJob
->dwStatus
|= JOB_STATUS_ERROR
;
794 if (pJobInfo
->Status
& JOB_STATUS_OFFLINE
)
795 pJob
->dwStatus
|= JOB_STATUS_OFFLINE
;
797 if (pJobInfo
->Status
& JOB_STATUS_PAPEROUT
)
798 pJob
->dwStatus
|= JOB_STATUS_PAPEROUT
;
801 dwErrorCode
= ERROR_SUCCESS
;
808 _LocalSetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PJOB_INFO_2W pJobInfo
)
811 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
813 // First check the validity of the input before changing anything.
814 pPrintProcessor
= FindPrintProcessor(pJobInfo
->pPrintProcessor
);
815 if (!pPrintProcessor
)
817 dwErrorCode
= ERROR_UNKNOWN_PRINTPROCESSOR
;
821 if (!FindDatatype(pPrintProcessor
, pJobInfo
->pDatatype
))
823 dwErrorCode
= ERROR_INVALID_DATATYPE
;
827 // Check if the datatype has changed.
828 if (!_EqualStrings(pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
830 // Use the new value.
831 if (!ReallocSplStr(&pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
833 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
834 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
839 // Check if the document name has changed. An empty string is permitted here!
840 if (!_EqualStrings(pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
842 // Use the new value.
843 if (!ReallocSplStr(&pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
845 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
846 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
851 // Check if the notify name has changed. An empty string is permitted here!
852 if (!_EqualStrings(pJob
->pwszNotifyName
, pJobInfo
->pNotifyName
))
854 // The new notify name doesn't need to exist, so no additional verification is required.
856 // Use the new value.
857 if (!ReallocSplStr(&pJob
->pwszNotifyName
, pJobInfo
->pNotifyName
))
859 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
860 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
865 // Check if the Print Processor Parameters have changed. An empty string is permitted here!
866 if (!_EqualStrings(pJob
->pwszPrintProcessorParameters
, pJobInfo
->pParameters
))
868 // Use the new value.
869 if (!ReallocSplStr(&pJob
->pwszPrintProcessorParameters
, pJobInfo
->pParameters
))
871 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
872 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
877 // Check if the Status Message has changed. An empty string is permitted here!
878 if (!_EqualStrings(pJob
->pwszStatus
, pJobInfo
->pStatus
))
880 // Use the new value.
881 if (!ReallocSplStr(&pJob
->pwszStatus
, pJobInfo
->pStatus
))
883 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
884 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
889 // Check if the user name has changed. An empty string is permitted here!
890 if (!_EqualStrings(pJob
->pwszUserName
, pJobInfo
->pUserName
))
892 // The new user name doesn't need to exist, so no additional verification is required.
894 // Use the new value.
895 if (!ReallocSplStr(&pJob
->pwszUserName
, pJobInfo
->pUserName
))
897 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
898 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
903 // Check if the priority has changed.
904 if (pJob
->dwPriority
!= pJobInfo
->Priority
&& IS_VALID_PRIORITY(pJobInfo
->Priority
))
906 // Set the new priority.
907 pJob
->dwPriority
= pJobInfo
->Priority
;
909 // Remove and reinsert the job in the Printer's Job List.
910 // The Compare function will be used to find the right position now considering the new priority.
911 DeleteElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
912 InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
915 // Check if the status flags have changed.
916 if (pJob
->dwStatus
!= pJobInfo
->Status
)
918 // Only add status flags that make sense.
919 if (pJobInfo
->Status
& JOB_STATUS_PAUSED
)
920 pJob
->dwStatus
|= JOB_STATUS_PAUSED
;
922 if (pJobInfo
->Status
& JOB_STATUS_ERROR
)
923 pJob
->dwStatus
|= JOB_STATUS_ERROR
;
925 if (pJobInfo
->Status
& JOB_STATUS_OFFLINE
)
926 pJob
->dwStatus
|= JOB_STATUS_OFFLINE
;
928 if (pJobInfo
->Status
& JOB_STATUS_PAPEROUT
)
929 pJob
->dwStatus
|= JOB_STATUS_PAPEROUT
;
932 dwErrorCode
= ERROR_SUCCESS
;
939 LocalSetJob(HANDLE hPrinter
, DWORD JobId
, DWORD Level
, PBYTE pJobInfo
, DWORD Command
)
942 PLOCAL_HANDLE pHandle
;
944 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
945 WCHAR wszFullPath
[MAX_PATH
];
947 // Check if this is a printer handle.
948 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
949 if (pHandle
->HandleType
!= HandleType_Printer
)
951 dwErrorCode
= ERROR_INVALID_HANDLE
;
955 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
957 // Get the desired job.
958 pJob
= LookupElementSkiplist(&GlobalJobList
, &JobId
, NULL
);
959 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
961 dwErrorCode
= ERROR_INVALID_PARAMETER
;
965 // Setting job information is handled differently for each level.
969 dwErrorCode
= _LocalSetJobLevel1(pPrinterHandle
, pJob
, (PJOB_INFO_1W
)pJobInfo
);
971 dwErrorCode
= _LocalSetJobLevel2(pPrinterHandle
, pJob
, (PJOB_INFO_2W
)pJobInfo
);
973 dwErrorCode
= ERROR_INVALID_LEVEL
;
976 if (dwErrorCode
!= ERROR_SUCCESS
)
979 // If we do spooled printing, the job information is written down into a shadow file.
980 if (!(pPrinterHandle
->pPrinter
->dwAttributes
& PRINTER_ATTRIBUTE_DIRECT
))
982 // Write the job data into the shadow file.
983 GetJobFilePath(L
"SHD", JobId
, wszFullPath
);
984 WriteJobShadowFile(wszFullPath
, pJob
);
987 // Perform an additional command if desired.
990 if (Command
== JOB_CONTROL_SENT_TO_PRINTER
)
992 // This indicates the end of the Print Job.
994 // Cancel the Job at the Print Processor.
995 if (pJob
->hPrintProcessor
)
996 pJob
->pPrintProcessor
->pfnControlPrintProcessor(pJob
->hPrintProcessor
, JOB_CONTROL_CANCEL
);
1000 // TODO: All open handles associated with the job need to be invalidated.
1001 // This certainly needs handle tracking...
1005 ERR("Unimplemented SetJob Command: %lu!\n", Command
);
1009 dwErrorCode
= ERROR_SUCCESS
;
1012 SetLastError(dwErrorCode
);
1013 return (dwErrorCode
== ERROR_SUCCESS
);
1017 LocalEnumJobs(HANDLE hPrinter
, DWORD FirstJob
, DWORD NoJobs
, DWORD Level
, PBYTE pStart
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
1021 PBYTE pEnd
= &pStart
[cbBuf
];
1022 PLOCAL_HANDLE pHandle
;
1024 PSKIPLIST_NODE pFirstJobNode
;
1025 PSKIPLIST_NODE pNode
;
1026 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1028 // Check if this is a printer handle.
1029 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1030 if (pHandle
->HandleType
!= HandleType_Printer
)
1032 dwErrorCode
= ERROR_INVALID_HANDLE
;
1036 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1041 dwErrorCode
= ERROR_INVALID_LEVEL
;
1049 // Lookup the node of the first job requested by the caller in the Printer's Job List.
1050 pFirstJobNode
= LookupNodeByIndexSkiplist(&pPrinterHandle
->pPrinter
->JobList
, FirstJob
);
1052 // Count the required buffer size and the number of jobs.
1054 pNode
= pFirstJobNode
;
1056 while (i
< NoJobs
&& pNode
)
1058 pJob
= (PLOCAL_JOB
)pNode
->Element
;
1060 // The function behaves differently for each level.
1062 _LocalGetJobLevel1(pPrinterHandle
, pJob
, NULL
, NULL
, 0, pcbNeeded
);
1063 else if (Level
== 2)
1064 _LocalGetJobLevel2(pPrinterHandle
, pJob
, NULL
, NULL
, 0, pcbNeeded
);
1066 // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
1068 pNode
= pNode
->Next
[0];
1071 // Check if the supplied buffer is large enough.
1072 if (cbBuf
< *pcbNeeded
)
1074 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
1078 // Begin counting again and also empty the given buffer.
1080 ZeroMemory(pStart
, cbBuf
);
1082 // Now call the same functions again to copy the actual data for each job into the buffer.
1084 pNode
= pFirstJobNode
;
1086 while (i
< NoJobs
&& pNode
)
1088 pJob
= (PLOCAL_JOB
)pNode
->Element
;
1090 // The function behaves differently for each level.
1092 dwErrorCode
= _LocalGetJobLevel1(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
1093 else if (Level
== 2)
1094 dwErrorCode
= _LocalGetJobLevel2(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
1096 if (dwErrorCode
!= ERROR_SUCCESS
)
1099 // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
1101 pNode
= pNode
->Next
[0];
1105 dwErrorCode
= ERROR_SUCCESS
;
1108 SetLastError(dwErrorCode
);
1109 return (dwErrorCode
== ERROR_SUCCESS
);
1113 LocalScheduleJob(HANDLE hPrinter
, DWORD dwJobID
)
1119 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1120 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1121 WCHAR wszFullPath
[MAX_PATH
];
1123 // Check if this is a printer handle.
1124 if (pHandle
->HandleType
!= HandleType_Printer
)
1126 dwErrorCode
= ERROR_INVALID_HANDLE
;
1130 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1132 // Check if the Job ID is valid.
1133 pJob
= LookupElementSkiplist(&GlobalJobList
, &dwJobID
, NULL
);
1134 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
1136 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1140 // Check if this Job was started with AddJob.
1141 if (!pJob
->bAddedJob
)
1143 dwErrorCode
= ERROR_SPL_NO_ADDJOB
;
1147 // Construct the full path to the spool file.
1148 GetJobFilePath(L
"SPL", dwJobID
, wszFullPath
);
1150 // Check if it exists.
1151 dwAttributes
= GetFileAttributesW(wszFullPath
);
1152 if (dwAttributes
== INVALID_FILE_ATTRIBUTES
|| dwAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1154 dwErrorCode
= ERROR_SPOOL_FILE_NOT_FOUND
;
1158 // Spooling is finished at this point.
1159 pJob
->dwStatus
&= ~JOB_STATUS_SPOOLING
;
1161 // Write the job data into the shadow file.
1162 wcscpy(wcsrchr(wszFullPath
, L
'.'), L
".SHD");
1163 WriteJobShadowFile(wszFullPath
, pJob
);
1165 // Create the thread for performing the printing process.
1166 hThread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)PrintingThreadProc
, pJob
, 0, NULL
);
1169 dwErrorCode
= GetLastError();
1170 ERR("CreateThread failed with error %lu!\n", dwErrorCode
);
1174 // We don't need the thread handle. Keeping it open blocks the thread from terminating.
1175 CloseHandle(hThread
);
1177 // ScheduleJob has done its job. The rest happens inside the thread.
1178 dwErrorCode
= ERROR_SUCCESS
;
1181 SetLastError(dwErrorCode
);
1182 return (dwErrorCode
== ERROR_SUCCESS
);
1186 ReadJobShadowFile(PCWSTR pwszFilePath
)
1190 HANDLE hFile
= INVALID_HANDLE_VALUE
;
1192 PLOCAL_JOB pReturnValue
= NULL
;
1193 PLOCAL_PRINTER pPrinter
;
1194 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
1195 PSHD_HEADER pShadowFile
= NULL
;
1196 PWSTR pwszPrinterName
;
1197 PWSTR pwszPrintProcessor
;
1199 // Try to open the file.
1200 hFile
= CreateFileW(pwszFilePath
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
1201 if (hFile
== INVALID_HANDLE_VALUE
)
1203 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1207 // Get its file size (small enough for a single DWORD) and allocate memory for all of it.
1208 cbFileSize
= GetFileSize(hFile
, NULL
);
1209 pShadowFile
= DllAllocSplMem(cbFileSize
);
1212 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1216 // Read the entire file.
1217 if (!ReadFile(hFile
, pShadowFile
, cbFileSize
, &cbRead
, NULL
))
1219 ERR("ReadFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1223 // Check signature and header size.
1224 if (pShadowFile
->dwSignature
!= SHD_WIN2003_SIGNATURE
|| pShadowFile
->cbHeader
!= sizeof(SHD_HEADER
))
1226 ERR("Signature or Header Size mismatch for file \"%S\"!\n", pwszFilePath
);
1230 // Retrieve the associated printer from the list.
1231 pwszPrinterName
= (PWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrinterName
);
1232 pPrinter
= LookupElementSkiplist(&PrinterList
, &pwszPrinterName
, NULL
);
1235 ERR("Shadow file \"%S\" references a non-existing printer \"%S\"!\n", pwszFilePath
, pwszPrinterName
);
1239 // Retrieve the associated Print Processor from the list.
1240 pwszPrintProcessor
= (PWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrintProcessor
);
1241 pPrintProcessor
= FindPrintProcessor(pwszPrintProcessor
);
1242 if (!pPrintProcessor
)
1244 ERR("Shadow file \"%S\" references a non-existing Print Processor \"%S\"!\n", pwszFilePath
, pwszPrintProcessor
);
1248 // Create a new job structure and copy over the relevant fields.
1249 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
1252 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1256 pJob
->dwJobID
= pShadowFile
->dwJobID
;
1257 pJob
->dwPriority
= pShadowFile
->dwPriority
;
1258 pJob
->dwStartTime
= pShadowFile
->dwStartTime
;
1259 pJob
->dwTotalPages
= pShadowFile
->dwTotalPages
;
1260 pJob
->dwUntilTime
= pShadowFile
->dwUntilTime
;
1261 pJob
->pPrinter
= pPrinter
;
1262 pJob
->pPrintProcessor
= pPrintProcessor
;
1263 pJob
->pDevMode
= DuplicateDevMode((PDEVMODEW
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDevMode
));
1264 pJob
->pwszDatatype
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDatatype
));
1265 pJob
->pwszMachineName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offMachineName
));
1266 CopyMemory(&pJob
->stSubmitted
, &pShadowFile
->stSubmitted
, sizeof(SYSTEMTIME
));
1268 // Copy the optional values.
1269 if (pShadowFile
->offDocumentName
)
1270 pJob
->pwszDocumentName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDocumentName
));
1272 if (pShadowFile
->offNotifyName
)
1273 pJob
->pwszNotifyName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offNotifyName
));
1275 if (pShadowFile
->offPrintProcessorParameters
)
1276 pJob
->pwszPrintProcessorParameters
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrintProcessorParameters
));
1278 if (pShadowFile
->offUserName
)
1279 pJob
->pwszUserName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offUserName
));
1281 // Jobs read from shadow files were always added using AddJob.
1282 pJob
->bAddedJob
= TRUE
;
1284 pReturnValue
= pJob
;
1288 DllFreeSplMem(pShadowFile
);
1290 if (hFile
!= INVALID_HANDLE_VALUE
)
1293 return pReturnValue
;
1297 WriteJobShadowFile(PWSTR pwszFilePath
, const PLOCAL_JOB pJob
)
1299 BOOL bReturnValue
= FALSE
;
1300 DWORD cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
1301 DWORD cbDevMode
= pJob
->pDevMode
->dmSize
+ pJob
->pDevMode
->dmDriverExtra
;
1302 DWORD cbDocumentName
= 0;
1304 DWORD cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
1305 DWORD cbNotifyName
= 0;
1306 DWORD cbPrinterDriver
= (wcslen(pJob
->pPrinter
->pwszPrinterDriver
) + 1) * sizeof(WCHAR
);
1307 DWORD cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
1308 DWORD cbPrintProcessor
= (wcslen(pJob
->pPrintProcessor
->pwszName
) + 1) * sizeof(WCHAR
);
1309 DWORD cbPrintProcessorParameters
= 0;
1310 DWORD cbUserName
= 0;
1312 DWORD dwCurrentOffset
;
1313 HANDLE hSHDFile
= INVALID_HANDLE_VALUE
;
1314 HANDLE hSPLFile
= INVALID_HANDLE_VALUE
;
1315 PSHD_HEADER pShadowFile
= NULL
;
1317 // Try to open the SHD file.
1318 hSHDFile
= CreateFileW(pwszFilePath
, GENERIC_WRITE
, FILE_SHARE_READ
, NULL
, CREATE_ALWAYS
, 0, NULL
);
1319 if (hSHDFile
== INVALID_HANDLE_VALUE
)
1321 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1325 // Calculate the lengths of the optional values and the total size of the shadow file.
1326 if (pJob
->pwszDocumentName
)
1327 cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
1329 if (pJob
->pwszNotifyName
)
1330 cbNotifyName
= (wcslen(pJob
->pwszNotifyName
) + 1) * sizeof(WCHAR
);
1332 if (pJob
->pwszPrintProcessorParameters
)
1333 cbPrintProcessorParameters
= (wcslen(pJob
->pwszPrintProcessorParameters
) + 1) * sizeof(WCHAR
);
1335 if (pJob
->pwszUserName
)
1336 cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
1338 cbFileSize
= sizeof(SHD_HEADER
) + cbDatatype
+ cbDocumentName
+ cbDevMode
+ cbMachineName
+ cbNotifyName
+ cbPrinterDriver
+ cbPrinterName
+ cbPrintProcessor
+ cbPrintProcessorParameters
+ cbUserName
;
1340 // Allocate memory for it.
1341 pShadowFile
= DllAllocSplMem(cbFileSize
);
1344 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1348 // Fill out the shadow file header information.
1349 pShadowFile
->dwSignature
= SHD_WIN2003_SIGNATURE
;
1350 pShadowFile
->cbHeader
= sizeof(SHD_HEADER
);
1353 pShadowFile
->dwJobID
= pJob
->dwJobID
;
1354 pShadowFile
->dwPriority
= pJob
->dwPriority
;
1355 pShadowFile
->dwStartTime
= pJob
->dwStartTime
;
1356 pShadowFile
->dwTotalPages
= pJob
->dwTotalPages
;
1357 pShadowFile
->dwUntilTime
= pJob
->dwUntilTime
;
1358 CopyMemory(&pShadowFile
->stSubmitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
1360 // Determine the file size of the .SPL file
1361 wcscpy(wcsrchr(pwszFilePath
, L
'.'), L
".SPL");
1362 hSPLFile
= CreateFileW(pwszFilePath
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
1363 if (hSPLFile
!= INVALID_HANDLE_VALUE
)
1364 pShadowFile
->dwSPLSize
= GetFileSize(hSPLFile
, NULL
);
1366 // Add the extra values that are stored as offsets in the shadow file.
1367 // The first value begins right after the shadow file header.
1368 dwCurrentOffset
= sizeof(SHD_HEADER
);
1370 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszDatatype
, cbDatatype
);
1371 pShadowFile
->offDatatype
= dwCurrentOffset
;
1372 dwCurrentOffset
+= cbDatatype
;
1374 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pDevMode
, cbDevMode
);
1375 pShadowFile
->offDevMode
= dwCurrentOffset
;
1376 dwCurrentOffset
+= cbDevMode
;
1378 // offDriverName is only written, but automatically determined through offPrinterName when reading.
1379 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrinter
->pwszPrinterDriver
, cbPrinterDriver
);
1380 pShadowFile
->offDriverName
= dwCurrentOffset
;
1381 dwCurrentOffset
+= cbPrinterDriver
;
1383 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszMachineName
, cbMachineName
);
1384 pShadowFile
->offMachineName
= dwCurrentOffset
;
1385 dwCurrentOffset
+= cbMachineName
;
1387 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
1388 pShadowFile
->offPrinterName
= dwCurrentOffset
;
1389 dwCurrentOffset
+= cbPrinterName
;
1391 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrintProcessor
->pwszName
, cbPrintProcessor
);
1392 pShadowFile
->offPrintProcessor
= dwCurrentOffset
;
1393 dwCurrentOffset
+= cbPrintProcessor
;
1395 // Copy the optional values.
1398 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszDocumentName
, cbDocumentName
);
1399 pShadowFile
->offDocumentName
= dwCurrentOffset
;
1400 dwCurrentOffset
+= cbDocumentName
;
1405 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszNotifyName
, cbNotifyName
);
1406 pShadowFile
->offNotifyName
= dwCurrentOffset
;
1407 dwCurrentOffset
+= cbNotifyName
;
1410 if (cbPrintProcessorParameters
)
1412 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszPrintProcessorParameters
, cbPrintProcessorParameters
);
1413 pShadowFile
->offPrintProcessorParameters
= dwCurrentOffset
;
1414 dwCurrentOffset
+= cbPrintProcessorParameters
;
1419 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszUserName
, cbUserName
);
1420 pShadowFile
->offUserName
= dwCurrentOffset
;
1421 dwCurrentOffset
+= cbUserName
;
1425 if (!WriteFile(hSHDFile
, pShadowFile
, cbFileSize
, &cbWritten
, NULL
))
1427 ERR("WriteFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1431 bReturnValue
= TRUE
;
1435 DllFreeSplMem(pShadowFile
);
1437 if (hSHDFile
!= INVALID_HANDLE_VALUE
)
1438 CloseHandle(hSHDFile
);
1440 if (hSPLFile
!= INVALID_HANDLE_VALUE
)
1441 CloseHandle(hSPLFile
);
1443 return bReturnValue
;
1447 FreeJob(PLOCAL_JOB pJob
)
1451 // Remove the Job from both Job Lists.
1452 DeleteElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
1453 DeleteElementSkiplist(&GlobalJobList
, pJob
);
1455 // Try to delete the corresponding .SHD file.
1456 pwszSHDFile
= DllAllocSplMem(GetJobFilePath(L
"SHD", 0, NULL
));
1457 if (pwszSHDFile
&& GetJobFilePath(L
"SHD", pJob
->dwJobID
, pwszSHDFile
))
1458 DeleteFileW(pwszSHDFile
);
1460 // Free memory for the mandatory fields.
1461 DllFreeSplMem(pJob
->pDevMode
);
1462 DllFreeSplStr(pJob
->pwszDatatype
);
1463 DllFreeSplStr(pJob
->pwszDocumentName
);
1464 DllFreeSplStr(pJob
->pwszMachineName
);
1465 DllFreeSplStr(pJob
->pwszNotifyName
);
1466 DllFreeSplStr(pJob
->pwszUserName
);
1468 // Free memory for the optional fields if they are present.
1469 if (pJob
->pwszOutputFile
)
1470 DllFreeSplStr(pJob
->pwszOutputFile
);
1472 if (pJob
->pwszPrintProcessorParameters
)
1473 DllFreeSplStr(pJob
->pwszPrintProcessorParameters
);
1475 if (pJob
->pwszStatus
)
1476 DllFreeSplStr(pJob
->pwszStatus
);
1478 // Finally free the job structure itself.
1479 DllFreeSplMem(pJob
);