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-2017 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
)
140 CopyMemory(pwszOutput
, wszJobDirectory
, cchJobDirectory
* sizeof(WCHAR
));
141 swprintf(&pwszOutput
[cchJobDirectory
], L
"\\%05lu.%s", dwJobID
, pwszExtension
);
144 // pwszExtension may be L"SPL" or L"SHD", same length for both!
145 return (cchJobDirectory
+ sizeof("\\?????.SPL")) * sizeof(WCHAR
);
149 InitializeGlobalJobList()
151 const WCHAR wszPath
[] = L
"\\?????.SHD";
152 const DWORD cchPath
= _countof(wszPath
) - 1;
157 PLOCAL_JOB pJob
= NULL
;
159 WCHAR wszFullPath
[MAX_PATH
];
160 WIN32_FIND_DATAW FindData
;
162 // This one is incremented in _GetNextJobID.
165 // Initialize an empty list for all jobs of all local printers.
166 // We will search it by Job ID (supply a pointer to a DWORD in LookupElementSkiplist).
167 InitializeSkiplist(&GlobalJobList
, DllAllocSplMem
, _GlobalJobListCompareRoutine
, (PSKIPLIST_FREE_ROUTINE
)DllFreeSplMem
);
169 // Construct the full path search pattern.
170 CopyMemory(wszFullPath
, wszJobDirectory
, cchJobDirectory
* sizeof(WCHAR
));
171 CopyMemory(&wszFullPath
[cchJobDirectory
], wszPath
, (cchPath
+ 1) * sizeof(WCHAR
));
173 // Use the search pattern to look for unfinished jobs serialized in shadow files (.SHD)
174 hFind
= FindFirstFileW(wszFullPath
, &FindData
);
175 if (hFind
== INVALID_HANDLE_VALUE
)
177 // No unfinished jobs found.
178 dwErrorCode
= ERROR_SUCCESS
;
184 // Skip possible subdirectories.
185 if (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
188 // Extract the Job ID and verify the file name format at the same time.
189 // This includes all valid names (like "00005.SHD") and excludes invalid ones (like "10ABC.SHD").
190 dwJobID
= wcstoul(FindData
.cFileName
, &p
, 10);
191 if (!IS_VALID_JOB_ID(dwJobID
))
194 if (wcsicmp(p
, L
".SHD") != 0)
197 // This shadow file has a valid name. Construct the full path and try to load it.
198 GetJobFilePath(L
"SHD", dwJobID
, wszFullPath
);
199 pJob
= ReadJobShadowFile(wszFullPath
);
203 // Add it to the Global Job List.
204 if (!InsertElementSkiplist(&GlobalJobList
, pJob
))
206 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
207 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob
->dwJobID
);
211 // Add it to the Printer's Job List.
212 if (!InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
))
214 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
215 ERR("InsertElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob
->dwJobID
);
219 while (FindNextFileW(hFind
, &FindData
));
221 dwErrorCode
= ERROR_SUCCESS
;
228 SetLastError(dwErrorCode
);
229 return (dwErrorCode
== ERROR_SUCCESS
);
233 InitializePrinterJobList(PLOCAL_PRINTER pPrinter
)
235 // Initialize an empty list for this printer's jobs.
236 // This one is only for sorting the jobs. If you need to lookup a job, search the GlobalJobList by Job ID.
237 InitializeSkiplist(&pPrinter
->JobList
, DllAllocSplMem
, _PrinterJobListCompareRoutine
, (PSKIPLIST_FREE_ROUTINE
)DllFreeSplMem
);
241 CreateJob(PLOCAL_PRINTER_HANDLE pPrinterHandle
)
243 const WCHAR wszDoubleBackslash
[] = L
"\\";
244 const DWORD cchDoubleBackslash
= _countof(wszDoubleBackslash
) - 1;
246 DWORD cchMachineName
;
250 RPC_BINDING_HANDLE hServerBinding
= NULL
;
251 RPC_WSTR pwszBinding
= NULL
;
252 RPC_WSTR pwszMachineName
= NULL
;
255 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
258 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
259 ERR("DllAllocSplMem failed!\n");
263 // Reserve an ID for this job.
264 if (!_GetNextJobID(&pJob
->dwJobID
))
266 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
270 // Copy over defaults to the LOCAL_JOB structure.
271 pJob
->pPrinter
= pPrinterHandle
->pPrinter
;
272 pJob
->pPrintProcessor
= pPrinterHandle
->pPrinter
->pPrintProcessor
;
273 pJob
->dwPriority
= DEF_PRIORITY
;
274 pJob
->dwStatus
= JOB_STATUS_SPOOLING
;
275 pJob
->pwszDatatype
= AllocSplStr(pPrinterHandle
->pwszDatatype
);
276 pJob
->pwszDocumentName
= AllocSplStr(wszDefaultDocumentName
);
277 pJob
->pDevMode
= DuplicateDevMode(pPrinterHandle
->pDevMode
);
278 GetSystemTime(&pJob
->stSubmitted
);
280 // Get the user name for the Job.
281 cchUserName
= UNLEN
+ 1;
282 pJob
->pwszUserName
= DllAllocSplMem(cchUserName
* sizeof(WCHAR
));
283 if (!GetUserNameW(pJob
->pwszUserName
, &cchUserName
))
285 dwErrorCode
= GetLastError();
286 ERR("GetUserNameW failed with error %lu!\n", dwErrorCode
);
290 // FIXME: For now, pwszNotifyName equals pwszUserName.
291 pJob
->pwszNotifyName
= AllocSplStr(pJob
->pwszUserName
);
293 // Get the name of the machine that submitted the Job over RPC.
294 dwErrorCode
= RpcBindingServerFromClient(NULL
, &hServerBinding
);
295 if (dwErrorCode
!= RPC_S_OK
)
297 ERR("RpcBindingServerFromClient failed with status %lu!\n", dwErrorCode
);
301 dwErrorCode
= RpcBindingToStringBindingW(hServerBinding
, &pwszBinding
);
302 if (dwErrorCode
!= RPC_S_OK
)
304 ERR("RpcBindingToStringBindingW failed with status %lu!\n", dwErrorCode
);
308 dwErrorCode
= RpcStringBindingParseW(pwszBinding
, NULL
, NULL
, &pwszMachineName
, NULL
, NULL
);
309 if (dwErrorCode
!= RPC_S_OK
)
311 ERR("RpcStringBindingParseW failed with status %lu!\n", dwErrorCode
);
315 cchMachineName
= wcslen(pwszMachineName
);
316 pJob
->pwszMachineName
= DllAllocSplMem((cchMachineName
+ cchDoubleBackslash
+ 1) * sizeof(WCHAR
));
317 CopyMemory(pJob
->pwszMachineName
, wszDoubleBackslash
, cchDoubleBackslash
* sizeof(WCHAR
));
318 CopyMemory(&pJob
->pwszMachineName
[cchDoubleBackslash
], pwszMachineName
, (cchMachineName
+ 1) * sizeof(WCHAR
));
320 // Add the job to the Global Job List.
321 if (!InsertElementSkiplist(&GlobalJobList
, pJob
))
323 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
324 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob
->dwJobID
);
328 // Add the job at the end of the Printer's Job List.
329 // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
330 if (!InsertTailElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
))
332 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
333 ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob
->dwJobID
);
337 // We were successful!
338 pPrinterHandle
->bStartedDoc
= TRUE
;
339 pPrinterHandle
->pJob
= pJob
;
340 dwErrorCode
= ERROR_SUCCESS
;
342 // Don't let the cleanup routine free this.
350 RpcStringFreeW(&pwszMachineName
);
353 RpcStringFreeW(&pwszBinding
);
356 RpcBindingFree(&hServerBinding
);
362 LocalAddJob(HANDLE hPrinter
, DWORD Level
, PBYTE pData
, DWORD cbBuf
, PDWORD pcbNeeded
)
364 ADDJOB_INFO_1W AddJobInfo1
;
366 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
367 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
369 // Check if this is a printer handle.
370 if (pHandle
->HandleType
!= HandleType_Printer
)
372 dwErrorCode
= ERROR_INVALID_HANDLE
;
376 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
378 // This handle must not have started a job yet!
379 if (pPrinterHandle
->pJob
)
381 dwErrorCode
= ERROR_INVALID_HANDLE
;
385 // Check if this is the right structure level.
388 dwErrorCode
= ERROR_INVALID_LEVEL
;
392 // Check if the printer is set to do direct printing.
393 // The Job List isn't used in this case.
394 if (pPrinterHandle
->pPrinter
->dwAttributes
& PRINTER_ATTRIBUTE_DIRECT
)
396 dwErrorCode
= ERROR_INVALID_ACCESS
;
400 // Check if the supplied buffer is large enough.
401 *pcbNeeded
= sizeof(ADDJOB_INFO_1W
) + GetJobFilePath(L
"SPL", 0, NULL
);
402 if (cbBuf
< *pcbNeeded
)
404 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
408 // All requirements are met - create a new job.
409 dwErrorCode
= CreateJob(pPrinterHandle
);
410 if (dwErrorCode
!= ERROR_SUCCESS
)
413 // Mark that this job was started with AddJob (so that it can be scheduled for printing with ScheduleJob).
414 pPrinterHandle
->pJob
->bAddedJob
= TRUE
;
416 // Return a proper ADDJOB_INFO_1W structure.
417 AddJobInfo1
.JobId
= pPrinterHandle
->pJob
->dwJobID
;
418 AddJobInfo1
.Path
= (PWSTR
)(pData
+ sizeof(ADDJOB_INFO_1W
));
420 CopyMemory(pData
, &AddJobInfo1
, sizeof(ADDJOB_INFO_1W
));
421 GetJobFilePath(L
"SPL", AddJobInfo1
.JobId
, AddJobInfo1
.Path
);
424 SetLastError(dwErrorCode
);
425 return (dwErrorCode
== ERROR_SUCCESS
);
430 _LocalGetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PBYTE
* ppStart
, PBYTE
* ppEnd
, DWORD cbBuf
, PDWORD pcbNeeded
)
432 DWORD cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
433 DWORD cbDocumentName
= 0;
434 DWORD cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
435 DWORD cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
437 DWORD cbUserName
= 0;
439 JOB_INFO_1W JobInfo1
= { 0 };
441 // Calculate the lengths of the optional values.
442 if (pJob
->pwszDocumentName
)
443 cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
445 if (pJob
->pwszStatus
)
446 cbStatus
= (wcslen(pJob
->pwszStatus
) + 1) * sizeof(WCHAR
);
448 if (pJob
->pwszUserName
)
449 cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
451 // Check if the supplied buffer is large enough.
452 *pcbNeeded
+= sizeof(JOB_INFO_1W
) + cbDatatype
+ cbDocumentName
+ cbMachineName
+ cbPrinterName
+ cbStatus
+ cbUserName
;
453 if (cbBuf
< *pcbNeeded
)
455 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
459 // Put the strings at the end of the buffer.
460 *ppEnd
-= cbDatatype
;
461 JobInfo1
.pDatatype
= (PWSTR
)*ppEnd
;
462 CopyMemory(*ppEnd
, pJob
->pwszDatatype
, cbDatatype
);
464 *ppEnd
-= cbMachineName
;
465 JobInfo1
.pMachineName
= (PWSTR
)*ppEnd
;
466 CopyMemory(*ppEnd
, pJob
->pwszMachineName
, cbMachineName
);
468 *ppEnd
-= cbPrinterName
;
469 JobInfo1
.pPrinterName
= (PWSTR
)*ppEnd
;
470 CopyMemory(*ppEnd
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
472 // Copy the optional values.
475 *ppEnd
-= cbDocumentName
;
476 JobInfo1
.pDocument
= (PWSTR
)*ppEnd
;
477 CopyMemory(*ppEnd
, pJob
->pwszDocumentName
, cbDocumentName
);
483 JobInfo1
.pStatus
= (PWSTR
)*ppEnd
;
484 CopyMemory(*ppEnd
, pJob
->pwszStatus
, cbStatus
);
489 *ppEnd
-= cbUserName
;
490 JobInfo1
.pUserName
= (PWSTR
)*ppEnd
;
491 CopyMemory(*ppEnd
, pJob
->pwszUserName
, cbUserName
);
494 // Fill the rest of the structure.
495 JobInfo1
.JobId
= pJob
->dwJobID
;
496 JobInfo1
.Priority
= pJob
->dwPriority
;
497 JobInfo1
.Status
= pJob
->dwStatus
;
498 JobInfo1
.TotalPages
= pJob
->dwTotalPages
;
499 CopyMemory(&JobInfo1
.Submitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
501 // Finally copy the structure to the output pointer.
502 CopyMemory(*ppStart
, &JobInfo1
, sizeof(JOB_INFO_1W
));
503 *ppStart
+= sizeof(JOB_INFO_1W
);
504 dwErrorCode
= ERROR_SUCCESS
;
511 _LocalGetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PBYTE
* ppStart
, PBYTE
* ppEnd
, DWORD cbBuf
, PDWORD pcbNeeded
)
513 DWORD cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
514 DWORD cbDevMode
= pJob
->pDevMode
->dmSize
+ pJob
->pDevMode
->dmDriverExtra
;
515 DWORD cbDocumentName
= 0;
516 DWORD cbDriverName
= (wcslen(pJob
->pPrinter
->pwszPrinterDriver
) + 1) * sizeof(WCHAR
);
517 DWORD cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
518 DWORD cbNotifyName
= 0;
519 DWORD cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
520 DWORD cbPrintProcessor
= (wcslen(pJob
->pPrintProcessor
->pwszName
) + 1) * sizeof(WCHAR
);
521 DWORD cbPrintProcessorParameters
= 0;
523 DWORD cbUserName
= 0;
526 FILETIME ftSubmitted
;
527 JOB_INFO_2W JobInfo2
= { 0 };
528 ULARGE_INTEGER uliNow
;
529 ULARGE_INTEGER uliSubmitted
;
531 // Calculate the lengths of the optional values.
532 if (pJob
->pwszDocumentName
)
533 cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
535 if (pJob
->pwszNotifyName
)
536 cbNotifyName
= (wcslen(pJob
->pwszNotifyName
) + 1) * sizeof(WCHAR
);
538 if (pJob
->pwszPrintProcessorParameters
)
539 cbPrintProcessorParameters
= (wcslen(pJob
->pwszPrintProcessorParameters
) + 1) * sizeof(WCHAR
);
541 if (pJob
->pwszStatus
)
542 cbStatus
= (wcslen(pJob
->pwszStatus
) + 1) * sizeof(WCHAR
);
544 if (pJob
->pwszUserName
)
545 cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
547 // Check if the supplied buffer is large enough.
548 *pcbNeeded
+= sizeof(JOB_INFO_2W
) + cbDatatype
+ cbDevMode
+ cbDocumentName
+ cbDriverName
+ cbMachineName
+ cbNotifyName
+ cbPrinterName
+ cbPrintProcessor
+ cbPrintProcessorParameters
+ cbStatus
+ cbUserName
;
549 if (cbBuf
< *pcbNeeded
)
551 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
555 // Put the strings at the end of the buffer.
556 *ppEnd
-= cbDatatype
;
557 JobInfo2
.pDatatype
= (PWSTR
)*ppEnd
;
558 CopyMemory(*ppEnd
, pJob
->pwszDatatype
, cbDatatype
);
561 JobInfo2
.pDevMode
= (PDEVMODEW
)*ppEnd
;
562 CopyMemory(*ppEnd
, pJob
->pDevMode
, cbDevMode
);
564 *ppEnd
-= cbDriverName
;
565 JobInfo2
.pDriverName
= (PWSTR
)*ppEnd
;
566 CopyMemory(*ppEnd
, pJob
->pPrinter
->pwszPrinterDriver
, cbDriverName
);
568 *ppEnd
-= cbMachineName
;
569 JobInfo2
.pMachineName
= (PWSTR
)*ppEnd
;
570 CopyMemory(*ppEnd
, pJob
->pwszMachineName
, cbMachineName
);
572 *ppEnd
-= cbPrinterName
;
573 JobInfo2
.pPrinterName
= (PWSTR
)*ppEnd
;
574 CopyMemory(*ppEnd
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
576 *ppEnd
-= cbPrintProcessor
;
577 JobInfo2
.pPrintProcessor
= (PWSTR
)*ppEnd
;
578 CopyMemory(*ppEnd
, pJob
->pPrintProcessor
->pwszName
, cbPrintProcessor
);
580 // Copy the optional values.
583 *ppEnd
-= cbDocumentName
;
584 JobInfo2
.pDocument
= (PWSTR
)*ppEnd
;
585 CopyMemory(*ppEnd
, pJob
->pwszDocumentName
, cbDocumentName
);
590 *ppEnd
-= cbNotifyName
;
591 JobInfo2
.pNotifyName
= (PWSTR
)*ppEnd
;
592 CopyMemory(*ppEnd
, pJob
->pwszNotifyName
, cbNotifyName
);
595 if (cbPrintProcessorParameters
)
597 *ppEnd
-= cbPrintProcessorParameters
;
598 JobInfo2
.pParameters
= (PWSTR
)*ppEnd
;
599 CopyMemory(*ppEnd
, pJob
->pwszPrintProcessorParameters
, cbPrintProcessorParameters
);
605 JobInfo2
.pStatus
= (PWSTR
)*ppEnd
;
606 CopyMemory(*ppEnd
, pJob
->pwszStatus
, cbStatus
);
611 *ppEnd
-= cbUserName
;
612 JobInfo2
.pUserName
= (PWSTR
)*ppEnd
;
613 CopyMemory(*ppEnd
, pJob
->pwszUserName
, cbUserName
);
616 // Time in JOB_INFO_2W is the number of milliseconds elapsed since the job was submitted. Calculate this time.
617 if (!SystemTimeToFileTime(&pJob
->stSubmitted
, &ftSubmitted
))
619 ERR("SystemTimeToFileTime failed with error %lu!\n", GetLastError());
623 GetSystemTimeAsFileTime(&ftNow
);
624 uliSubmitted
.LowPart
= ftSubmitted
.dwLowDateTime
;
625 uliSubmitted
.HighPart
= ftSubmitted
.dwHighDateTime
;
626 uliNow
.LowPart
= ftNow
.dwLowDateTime
;
627 uliNow
.HighPart
= ftNow
.dwHighDateTime
;
628 JobInfo2
.Time
= (DWORD
)((uliNow
.QuadPart
- uliSubmitted
.QuadPart
) / 10000);
630 // Position in JOB_INFO_2W is the 1-based index of the job in the processing queue.
631 // Retrieve this through the element index of the job in the Printer's Job List.
632 if (!LookupElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
, &JobInfo2
.Position
))
634 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
635 ERR("pJob could not be located in the Printer's Job List!\n");
639 // Make the index 1-based.
642 // Fill the rest of the structure.
643 JobInfo2
.JobId
= pJob
->dwJobID
;
644 JobInfo2
.PagesPrinted
= pJob
->dwPagesPrinted
;
645 JobInfo2
.Priority
= pJob
->dwPriority
;
646 JobInfo2
.StartTime
= pJob
->dwStartTime
;
647 JobInfo2
.Status
= pJob
->dwStatus
;
648 JobInfo2
.TotalPages
= pJob
->dwTotalPages
;
649 JobInfo2
.UntilTime
= pJob
->dwUntilTime
;
650 CopyMemory(&JobInfo2
.Submitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
652 // Finally copy the structure to the output pointer.
653 CopyMemory(*ppStart
, &JobInfo2
, sizeof(JOB_INFO_2W
));
654 *ppStart
+= sizeof(JOB_INFO_2W
);
655 dwErrorCode
= ERROR_SUCCESS
;
662 LocalGetJob(HANDLE hPrinter
, DWORD JobId
, DWORD Level
, PBYTE pStart
, DWORD cbBuf
, LPDWORD pcbNeeded
)
665 PBYTE pEnd
= &pStart
[cbBuf
];
666 PLOCAL_HANDLE pHandle
;
668 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
670 // Check if this is a printer handle.
671 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
672 if (pHandle
->HandleType
!= HandleType_Printer
)
674 dwErrorCode
= ERROR_INVALID_HANDLE
;
678 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
680 // Get the desired job.
681 pJob
= LookupElementSkiplist(&GlobalJobList
, &JobId
, NULL
);
682 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
684 dwErrorCode
= ERROR_INVALID_PARAMETER
;
691 // The function behaves differently for each level.
693 dwErrorCode
= _LocalGetJobLevel1(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
695 dwErrorCode
= _LocalGetJobLevel2(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
697 dwErrorCode
= ERROR_INVALID_LEVEL
;
700 SetLastError(dwErrorCode
);
701 return (dwErrorCode
== ERROR_SUCCESS
);
705 _LocalSetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PJOB_INFO_1W pJobInfo
)
709 // First check the validity of the input before changing anything.
710 if (!FindDatatype(pJob
->pPrintProcessor
, pJobInfo
->pDatatype
))
712 dwErrorCode
= ERROR_INVALID_DATATYPE
;
716 // Check if the datatype has changed.
717 if (!_EqualStrings(pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
719 // Use the new value.
720 if (!ReallocSplStr(&pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
722 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
723 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
728 // Check if the document name has changed. An empty string is permitted here!
729 if (!_EqualStrings(pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
731 // Use the new value.
732 if (!ReallocSplStr(&pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
734 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
735 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
740 // Check if the status message has changed. An empty string is permitted here!
741 if (!_EqualStrings(pJob
->pwszStatus
, pJobInfo
->pStatus
))
743 // Use the new value.
744 if (!ReallocSplStr(&pJob
->pwszStatus
, pJobInfo
->pStatus
))
746 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
747 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
752 // Check if the user name has changed. An empty string is permitted here!
753 if (!_EqualStrings(pJob
->pwszUserName
, pJobInfo
->pUserName
) != 0)
755 // The new user name doesn't need to exist, so no additional verification is required.
757 // Use the new value.
758 if (!ReallocSplStr(&pJob
->pwszUserName
, pJobInfo
->pUserName
))
760 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
761 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
766 // Check if the priority has changed.
767 if (pJob
->dwPriority
!= pJobInfo
->Priority
&& IS_VALID_PRIORITY(pJobInfo
->Priority
))
769 // Set the new priority.
770 pJob
->dwPriority
= pJobInfo
->Priority
;
772 // Remove and reinsert the job in the Printer's Job List.
773 // The Compare function will be used to find the right position now considering the new priority.
774 DeleteElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
775 InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
778 // Check if the status flags have changed.
779 if (pJob
->dwStatus
!= pJobInfo
->Status
)
781 // Only add status flags that make sense.
782 if (pJobInfo
->Status
& JOB_STATUS_PAUSED
)
783 pJob
->dwStatus
|= JOB_STATUS_PAUSED
;
785 if (pJobInfo
->Status
& JOB_STATUS_ERROR
)
786 pJob
->dwStatus
|= JOB_STATUS_ERROR
;
788 if (pJobInfo
->Status
& JOB_STATUS_OFFLINE
)
789 pJob
->dwStatus
|= JOB_STATUS_OFFLINE
;
791 if (pJobInfo
->Status
& JOB_STATUS_PAPEROUT
)
792 pJob
->dwStatus
|= JOB_STATUS_PAPEROUT
;
795 dwErrorCode
= ERROR_SUCCESS
;
802 _LocalSetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PJOB_INFO_2W pJobInfo
)
805 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
807 // First check the validity of the input before changing anything.
808 pPrintProcessor
= FindPrintProcessor(pJobInfo
->pPrintProcessor
);
809 if (!pPrintProcessor
)
811 dwErrorCode
= ERROR_UNKNOWN_PRINTPROCESSOR
;
815 if (!FindDatatype(pPrintProcessor
, pJobInfo
->pDatatype
))
817 dwErrorCode
= ERROR_INVALID_DATATYPE
;
821 // Check if the datatype has changed.
822 if (!_EqualStrings(pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
824 // Use the new value.
825 if (!ReallocSplStr(&pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
827 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
828 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
833 // Check if the document name has changed. An empty string is permitted here!
834 if (!_EqualStrings(pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
836 // Use the new value.
837 if (!ReallocSplStr(&pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
839 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
840 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
845 // Check if the notify name has changed. An empty string is permitted here!
846 if (!_EqualStrings(pJob
->pwszNotifyName
, pJobInfo
->pNotifyName
))
848 // The new notify name doesn't need to exist, so no additional verification is required.
850 // Use the new value.
851 if (!ReallocSplStr(&pJob
->pwszNotifyName
, pJobInfo
->pNotifyName
))
853 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
854 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
859 // Check if the Print Processor Parameters have changed. An empty string is permitted here!
860 if (!_EqualStrings(pJob
->pwszPrintProcessorParameters
, pJobInfo
->pParameters
))
862 // Use the new value.
863 if (!ReallocSplStr(&pJob
->pwszPrintProcessorParameters
, pJobInfo
->pParameters
))
865 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
866 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
871 // Check if the Status Message has changed. An empty string is permitted here!
872 if (!_EqualStrings(pJob
->pwszStatus
, pJobInfo
->pStatus
))
874 // Use the new value.
875 if (!ReallocSplStr(&pJob
->pwszStatus
, pJobInfo
->pStatus
))
877 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
878 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
883 // Check if the user name has changed. An empty string is permitted here!
884 if (!_EqualStrings(pJob
->pwszUserName
, pJobInfo
->pUserName
))
886 // The new user name doesn't need to exist, so no additional verification is required.
888 // Use the new value.
889 if (!ReallocSplStr(&pJob
->pwszUserName
, pJobInfo
->pUserName
))
891 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
892 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
897 // Check if the priority has changed.
898 if (pJob
->dwPriority
!= pJobInfo
->Priority
&& IS_VALID_PRIORITY(pJobInfo
->Priority
))
900 // Set the new priority.
901 pJob
->dwPriority
= pJobInfo
->Priority
;
903 // Remove and reinsert the job in the Printer's Job List.
904 // The Compare function will be used to find the right position now considering the new priority.
905 DeleteElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
906 InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
909 // Check if the status flags have changed.
910 if (pJob
->dwStatus
!= pJobInfo
->Status
)
912 // Only add status flags that make sense.
913 if (pJobInfo
->Status
& JOB_STATUS_PAUSED
)
914 pJob
->dwStatus
|= JOB_STATUS_PAUSED
;
916 if (pJobInfo
->Status
& JOB_STATUS_ERROR
)
917 pJob
->dwStatus
|= JOB_STATUS_ERROR
;
919 if (pJobInfo
->Status
& JOB_STATUS_OFFLINE
)
920 pJob
->dwStatus
|= JOB_STATUS_OFFLINE
;
922 if (pJobInfo
->Status
& JOB_STATUS_PAPEROUT
)
923 pJob
->dwStatus
|= JOB_STATUS_PAPEROUT
;
926 dwErrorCode
= ERROR_SUCCESS
;
933 LocalSetJob(HANDLE hPrinter
, DWORD JobId
, DWORD Level
, PBYTE pJobInfo
, DWORD Command
)
936 PLOCAL_HANDLE pHandle
;
938 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
939 WCHAR wszFullPath
[MAX_PATH
];
941 // Check if this is a printer handle.
942 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
943 if (pHandle
->HandleType
!= HandleType_Printer
)
945 dwErrorCode
= ERROR_INVALID_HANDLE
;
949 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
951 // Get the desired job.
952 pJob
= LookupElementSkiplist(&GlobalJobList
, &JobId
, NULL
);
953 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
955 dwErrorCode
= ERROR_INVALID_PARAMETER
;
959 // Setting job information is handled differently for each level.
963 dwErrorCode
= _LocalSetJobLevel1(pPrinterHandle
, pJob
, (PJOB_INFO_1W
)pJobInfo
);
965 dwErrorCode
= _LocalSetJobLevel2(pPrinterHandle
, pJob
, (PJOB_INFO_2W
)pJobInfo
);
967 dwErrorCode
= ERROR_INVALID_LEVEL
;
970 if (dwErrorCode
!= ERROR_SUCCESS
)
973 // If we do spooled printing, the job information is written down into a shadow file.
974 if (!(pPrinterHandle
->pPrinter
->dwAttributes
& PRINTER_ATTRIBUTE_DIRECT
))
976 // Write the job data into the shadow file.
977 GetJobFilePath(L
"SHD", JobId
, wszFullPath
);
978 WriteJobShadowFile(wszFullPath
, pJob
);
981 // Perform an additional command if desired.
984 if (Command
== JOB_CONTROL_SENT_TO_PRINTER
)
986 // This indicates the end of the Print Job.
988 // Cancel the Job at the Print Processor.
989 if (pJob
->hPrintProcessor
)
990 pJob
->pPrintProcessor
->pfnControlPrintProcessor(pJob
->hPrintProcessor
, JOB_CONTROL_CANCEL
);
994 // TODO: All open handles associated with the job need to be invalidated.
995 // This certainly needs handle tracking...
999 ERR("Unimplemented SetJob Command: %lu!\n", Command
);
1003 dwErrorCode
= ERROR_SUCCESS
;
1006 SetLastError(dwErrorCode
);
1007 return (dwErrorCode
== ERROR_SUCCESS
);
1011 LocalEnumJobs(HANDLE hPrinter
, DWORD FirstJob
, DWORD NoJobs
, DWORD Level
, PBYTE pStart
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
1015 PBYTE pEnd
= &pStart
[cbBuf
];
1016 PLOCAL_HANDLE pHandle
;
1018 PSKIPLIST_NODE pFirstJobNode
;
1019 PSKIPLIST_NODE pNode
;
1020 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1022 // Check if this is a printer handle.
1023 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1024 if (pHandle
->HandleType
!= HandleType_Printer
)
1026 dwErrorCode
= ERROR_INVALID_HANDLE
;
1030 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1035 dwErrorCode
= ERROR_INVALID_LEVEL
;
1043 // Lookup the node of the first job requested by the caller in the Printer's Job List.
1044 pFirstJobNode
= LookupNodeByIndexSkiplist(&pPrinterHandle
->pPrinter
->JobList
, FirstJob
);
1046 // Count the required buffer size and the number of jobs.
1048 pNode
= pFirstJobNode
;
1050 while (i
< NoJobs
&& pNode
)
1052 pJob
= (PLOCAL_JOB
)pNode
->Element
;
1054 // The function behaves differently for each level.
1056 _LocalGetJobLevel1(pPrinterHandle
, pJob
, NULL
, NULL
, 0, pcbNeeded
);
1057 else if (Level
== 2)
1058 _LocalGetJobLevel2(pPrinterHandle
, pJob
, NULL
, NULL
, 0, pcbNeeded
);
1060 // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
1062 pNode
= pNode
->Next
[0];
1065 // Check if the supplied buffer is large enough.
1066 if (cbBuf
< *pcbNeeded
)
1068 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
1072 // Begin counting again and also empty the given buffer.
1074 ZeroMemory(pStart
, cbBuf
);
1076 // Now call the same functions again to copy the actual data for each job into the buffer.
1078 pNode
= pFirstJobNode
;
1080 while (i
< NoJobs
&& pNode
)
1082 pJob
= (PLOCAL_JOB
)pNode
->Element
;
1084 // The function behaves differently for each level.
1086 dwErrorCode
= _LocalGetJobLevel1(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
1087 else if (Level
== 2)
1088 dwErrorCode
= _LocalGetJobLevel2(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
1090 if (dwErrorCode
!= ERROR_SUCCESS
)
1093 // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
1095 pNode
= pNode
->Next
[0];
1099 dwErrorCode
= ERROR_SUCCESS
;
1102 SetLastError(dwErrorCode
);
1103 return (dwErrorCode
== ERROR_SUCCESS
);
1107 LocalScheduleJob(HANDLE hPrinter
, DWORD dwJobID
)
1113 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1114 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1115 WCHAR wszFullPath
[MAX_PATH
];
1117 // Check if this is a printer handle.
1118 if (pHandle
->HandleType
!= HandleType_Printer
)
1120 dwErrorCode
= ERROR_INVALID_HANDLE
;
1124 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1126 // Check if the Job ID is valid.
1127 pJob
= LookupElementSkiplist(&GlobalJobList
, &dwJobID
, NULL
);
1128 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
1130 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1134 // Check if this Job was started with AddJob.
1135 if (!pJob
->bAddedJob
)
1137 dwErrorCode
= ERROR_SPL_NO_ADDJOB
;
1141 // Construct the full path to the spool file.
1142 GetJobFilePath(L
"SPL", dwJobID
, wszFullPath
);
1144 // Check if it exists.
1145 dwAttributes
= GetFileAttributesW(wszFullPath
);
1146 if (dwAttributes
== INVALID_FILE_ATTRIBUTES
|| dwAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1148 dwErrorCode
= ERROR_SPOOL_FILE_NOT_FOUND
;
1152 // Spooling is finished at this point.
1153 pJob
->dwStatus
&= ~JOB_STATUS_SPOOLING
;
1155 // Write the job data into the shadow file.
1156 wcscpy(wcsrchr(wszFullPath
, L
'.'), L
".SHD");
1157 WriteJobShadowFile(wszFullPath
, pJob
);
1159 // Create the thread for performing the printing process.
1160 hThread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)PrintingThreadProc
, pJob
, 0, NULL
);
1163 dwErrorCode
= GetLastError();
1164 ERR("CreateThread failed with error %lu!\n", dwErrorCode
);
1168 // We don't need the thread handle. Keeping it open blocks the thread from terminating.
1169 CloseHandle(hThread
);
1171 // ScheduleJob has done its job. The rest happens inside the thread.
1172 dwErrorCode
= ERROR_SUCCESS
;
1175 SetLastError(dwErrorCode
);
1176 return (dwErrorCode
== ERROR_SUCCESS
);
1180 ReadJobShadowFile(PCWSTR pwszFilePath
)
1184 HANDLE hFile
= INVALID_HANDLE_VALUE
;
1186 PLOCAL_JOB pReturnValue
= NULL
;
1187 PLOCAL_PRINTER pPrinter
;
1188 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
1189 PSHD_HEADER pShadowFile
= NULL
;
1190 PWSTR pwszPrinterName
;
1191 PWSTR pwszPrintProcessor
;
1193 // Try to open the file.
1194 hFile
= CreateFileW(pwszFilePath
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
1195 if (hFile
== INVALID_HANDLE_VALUE
)
1197 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1201 // Get its file size (small enough for a single DWORD) and allocate memory for all of it.
1202 cbFileSize
= GetFileSize(hFile
, NULL
);
1203 pShadowFile
= DllAllocSplMem(cbFileSize
);
1206 ERR("DllAllocSplMem failed for file \"%S\"!\n", pwszFilePath
);
1210 // Read the entire file.
1211 if (!ReadFile(hFile
, pShadowFile
, cbFileSize
, &cbRead
, NULL
))
1213 ERR("ReadFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1217 // Check signature and header size.
1218 if (pShadowFile
->dwSignature
!= SHD_WIN2003_SIGNATURE
|| pShadowFile
->cbHeader
!= sizeof(SHD_HEADER
))
1220 ERR("Signature or Header Size mismatch for file \"%S\"!\n", pwszFilePath
);
1224 // Retrieve the associated printer from the list.
1225 pwszPrinterName
= (PWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrinterName
);
1226 pPrinter
= LookupElementSkiplist(&PrinterList
, &pwszPrinterName
, NULL
);
1229 ERR("Shadow file \"%S\" references a non-existing printer \"%S\"!\n", pwszFilePath
, pwszPrinterName
);
1233 // Retrieve the associated Print Processor from the list.
1234 pwszPrintProcessor
= (PWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrintProcessor
);
1235 pPrintProcessor
= FindPrintProcessor(pwszPrintProcessor
);
1236 if (!pPrintProcessor
)
1238 ERR("Shadow file \"%S\" references a non-existing Print Processor \"%S\"!\n", pwszFilePath
, pwszPrintProcessor
);
1242 // Create a new job structure and copy over the relevant fields.
1243 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
1246 ERR("DllAllocSplMem failed for file \"%S\"!\n", pwszFilePath
);
1250 pJob
->dwJobID
= pShadowFile
->dwJobID
;
1251 pJob
->dwPriority
= pShadowFile
->dwPriority
;
1252 pJob
->dwStartTime
= pShadowFile
->dwStartTime
;
1253 pJob
->dwTotalPages
= pShadowFile
->dwTotalPages
;
1254 pJob
->dwUntilTime
= pShadowFile
->dwUntilTime
;
1255 pJob
->pPrinter
= pPrinter
;
1256 pJob
->pPrintProcessor
= pPrintProcessor
;
1257 pJob
->pDevMode
= DuplicateDevMode((PDEVMODEW
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDevMode
));
1258 pJob
->pwszDatatype
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDatatype
));
1259 pJob
->pwszMachineName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offMachineName
));
1260 CopyMemory(&pJob
->stSubmitted
, &pShadowFile
->stSubmitted
, sizeof(SYSTEMTIME
));
1262 // Copy the optional values.
1263 if (pShadowFile
->offDocumentName
)
1264 pJob
->pwszDocumentName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDocumentName
));
1266 if (pShadowFile
->offNotifyName
)
1267 pJob
->pwszNotifyName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offNotifyName
));
1269 if (pShadowFile
->offPrintProcessorParameters
)
1270 pJob
->pwszPrintProcessorParameters
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrintProcessorParameters
));
1272 if (pShadowFile
->offUserName
)
1273 pJob
->pwszUserName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offUserName
));
1275 // Jobs read from shadow files were always added using AddJob.
1276 pJob
->bAddedJob
= TRUE
;
1278 pReturnValue
= pJob
;
1282 DllFreeSplMem(pShadowFile
);
1284 if (hFile
!= INVALID_HANDLE_VALUE
)
1287 return pReturnValue
;
1291 WriteJobShadowFile(PWSTR pwszFilePath
, const PLOCAL_JOB pJob
)
1293 BOOL bReturnValue
= FALSE
;
1294 DWORD cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
1295 DWORD cbDevMode
= pJob
->pDevMode
->dmSize
+ pJob
->pDevMode
->dmDriverExtra
;
1296 DWORD cbDocumentName
= 0;
1298 DWORD cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
1299 DWORD cbNotifyName
= 0;
1300 DWORD cbPrinterDriver
= (wcslen(pJob
->pPrinter
->pwszPrinterDriver
) + 1) * sizeof(WCHAR
);
1301 DWORD cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
1302 DWORD cbPrintProcessor
= (wcslen(pJob
->pPrintProcessor
->pwszName
) + 1) * sizeof(WCHAR
);
1303 DWORD cbPrintProcessorParameters
= 0;
1304 DWORD cbUserName
= 0;
1306 DWORD dwCurrentOffset
;
1307 HANDLE hSHDFile
= INVALID_HANDLE_VALUE
;
1308 HANDLE hSPLFile
= INVALID_HANDLE_VALUE
;
1309 PSHD_HEADER pShadowFile
= NULL
;
1311 // Try to open the SHD file.
1312 hSHDFile
= CreateFileW(pwszFilePath
, GENERIC_WRITE
, FILE_SHARE_READ
, NULL
, CREATE_ALWAYS
, 0, NULL
);
1313 if (hSHDFile
== INVALID_HANDLE_VALUE
)
1315 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1319 // Calculate the lengths of the optional values and the total size of the shadow file.
1320 if (pJob
->pwszDocumentName
)
1321 cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
1323 if (pJob
->pwszNotifyName
)
1324 cbNotifyName
= (wcslen(pJob
->pwszNotifyName
) + 1) * sizeof(WCHAR
);
1326 if (pJob
->pwszPrintProcessorParameters
)
1327 cbPrintProcessorParameters
= (wcslen(pJob
->pwszPrintProcessorParameters
) + 1) * sizeof(WCHAR
);
1329 if (pJob
->pwszUserName
)
1330 cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
1332 cbFileSize
= sizeof(SHD_HEADER
) + cbDatatype
+ cbDocumentName
+ cbDevMode
+ cbMachineName
+ cbNotifyName
+ cbPrinterDriver
+ cbPrinterName
+ cbPrintProcessor
+ cbPrintProcessorParameters
+ cbUserName
;
1334 // Allocate memory for it.
1335 pShadowFile
= DllAllocSplMem(cbFileSize
);
1338 ERR("DllAllocSplMem failed for file \"%S\"!\n", pwszFilePath
);
1342 // Fill out the shadow file header information.
1343 pShadowFile
->dwSignature
= SHD_WIN2003_SIGNATURE
;
1344 pShadowFile
->cbHeader
= sizeof(SHD_HEADER
);
1347 pShadowFile
->dwJobID
= pJob
->dwJobID
;
1348 pShadowFile
->dwPriority
= pJob
->dwPriority
;
1349 pShadowFile
->dwStartTime
= pJob
->dwStartTime
;
1350 pShadowFile
->dwTotalPages
= pJob
->dwTotalPages
;
1351 pShadowFile
->dwUntilTime
= pJob
->dwUntilTime
;
1352 CopyMemory(&pShadowFile
->stSubmitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
1354 // Determine the file size of the .SPL file
1355 wcscpy(wcsrchr(pwszFilePath
, L
'.'), L
".SPL");
1356 hSPLFile
= CreateFileW(pwszFilePath
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
1357 if (hSPLFile
!= INVALID_HANDLE_VALUE
)
1358 pShadowFile
->dwSPLSize
= GetFileSize(hSPLFile
, NULL
);
1360 // Add the extra values that are stored as offsets in the shadow file.
1361 // The first value begins right after the shadow file header.
1362 dwCurrentOffset
= sizeof(SHD_HEADER
);
1364 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszDatatype
, cbDatatype
);
1365 pShadowFile
->offDatatype
= dwCurrentOffset
;
1366 dwCurrentOffset
+= cbDatatype
;
1368 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pDevMode
, cbDevMode
);
1369 pShadowFile
->offDevMode
= dwCurrentOffset
;
1370 dwCurrentOffset
+= cbDevMode
;
1372 // offDriverName is only written, but automatically determined through offPrinterName when reading.
1373 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrinter
->pwszPrinterDriver
, cbPrinterDriver
);
1374 pShadowFile
->offDriverName
= dwCurrentOffset
;
1375 dwCurrentOffset
+= cbPrinterDriver
;
1377 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszMachineName
, cbMachineName
);
1378 pShadowFile
->offMachineName
= dwCurrentOffset
;
1379 dwCurrentOffset
+= cbMachineName
;
1381 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
1382 pShadowFile
->offPrinterName
= dwCurrentOffset
;
1383 dwCurrentOffset
+= cbPrinterName
;
1385 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrintProcessor
->pwszName
, cbPrintProcessor
);
1386 pShadowFile
->offPrintProcessor
= dwCurrentOffset
;
1387 dwCurrentOffset
+= cbPrintProcessor
;
1389 // Copy the optional values.
1392 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszDocumentName
, cbDocumentName
);
1393 pShadowFile
->offDocumentName
= dwCurrentOffset
;
1394 dwCurrentOffset
+= cbDocumentName
;
1399 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszNotifyName
, cbNotifyName
);
1400 pShadowFile
->offNotifyName
= dwCurrentOffset
;
1401 dwCurrentOffset
+= cbNotifyName
;
1404 if (cbPrintProcessorParameters
)
1406 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszPrintProcessorParameters
, cbPrintProcessorParameters
);
1407 pShadowFile
->offPrintProcessorParameters
= dwCurrentOffset
;
1408 dwCurrentOffset
+= cbPrintProcessorParameters
;
1413 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszUserName
, cbUserName
);
1414 pShadowFile
->offUserName
= dwCurrentOffset
;
1415 dwCurrentOffset
+= cbUserName
;
1419 if (!WriteFile(hSHDFile
, pShadowFile
, cbFileSize
, &cbWritten
, NULL
))
1421 ERR("WriteFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1425 bReturnValue
= TRUE
;
1429 DllFreeSplMem(pShadowFile
);
1431 if (hSHDFile
!= INVALID_HANDLE_VALUE
)
1432 CloseHandle(hSHDFile
);
1434 if (hSPLFile
!= INVALID_HANDLE_VALUE
)
1435 CloseHandle(hSPLFile
);
1437 return bReturnValue
;
1441 FreeJob(PLOCAL_JOB pJob
)
1445 // Remove the Job from both Job Lists.
1446 DeleteElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
1447 DeleteElementSkiplist(&GlobalJobList
, pJob
);
1449 // Try to delete the corresponding .SHD file.
1450 pwszSHDFile
= DllAllocSplMem(GetJobFilePath(L
"SHD", 0, NULL
));
1451 if (pwszSHDFile
&& GetJobFilePath(L
"SHD", pJob
->dwJobID
, pwszSHDFile
))
1452 DeleteFileW(pwszSHDFile
);
1454 // Free memory for the mandatory fields.
1455 DllFreeSplMem(pJob
->pDevMode
);
1456 DllFreeSplStr(pJob
->pwszDatatype
);
1457 DllFreeSplStr(pJob
->pwszDocumentName
);
1458 DllFreeSplStr(pJob
->pwszMachineName
);
1459 DllFreeSplStr(pJob
->pwszNotifyName
);
1460 DllFreeSplStr(pJob
->pwszUserName
);
1462 // Free memory for the optional fields if they are present.
1463 if (pJob
->pwszOutputFile
)
1464 DllFreeSplStr(pJob
->pwszOutputFile
);
1466 if (pJob
->pwszPrintProcessorParameters
)
1467 DllFreeSplStr(pJob
->pwszPrintProcessorParameters
);
1469 if (pJob
->pwszStatus
)
1470 DllFreeSplStr(pJob
->pwszStatus
);
1472 // Finally free the job structure itself.
1473 DllFreeSplMem(pJob
);