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
;
18 _GetNextJobID(PDWORD dwJobID
)
22 while (LookupElementSkiplist(&GlobalJobList
, &_dwLastJobID
, NULL
))
24 // This ID is already taken. Try the next one.
28 if (!IS_VALID_JOB_ID(_dwLastJobID
))
30 ERR("Job ID %lu isn't valid!\n", _dwLastJobID
);
34 *dwJobID
= _dwLastJobID
;
39 * @name _GlobalJobListCompareRoutine
41 * SKIPLIST_COMPARE_ROUTINE for the Global Job List.
42 * We need the Global Job List to check whether a Job ID is already in use. Consequently, this list is sorted by ID.
45 _GlobalJobListCompareRoutine(PVOID FirstStruct
, PVOID SecondStruct
)
47 PLOCAL_JOB A
= (PLOCAL_JOB
)FirstStruct
;
48 PLOCAL_JOB B
= (PLOCAL_JOB
)SecondStruct
;
50 return A
->dwJobID
- B
->dwJobID
;
54 * @name _PrinterJobListCompareRoutine
56 * SKIPLIST_COMPARE_ROUTINE for the each Printer's Job List.
57 * Jobs in this list are sorted in the desired order of processing.
60 _PrinterJobListCompareRoutine(PVOID FirstStruct
, PVOID SecondStruct
)
62 PLOCAL_JOB A
= (PLOCAL_JOB
)FirstStruct
;
63 PLOCAL_JOB B
= (PLOCAL_JOB
)SecondStruct
;
65 FILETIME ftSubmittedA
;
66 FILETIME ftSubmittedB
;
67 ULARGE_INTEGER uliSubmittedA
;
68 ULARGE_INTEGER uliSubmittedB
;
71 // First compare the priorities to determine the order.
72 // The job with a higher priority shall come first.
73 iComparison
= A
->dwPriority
- B
->dwPriority
;
77 // Both have the same priority, so go by creation time.
78 // Comparison is done using the MSDN-recommended way for comparing SYSTEMTIMEs.
79 if (!SystemTimeToFileTime(&A
->stSubmitted
, &ftSubmittedA
))
81 ERR("SystemTimeToFileTime failed for A with error %lu!\n", GetLastError());
85 if (!SystemTimeToFileTime(&B
->stSubmitted
, &ftSubmittedB
))
87 ERR("SystemTimeToFileTime failed for B with error %lu!\n", GetLastError());
91 uliSubmittedA
.LowPart
= ftSubmittedA
.dwLowDateTime
;
92 uliSubmittedA
.HighPart
= ftSubmittedA
.dwHighDateTime
;
93 uliSubmittedB
.LowPart
= ftSubmittedB
.dwLowDateTime
;
94 uliSubmittedB
.HighPart
= ftSubmittedB
.dwHighDateTime
;
95 ullResult
= uliSubmittedA
.QuadPart
- uliSubmittedB
.QuadPart
;
99 else if (ullResult
> 0)
106 GetJobFilePath(PCWSTR pwszExtension
, DWORD dwJobID
, PWSTR pwszOutput
)
108 const WCHAR wszPrintersPath
[] = L
"\\PRINTERS\\";
109 const DWORD cchPrintersPath
= _countof(wszPrintersPath
) - 1;
110 const DWORD cchSpoolerFile
= sizeof("?????.") - 1;
111 const DWORD cchExtension
= sizeof("SPL") - 1; // pwszExtension may be L"SPL" or L"SHD", same length for both!
115 CopyMemory(pwszOutput
, wszSpoolDirectory
, cchSpoolDirectory
);
116 CopyMemory(&pwszOutput
[cchSpoolDirectory
], wszPrintersPath
, cchPrintersPath
);
117 swprintf(&pwszOutput
[cchSpoolDirectory
+ cchPrintersPath
], L
"%05lu.", dwJobID
);
118 CopyMemory(&pwszOutput
[cchSpoolDirectory
+ cchPrintersPath
+ cchSpoolerFile
], pwszExtension
, (cchExtension
+ 1) * sizeof(WCHAR
));
121 return (cchSpoolDirectory
+ cchPrintersPath
+ cchSpoolerFile
+ cchExtension
+ 1) * sizeof(WCHAR
);
125 InitializeGlobalJobList()
127 const WCHAR wszPath
[] = L
"\\PRINTERS\\?????.SHD";
128 const DWORD cchPath
= _countof(wszPath
) - 1;
133 PLOCAL_JOB pJob
= NULL
;
135 WCHAR wszFullPath
[MAX_PATH
];
136 WIN32_FIND_DATAW FindData
;
138 // This one is incremented in _GetNextJobID.
141 // Initialize an empty list for all jobs of all local printers.
142 // We will search it by Job ID (supply a pointer to a DWORD in LookupElementSkiplist).
143 InitializeSkiplist(&GlobalJobList
, DllAllocSplMem
, _GlobalJobListCompareRoutine
, (PSKIPLIST_FREE_ROUTINE
)DllFreeSplMem
);
145 // Construct the full path search pattern.
146 CopyMemory(wszFullPath
, wszSpoolDirectory
, cchSpoolDirectory
* sizeof(WCHAR
));
147 CopyMemory(&wszFullPath
[cchSpoolDirectory
], wszPath
, (cchPath
+ 1) * sizeof(WCHAR
));
149 // Use the search pattern to look for unfinished jobs serialized in shadow files (.SHD)
150 hFind
= FindFirstFileW(wszFullPath
, &FindData
);
151 if (hFind
== INVALID_HANDLE_VALUE
)
153 // No unfinished jobs found.
154 dwErrorCode
= ERROR_SUCCESS
;
160 // Skip possible subdirectories.
161 if (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
164 // Extract the Job ID and verify the file name format at the same time.
165 // This includes all valid names (like "00005.SHD") and excludes invalid ones (like "10ABC.SHD").
166 dwJobID
= wcstoul(FindData
.cFileName
, &p
, 10);
167 if (!IS_VALID_JOB_ID(dwJobID
))
170 if (wcsicmp(p
, L
".SHD") != 0)
173 // This shadow file has a valid name. Construct the full path and try to load it.
174 GetJobFilePath(L
"SHD", dwJobID
, wszFullPath
);
175 pJob
= ReadJobShadowFile(wszFullPath
);
179 // Add it to the Global Job List.
180 if (!InsertElementSkiplist(&GlobalJobList
, pJob
))
182 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
183 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob
->dwJobID
);
187 // Add it to the Printer's Job List.
188 if (!InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
))
190 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
191 ERR("InsertElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob
->dwJobID
);
195 while (FindNextFileW(hFind
, &FindData
));
197 dwErrorCode
= ERROR_SUCCESS
;
204 SetLastError(dwErrorCode
);
205 return (dwErrorCode
== ERROR_SUCCESS
);
209 InitializePrinterJobList(PLOCAL_PRINTER pPrinter
)
211 // Initialize an empty list for this printer's jobs.
212 // This one is only for sorting the jobs. If you need to lookup a job, search the GlobalJobList by Job ID.
213 InitializeSkiplist(&pPrinter
->JobList
, DllAllocSplMem
, _PrinterJobListCompareRoutine
, (PSKIPLIST_FREE_ROUTINE
)DllFreeSplMem
);
217 CreateJob(PLOCAL_PRINTER_HANDLE pPrinterHandle
)
219 const WCHAR wszDoubleBackslash
[] = L
"\\";
220 const DWORD cchDoubleBackslash
= _countof(wszDoubleBackslash
) - 1;
222 DWORD cchMachineName
;
226 RPC_BINDING_HANDLE hServerBinding
= NULL
;
227 RPC_WSTR pwszBinding
= NULL
;
228 RPC_WSTR pwszMachineName
= NULL
;
231 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
234 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
235 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
239 // Reserve an ID for this job.
240 if (!_GetNextJobID(&pJob
->dwJobID
))
242 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
246 // Copy over defaults to the LOCAL_JOB structure.
247 pJob
->pPrinter
= pPrinterHandle
->pPrinter
;
248 pJob
->pPrintProcessor
= pPrinterHandle
->pPrinter
->pPrintProcessor
;
249 pJob
->dwPriority
= DEF_PRIORITY
;
250 pJob
->dwStatus
= JOB_STATUS_SPOOLING
;
251 pJob
->pwszDatatype
= AllocSplStr(pPrinterHandle
->pwszDatatype
);
252 pJob
->pwszDocumentName
= AllocSplStr(wszDefaultDocumentName
);
253 pJob
->pDevMode
= DuplicateDevMode(pPrinterHandle
->pDevMode
);
254 GetSystemTime(&pJob
->stSubmitted
);
256 // Get the user name for the Job.
257 cchUserName
= UNLEN
+ 1;
258 pJob
->pwszUserName
= DllAllocSplMem(cchUserName
* sizeof(WCHAR
));
259 if (!GetUserNameW(pJob
->pwszUserName
, &cchUserName
))
261 dwErrorCode
= GetLastError();
262 ERR("GetUserNameW failed with error %lu!\n", dwErrorCode
);
266 // FIXME: For now, pwszNotifyName equals pwszUserName.
267 pJob
->pwszNotifyName
= AllocSplStr(pJob
->pwszUserName
);
269 // Get the name of the machine that submitted the Job over RPC.
270 dwErrorCode
= RpcBindingServerFromClient(NULL
, &hServerBinding
);
271 if (dwErrorCode
!= RPC_S_OK
)
273 ERR("RpcBindingServerFromClient failed with status %lu!\n", dwErrorCode
);
277 dwErrorCode
= RpcBindingToStringBindingW(hServerBinding
, &pwszBinding
);
278 if (dwErrorCode
!= RPC_S_OK
)
280 ERR("RpcBindingToStringBindingW failed with status %lu!\n", dwErrorCode
);
284 dwErrorCode
= RpcStringBindingParseW(pwszBinding
, NULL
, NULL
, &pwszMachineName
, NULL
, NULL
);
285 if (dwErrorCode
!= RPC_S_OK
)
287 ERR("RpcStringBindingParseW failed with status %lu!\n", dwErrorCode
);
291 cchMachineName
= wcslen(pwszMachineName
);
292 pJob
->pwszMachineName
= DllAllocSplMem((cchMachineName
+ cchDoubleBackslash
+ 1) * sizeof(WCHAR
));
293 CopyMemory(pJob
->pwszMachineName
, wszDoubleBackslash
, cchDoubleBackslash
* sizeof(WCHAR
));
294 CopyMemory(pJob
->pwszMachineName
+ cchDoubleBackslash
, pwszMachineName
, (cchMachineName
+ 1) * sizeof(WCHAR
));
296 // Add the job to the Global Job List.
297 if (!InsertElementSkiplist(&GlobalJobList
, pJob
))
299 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
300 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob
->dwJobID
);
304 // Add the job at the end of the Printer's Job List.
305 // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
306 if (!InsertTailElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
))
308 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
309 ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob
->dwJobID
);
313 // We were successful!
314 pPrinterHandle
->bStartedDoc
= TRUE
;
315 pPrinterHandle
->pJob
= pJob
;
316 dwErrorCode
= ERROR_SUCCESS
;
318 // Don't let the cleanup routine free this.
326 RpcStringFreeW(&pwszMachineName
);
329 RpcStringFreeW(&pwszBinding
);
332 RpcBindingFree(&hServerBinding
);
338 LocalAddJob(HANDLE hPrinter
, DWORD Level
, PBYTE pData
, DWORD cbBuf
, PDWORD pcbNeeded
)
340 ADDJOB_INFO_1W AddJobInfo1
;
342 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
343 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
345 // Check if this is a printer handle.
346 if (pHandle
->HandleType
!= HandleType_Printer
)
348 dwErrorCode
= ERROR_INVALID_HANDLE
;
352 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
354 // This handle must not have started a job yet!
355 if (pPrinterHandle
->pJob
)
357 dwErrorCode
= ERROR_INVALID_HANDLE
;
361 // Check if this is the right structure level.
364 dwErrorCode
= ERROR_INVALID_LEVEL
;
368 // Check if the printer is set to do direct printing.
369 // The Job List isn't used in this case.
370 if (pPrinterHandle
->pPrinter
->dwAttributes
& PRINTER_ATTRIBUTE_DIRECT
)
372 dwErrorCode
= ERROR_INVALID_ACCESS
;
376 // Check if the supplied buffer is large enough.
377 *pcbNeeded
= sizeof(ADDJOB_INFO_1W
) + GetJobFilePath(L
"SPL", 0, NULL
);
378 if (cbBuf
< *pcbNeeded
)
380 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
384 // All requirements are met - create a new job.
385 dwErrorCode
= CreateJob(pPrinterHandle
);
386 if (dwErrorCode
!= ERROR_SUCCESS
)
389 // Mark that this job was started with AddJob (so that it can be scheduled for printing with ScheduleJob).
390 pPrinterHandle
->pJob
->bAddedJob
= TRUE
;
392 // Return a proper ADDJOB_INFO_1W structure.
393 AddJobInfo1
.JobId
= pPrinterHandle
->pJob
->dwJobID
;
394 AddJobInfo1
.Path
= (PWSTR
)(pData
+ sizeof(ADDJOB_INFO_1W
));
396 CopyMemory(pData
, &AddJobInfo1
, sizeof(ADDJOB_INFO_1W
));
397 GetJobFilePath(L
"SPL", AddJobInfo1
.JobId
, AddJobInfo1
.Path
);
400 SetLastError(dwErrorCode
);
401 return (dwErrorCode
== ERROR_SUCCESS
);
406 _LocalGetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PBYTE
* ppStart
, PBYTE
* ppEnd
, DWORD cbBuf
, PDWORD pcbNeeded
)
408 DWORD cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
409 DWORD cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
410 DWORD cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
411 DWORD cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
413 DWORD cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
415 JOB_INFO_1W JobInfo1
= { 0 };
417 // A Status Message is optional.
418 if (pJob
->pwszStatus
)
419 cbStatus
= (wcslen(pJob
->pwszStatus
) + 1) * sizeof(WCHAR
);
421 // Check if the supplied buffer is large enough.
422 *pcbNeeded
+= sizeof(JOB_INFO_1W
) + cbDatatype
+ cbDocumentName
+ cbMachineName
+ cbPrinterName
+ cbStatus
+ cbUserName
;
423 if (cbBuf
< *pcbNeeded
)
425 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
429 // Put the strings at the end of the buffer.
430 *ppEnd
-= cbDatatype
;
431 JobInfo1
.pDatatype
= (PWSTR
)*ppEnd
;
432 CopyMemory(*ppEnd
, pJob
->pwszDatatype
, cbDatatype
);
434 *ppEnd
-= cbDocumentName
;
435 JobInfo1
.pDocument
= (PWSTR
)*ppEnd
;
436 CopyMemory(*ppEnd
, pJob
->pwszDocumentName
, cbDocumentName
);
438 *ppEnd
-= cbMachineName
;
439 JobInfo1
.pMachineName
= (PWSTR
)*ppEnd
;
440 CopyMemory(*ppEnd
, pJob
->pwszMachineName
, cbMachineName
);
442 *ppEnd
-= cbPrinterName
;
443 JobInfo1
.pPrinterName
= (PWSTR
)*ppEnd
;
444 CopyMemory(*ppEnd
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
449 JobInfo1
.pStatus
= (PWSTR
)*ppEnd
;
450 CopyMemory(*ppEnd
, pJob
->pwszStatus
, cbStatus
);
453 *ppEnd
-= cbUserName
;
454 JobInfo1
.pUserName
= (PWSTR
)*ppEnd
;
455 CopyMemory(*ppEnd
, pJob
->pwszUserName
, cbUserName
);
457 // Fill the rest of the structure.
458 JobInfo1
.JobId
= pJob
->dwJobID
;
459 JobInfo1
.Priority
= pJob
->dwPriority
;
460 JobInfo1
.Status
= pJob
->dwStatus
;
461 JobInfo1
.TotalPages
= pJob
->dwTotalPages
;
462 CopyMemory(&JobInfo1
.Submitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
464 // Finally copy the structure to the output pointer.
465 CopyMemory(*ppStart
, &JobInfo1
, sizeof(JOB_INFO_1W
));
466 *ppStart
+= sizeof(JOB_INFO_1W
);
467 dwErrorCode
= ERROR_SUCCESS
;
474 _LocalGetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PBYTE
* ppStart
, PBYTE
* ppEnd
, DWORD cbBuf
, PDWORD pcbNeeded
)
476 DWORD cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
477 DWORD cbDevMode
= pJob
->pDevMode
->dmSize
+ pJob
->pDevMode
->dmDriverExtra
;
478 DWORD cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
479 DWORD cbDriverName
= (wcslen(pJob
->pPrinter
->pwszPrinterDriver
) + 1) * sizeof(WCHAR
);
480 DWORD cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
481 DWORD cbNotifyName
= (wcslen(pJob
->pwszNotifyName
) + 1) * sizeof(WCHAR
);
482 DWORD cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
483 DWORD cbPrintProcessor
= (wcslen(pJob
->pPrintProcessor
->pwszName
) + 1) * sizeof(WCHAR
);
484 DWORD cbPrintProcessorParameters
= 0;
486 DWORD cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
489 FILETIME ftSubmitted
;
490 JOB_INFO_2W JobInfo2
= { 0 };
491 ULARGE_INTEGER uliNow
;
492 ULARGE_INTEGER uliSubmitted
;
494 // Print Processor Parameters and Status Message are optional.
495 if (pJob
->pwszPrintProcessorParameters
)
496 cbPrintProcessorParameters
= (wcslen(pJob
->pwszPrintProcessorParameters
) + 1) * sizeof(WCHAR
);
498 if (pJob
->pwszStatus
)
499 cbStatus
= (wcslen(pJob
->pwszStatus
) + 1) * sizeof(WCHAR
);
501 // Check if the supplied buffer is large enough.
502 *pcbNeeded
+= sizeof(JOB_INFO_2W
) + cbDatatype
+ cbDevMode
+ cbDocumentName
+ cbDriverName
+ cbMachineName
+ cbNotifyName
+ cbPrinterName
+ cbPrintProcessor
+ cbPrintProcessorParameters
+ cbStatus
+ cbUserName
;
503 if (cbBuf
< *pcbNeeded
)
505 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
509 // Put the strings at the end of the buffer.
510 *ppEnd
-= cbDatatype
;
511 JobInfo2
.pDatatype
= (PWSTR
)*ppEnd
;
512 CopyMemory(*ppEnd
, pJob
->pwszDatatype
, cbDatatype
);
515 JobInfo2
.pDevMode
= (PDEVMODEW
)*ppEnd
;
516 CopyMemory(*ppEnd
, pJob
->pDevMode
, cbDevMode
);
518 *ppEnd
-= cbDocumentName
;
519 JobInfo2
.pDocument
= (PWSTR
)*ppEnd
;
520 CopyMemory(*ppEnd
, pJob
->pwszDocumentName
, cbDocumentName
);
522 *ppEnd
-= cbDriverName
;
523 JobInfo2
.pDriverName
= (PWSTR
)*ppEnd
;
524 CopyMemory(*ppEnd
, pJob
->pPrinter
->pwszPrinterDriver
, cbDriverName
);
526 *ppEnd
-= cbMachineName
;
527 JobInfo2
.pMachineName
= (PWSTR
)*ppEnd
;
528 CopyMemory(*ppEnd
, pJob
->pwszMachineName
, cbMachineName
);
530 *ppEnd
-= cbNotifyName
;
531 JobInfo2
.pNotifyName
= (PWSTR
)*ppEnd
;
532 CopyMemory(*ppEnd
, pJob
->pwszNotifyName
, cbNotifyName
);
534 *ppEnd
-= cbPrinterName
;
535 JobInfo2
.pPrinterName
= (PWSTR
)*ppEnd
;
536 CopyMemory(*ppEnd
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
538 *ppEnd
-= cbPrintProcessor
;
539 JobInfo2
.pPrintProcessor
= (PWSTR
)*ppEnd
;
540 CopyMemory(*ppEnd
, pJob
->pPrintProcessor
->pwszName
, cbPrintProcessor
);
542 if (cbPrintProcessorParameters
)
544 *ppEnd
-= cbPrintProcessorParameters
;
545 JobInfo2
.pParameters
= (PWSTR
)*ppEnd
;
546 CopyMemory(*ppEnd
, pJob
->pwszPrintProcessorParameters
, cbPrintProcessorParameters
);
552 JobInfo2
.pStatus
= (PWSTR
)*ppEnd
;
553 CopyMemory(*ppEnd
, pJob
->pwszStatus
, cbStatus
);
556 *ppEnd
-= cbUserName
;
557 JobInfo2
.pUserName
= (PWSTR
)*ppEnd
;
558 CopyMemory(*ppEnd
, pJob
->pwszUserName
, cbUserName
);
560 // Time in JOB_INFO_2W is the number of milliseconds elapsed since the job was submitted. Calculate this time.
561 if (!SystemTimeToFileTime(&pJob
->stSubmitted
, &ftSubmitted
))
563 ERR("SystemTimeToFileTime failed with error %lu!\n", GetLastError());
567 GetSystemTimeAsFileTime(&ftNow
);
568 uliSubmitted
.LowPart
= ftSubmitted
.dwLowDateTime
;
569 uliSubmitted
.HighPart
= ftSubmitted
.dwHighDateTime
;
570 uliNow
.LowPart
= ftNow
.dwLowDateTime
;
571 uliNow
.HighPart
= ftNow
.dwHighDateTime
;
572 JobInfo2
.Time
= (DWORD
)((uliNow
.QuadPart
- uliSubmitted
.QuadPart
) / 10000);
574 // Position in JOB_INFO_2W is the 1-based index of the job in the processing queue.
575 // Retrieve this through the element index of the job in the Printer's Job List.
576 if (!LookupElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
, &JobInfo2
.Position
))
578 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
579 ERR("pJob could not be located in the Printer's Job List!\n");
583 // Make the index 1-based.
586 // Fill the rest of the structure.
587 JobInfo2
.JobId
= pJob
->dwJobID
;
588 JobInfo2
.PagesPrinted
= pJob
->dwPagesPrinted
;
589 JobInfo2
.Priority
= pJob
->dwPriority
;
590 JobInfo2
.StartTime
= pJob
->dwStartTime
;
591 JobInfo2
.Status
= pJob
->dwStatus
;
592 JobInfo2
.TotalPages
= pJob
->dwTotalPages
;
593 JobInfo2
.UntilTime
= pJob
->dwUntilTime
;
594 CopyMemory(&JobInfo2
.Submitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
596 // Finally copy the structure to the output pointer.
597 CopyMemory(*ppStart
, &JobInfo2
, sizeof(JOB_INFO_2W
));
598 *ppStart
+= sizeof(JOB_INFO_2W
);
599 dwErrorCode
= ERROR_SUCCESS
;
606 LocalGetJob(HANDLE hPrinter
, DWORD JobId
, DWORD Level
, PBYTE pStart
, DWORD cbBuf
, LPDWORD pcbNeeded
)
609 PBYTE pEnd
= &pStart
[cbBuf
];
610 PLOCAL_HANDLE pHandle
;
612 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
614 // Check if this is a printer handle.
615 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
616 if (pHandle
->HandleType
!= HandleType_Printer
)
618 dwErrorCode
= ERROR_INVALID_HANDLE
;
622 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
624 // Get the desired job.
625 pJob
= LookupElementSkiplist(&GlobalJobList
, &JobId
, NULL
);
626 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
628 dwErrorCode
= ERROR_INVALID_PARAMETER
;
635 // The function behaves differently for each level.
637 dwErrorCode
= _LocalGetJobLevel1(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
639 dwErrorCode
= _LocalGetJobLevel2(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
641 dwErrorCode
= ERROR_INVALID_LEVEL
;
644 SetLastError(dwErrorCode
);
645 return (dwErrorCode
== ERROR_SUCCESS
);
649 _LocalSetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PJOB_INFO_1W pJobInfo
)
653 // First check the validity of the input before changing anything.
654 if (!FindDatatype(pJob
->pPrintProcessor
, pJobInfo
->pDatatype
))
656 dwErrorCode
= ERROR_INVALID_DATATYPE
;
660 // Check if the datatype has changed.
661 if (wcscmp(pJob
->pwszDatatype
, pJobInfo
->pDatatype
) != 0)
663 // Use the new value.
664 if (!ReallocSplStr(&pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
666 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
667 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
672 // Check if the document name has changed.
673 if (wcscmp(pJob
->pwszDocumentName
, pJobInfo
->pDocument
) != 0)
675 // Use the new value.
676 if (!ReallocSplStr(&pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
678 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
679 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
684 // Check if the status message has changed.
685 if ((!pJob
->pwszStatus
&& pJobInfo
->pStatus
) || wcscmp(pJob
->pwszStatus
, pJobInfo
->pStatus
) != 0)
687 // Use the new value.
688 if (!ReallocSplStr(&pJob
->pwszStatus
, pJobInfo
->pStatus
))
690 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
691 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
696 // Check if the user name has changed.
697 if (wcscmp(pJob
->pwszUserName
, pJobInfo
->pUserName
) != 0)
699 // The new user name doesn't need to exist, so no additional verification is required.
701 // Use the new value.
702 if (!ReallocSplStr(&pJob
->pwszUserName
, pJobInfo
->pUserName
))
704 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
705 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
710 // Check if the priority has changed.
711 if (pJob
->dwPriority
!= pJobInfo
->Priority
&& IS_VALID_PRIORITY(pJobInfo
->Priority
))
713 // Set the new priority.
714 pJob
->dwPriority
= pJobInfo
->Priority
;
716 // Remove and reinsert the job in the Printer's Job List.
717 // The Compare function will be used to find the right position now considering the new priority.
718 DeleteElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
719 InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
722 // Check if the status flags have changed.
723 if (pJob
->dwStatus
!= pJobInfo
->Status
)
725 // Only add status flags that make sense.
726 if (pJobInfo
->Status
& JOB_STATUS_PAUSED
)
727 pJob
->dwStatus
|= JOB_STATUS_PAUSED
;
729 if (pJobInfo
->Status
& JOB_STATUS_ERROR
)
730 pJob
->dwStatus
|= JOB_STATUS_ERROR
;
732 if (pJobInfo
->Status
& JOB_STATUS_OFFLINE
)
733 pJob
->dwStatus
|= JOB_STATUS_OFFLINE
;
735 if (pJobInfo
->Status
& JOB_STATUS_PAPEROUT
)
736 pJob
->dwStatus
|= JOB_STATUS_PAPEROUT
;
739 dwErrorCode
= ERROR_SUCCESS
;
746 _LocalSetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PJOB_INFO_2W pJobInfo
)
749 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
751 // First check the validity of the input before changing anything.
752 pPrintProcessor
= FindPrintProcessor(pJobInfo
->pPrintProcessor
);
753 if (!pPrintProcessor
)
755 dwErrorCode
= ERROR_UNKNOWN_PRINTPROCESSOR
;
759 if (!FindDatatype(pPrintProcessor
, pJobInfo
->pDatatype
))
761 dwErrorCode
= ERROR_INVALID_DATATYPE
;
765 // Check if the datatype has changed.
766 if (wcscmp(pJob
->pwszDatatype
, pJobInfo
->pDatatype
) != 0)
768 // Use the new value.
769 if (!ReallocSplStr(&pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
771 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
772 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
777 // Check if the document name has changed.
778 if (wcscmp(pJob
->pwszDocumentName
, pJobInfo
->pDocument
) != 0)
780 // Use the new value.
781 if (!ReallocSplStr(&pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
783 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
784 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
789 // Check if the notify name has changed.
790 if (wcscmp(pJob
->pwszNotifyName
, pJobInfo
->pNotifyName
) != 0)
792 // The new notify name doesn't need to exist, so no additional verification is required.
794 // Use the new value.
795 if (!ReallocSplStr(&pJob
->pwszNotifyName
, pJobInfo
->pNotifyName
))
797 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
798 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
803 // Check if the (optional) Print Processor Parameters have changed.
804 if ((!pJob
->pwszPrintProcessorParameters
&& pJobInfo
->pParameters
) || wcscmp(pJob
->pwszPrintProcessorParameters
, pJobInfo
->pParameters
) != 0)
806 // Use the new value.
807 if (!ReallocSplStr(&pJob
->pwszPrintProcessorParameters
, pJobInfo
->pParameters
))
809 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
810 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
815 // Check if the (optional) Status Message has changed.
816 if ((!pJob
->pwszStatus
&& pJobInfo
->pStatus
) || wcscmp(pJob
->pwszStatus
, pJobInfo
->pStatus
) != 0)
818 // Use the new value.
819 if (!ReallocSplStr(&pJob
->pwszStatus
, pJobInfo
->pStatus
))
821 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
822 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
827 // Check if the user name has changed.
828 if (wcscmp(pJob
->pwszUserName
, pJobInfo
->pUserName
) != 0)
830 // The new user name doesn't need to exist, so no additional verification is required.
832 // Use the new value.
833 if (!ReallocSplStr(&pJob
->pwszUserName
, pJobInfo
->pUserName
))
835 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
836 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
841 // Check if the priority has changed.
842 if (pJob
->dwPriority
!= pJobInfo
->Priority
&& IS_VALID_PRIORITY(pJobInfo
->Priority
))
844 // Set the new priority.
845 pJob
->dwPriority
= pJobInfo
->Priority
;
847 // Remove and reinsert the job in the Printer's Job List.
848 // The Compare function will be used to find the right position now considering the new priority.
849 DeleteElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
850 InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
853 // Check if the status flags have changed.
854 if (pJob
->dwStatus
!= pJobInfo
->Status
)
856 // Only add status flags that make sense.
857 if (pJobInfo
->Status
& JOB_STATUS_PAUSED
)
858 pJob
->dwStatus
|= JOB_STATUS_PAUSED
;
860 if (pJobInfo
->Status
& JOB_STATUS_ERROR
)
861 pJob
->dwStatus
|= JOB_STATUS_ERROR
;
863 if (pJobInfo
->Status
& JOB_STATUS_OFFLINE
)
864 pJob
->dwStatus
|= JOB_STATUS_OFFLINE
;
866 if (pJobInfo
->Status
& JOB_STATUS_PAPEROUT
)
867 pJob
->dwStatus
|= JOB_STATUS_PAPEROUT
;
870 dwErrorCode
= ERROR_SUCCESS
;
877 LocalSetJob(HANDLE hPrinter
, DWORD JobId
, DWORD Level
, PBYTE pJobInfo
, DWORD Command
)
880 PLOCAL_HANDLE pHandle
;
882 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
883 WCHAR wszFullPath
[MAX_PATH
];
885 // Check if this is a printer handle.
886 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
887 if (pHandle
->HandleType
!= HandleType_Printer
)
889 dwErrorCode
= ERROR_INVALID_HANDLE
;
893 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
895 // Get the desired job.
896 pJob
= LookupElementSkiplist(&GlobalJobList
, &JobId
, NULL
);
897 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
899 dwErrorCode
= ERROR_INVALID_PARAMETER
;
903 // Setting job information is handled differently for each level.
907 dwErrorCode
= _LocalSetJobLevel1(pPrinterHandle
, pJob
, (PJOB_INFO_1W
)pJobInfo
);
909 dwErrorCode
= _LocalSetJobLevel2(pPrinterHandle
, pJob
, (PJOB_INFO_2W
)pJobInfo
);
911 dwErrorCode
= ERROR_INVALID_LEVEL
;
914 if (dwErrorCode
!= ERROR_SUCCESS
)
917 // If we do spooled printing, the job information is written down into a shadow file.
918 if (!(pPrinterHandle
->pPrinter
->dwAttributes
& PRINTER_ATTRIBUTE_DIRECT
))
920 // Write the job data into the shadow file.
921 GetJobFilePath(L
"SHD", JobId
, wszFullPath
);
922 WriteJobShadowFile(wszFullPath
, pJob
);
925 // Perform an additional command if desired.
928 if (Command
== JOB_CONTROL_SENT_TO_PRINTER
)
930 // This indicates the end of the Print Job.
932 // Cancel the Job at the Print Processor.
933 if (pJob
->hPrintProcessor
)
934 pJob
->pPrintProcessor
->pfnControlPrintProcessor(pJob
->hPrintProcessor
, JOB_CONTROL_CANCEL
);
938 // TODO: All open handles associated with the job need to be invalidated.
939 // This certainly needs handle tracking...
943 ERR("Unimplemented SetJob Command: %lu!\n", Command
);
947 dwErrorCode
= ERROR_SUCCESS
;
950 SetLastError(dwErrorCode
);
951 return (dwErrorCode
== ERROR_SUCCESS
);
955 LocalEnumJobs(HANDLE hPrinter
, DWORD FirstJob
, DWORD NoJobs
, DWORD Level
, PBYTE pStart
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
959 PBYTE pEnd
= &pStart
[cbBuf
];
960 PLOCAL_HANDLE pHandle
;
962 PSKIPLIST_NODE pFirstJobNode
;
963 PSKIPLIST_NODE pNode
;
964 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
966 // Check if this is a printer handle.
967 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
968 if (pHandle
->HandleType
!= HandleType_Printer
)
970 dwErrorCode
= ERROR_INVALID_HANDLE
;
974 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
979 dwErrorCode
= ERROR_INVALID_LEVEL
;
987 // Lookup the node of the first job requested by the caller in the Printer's Job List.
988 pFirstJobNode
= LookupNodeByIndexSkiplist(&pPrinterHandle
->pPrinter
->JobList
, FirstJob
);
990 // Count the required buffer size and the number of jobs.
992 pNode
= pFirstJobNode
;
994 while (i
< NoJobs
&& pNode
)
996 pJob
= (PLOCAL_JOB
)pNode
->Element
;
998 // The function behaves differently for each level.
1000 _LocalGetJobLevel1(pPrinterHandle
, pJob
, NULL
, NULL
, 0, pcbNeeded
);
1001 else if (Level
== 2)
1002 _LocalGetJobLevel2(pPrinterHandle
, pJob
, NULL
, NULL
, 0, pcbNeeded
);
1004 // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
1006 pNode
= pNode
->Next
[0];
1009 // Check if the supplied buffer is large enough.
1010 if (cbBuf
< *pcbNeeded
)
1012 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
1016 // Begin counting again and also empty the given buffer.
1018 ZeroMemory(pStart
, cbBuf
);
1020 // Now call the same functions again to copy the actual data for each job into the buffer.
1022 pNode
= pFirstJobNode
;
1024 while (i
< NoJobs
&& pNode
)
1026 pJob
= (PLOCAL_JOB
)pNode
->Element
;
1028 // The function behaves differently for each level.
1030 dwErrorCode
= _LocalGetJobLevel1(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
1031 else if (Level
== 2)
1032 dwErrorCode
= _LocalGetJobLevel2(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
1034 if (dwErrorCode
!= ERROR_SUCCESS
)
1037 // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
1039 pNode
= pNode
->Next
[0];
1043 dwErrorCode
= ERROR_SUCCESS
;
1046 SetLastError(dwErrorCode
);
1047 return (dwErrorCode
== ERROR_SUCCESS
);
1051 LocalScheduleJob(HANDLE hPrinter
, DWORD dwJobID
)
1057 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1058 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1059 WCHAR wszFullPath
[MAX_PATH
];
1061 // Check if this is a printer handle.
1062 if (pHandle
->HandleType
!= HandleType_Printer
)
1064 dwErrorCode
= ERROR_INVALID_HANDLE
;
1068 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1070 // Check if the Job ID is valid.
1071 pJob
= LookupElementSkiplist(&GlobalJobList
, &dwJobID
, NULL
);
1072 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
1074 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1078 // Check if this Job was started with AddJob.
1079 if (!pJob
->bAddedJob
)
1081 dwErrorCode
= ERROR_SPL_NO_ADDJOB
;
1085 // Construct the full path to the spool file.
1086 GetJobFilePath(L
"SPL", dwJobID
, wszFullPath
);
1088 // Check if it exists.
1089 dwAttributes
= GetFileAttributesW(wszFullPath
);
1090 if (dwAttributes
== INVALID_FILE_ATTRIBUTES
|| dwAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1092 dwErrorCode
= ERROR_SPOOL_FILE_NOT_FOUND
;
1096 // Switch from spooling to printing.
1097 pJob
->dwStatus
&= ~JOB_STATUS_SPOOLING
;
1098 pJob
->dwStatus
|= JOB_STATUS_PRINTING
;
1100 // Write the job data into the shadow file.
1101 wcscpy(wcsrchr(wszFullPath
, L
'.'), L
".SHD");
1102 WriteJobShadowFile(wszFullPath
, pJob
);
1104 // Create the thread for performing the printing process.
1105 hThread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)PrintingThreadProc
, pJob
, 0, NULL
);
1108 dwErrorCode
= GetLastError();
1109 ERR("CreateThread failed with error %lu!\n", dwErrorCode
);
1113 // We don't need the thread handle. Keeping it open blocks the thread from terminating.
1114 CloseHandle(hThread
);
1116 // ScheduleJob has done its job. The rest happens inside the thread.
1117 dwErrorCode
= ERROR_SUCCESS
;
1120 SetLastError(dwErrorCode
);
1121 return (dwErrorCode
== ERROR_SUCCESS
);
1125 ReadJobShadowFile(PCWSTR pwszFilePath
)
1129 HANDLE hFile
= INVALID_HANDLE_VALUE
;
1131 PLOCAL_JOB pReturnValue
= NULL
;
1132 PLOCAL_PRINTER pPrinter
;
1133 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
1134 PSHD_HEADER pShadowFile
= NULL
;
1135 PWSTR pwszPrinterName
;
1136 PWSTR pwszPrintProcessor
;
1138 // Try to open the file.
1139 hFile
= CreateFileW(pwszFilePath
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
1140 if (hFile
== INVALID_HANDLE_VALUE
)
1142 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1146 // Get its file size (small enough for a single DWORD) and allocate memory for all of it.
1147 cbFileSize
= GetFileSize(hFile
, NULL
);
1148 pShadowFile
= DllAllocSplMem(cbFileSize
);
1151 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1155 // Read the entire file.
1156 if (!ReadFile(hFile
, pShadowFile
, cbFileSize
, &cbRead
, NULL
))
1158 ERR("ReadFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1162 // Check signature and header size.
1163 if (pShadowFile
->dwSignature
!= SHD_WIN2003_SIGNATURE
|| pShadowFile
->cbHeader
!= sizeof(SHD_HEADER
))
1165 ERR("Signature or Header Size mismatch for file \"%S\"!\n", pwszFilePath
);
1169 // Retrieve the associated printer from the list.
1170 pwszPrinterName
= (PWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrinterName
);
1171 pPrinter
= LookupElementSkiplist(&PrinterList
, &pwszPrinterName
, NULL
);
1174 ERR("Shadow file \"%S\" references a non-existing printer \"%S\"!\n", pwszFilePath
, pwszPrinterName
);
1178 // Retrieve the associated Print Processor from the list.
1179 pwszPrintProcessor
= (PWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrintProcessor
);
1180 pPrintProcessor
= FindPrintProcessor(pwszPrintProcessor
);
1181 if (!pPrintProcessor
)
1183 ERR("Shadow file \"%S\" references a non-existing Print Processor \"%S\"!\n", pwszFilePath
, pwszPrintProcessor
);
1187 // Create a new job structure and copy over the relevant fields.
1188 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
1191 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1195 pJob
->dwJobID
= pShadowFile
->dwJobID
;
1196 pJob
->dwPriority
= pShadowFile
->dwPriority
;
1197 pJob
->dwStartTime
= pShadowFile
->dwStartTime
;
1198 pJob
->dwTotalPages
= pShadowFile
->dwTotalPages
;
1199 pJob
->dwUntilTime
= pShadowFile
->dwUntilTime
;
1200 pJob
->pPrinter
= pPrinter
;
1201 pJob
->pPrintProcessor
= pPrintProcessor
;
1202 pJob
->pDevMode
= DuplicateDevMode((PDEVMODEW
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDevMode
));
1203 pJob
->pwszDatatype
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDatatype
));
1204 pJob
->pwszDocumentName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDocumentName
));
1205 pJob
->pwszMachineName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offMachineName
));
1206 pJob
->pwszNotifyName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offNotifyName
));
1208 if (pShadowFile
->offPrintProcessorParameters
)
1209 pJob
->pwszPrintProcessorParameters
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrintProcessorParameters
));
1211 pJob
->pwszUserName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offUserName
));
1212 CopyMemory(&pJob
->stSubmitted
, &pShadowFile
->stSubmitted
, sizeof(SYSTEMTIME
));
1214 // Jobs read from shadow files were always added using AddJob.
1215 pJob
->bAddedJob
= TRUE
;
1217 pReturnValue
= pJob
;
1221 DllFreeSplMem(pShadowFile
);
1223 if (hFile
!= INVALID_HANDLE_VALUE
)
1226 return pReturnValue
;
1230 WriteJobShadowFile(PWSTR pwszFilePath
, const PLOCAL_JOB pJob
)
1232 BOOL bReturnValue
= FALSE
;
1235 DWORD cbDocumentName
;
1237 DWORD cbMachineName
;
1239 DWORD cbPrinterDriver
;
1240 DWORD cbPrinterName
;
1241 DWORD cbPrintProcessor
;
1242 DWORD cbPrintProcessorParameters
= 0;
1245 DWORD dwCurrentOffset
;
1246 HANDLE hSHDFile
= INVALID_HANDLE_VALUE
;
1247 HANDLE hSPLFile
= INVALID_HANDLE_VALUE
;
1248 PSHD_HEADER pShadowFile
= NULL
;
1250 // Try to open the SHD file.
1251 hSHDFile
= CreateFileW(pwszFilePath
, GENERIC_WRITE
, FILE_SHARE_READ
, NULL
, CREATE_ALWAYS
, 0, NULL
);
1252 if (hSHDFile
== INVALID_HANDLE_VALUE
)
1254 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1258 // Compute the total size of the shadow file.
1259 cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
1260 cbDevMode
= pJob
->pDevMode
->dmSize
+ pJob
->pDevMode
->dmDriverExtra
;
1261 cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
1262 cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
1263 cbNotifyName
= (wcslen(pJob
->pwszNotifyName
) + 1) * sizeof(WCHAR
);
1264 cbPrinterDriver
= (wcslen(pJob
->pPrinter
->pwszPrinterDriver
) + 1) * sizeof(WCHAR
);
1265 cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
1266 cbPrintProcessor
= (wcslen(pJob
->pPrintProcessor
->pwszName
) + 1) * sizeof(WCHAR
);
1267 cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
1269 // Print Processor Parameters are optional.
1270 if (pJob
->pwszPrintProcessorParameters
)
1271 cbPrintProcessorParameters
= (wcslen(pJob
->pwszPrintProcessorParameters
) + 1) * sizeof(WCHAR
);
1273 cbFileSize
= sizeof(SHD_HEADER
) + cbDatatype
+ cbDocumentName
+ cbDevMode
+ cbMachineName
+ cbNotifyName
+ cbPrinterDriver
+ cbPrinterName
+ cbPrintProcessor
+ cbPrintProcessorParameters
+ cbUserName
;
1275 // Allocate memory for it.
1276 pShadowFile
= DllAllocSplMem(cbFileSize
);
1279 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1283 // Fill out the shadow file header information.
1284 pShadowFile
->dwSignature
= SHD_WIN2003_SIGNATURE
;
1285 pShadowFile
->cbHeader
= sizeof(SHD_HEADER
);
1288 pShadowFile
->dwJobID
= pJob
->dwJobID
;
1289 pShadowFile
->dwPriority
= pJob
->dwPriority
;
1290 pShadowFile
->dwStartTime
= pJob
->dwStartTime
;
1291 pShadowFile
->dwTotalPages
= pJob
->dwTotalPages
;
1292 pShadowFile
->dwUntilTime
= pJob
->dwUntilTime
;
1293 CopyMemory(&pShadowFile
->stSubmitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
1295 // Determine the file size of the .SPL file
1296 wcscpy(wcsrchr(pwszFilePath
, L
'.'), L
".SPL");
1297 hSPLFile
= CreateFileW(pwszFilePath
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
1298 if (hSPLFile
!= INVALID_HANDLE_VALUE
)
1299 pShadowFile
->dwSPLSize
= GetFileSize(hSPLFile
, NULL
);
1301 // Add the extra values that are stored as offsets in the shadow file.
1302 // The first value begins right after the shadow file header.
1303 dwCurrentOffset
= sizeof(SHD_HEADER
);
1305 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszDatatype
, cbDatatype
);
1306 pShadowFile
->offDatatype
= dwCurrentOffset
;
1307 dwCurrentOffset
+= cbDatatype
;
1309 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszDocumentName
, cbDocumentName
);
1310 pShadowFile
->offDocumentName
= dwCurrentOffset
;
1311 dwCurrentOffset
+= cbDocumentName
;
1313 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pDevMode
, cbDevMode
);
1314 pShadowFile
->offDevMode
= dwCurrentOffset
;
1315 dwCurrentOffset
+= cbDevMode
;
1317 // offDriverName is only written, but automatically determined through offPrinterName when reading.
1318 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrinter
->pwszPrinterDriver
, cbPrinterDriver
);
1319 pShadowFile
->offDriverName
= dwCurrentOffset
;
1320 dwCurrentOffset
+= cbPrinterDriver
;
1322 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszMachineName
, cbMachineName
);
1323 pShadowFile
->offMachineName
= dwCurrentOffset
;
1324 dwCurrentOffset
+= cbMachineName
;
1326 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszNotifyName
, cbNotifyName
);
1327 pShadowFile
->offNotifyName
= dwCurrentOffset
;
1328 dwCurrentOffset
+= cbNotifyName
;
1330 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
1331 pShadowFile
->offPrinterName
= dwCurrentOffset
;
1332 dwCurrentOffset
+= cbPrinterName
;
1334 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrintProcessor
->pwszName
, cbPrintProcessor
);
1335 pShadowFile
->offPrintProcessor
= dwCurrentOffset
;
1336 dwCurrentOffset
+= cbPrintProcessor
;
1338 if (cbPrintProcessorParameters
)
1340 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszPrintProcessorParameters
, cbPrintProcessorParameters
);
1341 pShadowFile
->offPrintProcessorParameters
= dwCurrentOffset
;
1342 dwCurrentOffset
+= cbPrintProcessorParameters
;
1345 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszUserName
, cbUserName
);
1346 pShadowFile
->offUserName
= dwCurrentOffset
;
1347 dwCurrentOffset
+= cbUserName
;
1350 if (!WriteFile(hSHDFile
, pShadowFile
, cbFileSize
, &cbWritten
, NULL
))
1352 ERR("WriteFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1356 bReturnValue
= TRUE
;
1360 DllFreeSplMem(pShadowFile
);
1362 if (hSHDFile
!= INVALID_HANDLE_VALUE
)
1363 CloseHandle(hSHDFile
);
1365 if (hSPLFile
!= INVALID_HANDLE_VALUE
)
1366 CloseHandle(hSPLFile
);
1368 return bReturnValue
;
1372 FreeJob(PLOCAL_JOB pJob
)
1376 // Remove the Job from both Job Lists.
1377 DeleteElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
1378 DeleteElementSkiplist(&GlobalJobList
, pJob
);
1380 // Try to delete the corresponding .SHD file.
1381 pwszSHDFile
= DllAllocSplMem(GetJobFilePath(L
"SHD", 0, NULL
));
1382 if (pwszSHDFile
&& GetJobFilePath(L
"SHD", pJob
->dwJobID
, pwszSHDFile
))
1383 DeleteFileW(pwszSHDFile
);
1385 // Free memory for the mandatory fields.
1386 DllFreeSplMem(pJob
->pDevMode
);
1387 DllFreeSplStr(pJob
->pwszDatatype
);
1388 DllFreeSplStr(pJob
->pwszDocumentName
);
1389 DllFreeSplStr(pJob
->pwszMachineName
);
1390 DllFreeSplStr(pJob
->pwszNotifyName
);
1391 DllFreeSplStr(pJob
->pwszUserName
);
1393 // Free memory for the optional fields if they are present.
1394 if (pJob
->pwszOutputFile
)
1395 DllFreeSplStr(pJob
->pwszOutputFile
);
1397 if (pJob
->pwszPrintProcessorParameters
)
1398 DllFreeSplStr(pJob
->pwszPrintProcessorParameters
);
1400 if (pJob
->pwszStatus
)
1401 DllFreeSplStr(pJob
->pwszStatus
);
1403 // Finally free the job structure itself.
1404 DllFreeSplMem(pJob
);