2 * PROJECT: ReactOS Local Spooler
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Functions for managing print jobs
5 * COPYRIGHT: Copyright 2015-2017 Colin Finck (colin@reactos.org)
11 SKIPLIST GlobalJobList
;
14 static DWORD _dwLastJobID
;
17 static DWORD dwJobInfo1Offsets
[] = {
18 FIELD_OFFSET(JOB_INFO_1W
, pPrinterName
),
19 FIELD_OFFSET(JOB_INFO_1W
, pMachineName
),
20 FIELD_OFFSET(JOB_INFO_1W
, pUserName
),
21 FIELD_OFFSET(JOB_INFO_1W
, pDocument
),
22 FIELD_OFFSET(JOB_INFO_1W
, pDatatype
),
23 FIELD_OFFSET(JOB_INFO_1W
, pStatus
),
27 static DWORD dwJobInfo2Offsets
[] = {
28 FIELD_OFFSET(JOB_INFO_2W
, pPrinterName
),
29 FIELD_OFFSET(JOB_INFO_2W
, pMachineName
),
30 FIELD_OFFSET(JOB_INFO_2W
, pUserName
),
31 FIELD_OFFSET(JOB_INFO_2W
, pDocument
),
32 FIELD_OFFSET(JOB_INFO_2W
, pNotifyName
),
33 FIELD_OFFSET(JOB_INFO_2W
, pDatatype
),
34 FIELD_OFFSET(JOB_INFO_2W
, pPrintProcessor
),
35 FIELD_OFFSET(JOB_INFO_2W
, pParameters
),
36 FIELD_OFFSET(JOB_INFO_2W
, pDriverName
),
37 FIELD_OFFSET(JOB_INFO_2W
, pStatus
),
45 * Returns whether two strings are equal.
46 * Unlike wcscmp, this function also works with NULL strings.
49 * First string to compare.
52 * Second string to compare.
55 * TRUE if the strings are equal, FALSE if they differ.
58 _EqualStrings(PCWSTR pwszA
, PCWSTR pwszB
)
69 return (wcscmp(pwszA
, pwszB
) == 0);
73 _GetNextJobID(PDWORD dwJobID
)
77 while (LookupElementSkiplist(&GlobalJobList
, &_dwLastJobID
, NULL
))
79 // This ID is already taken. Try the next one.
83 if (!IS_VALID_JOB_ID(_dwLastJobID
))
85 ERR("Job ID %lu isn't valid!\n", _dwLastJobID
);
89 *dwJobID
= _dwLastJobID
;
94 * @name _GlobalJobListCompareRoutine
96 * SKIPLIST_COMPARE_ROUTINE for the Global Job List.
97 * We need the Global Job List to check whether a Job ID is already in use. Consequently, this list is sorted by ID.
100 _GlobalJobListCompareRoutine(PVOID FirstStruct
, PVOID SecondStruct
)
102 PLOCAL_JOB A
= (PLOCAL_JOB
)FirstStruct
;
103 PLOCAL_JOB B
= (PLOCAL_JOB
)SecondStruct
;
105 return A
->dwJobID
- B
->dwJobID
;
109 * @name _PrinterJobListCompareRoutine
111 * SKIPLIST_COMPARE_ROUTINE for each Printer's Job List.
112 * Jobs in this list are sorted in the desired order of processing.
115 _PrinterJobListCompareRoutine(PVOID FirstStruct
, PVOID SecondStruct
)
117 PLOCAL_JOB A
= (PLOCAL_JOB
)FirstStruct
;
118 PLOCAL_JOB B
= (PLOCAL_JOB
)SecondStruct
;
120 FILETIME ftSubmittedA
;
121 FILETIME ftSubmittedB
;
123 // First compare the priorities to determine the order.
124 // The job with a higher priority shall come first.
125 iComparison
= A
->dwPriority
- B
->dwPriority
;
126 if (iComparison
!= 0)
129 // Both have the same priority, so go by creation time.
130 if (!SystemTimeToFileTime(&A
->stSubmitted
, &ftSubmittedA
))
132 ERR("SystemTimeToFileTime failed for A with error %lu!\n", GetLastError());
136 if (!SystemTimeToFileTime(&B
->stSubmitted
, &ftSubmittedB
))
138 ERR("SystemTimeToFileTime failed for B with error %lu!\n", GetLastError());
142 return CompareFileTime(&ftSubmittedA
, &ftSubmittedB
);
146 GetJobFilePath(PCWSTR pwszExtension
, DWORD dwJobID
, PWSTR pwszOutput
)
148 TRACE("GetJobFilePath(%S, %lu, %p)\n", pwszExtension
, dwJobID
, pwszOutput
);
152 CopyMemory(pwszOutput
, wszJobDirectory
, cchJobDirectory
* sizeof(WCHAR
));
153 swprintf(&pwszOutput
[cchJobDirectory
], L
"\\%05lu.%s", dwJobID
, pwszExtension
);
156 // pwszExtension may be L"SPL" or L"SHD", same length for both!
157 return (cchJobDirectory
+ sizeof("\\?????.SPL")) * sizeof(WCHAR
);
161 InitializeGlobalJobList(void)
163 const WCHAR wszPath
[] = L
"\\?????.SHD";
164 const DWORD cchPath
= _countof(wszPath
) - 1;
169 PLOCAL_JOB pJob
= NULL
;
171 WCHAR wszFullPath
[MAX_PATH
];
172 WIN32_FIND_DATAW FindData
;
174 TRACE("InitializeGlobalJobList()\n");
176 // This one is incremented in _GetNextJobID.
179 // Initialize an empty list for all jobs of all local printers.
180 // We will search it by Job ID (supply a pointer to a DWORD in LookupElementSkiplist).
181 InitializeSkiplist(&GlobalJobList
, DllAllocSplMem
, _GlobalJobListCompareRoutine
, (PSKIPLIST_FREE_ROUTINE
)DllFreeSplMem
);
183 // Construct the full path search pattern.
184 CopyMemory(wszFullPath
, wszJobDirectory
, cchJobDirectory
* sizeof(WCHAR
));
185 CopyMemory(&wszFullPath
[cchJobDirectory
], wszPath
, (cchPath
+ 1) * sizeof(WCHAR
));
187 // Use the search pattern to look for unfinished jobs serialized in shadow files (.SHD)
188 hFind
= FindFirstFileW(wszFullPath
, &FindData
);
189 if (hFind
== INVALID_HANDLE_VALUE
)
191 // No unfinished jobs found.
192 dwErrorCode
= ERROR_SUCCESS
;
198 // Skip possible subdirectories.
199 if (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
202 // Extract the Job ID and verify the file name format at the same time.
203 // This includes all valid names (like "00005.SHD") and excludes invalid ones (like "10ABC.SHD").
204 dwJobID
= wcstoul(FindData
.cFileName
, &p
, 10);
205 if (!IS_VALID_JOB_ID(dwJobID
))
208 if (wcsicmp(p
, L
".SHD") != 0)
211 // This shadow file has a valid name. Construct the full path and try to load it.
212 GetJobFilePath(L
"SHD", dwJobID
, wszFullPath
);
213 pJob
= ReadJobShadowFile(wszFullPath
);
217 // Add it to the Global Job List.
218 if (!InsertElementSkiplist(&GlobalJobList
, pJob
))
220 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
221 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob
->dwJobID
);
225 // Add it to the Printer's Job List.
226 if (!InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
))
228 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
229 ERR("InsertElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob
->dwJobID
);
233 while (FindNextFileW(hFind
, &FindData
));
235 dwErrorCode
= ERROR_SUCCESS
;
242 SetLastError(dwErrorCode
);
243 return (dwErrorCode
== ERROR_SUCCESS
);
247 InitializePrinterJobList(PLOCAL_PRINTER pPrinter
)
249 TRACE("InitializePrinterJobList(%p)\n", pPrinter
);
251 // Initialize an empty list for this printer's jobs.
252 // This one is only for sorting the jobs. If you need to lookup a job, search the GlobalJobList by Job ID.
253 InitializeSkiplist(&pPrinter
->JobList
, DllAllocSplMem
, _PrinterJobListCompareRoutine
, (PSKIPLIST_FREE_ROUTINE
)DllFreeSplMem
);
257 CreateJob(PLOCAL_PRINTER_HANDLE pPrinterHandle
)
259 const WCHAR wszDoubleBackslash
[] = L
"\\";
260 const DWORD cchDoubleBackslash
= _countof(wszDoubleBackslash
) - 1;
262 DWORD cchMachineName
;
266 RPC_BINDING_HANDLE hServerBinding
= NULL
;
267 RPC_WSTR pwszBinding
= NULL
;
268 RPC_WSTR pwszMachineName
= NULL
;
270 TRACE("CreateJob(%p)\n", pPrinterHandle
);
273 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
276 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
277 ERR("DllAllocSplMem failed!\n");
281 // Reserve an ID for this job.
282 if (!_GetNextJobID(&pJob
->dwJobID
))
284 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
288 // Copy over defaults to the LOCAL_JOB structure.
289 pJob
->pPrinter
= pPrinterHandle
->pPrinter
;
290 pJob
->pPrintProcessor
= pPrinterHandle
->pPrinter
->pPrintProcessor
;
291 pJob
->dwPriority
= DEF_PRIORITY
;
292 pJob
->dwStatus
= JOB_STATUS_SPOOLING
;
293 pJob
->pwszDatatype
= AllocSplStr(pPrinterHandle
->pwszDatatype
);
294 pJob
->pwszDocumentName
= AllocSplStr(wszDefaultDocumentName
);
295 pJob
->pDevMode
= DuplicateDevMode(pPrinterHandle
->pDevMode
);
296 GetSystemTime(&pJob
->stSubmitted
);
298 // Get the user name for the Job.
299 cchUserName
= UNLEN
+ 1;
300 pJob
->pwszUserName
= DllAllocSplMem(cchUserName
* sizeof(WCHAR
));
301 if (!GetUserNameW(pJob
->pwszUserName
, &cchUserName
))
303 dwErrorCode
= GetLastError();
304 ERR("GetUserNameW failed with error %lu!\n", dwErrorCode
);
308 // FIXME: For now, pwszNotifyName equals pwszUserName.
309 pJob
->pwszNotifyName
= AllocSplStr(pJob
->pwszUserName
);
311 // Get the name of the machine that submitted the Job over RPC.
312 dwErrorCode
= RpcBindingServerFromClient(NULL
, &hServerBinding
);
313 if (dwErrorCode
!= RPC_S_OK
)
315 ERR("RpcBindingServerFromClient failed with status %lu!\n", dwErrorCode
);
319 dwErrorCode
= RpcBindingToStringBindingW(hServerBinding
, &pwszBinding
);
320 if (dwErrorCode
!= RPC_S_OK
)
322 ERR("RpcBindingToStringBindingW failed with status %lu!\n", dwErrorCode
);
326 dwErrorCode
= RpcStringBindingParseW(pwszBinding
, NULL
, NULL
, &pwszMachineName
, NULL
, NULL
);
327 if (dwErrorCode
!= RPC_S_OK
)
329 ERR("RpcStringBindingParseW failed with status %lu!\n", dwErrorCode
);
333 cchMachineName
= wcslen(pwszMachineName
);
334 pJob
->pwszMachineName
= DllAllocSplMem((cchMachineName
+ cchDoubleBackslash
+ 1) * sizeof(WCHAR
));
335 CopyMemory(pJob
->pwszMachineName
, wszDoubleBackslash
, cchDoubleBackslash
* sizeof(WCHAR
));
336 CopyMemory(&pJob
->pwszMachineName
[cchDoubleBackslash
], pwszMachineName
, (cchMachineName
+ 1) * sizeof(WCHAR
));
338 // Add the job to the Global Job List.
339 if (!InsertElementSkiplist(&GlobalJobList
, pJob
))
341 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
342 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob
->dwJobID
);
346 // Add the job at the end of the Printer's Job List.
347 // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
348 if (!InsertTailElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
))
350 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
351 ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob
->dwJobID
);
355 // We were successful!
356 pPrinterHandle
->bStartedDoc
= TRUE
;
357 pPrinterHandle
->pJob
= pJob
;
358 dwErrorCode
= ERROR_SUCCESS
;
360 // Don't let the cleanup routine free this.
368 RpcStringFreeW(&pwszMachineName
);
371 RpcStringFreeW(&pwszBinding
);
374 RpcBindingFree(&hServerBinding
);
380 LocalAddJob(HANDLE hPrinter
, DWORD Level
, PBYTE pData
, DWORD cbBuf
, PDWORD pcbNeeded
)
382 ADDJOB_INFO_1W AddJobInfo1
;
384 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
385 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
387 TRACE("LocalAddJob(%p, %lu, %p, %lu, %p)\n", hPrinter
, Level
, pData
, cbBuf
, pcbNeeded
);
389 // Check if this is a printer handle.
390 if (pHandle
->HandleType
!= HandleType_Printer
)
392 dwErrorCode
= ERROR_INVALID_HANDLE
;
396 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
398 // This handle must not have started a job yet!
399 if (pPrinterHandle
->pJob
)
401 dwErrorCode
= ERROR_INVALID_HANDLE
;
405 // Check if this is the right structure level.
408 dwErrorCode
= ERROR_INVALID_LEVEL
;
412 // Check if the printer is set to do direct printing.
413 // The Job List isn't used in this case.
414 if (pPrinterHandle
->pPrinter
->dwAttributes
& PRINTER_ATTRIBUTE_DIRECT
)
416 dwErrorCode
= ERROR_INVALID_ACCESS
;
420 // Check if the supplied buffer is large enough.
421 *pcbNeeded
= sizeof(ADDJOB_INFO_1W
) + GetJobFilePath(L
"SPL", 0, NULL
);
422 if (cbBuf
< *pcbNeeded
)
424 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
428 // All requirements are met - create a new job.
429 dwErrorCode
= CreateJob(pPrinterHandle
);
430 if (dwErrorCode
!= ERROR_SUCCESS
)
433 // Mark that this job was started with AddJob (so that it can be scheduled for printing with ScheduleJob).
434 pPrinterHandle
->pJob
->bAddedJob
= TRUE
;
436 // Return a proper ADDJOB_INFO_1W structure.
437 AddJobInfo1
.JobId
= pPrinterHandle
->pJob
->dwJobID
;
438 AddJobInfo1
.Path
= (PWSTR
)(pData
+ sizeof(ADDJOB_INFO_1W
));
440 CopyMemory(pData
, &AddJobInfo1
, sizeof(ADDJOB_INFO_1W
));
441 GetJobFilePath(L
"SPL", AddJobInfo1
.JobId
, AddJobInfo1
.Path
);
444 SetLastError(dwErrorCode
);
445 return (dwErrorCode
== ERROR_SUCCESS
);
450 _LocalGetJobLevel1(PLOCAL_JOB pJob
, PJOB_INFO_1W
* ppJobInfo
, PBYTE
* ppJobInfoEnd
, PDWORD pcbNeeded
)
453 DWORD cbDocumentName
= 0;
457 DWORD cbUserName
= 0;
458 PWSTR pwszStrings
[6];
460 // Calculate the string lengths.
463 cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
464 cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
465 cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
467 // These values are optional.
468 if (pJob
->pwszDocumentName
)
469 cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
471 if (pJob
->pwszStatus
)
472 cbStatus
= (wcslen(pJob
->pwszStatus
) + 1) * sizeof(WCHAR
);
474 if (pJob
->pwszUserName
)
475 cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
477 *pcbNeeded
+= sizeof(JOB_INFO_1W
) + cbDatatype
+ cbDocumentName
+ cbMachineName
+ cbPrinterName
+ cbStatus
+ cbUserName
;
481 // Set the general fields.
482 (*ppJobInfo
)->JobId
= pJob
->dwJobID
;
483 (*ppJobInfo
)->Status
= pJob
->dwStatus
;
484 (*ppJobInfo
)->Priority
= pJob
->dwPriority
;
485 (*ppJobInfo
)->TotalPages
= pJob
->dwTotalPages
;
486 (*ppJobInfo
)->PagesPrinted
= pJob
->dwPagesPrinted
;
487 CopyMemory(&(*ppJobInfo
)->Submitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
489 // Position in JOB_INFO_1W is the 1-based index of the job in the processing queue.
490 // Retrieve this through the element index of the job in the Printer's Job List.
491 if (!LookupElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
, &(*ppJobInfo
)->Position
))
493 ERR("pJob could not be located in the Printer's Job List!\n");
497 // Make the index 1-based.
498 ++(*ppJobInfo
)->Position
;
500 // Set the pPrinterName field.
501 pwszStrings
[0] = pJob
->pPrinter
->pwszPrinterName
;
503 // Set the pMachineName field.
504 pwszStrings
[1] = pJob
->pwszMachineName
;
506 // Set the pUserName field.
507 pwszStrings
[2] = pJob
->pwszUserName
;
509 // Set the pDocument field.
510 pwszStrings
[3] = pJob
->pwszDocumentName
;
512 // Set the pDatatype field.
513 pwszStrings
[4] = pJob
->pwszDatatype
;
515 // Set the pStatus field.
516 pwszStrings
[5] = pJob
->pwszStatus
;
518 // Finally copy the structure and advance to the next one in the output buffer.
519 *ppJobInfoEnd
= PackStrings(pwszStrings
, (PBYTE
)(*ppJobInfo
), dwJobInfo1Offsets
, *ppJobInfoEnd
);
524 _LocalGetJobLevel2(PLOCAL_JOB pJob
, PJOB_INFO_2W
* ppJobInfo
, PBYTE
* ppJobInfoEnd
, PDWORD pcbNeeded
)
528 DWORD cbDocumentName
= 0;
531 DWORD cbNotifyName
= 0;
533 DWORD cbPrintProcessor
;
534 DWORD cbPrintProcessorParameters
= 0;
536 DWORD cbUserName
= 0;
538 FILETIME ftSubmitted
;
539 PWSTR pwszStrings
[10];
540 ULARGE_INTEGER uliNow
;
541 ULARGE_INTEGER uliSubmitted
;
543 // Calculate the string lengths.
544 cbDevMode
= pJob
->pDevMode
->dmSize
+ pJob
->pDevMode
->dmDriverExtra
;
548 cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
549 cbDriverName
= (wcslen(pJob
->pPrinter
->pwszPrinterDriver
) + 1) * sizeof(WCHAR
);
550 cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
551 cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
552 cbPrintProcessor
= (wcslen(pJob
->pPrintProcessor
->pwszName
) + 1) * sizeof(WCHAR
);
554 // These values are optional.
555 if (pJob
->pwszDocumentName
)
556 cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
558 if (pJob
->pwszNotifyName
)
559 cbNotifyName
= (wcslen(pJob
->pwszNotifyName
) + 1) * sizeof(WCHAR
);
561 if (pJob
->pwszPrintProcessorParameters
)
562 cbPrintProcessorParameters
= (wcslen(pJob
->pwszPrintProcessorParameters
) + 1) * sizeof(WCHAR
);
564 if (pJob
->pwszStatus
)
565 cbStatus
= (wcslen(pJob
->pwszStatus
) + 1) * sizeof(WCHAR
);
567 if (pJob
->pwszUserName
)
568 cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
570 *pcbNeeded
+= sizeof(JOB_INFO_2W
) + cbDatatype
+ cbDevMode
+ cbDocumentName
+ cbDriverName
+ cbMachineName
+ cbNotifyName
+ cbPrinterName
+ cbPrintProcessor
+ cbPrintProcessorParameters
+ cbStatus
+ cbUserName
;
574 // Set the general fields.
575 (*ppJobInfo
)->JobId
= pJob
->dwJobID
;
576 (*ppJobInfo
)->Status
= pJob
->dwStatus
;
577 (*ppJobInfo
)->Priority
= pJob
->dwPriority
;
578 (*ppJobInfo
)->StartTime
= pJob
->dwStartTime
;
579 (*ppJobInfo
)->UntilTime
= pJob
->dwUntilTime
;
580 (*ppJobInfo
)->TotalPages
= pJob
->dwTotalPages
;
581 (*ppJobInfo
)->PagesPrinted
= pJob
->dwPagesPrinted
;
582 CopyMemory(&(*ppJobInfo
)->Submitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
584 // Time in JOB_INFO_2W is the number of milliseconds elapsed since the job was submitted. Calculate this time.
585 if (!SystemTimeToFileTime(&pJob
->stSubmitted
, &ftSubmitted
))
587 ERR("SystemTimeToFileTime failed with error %lu!\n", GetLastError());
591 GetSystemTimeAsFileTime(&ftNow
);
592 uliSubmitted
.LowPart
= ftSubmitted
.dwLowDateTime
;
593 uliSubmitted
.HighPart
= ftSubmitted
.dwHighDateTime
;
594 uliNow
.LowPart
= ftNow
.dwLowDateTime
;
595 uliNow
.HighPart
= ftNow
.dwHighDateTime
;
596 (*ppJobInfo
)->Time
= (DWORD
)((uliNow
.QuadPart
- uliSubmitted
.QuadPart
) / 10000);
598 // Position in JOB_INFO_2W is the 1-based index of the job in the processing queue.
599 // Retrieve this through the element index of the job in the Printer's Job List.
600 if (!LookupElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
, &(*ppJobInfo
)->Position
))
602 ERR("pJob could not be located in the Printer's Job List!\n");
606 // Make the index 1-based.
607 ++(*ppJobInfo
)->Position
;
610 FIXME("Setting pSecurityDescriptor and Size to 0 for now!\n");
611 (*ppJobInfo
)->pSecurityDescriptor
= NULL
;
612 (*ppJobInfo
)->Size
= 0;
614 // Set the pDevMode field (and copy the DevMode).
615 *ppJobInfoEnd
-= cbDevMode
;
616 CopyMemory(*ppJobInfoEnd
, pJob
->pDevMode
, cbDevMode
);
617 (*ppJobInfo
)->pDevMode
= (PDEVMODEW
)(*ppJobInfoEnd
);
619 // Set the pPrinterName field.
620 pwszStrings
[0] = pJob
->pPrinter
->pwszPrinterName
;
622 // Set the pMachineName field.
623 pwszStrings
[1] = pJob
->pwszMachineName
;
625 // Set the pUserName field.
626 pwszStrings
[2] = pJob
->pwszUserName
;
628 // Set the pDocument field.
629 pwszStrings
[3] = pJob
->pwszDocumentName
;
631 // Set the pNotifyName field.
632 pwszStrings
[4] = pJob
->pwszNotifyName
;
634 // Set the pDatatype field.
635 pwszStrings
[5] = pJob
->pwszDatatype
;
637 // Set the pPrintProcessor field.
638 pwszStrings
[6] = pJob
->pPrintProcessor
->pwszName
;
640 // Set the pParameters field.
641 pwszStrings
[7] = pJob
->pwszPrintProcessorParameters
;
643 // Set the pDriverName field.
644 pwszStrings
[8] = pJob
->pPrinter
->pwszPrinterDriver
;
646 // Set the pStatus field.
647 pwszStrings
[9] = pJob
->pwszStatus
;
649 // Finally copy the structure and advance to the next one in the output buffer.
650 *ppJobInfoEnd
= PackStrings(pwszStrings
, (PBYTE
)(*ppJobInfo
), dwJobInfo2Offsets
, *ppJobInfoEnd
);
655 LocalGetJob(HANDLE hPrinter
, DWORD JobId
, DWORD Level
, PBYTE pStart
, DWORD cbBuf
, LPDWORD pcbNeeded
)
658 PBYTE pEnd
= &pStart
[cbBuf
];
659 PLOCAL_HANDLE pHandle
;
661 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
663 TRACE("LocalGetJob(%p, %lu, %lu, %p, %lu, %p)\n", hPrinter
, JobId
, Level
, pStart
, cbBuf
, pcbNeeded
);
665 // Check if this is a printer handle.
666 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
667 if (pHandle
->HandleType
!= HandleType_Printer
)
669 dwErrorCode
= ERROR_INVALID_HANDLE
;
673 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
675 // Get the desired job.
676 pJob
= LookupElementSkiplist(&GlobalJobList
, &JobId
, NULL
);
677 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
679 dwErrorCode
= ERROR_INVALID_PARAMETER
;
685 // The caller supplied an invalid level for GetJob.
686 dwErrorCode
= ERROR_INVALID_LEVEL
;
690 // Count the required buffer size.
694 _LocalGetJobLevel1(pJob
, NULL
, NULL
, pcbNeeded
);
696 _LocalGetJobLevel2(pJob
, NULL
, NULL
, pcbNeeded
);
698 // Check if the supplied buffer is large enough.
699 if (cbBuf
< *pcbNeeded
)
701 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
705 // Copy over the Job information.
706 pEnd
= &pStart
[*pcbNeeded
];
709 _LocalGetJobLevel1(pJob
, (PJOB_INFO_1W
*)&pStart
, &pEnd
, NULL
);
711 _LocalGetJobLevel2(pJob
, (PJOB_INFO_2W
*)&pStart
, &pEnd
, NULL
);
713 dwErrorCode
= ERROR_SUCCESS
;
716 SetLastError(dwErrorCode
);
717 return (dwErrorCode
== ERROR_SUCCESS
);
721 _LocalSetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PJOB_INFO_1W pJobInfo
)
725 // First check the validity of the input before changing anything.
726 if (!FindDatatype(pJob
->pPrintProcessor
, pJobInfo
->pDatatype
))
728 dwErrorCode
= ERROR_INVALID_DATATYPE
;
732 // Check if the datatype has changed.
733 if (!_EqualStrings(pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
735 // Use the new value.
736 if (!ReallocSplStr(&pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
738 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
739 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
744 // Check if the document name has changed. An empty string is permitted here!
745 if (!_EqualStrings(pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
747 // Use the new value.
748 if (!ReallocSplStr(&pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
750 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
751 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
756 // Check if the status message has changed. An empty string is permitted here!
757 if (!_EqualStrings(pJob
->pwszStatus
, pJobInfo
->pStatus
))
759 // Use the new value.
760 if (!ReallocSplStr(&pJob
->pwszStatus
, pJobInfo
->pStatus
))
762 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
763 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
768 // Check if the user name has changed. An empty string is permitted here!
769 if (!_EqualStrings(pJob
->pwszUserName
, pJobInfo
->pUserName
))
771 // The new user name doesn't need to exist, so no additional verification is required.
773 // Use the new value.
774 if (!ReallocSplStr(&pJob
->pwszUserName
, pJobInfo
->pUserName
))
776 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
777 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
782 // Check if the priority has changed.
783 if (pJob
->dwPriority
!= pJobInfo
->Priority
&& IS_VALID_PRIORITY(pJobInfo
->Priority
))
785 // Set the new priority.
786 pJob
->dwPriority
= pJobInfo
->Priority
;
788 // Remove and reinsert the job in the Printer's Job List.
789 // The Compare function will be used to find the right position now considering the new priority.
790 DeleteElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
791 InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
794 // Check if the status flags have changed.
795 if (pJob
->dwStatus
!= pJobInfo
->Status
)
797 // Only add status flags that make sense.
798 if (pJobInfo
->Status
& JOB_STATUS_PAUSED
)
799 pJob
->dwStatus
|= JOB_STATUS_PAUSED
;
801 if (pJobInfo
->Status
& JOB_STATUS_ERROR
)
802 pJob
->dwStatus
|= JOB_STATUS_ERROR
;
804 if (pJobInfo
->Status
& JOB_STATUS_OFFLINE
)
805 pJob
->dwStatus
|= JOB_STATUS_OFFLINE
;
807 if (pJobInfo
->Status
& JOB_STATUS_PAPEROUT
)
808 pJob
->dwStatus
|= JOB_STATUS_PAPEROUT
;
811 dwErrorCode
= ERROR_SUCCESS
;
818 _LocalSetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PJOB_INFO_2W pJobInfo
)
821 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
823 // First check the validity of the input before changing anything.
824 pPrintProcessor
= FindPrintProcessor(pJobInfo
->pPrintProcessor
);
825 if (!pPrintProcessor
)
827 dwErrorCode
= ERROR_UNKNOWN_PRINTPROCESSOR
;
831 if (!FindDatatype(pPrintProcessor
, pJobInfo
->pDatatype
))
833 dwErrorCode
= ERROR_INVALID_DATATYPE
;
837 // Check if the datatype has changed.
838 if (!_EqualStrings(pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
840 // Use the new value.
841 if (!ReallocSplStr(&pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
843 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
844 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
849 // Check if the document name has changed. An empty string is permitted here!
850 if (!_EqualStrings(pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
852 // Use the new value.
853 if (!ReallocSplStr(&pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
855 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
856 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
861 // Check if the notify name has changed. An empty string is permitted here!
862 if (!_EqualStrings(pJob
->pwszNotifyName
, pJobInfo
->pNotifyName
))
864 // The new notify name doesn't need to exist, so no additional verification is required.
866 // Use the new value.
867 if (!ReallocSplStr(&pJob
->pwszNotifyName
, pJobInfo
->pNotifyName
))
869 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
870 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
875 // Check if the Print Processor Parameters have changed. An empty string is permitted here!
876 if (!_EqualStrings(pJob
->pwszPrintProcessorParameters
, pJobInfo
->pParameters
))
878 // Use the new value.
879 if (!ReallocSplStr(&pJob
->pwszPrintProcessorParameters
, pJobInfo
->pParameters
))
881 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
882 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
887 // Check if the Status Message has changed. An empty string is permitted here!
888 if (!_EqualStrings(pJob
->pwszStatus
, pJobInfo
->pStatus
))
890 // Use the new value.
891 if (!ReallocSplStr(&pJob
->pwszStatus
, pJobInfo
->pStatus
))
893 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
894 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
899 // Check if the user name has changed. An empty string is permitted here!
900 if (!_EqualStrings(pJob
->pwszUserName
, pJobInfo
->pUserName
))
902 // The new user name doesn't need to exist, so no additional verification is required.
904 // Use the new value.
905 if (!ReallocSplStr(&pJob
->pwszUserName
, pJobInfo
->pUserName
))
907 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
908 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
913 // Check if the priority has changed.
914 if (pJob
->dwPriority
!= pJobInfo
->Priority
&& IS_VALID_PRIORITY(pJobInfo
->Priority
))
916 // Set the new priority.
917 pJob
->dwPriority
= pJobInfo
->Priority
;
919 // Remove and reinsert the job in the Printer's Job List.
920 // The Compare function will be used to find the right position now considering the new priority.
921 DeleteElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
922 InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
925 // Check if the status flags have changed.
926 if (pJob
->dwStatus
!= pJobInfo
->Status
)
928 // Only add status flags that make sense.
929 if (pJobInfo
->Status
& JOB_STATUS_PAUSED
)
930 pJob
->dwStatus
|= JOB_STATUS_PAUSED
;
932 if (pJobInfo
->Status
& JOB_STATUS_ERROR
)
933 pJob
->dwStatus
|= JOB_STATUS_ERROR
;
935 if (pJobInfo
->Status
& JOB_STATUS_OFFLINE
)
936 pJob
->dwStatus
|= JOB_STATUS_OFFLINE
;
938 if (pJobInfo
->Status
& JOB_STATUS_PAPEROUT
)
939 pJob
->dwStatus
|= JOB_STATUS_PAPEROUT
;
942 dwErrorCode
= ERROR_SUCCESS
;
949 LocalSetJob(HANDLE hPrinter
, DWORD JobId
, DWORD Level
, PBYTE pJobInfo
, DWORD Command
)
951 DWORD dwErrorCode
= ERROR_SUCCESS
;
952 PLOCAL_HANDLE pHandle
;
954 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
955 WCHAR wszFullPath
[MAX_PATH
];
957 TRACE("LocalSetJob(%p, %lu, %lu, %p, %lu)\n", hPrinter
, JobId
, Level
, pJobInfo
, Command
);
959 // Check if this is a printer handle.
960 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
961 if (!pHandle
|| pHandle
->HandleType
!= HandleType_Printer
)
963 dwErrorCode
= ERROR_INVALID_HANDLE
;
967 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
969 // Get the desired job.
970 pJob
= LookupElementSkiplist(&GlobalJobList
, &JobId
, NULL
);
971 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
973 dwErrorCode
= ERROR_INVALID_PARAMETER
;
977 // Set new job information if a valid level was given.
979 dwErrorCode
= _LocalSetJobLevel1(pPrinterHandle
, pJob
, (PJOB_INFO_1W
)pJobInfo
);
981 dwErrorCode
= _LocalSetJobLevel2(pPrinterHandle
, pJob
, (PJOB_INFO_2W
)pJobInfo
);
983 if (dwErrorCode
!= ERROR_SUCCESS
)
986 // If we do spooled printing, the job information is written down into a shadow file.
987 if (!(pPrinterHandle
->pPrinter
->dwAttributes
& PRINTER_ATTRIBUTE_DIRECT
))
989 // Write the job data into the shadow file.
990 GetJobFilePath(L
"SHD", JobId
, wszFullPath
);
991 WriteJobShadowFile(wszFullPath
, pJob
);
994 // Perform an additional command if desired.
997 if (Command
== JOB_CONTROL_SENT_TO_PRINTER
)
999 // This indicates the end of the Print Job.
1001 // Cancel the Job at the Print Processor.
1002 if (pJob
->hPrintProcessor
)
1003 pJob
->pPrintProcessor
->pfnControlPrintProcessor(pJob
->hPrintProcessor
, JOB_CONTROL_CANCEL
);
1007 // TODO: All open handles associated with the job need to be invalidated.
1008 // This certainly needs handle tracking...
1012 ERR("Unimplemented SetJob Command: %lu!\n", Command
);
1017 SetLastError(dwErrorCode
);
1018 return (dwErrorCode
== ERROR_SUCCESS
);
1022 LocalEnumJobs(HANDLE hPrinter
, DWORD FirstJob
, DWORD NoJobs
, DWORD Level
, PBYTE pStart
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
1027 PLOCAL_HANDLE pHandle
;
1029 PSKIPLIST_NODE pFirstJobNode
;
1030 PSKIPLIST_NODE pNode
;
1031 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1033 TRACE("LocalEnumJobs(%p, %lu, %lu, %lu, %p, %lu, %p, %p)\n", hPrinter
, FirstJob
, NoJobs
, Level
, pStart
, cbBuf
, pcbNeeded
, pcReturned
);
1035 // Check if this is a printer handle.
1036 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1037 if (pHandle
->HandleType
!= HandleType_Printer
)
1039 dwErrorCode
= ERROR_INVALID_HANDLE
;
1043 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1048 dwErrorCode
= ERROR_INVALID_LEVEL
;
1056 // Lookup the node of the first job requested by the caller in the Printer's Job List.
1057 pFirstJobNode
= LookupNodeByIndexSkiplist(&pPrinterHandle
->pPrinter
->JobList
, FirstJob
);
1059 // Count the required buffer size and the number of jobs.
1061 pNode
= pFirstJobNode
;
1063 while (i
< NoJobs
&& pNode
)
1065 pJob
= (PLOCAL_JOB
)pNode
->Element
;
1068 _LocalGetJobLevel1(pJob
, NULL
, NULL
, pcbNeeded
);
1069 else if (Level
== 2)
1070 _LocalGetJobLevel2(pJob
, NULL
, NULL
, pcbNeeded
);
1072 // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
1074 pNode
= pNode
->Next
[0];
1077 // Check if the supplied buffer is large enough.
1078 if (cbBuf
< *pcbNeeded
)
1080 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
1084 // Copy over the Job information.
1086 pNode
= pFirstJobNode
;
1087 pEnd
= &pStart
[*pcbNeeded
];
1089 while (i
< NoJobs
&& pNode
)
1091 pJob
= (PLOCAL_JOB
)pNode
->Element
;
1094 _LocalGetJobLevel1(pJob
, (PJOB_INFO_1W
*)&pStart
, &pEnd
, NULL
);
1095 else if (Level
== 2)
1096 _LocalGetJobLevel2(pJob
, (PJOB_INFO_2W
*)&pStart
, &pEnd
, NULL
);
1098 // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
1100 pNode
= pNode
->Next
[0];
1104 dwErrorCode
= ERROR_SUCCESS
;
1107 SetLastError(dwErrorCode
);
1108 return (dwErrorCode
== ERROR_SUCCESS
);
1112 LocalScheduleJob(HANDLE hPrinter
, DWORD dwJobID
)
1118 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1119 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1120 WCHAR wszFullPath
[MAX_PATH
];
1122 TRACE("LocalScheduleJob(%p, %lu)\n", hPrinter
, dwJobID
);
1124 // Check if this is a printer handle.
1125 if (pHandle
->HandleType
!= HandleType_Printer
)
1127 dwErrorCode
= ERROR_INVALID_HANDLE
;
1131 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1133 // Check if the Job ID is valid.
1134 pJob
= LookupElementSkiplist(&GlobalJobList
, &dwJobID
, NULL
);
1135 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
1137 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1141 // Check if this Job was started with AddJob.
1142 if (!pJob
->bAddedJob
)
1144 dwErrorCode
= ERROR_SPL_NO_ADDJOB
;
1148 // Construct the full path to the spool file.
1149 GetJobFilePath(L
"SPL", dwJobID
, wszFullPath
);
1151 // Check if it exists.
1152 dwAttributes
= GetFileAttributesW(wszFullPath
);
1153 if (dwAttributes
== INVALID_FILE_ATTRIBUTES
|| dwAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1155 dwErrorCode
= ERROR_SPOOL_FILE_NOT_FOUND
;
1159 // Spooling is finished at this point.
1160 pJob
->dwStatus
&= ~JOB_STATUS_SPOOLING
;
1162 // Write the job data into the shadow file.
1163 wcscpy(wcsrchr(wszFullPath
, L
'.'), L
".SHD");
1164 WriteJobShadowFile(wszFullPath
, pJob
);
1166 // Create the thread for performing the printing process.
1167 hThread
= CreateThread(NULL
, 0, (LPTHREAD_START_ROUTINE
)PrintingThreadProc
, pJob
, 0, NULL
);
1170 dwErrorCode
= GetLastError();
1171 ERR("CreateThread failed with error %lu!\n", dwErrorCode
);
1175 // We don't need the thread handle. Keeping it open blocks the thread from terminating.
1176 CloseHandle(hThread
);
1178 // ScheduleJob has done its job. The rest happens inside the thread.
1179 dwErrorCode
= ERROR_SUCCESS
;
1182 SetLastError(dwErrorCode
);
1183 return (dwErrorCode
== ERROR_SUCCESS
);
1187 ReadJobShadowFile(PCWSTR pwszFilePath
)
1191 HANDLE hFile
= INVALID_HANDLE_VALUE
;
1193 PLOCAL_JOB pReturnValue
= NULL
;
1194 PLOCAL_PRINTER pPrinter
;
1195 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
1196 PSHD_HEADER pShadowFile
= NULL
;
1197 PWSTR pwszPrinterName
;
1198 PWSTR pwszPrintProcessor
;
1200 TRACE("ReadJobShadowFile(%S)\n", pwszFilePath
);
1202 // Try to open the file.
1203 hFile
= CreateFileW(pwszFilePath
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
1204 if (hFile
== INVALID_HANDLE_VALUE
)
1206 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1210 // Get its file size (small enough for a single DWORD) and allocate memory for all of it.
1211 cbFileSize
= GetFileSize(hFile
, NULL
);
1212 pShadowFile
= DllAllocSplMem(cbFileSize
);
1215 ERR("DllAllocSplMem failed for file \"%S\"!\n", pwszFilePath
);
1219 // Read the entire file.
1220 if (!ReadFile(hFile
, pShadowFile
, cbFileSize
, &cbRead
, NULL
))
1222 ERR("ReadFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1226 // Check signature and header size.
1227 if (pShadowFile
->dwSignature
!= SHD_WIN2003_SIGNATURE
|| pShadowFile
->cbHeader
!= sizeof(SHD_HEADER
))
1229 ERR("Signature or Header Size mismatch for file \"%S\"!\n", pwszFilePath
);
1233 // Retrieve the associated printer from the list.
1234 pwszPrinterName
= (PWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrinterName
);
1235 pPrinter
= LookupElementSkiplist(&PrinterList
, &pwszPrinterName
, NULL
);
1238 ERR("Shadow file \"%S\" references a non-existing printer \"%S\"!\n", pwszFilePath
, pwszPrinterName
);
1242 // Retrieve the associated Print Processor from the list.
1243 pwszPrintProcessor
= (PWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrintProcessor
);
1244 pPrintProcessor
= FindPrintProcessor(pwszPrintProcessor
);
1245 if (!pPrintProcessor
)
1247 ERR("Shadow file \"%S\" references a non-existing Print Processor \"%S\"!\n", pwszFilePath
, pwszPrintProcessor
);
1251 // Create a new job structure and copy over the relevant fields.
1252 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
1255 ERR("DllAllocSplMem failed for file \"%S\"!\n", pwszFilePath
);
1259 pJob
->dwJobID
= pShadowFile
->dwJobID
;
1260 pJob
->dwPriority
= pShadowFile
->dwPriority
;
1261 pJob
->dwStartTime
= pShadowFile
->dwStartTime
;
1262 pJob
->dwTotalPages
= pShadowFile
->dwTotalPages
;
1263 pJob
->dwUntilTime
= pShadowFile
->dwUntilTime
;
1264 pJob
->pPrinter
= pPrinter
;
1265 pJob
->pPrintProcessor
= pPrintProcessor
;
1266 pJob
->pDevMode
= DuplicateDevMode((PDEVMODEW
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDevMode
));
1267 pJob
->pwszDatatype
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDatatype
));
1268 pJob
->pwszMachineName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offMachineName
));
1269 CopyMemory(&pJob
->stSubmitted
, &pShadowFile
->stSubmitted
, sizeof(SYSTEMTIME
));
1271 // Copy the optional values.
1272 if (pShadowFile
->offDocumentName
)
1273 pJob
->pwszDocumentName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDocumentName
));
1275 if (pShadowFile
->offNotifyName
)
1276 pJob
->pwszNotifyName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offNotifyName
));
1278 if (pShadowFile
->offPrintProcessorParameters
)
1279 pJob
->pwszPrintProcessorParameters
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrintProcessorParameters
));
1281 if (pShadowFile
->offUserName
)
1282 pJob
->pwszUserName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offUserName
));
1284 // Jobs read from shadow files were always added using AddJob.
1285 pJob
->bAddedJob
= TRUE
;
1287 pReturnValue
= pJob
;
1291 DllFreeSplMem(pShadowFile
);
1293 if (hFile
!= INVALID_HANDLE_VALUE
)
1296 return pReturnValue
;
1300 WriteJobShadowFile(PWSTR pwszFilePath
, const PLOCAL_JOB pJob
)
1302 BOOL bReturnValue
= FALSE
;
1303 DWORD cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
1304 DWORD cbDevMode
= pJob
->pDevMode
->dmSize
+ pJob
->pDevMode
->dmDriverExtra
;
1305 DWORD cbDocumentName
= 0;
1307 DWORD cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
1308 DWORD cbNotifyName
= 0;
1309 DWORD cbPrinterDriver
= (wcslen(pJob
->pPrinter
->pwszPrinterDriver
) + 1) * sizeof(WCHAR
);
1310 DWORD cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
1311 DWORD cbPrintProcessor
= (wcslen(pJob
->pPrintProcessor
->pwszName
) + 1) * sizeof(WCHAR
);
1312 DWORD cbPrintProcessorParameters
= 0;
1313 DWORD cbUserName
= 0;
1315 DWORD dwCurrentOffset
;
1316 HANDLE hSHDFile
= INVALID_HANDLE_VALUE
;
1317 HANDLE hSPLFile
= INVALID_HANDLE_VALUE
;
1318 PSHD_HEADER pShadowFile
= NULL
;
1320 TRACE("WriteJobShadowFile(%S, %p)\n", pwszFilePath
, pJob
);
1322 // Try to open the SHD file.
1323 hSHDFile
= CreateFileW(pwszFilePath
, GENERIC_WRITE
, FILE_SHARE_READ
, NULL
, CREATE_ALWAYS
, 0, NULL
);
1324 if (hSHDFile
== INVALID_HANDLE_VALUE
)
1326 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1330 // Calculate the lengths of the optional values and the total size of the shadow file.
1331 if (pJob
->pwszDocumentName
)
1332 cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
1334 if (pJob
->pwszNotifyName
)
1335 cbNotifyName
= (wcslen(pJob
->pwszNotifyName
) + 1) * sizeof(WCHAR
);
1337 if (pJob
->pwszPrintProcessorParameters
)
1338 cbPrintProcessorParameters
= (wcslen(pJob
->pwszPrintProcessorParameters
) + 1) * sizeof(WCHAR
);
1340 if (pJob
->pwszUserName
)
1341 cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
1343 cbFileSize
= sizeof(SHD_HEADER
) + cbDatatype
+ cbDocumentName
+ cbDevMode
+ cbMachineName
+ cbNotifyName
+ cbPrinterDriver
+ cbPrinterName
+ cbPrintProcessor
+ cbPrintProcessorParameters
+ cbUserName
;
1345 // Allocate memory for it.
1346 pShadowFile
= DllAllocSplMem(cbFileSize
);
1349 ERR("DllAllocSplMem failed for file \"%S\"!\n", pwszFilePath
);
1353 // Fill out the shadow file header information.
1354 pShadowFile
->dwSignature
= SHD_WIN2003_SIGNATURE
;
1355 pShadowFile
->cbHeader
= sizeof(SHD_HEADER
);
1358 pShadowFile
->dwJobID
= pJob
->dwJobID
;
1359 pShadowFile
->dwPriority
= pJob
->dwPriority
;
1360 pShadowFile
->dwStartTime
= pJob
->dwStartTime
;
1361 pShadowFile
->dwTotalPages
= pJob
->dwTotalPages
;
1362 pShadowFile
->dwUntilTime
= pJob
->dwUntilTime
;
1363 CopyMemory(&pShadowFile
->stSubmitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
1365 // Determine the file size of the .SPL file
1366 wcscpy(wcsrchr(pwszFilePath
, L
'.'), L
".SPL");
1367 hSPLFile
= CreateFileW(pwszFilePath
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
1368 if (hSPLFile
!= INVALID_HANDLE_VALUE
)
1369 pShadowFile
->dwSPLSize
= GetFileSize(hSPLFile
, NULL
);
1371 // Add the extra values that are stored as offsets in the shadow file.
1372 // The first value begins right after the shadow file header.
1373 dwCurrentOffset
= sizeof(SHD_HEADER
);
1375 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszDatatype
, cbDatatype
);
1376 pShadowFile
->offDatatype
= dwCurrentOffset
;
1377 dwCurrentOffset
+= cbDatatype
;
1379 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pDevMode
, cbDevMode
);
1380 pShadowFile
->offDevMode
= dwCurrentOffset
;
1381 dwCurrentOffset
+= cbDevMode
;
1383 // offDriverName is only written, but automatically determined through offPrinterName when reading.
1384 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrinter
->pwszPrinterDriver
, cbPrinterDriver
);
1385 pShadowFile
->offDriverName
= dwCurrentOffset
;
1386 dwCurrentOffset
+= cbPrinterDriver
;
1388 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszMachineName
, cbMachineName
);
1389 pShadowFile
->offMachineName
= dwCurrentOffset
;
1390 dwCurrentOffset
+= cbMachineName
;
1392 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
1393 pShadowFile
->offPrinterName
= dwCurrentOffset
;
1394 dwCurrentOffset
+= cbPrinterName
;
1396 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrintProcessor
->pwszName
, cbPrintProcessor
);
1397 pShadowFile
->offPrintProcessor
= dwCurrentOffset
;
1398 dwCurrentOffset
+= cbPrintProcessor
;
1400 // Copy the optional values.
1403 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszDocumentName
, cbDocumentName
);
1404 pShadowFile
->offDocumentName
= dwCurrentOffset
;
1405 dwCurrentOffset
+= cbDocumentName
;
1410 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszNotifyName
, cbNotifyName
);
1411 pShadowFile
->offNotifyName
= dwCurrentOffset
;
1412 dwCurrentOffset
+= cbNotifyName
;
1415 if (cbPrintProcessorParameters
)
1417 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszPrintProcessorParameters
, cbPrintProcessorParameters
);
1418 pShadowFile
->offPrintProcessorParameters
= dwCurrentOffset
;
1419 dwCurrentOffset
+= cbPrintProcessorParameters
;
1424 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszUserName
, cbUserName
);
1425 pShadowFile
->offUserName
= dwCurrentOffset
;
1426 dwCurrentOffset
+= cbUserName
;
1430 if (!WriteFile(hSHDFile
, pShadowFile
, cbFileSize
, &cbWritten
, NULL
))
1432 ERR("WriteFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1436 bReturnValue
= TRUE
;
1440 DllFreeSplMem(pShadowFile
);
1442 if (hSHDFile
!= INVALID_HANDLE_VALUE
)
1443 CloseHandle(hSHDFile
);
1445 if (hSPLFile
!= INVALID_HANDLE_VALUE
)
1446 CloseHandle(hSPLFile
);
1448 return bReturnValue
;
1452 FreeJob(PLOCAL_JOB pJob
)
1456 TRACE("FreeJob(%p)\n", pJob
);
1458 // Remove the Job from both Job Lists.
1459 DeleteElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
1460 DeleteElementSkiplist(&GlobalJobList
, pJob
);
1462 // Try to delete the corresponding .SHD file.
1463 pwszSHDFile
= DllAllocSplMem(GetJobFilePath(L
"SHD", 0, NULL
));
1464 if (pwszSHDFile
&& GetJobFilePath(L
"SHD", pJob
->dwJobID
, pwszSHDFile
))
1465 DeleteFileW(pwszSHDFile
);
1467 // Free memory for the mandatory fields.
1468 DllFreeSplMem(pJob
->pDevMode
);
1469 DllFreeSplStr(pJob
->pwszDatatype
);
1470 DllFreeSplStr(pJob
->pwszDocumentName
);
1471 DllFreeSplStr(pJob
->pwszMachineName
);
1472 DllFreeSplStr(pJob
->pwszNotifyName
);
1473 DllFreeSplStr(pJob
->pwszUserName
);
1475 // Free memory for the optional fields if they are present.
1476 if (pJob
->pwszOutputFile
)
1477 DllFreeSplStr(pJob
->pwszOutputFile
);
1479 if (pJob
->pwszPrintProcessorParameters
)
1480 DllFreeSplStr(pJob
->pwszPrintProcessorParameters
);
1482 if (pJob
->pwszStatus
)
1483 DllFreeSplStr(pJob
->pwszStatus
);
1485 // Finally free the job structure itself.
1486 DllFreeSplMem(pJob
);