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 * @name _GlobalJobListCompareRoutine
20 * SKIPLIST_COMPARE_ROUTINE for the Global Job List.
21 * We need the Global Job List to check whether a Job ID is already in use. Consequently, this list is sorted by ID.
24 _GlobalJobListCompareRoutine(PVOID FirstStruct
, PVOID SecondStruct
)
26 PLOCAL_JOB A
= (PLOCAL_JOB
)FirstStruct
;
27 PLOCAL_JOB B
= (PLOCAL_JOB
)SecondStruct
;
29 return A
->dwJobID
- B
->dwJobID
;
33 * @name _PrinterJobListCompareRoutine
35 * SKIPLIST_COMPARE_ROUTINE for the each Printer's Job List.
36 * Jobs in this list are sorted in the desired order of processing.
39 _PrinterJobListCompareRoutine(PVOID FirstStruct
, PVOID SecondStruct
)
41 PLOCAL_JOB A
= (PLOCAL_JOB
)FirstStruct
;
42 PLOCAL_JOB B
= (PLOCAL_JOB
)SecondStruct
;
44 FILETIME ftSubmittedA
;
45 FILETIME ftSubmittedB
;
46 ULARGE_INTEGER uliSubmittedA
;
47 ULARGE_INTEGER uliSubmittedB
;
50 // First compare the priorities to determine the order.
51 // The job with a higher priority shall come first.
52 iComparison
= A
->dwPriority
- B
->dwPriority
;
56 // Both have the same priority, so go by creation time.
57 // Comparison is done using the MSDN-recommended way for comparing SYSTEMTIMEs.
58 if (!SystemTimeToFileTime(&A
->stSubmitted
, &ftSubmittedA
))
60 ERR("SystemTimeToFileTime failed for A with error %lu!\n", GetLastError());
64 if (!SystemTimeToFileTime(&B
->stSubmitted
, &ftSubmittedB
))
66 ERR("SystemTimeToFileTime failed for B with error %lu!\n", GetLastError());
70 uliSubmittedA
.LowPart
= ftSubmittedA
.dwLowDateTime
;
71 uliSubmittedA
.HighPart
= ftSubmittedA
.dwHighDateTime
;
72 uliSubmittedB
.LowPart
= ftSubmittedB
.dwLowDateTime
;
73 uliSubmittedB
.HighPart
= ftSubmittedB
.dwHighDateTime
;
74 ullResult
= uliSubmittedA
.QuadPart
- uliSubmittedB
.QuadPart
;
78 else if (ullResult
> 0)
85 GetNextJobID(PDWORD dwJobID
)
89 while (LookupElementSkiplist(&GlobalJobList
, &_dwLastJobID
, NULL
))
91 // This ID is already taken. Try the next one.
95 if (!IS_VALID_JOB_ID(_dwLastJobID
))
97 ERR("Job ID %lu isn't valid!\n", _dwLastJobID
);
101 *dwJobID
= _dwLastJobID
;
106 InitializeGlobalJobList()
108 const WCHAR wszPath
[] = L
"\\PRINTERS\\?????.SHD";
109 const DWORD cchPath
= _countof(wszPath
) - 1;
110 const DWORD cchFolders
= sizeof("\\PRINTERS\\") - 1;
111 const DWORD cchPattern
= sizeof("?????") - 1;
115 PLOCAL_JOB pJob
= NULL
;
117 WCHAR wszFullPath
[MAX_PATH
];
118 WIN32_FIND_DATAW FindData
;
120 // This one is incremented in GetNextJobID.
123 // Initialize an empty list for all jobs of all local printers.
124 // We will search it by Job ID (supply a pointer to a DWORD in LookupElementSkiplist).
125 InitializeSkiplist(&GlobalJobList
, DllAllocSplMem
, _GlobalJobListCompareRoutine
, (PSKIPLIST_FREE_ROUTINE
)DllFreeSplMem
);
127 // Construct the full path search pattern.
128 CopyMemory(wszFullPath
, wszSpoolDirectory
, cchSpoolDirectory
* sizeof(WCHAR
));
129 CopyMemory(&wszFullPath
[cchSpoolDirectory
], wszPath
, (cchPath
+ 1) * sizeof(WCHAR
));
131 // Use the search pattern to look for unfinished jobs serialized in shadow files (.SHD)
132 hFind
= FindFirstFileW(wszFullPath
, &FindData
);
133 if (hFind
== INVALID_HANDLE_VALUE
)
135 // No unfinished jobs found.
141 // Skip possible subdirectories.
142 if (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
145 // Extract the Job ID and verify the file name format at the same time.
146 dwJobID
= wcstoul(FindData
.cFileName
, &p
, 10);
147 if (!IS_VALID_JOB_ID(dwJobID
))
150 if (wcsicmp(p
, L
".SHD") != 0)
153 // This shadow file has a valid name. Construct the full path and try to load it.
154 CopyMemory(&wszFullPath
[cchSpoolDirectory
+ cchFolders
], FindData
.cFileName
, cchPattern
);
155 pJob
= ReadJobShadowFile(wszFullPath
);
159 // Add it to the Global Job List.
160 if (!InsertElementSkiplist(&GlobalJobList
, pJob
))
162 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob
->dwJobID
);
166 // Add it to the Printer's Job List.
167 if (!InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
))
169 ERR("InsertElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob
->dwJobID
);
173 while (FindNextFileW(hFind
, &FindData
));
182 InitializePrinterJobList(PLOCAL_PRINTER pPrinter
)
184 // Initialize an empty list for this printer's jobs.
185 // This one is only for sorting the jobs. If you need to lookup a job, search the GlobalJobList by Job ID.
186 InitializeSkiplist(&pPrinter
->JobList
, DllAllocSplMem
, _PrinterJobListCompareRoutine
, (PSKIPLIST_FREE_ROUTINE
)DllFreeSplMem
);
190 LocalAddJob(HANDLE hPrinter
, DWORD Level
, LPBYTE pData
, DWORD cbBuf
, LPDWORD pcbNeeded
)
192 const WCHAR wszDoubleBackslash
[] = L
"\\";
193 const DWORD cchDoubleBackslash
= _countof(wszDoubleBackslash
) - 1;
194 const WCHAR wszPrintersPath
[] = L
"\\PRINTERS\\";
195 const DWORD cchPrintersPath
= _countof(wszPrintersPath
) - 1;
196 const DWORD cchSpl
= _countof("?????.SPL") - 1;
198 ADDJOB_INFO_1W AddJobInfo1
;
199 DWORD cchMachineName
;
203 PLOCAL_HANDLE pHandle
;
205 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
206 RPC_BINDING_HANDLE hServerBinding
= NULL
;
207 RPC_WSTR pwszBinding
= NULL
;
208 RPC_WSTR pwszMachineName
= NULL
;
210 // Check if this is a printer handle.
211 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
212 if (pHandle
->HandleType
!= Printer
)
214 dwErrorCode
= ERROR_INVALID_HANDLE
;
218 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
220 // This handle must not have started a job yet!
221 if (pPrinterHandle
->pStartedJob
)
223 dwErrorCode
= ERROR_INVALID_HANDLE
;
227 // Check if this is the right structure level.
230 dwErrorCode
= ERROR_INVALID_LEVEL
;
234 // Check if the printer is set to do direct printing.
235 // The Job List isn't used in this case.
236 if (pPrinterHandle
->pPrinter
->dwAttributes
& PRINTER_ATTRIBUTE_DIRECT
)
238 dwErrorCode
= ERROR_INVALID_ACCESS
;
242 // Check if the supplied buffer is large enough.
243 *pcbNeeded
= sizeof(ADDJOB_INFO_1W
) + (cchSpoolDirectory
+ cchPrintersPath
+ cchSpl
+ 1) * sizeof(WCHAR
);
244 if (cbBuf
< *pcbNeeded
)
246 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
251 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
254 dwErrorCode
= GetLastError();
255 ERR("DllAllocSplMem failed with error %lu!\n", dwErrorCode
);
259 // Reserve an ID for this job.
260 if (!GetNextJobID(&pJob
->dwJobID
))
262 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
266 // Copy over defaults to the LOCAL_JOB structure.
267 pJob
->pPrinter
= pPrinterHandle
->pPrinter
;
268 pJob
->pPrintProcessor
= pPrinterHandle
->pPrinter
->pPrintProcessor
;
269 pJob
->dwPriority
= DEF_PRIORITY
;
270 pJob
->pwszDatatype
= AllocSplStr(pPrinterHandle
->pwszDatatype
);
271 pJob
->pwszDocumentName
= AllocSplStr(wszDefaultDocumentName
);
272 CopyMemory(&pJob
->DevMode
, &pPrinterHandle
->DevMode
, sizeof(DEVMODEW
));
273 GetSystemTime(&pJob
->stSubmitted
);
275 // Get the user name for the Job.
276 cchUserName
= UNLEN
+ 1;
277 pJob
->pwszUserName
= DllAllocSplMem(cchUserName
* sizeof(WCHAR
));
278 if (!GetUserNameW(pJob
->pwszUserName
, &cchUserName
))
280 dwErrorCode
= GetLastError();
281 ERR("GetUserNameW failed with error %lu!\n", dwErrorCode
);
285 // FIXME: For now, pwszNotifyName equals pwszUserName.
286 pJob
->pwszNotifyName
= AllocSplStr(pJob
->pwszUserName
);
288 // Get the name of the machine that submitted the Job over RPC.
289 dwErrorCode
= RpcBindingServerFromClient(NULL
, &hServerBinding
);
290 if (dwErrorCode
!= RPC_S_OK
)
292 ERR("RpcBindingServerFromClient failed with status %lu!\n", dwErrorCode
);
296 dwErrorCode
= RpcBindingToStringBindingW(hServerBinding
, &pwszBinding
);
297 if (dwErrorCode
!= RPC_S_OK
)
299 ERR("RpcBindingToStringBindingW failed with status %lu!\n", dwErrorCode
);
303 dwErrorCode
= RpcStringBindingParseW(pwszBinding
, NULL
, NULL
, &pwszMachineName
, NULL
, NULL
);
304 if (dwErrorCode
!= RPC_S_OK
)
306 ERR("RpcStringBindingParseW failed with status %lu!\n", dwErrorCode
);
310 cchMachineName
= wcslen(pwszMachineName
);
311 pJob
->pwszMachineName
= DllAllocSplMem((cchMachineName
+ cchDoubleBackslash
+ 1) * sizeof(WCHAR
));
312 CopyMemory(pJob
->pwszMachineName
, wszDoubleBackslash
, cchDoubleBackslash
* sizeof(WCHAR
));
313 CopyMemory(pJob
->pwszMachineName
+ cchDoubleBackslash
, pwszMachineName
, (cchMachineName
+ 1) * sizeof(WCHAR
));
315 // Add the job to the Global Job List.
316 if (!InsertElementSkiplist(&GlobalJobList
, pJob
))
318 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
319 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob
->dwJobID
);
323 // Add the job at the end of the Printer's Job List.
324 // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
325 if (!InsertTailElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
))
327 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
328 ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob
->dwJobID
);
332 // Return a proper ADDJOB_INFO_1W structure.
333 AddJobInfo1
.JobId
= pJob
->dwJobID
;
334 AddJobInfo1
.Path
= (PWSTR
)(pData
+ sizeof(ADDJOB_INFO_1W
));
336 CopyMemory(p
, &AddJobInfo1
, sizeof(ADDJOB_INFO_1W
));
337 p
+= sizeof(ADDJOB_INFO_1W
);
338 CopyMemory(p
, wszSpoolDirectory
, cchSpoolDirectory
);
339 p
+= cchSpoolDirectory
;
340 CopyMemory(p
, wszPrintersPath
, cchPrintersPath
);
341 p
+= cchPrintersPath
;
342 swprintf((PWSTR
)p
, L
"%05lu.SPL", pJob
->dwJobID
);
344 dwErrorCode
= ERROR_SUCCESS
;
348 RpcStringFreeW(&pwszMachineName
);
351 RpcStringFreeW(&pwszBinding
);
354 RpcBindingFree(&hServerBinding
);
356 SetLastError(dwErrorCode
);
357 return (dwErrorCode
== ERROR_SUCCESS
);
362 _LocalGetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PBYTE pOutput
, DWORD cbBuf
, PDWORD pcbNeeded
)
364 DWORD cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
365 DWORD cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
366 DWORD cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
367 DWORD cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
369 DWORD cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
371 JOB_INFO_1W JobInfo1
= { 0 };
374 // A Status Message is optional.
375 if (pJob
->pwszStatus
)
376 cbStatus
= (wcslen(pJob
->pwszStatus
) + 1) * sizeof(WCHAR
);
378 // Check if the supplied buffer is large enough.
379 *pcbNeeded
= sizeof(JOB_INFO_1W
) + cbDatatype
+ cbDocumentName
+ cbMachineName
+ cbPrinterName
+ cbStatus
+ cbUserName
;
380 if (cbBuf
< *pcbNeeded
)
382 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
386 // Put the strings right after the JOB_INFO_1W structure.
387 pString
= pOutput
+ sizeof(JOB_INFO_1W
);
389 JobInfo1
.pDatatype
= (PWSTR
)pString
;
390 CopyMemory(pString
, pJob
->pwszDatatype
, cbDatatype
);
391 pString
+= cbDatatype
;
393 JobInfo1
.pDocument
= (PWSTR
)pString
;
394 CopyMemory(pString
, pJob
->pwszDocumentName
, cbDocumentName
);
395 pString
+= cbDocumentName
;
397 JobInfo1
.pMachineName
= (PWSTR
)pString
;
398 CopyMemory(pString
, pJob
->pwszMachineName
, cbMachineName
);
399 pString
+= cbMachineName
;
401 JobInfo1
.pPrinterName
= (PWSTR
)pString
;
402 CopyMemory(pString
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
403 pString
+= cbPrinterName
;
407 JobInfo1
.pStatus
= (PWSTR
)pString
;
408 CopyMemory(pString
, pJob
->pwszStatus
, cbStatus
);
412 JobInfo1
.pUserName
= (PWSTR
)pString
;
413 CopyMemory(pString
, pJob
->pwszUserName
, cbUserName
);
414 pString
+= cbUserName
;
416 // Fill the structure and copy it as well.
417 JobInfo1
.JobId
= pJob
->dwJobID
;
418 JobInfo1
.Priority
= pJob
->dwPriority
;
419 JobInfo1
.Status
= pJob
->dwStatus
;
420 JobInfo1
.TotalPages
= pJob
->dwTotalPages
;
421 CopyMemory(&JobInfo1
.Submitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
422 CopyMemory(pOutput
, &JobInfo1
, sizeof(JOB_INFO_1W
));
424 dwErrorCode
= ERROR_SUCCESS
;
431 _LocalGetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PBYTE pOutput
, DWORD cbBuf
, PDWORD pcbNeeded
)
433 DWORD cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
434 DWORD cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
435 DWORD cbDriverName
= (wcslen(pJob
->pPrinter
->pwszPrinterDriver
) + 1) * sizeof(WCHAR
);
436 DWORD cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
437 DWORD cbNotifyName
= (wcslen(pJob
->pwszNotifyName
) + 1) * sizeof(WCHAR
);
438 DWORD cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
439 DWORD cbPrintProcessor
= (wcslen(pJob
->pPrintProcessor
->pwszName
) + 1) * sizeof(WCHAR
);
440 DWORD cbPrintProcessorParameters
= 0;
442 DWORD cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
445 FILETIME ftSubmitted
;
446 JOB_INFO_2W JobInfo2
= { 0 };
448 ULARGE_INTEGER uliNow
;
449 ULARGE_INTEGER uliSubmitted
;
451 // Print Processor Parameters and Status Message are optional.
452 if (pJob
->pwszPrintProcessorParameters
)
453 cbPrintProcessorParameters
= (wcslen(pJob
->pwszPrintProcessorParameters
) + 1) * sizeof(WCHAR
);
455 if (pJob
->pwszStatus
)
456 cbStatus
= (wcslen(pJob
->pwszStatus
) + 1) * sizeof(WCHAR
);
458 // Check if the supplied buffer is large enough.
459 *pcbNeeded
= sizeof(JOB_INFO_2W
) + cbDatatype
+ sizeof(DEVMODEW
) + cbDocumentName
+ cbDriverName
+ cbMachineName
+ cbNotifyName
+ cbPrinterName
+ cbPrintProcessor
+ cbPrintProcessorParameters
+ cbStatus
+ cbUserName
;
460 if (cbBuf
< *pcbNeeded
)
462 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
466 // Put the strings right after the JOB_INFO_2W structure.
467 pString
= pOutput
+ sizeof(JOB_INFO_2W
);
469 JobInfo2
.pDatatype
= (PWSTR
)pString
;
470 CopyMemory(pString
, pJob
->pwszDatatype
, cbDatatype
);
471 pString
+= cbDatatype
;
473 JobInfo2
.pDevMode
= (PDEVMODEW
)pString
;
474 CopyMemory(pString
, &pJob
->DevMode
, sizeof(DEVMODEW
));
475 pString
+= sizeof(DEVMODEW
);
477 JobInfo2
.pDocument
= (PWSTR
)pString
;
478 CopyMemory(pString
, pJob
->pwszDocumentName
, cbDocumentName
);
479 pString
+= cbDocumentName
;
481 JobInfo2
.pDriverName
= (PWSTR
)pString
;
482 CopyMemory(pString
, pJob
->pPrinter
->pwszPrinterDriver
, cbDriverName
);
483 pString
+= cbDriverName
;
485 JobInfo2
.pMachineName
= (PWSTR
)pString
;
486 CopyMemory(pString
, pJob
->pwszMachineName
, cbMachineName
);
487 pString
+= cbMachineName
;
489 JobInfo2
.pNotifyName
= (PWSTR
)pString
;
490 CopyMemory(pString
, pJob
->pwszNotifyName
, cbNotifyName
);
491 pString
+= cbNotifyName
;
493 JobInfo2
.pPrinterName
= (PWSTR
)pString
;
494 CopyMemory(pString
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
495 pString
+= cbPrinterName
;
497 JobInfo2
.pPrintProcessor
= (PWSTR
)pString
;
498 CopyMemory(pString
, pJob
->pPrintProcessor
->pwszName
, cbPrintProcessor
);
499 pString
+= cbPrintProcessor
;
501 if (cbPrintProcessorParameters
)
503 JobInfo2
.pParameters
= (PWSTR
)pString
;
504 CopyMemory(pString
, pJob
->pwszPrintProcessorParameters
, cbPrintProcessorParameters
);
505 pString
+= cbPrintProcessorParameters
;
510 JobInfo2
.pStatus
= (PWSTR
)pString
;
511 CopyMemory(pString
, pJob
->pwszStatus
, cbStatus
);
515 JobInfo2
.pUserName
= (PWSTR
)pString
;
516 CopyMemory(pString
, pJob
->pwszUserName
, cbUserName
);
517 pString
+= cbUserName
;
519 // Time in JOB_INFO_2W is the number of milliseconds elapsed since the job was submitted. Calculate this time.
520 if (!SystemTimeToFileTime(&pJob
->stSubmitted
, &ftSubmitted
))
522 ERR("SystemTimeToFileTime failed with error %lu!\n", GetLastError());
526 GetSystemTimeAsFileTime(&ftNow
);
527 uliSubmitted
.LowPart
= ftSubmitted
.dwLowDateTime
;
528 uliSubmitted
.HighPart
= ftSubmitted
.dwHighDateTime
;
529 uliNow
.LowPart
= ftNow
.dwLowDateTime
;
530 uliNow
.HighPart
= ftNow
.dwHighDateTime
;
531 JobInfo2
.Time
= (DWORD
)((uliNow
.QuadPart
- uliSubmitted
.QuadPart
) / 10000);
533 // Position in JOB_INFO_2W is the 1-based index of the job in the processing queue.
534 // Retrieve this through the element index of the job in the Printer's Job List.
535 if (!LookupElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
, &JobInfo2
.Position
))
537 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
538 ERR("pJob could not be located in the Printer's Job List!\n");
542 // Make the index 1-based.
545 // Fill the rest of the structure.
546 JobInfo2
.JobId
= pJob
->dwJobID
;
547 JobInfo2
.PagesPrinted
= pJob
->dwPagesPrinted
;
548 JobInfo2
.Priority
= pJob
->dwPriority
;
549 JobInfo2
.StartTime
= pJob
->dwStartTime
;
550 JobInfo2
.Status
= pJob
->dwStatus
;
551 JobInfo2
.TotalPages
= pJob
->dwTotalPages
;
552 JobInfo2
.UntilTime
= pJob
->dwUntilTime
;
553 CopyMemory(&JobInfo2
.Submitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
555 // Finally copy the structure to the output pointer.
556 CopyMemory(pOutput
, &JobInfo2
, sizeof(JOB_INFO_2W
));
557 dwErrorCode
= ERROR_SUCCESS
;
564 LocalGetJob(HANDLE hPrinter
, DWORD JobId
, DWORD Level
, PBYTE pOutput
, DWORD cbBuf
, LPDWORD pcbNeeded
)
567 PLOCAL_HANDLE pHandle
;
569 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
571 // Check if this is a printer handle.
572 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
573 if (pHandle
->HandleType
!= Printer
)
575 dwErrorCode
= ERROR_INVALID_HANDLE
;
579 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
581 // Get the desired job.
582 pJob
= LookupElementSkiplist(&GlobalJobList
, &JobId
, NULL
);
583 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
585 dwErrorCode
= ERROR_INVALID_PARAMETER
;
589 // The function behaves differently for each level.
591 dwErrorCode
= _LocalGetJobLevel1(pPrinterHandle
, pJob
, pOutput
, cbBuf
, pcbNeeded
);
593 dwErrorCode
= _LocalGetJobLevel2(pPrinterHandle
, pJob
, pOutput
, cbBuf
, pcbNeeded
);
595 dwErrorCode
= ERROR_INVALID_LEVEL
;
598 SetLastError(dwErrorCode
);
599 return (dwErrorCode
== ERROR_SUCCESS
);
603 _LocalSetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PJOB_INFO_1W pJobInfo
)
607 // First check the validity of the input before changing anything.
608 if (!FindDatatype(pJob
->pPrintProcessor
, pJobInfo
->pDatatype
))
610 dwErrorCode
= ERROR_INVALID_DATATYPE
;
614 // Check if the datatype has changed.
615 if (wcscmp(pJob
->pwszDatatype
, pJobInfo
->pDatatype
) != 0)
617 // Use the new value.
618 if (!ReallocSplStr(&pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
620 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
621 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
626 // Check if the document name has changed.
627 if (wcscmp(pJob
->pwszDocumentName
, pJobInfo
->pDocument
) != 0)
629 // Use the new value.
630 if (!ReallocSplStr(&pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
632 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
633 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
638 // Check if the status message has changed.
639 if ((!pJob
->pwszStatus
&& pJobInfo
->pStatus
) || wcscmp(pJob
->pwszStatus
, pJobInfo
->pStatus
) != 0)
641 // Use the new value.
642 if (!ReallocSplStr(&pJob
->pwszStatus
, pJobInfo
->pStatus
))
644 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
645 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
650 // Check if the user name has changed.
651 if (wcscmp(pJob
->pwszUserName
, pJobInfo
->pUserName
) != 0)
653 // The new user name doesn't need to exist, so no additional verification is required.
655 // Use the new value.
656 if (!ReallocSplStr(&pJob
->pwszUserName
, pJobInfo
->pUserName
))
658 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
659 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
664 // Check if the priority has changed.
665 if (pJob
->dwPriority
!= pJobInfo
->Priority
&& IS_VALID_PRIORITY(pJobInfo
->Priority
))
667 // Set the new priority.
668 pJob
->dwPriority
= pJobInfo
->Priority
;
670 // Remove and reinsert the job in the Printer's Job List.
671 // The Compare function will be used to find the right position now considering the new priority.
672 DeleteElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
673 InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
676 // Check if the status flags have changed.
677 if (pJob
->dwStatus
!= pJobInfo
->Status
)
679 // Only add status flags that make sense.
680 if (pJobInfo
->Status
& JOB_STATUS_PAUSED
)
681 pJob
->dwStatus
|= JOB_STATUS_PAUSED
;
683 if (pJobInfo
->Status
& JOB_STATUS_ERROR
)
684 pJob
->dwStatus
|= JOB_STATUS_ERROR
;
686 if (pJobInfo
->Status
& JOB_STATUS_OFFLINE
)
687 pJob
->dwStatus
|= JOB_STATUS_OFFLINE
;
689 if (pJobInfo
->Status
& JOB_STATUS_PAPEROUT
)
690 pJob
->dwStatus
|= JOB_STATUS_PAPEROUT
;
693 dwErrorCode
= ERROR_SUCCESS
;
700 _LocalSetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PJOB_INFO_2W pJobInfo
)
703 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
705 // First check the validity of the input before changing anything.
706 pPrintProcessor
= FindPrintProcessor(pJobInfo
->pPrintProcessor
);
707 if (!pPrintProcessor
)
709 dwErrorCode
= ERROR_UNKNOWN_PRINTPROCESSOR
;
713 if (!FindDatatype(pPrintProcessor
, pJobInfo
->pDatatype
))
715 dwErrorCode
= ERROR_INVALID_DATATYPE
;
719 // Check if the datatype has changed.
720 if (wcscmp(pJob
->pwszDatatype
, pJobInfo
->pDatatype
) != 0)
722 // Use the new value.
723 if (!ReallocSplStr(&pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
725 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
726 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
731 // Check if the document name has changed.
732 if (wcscmp(pJob
->pwszDocumentName
, pJobInfo
->pDocument
) != 0)
734 // Use the new value.
735 if (!ReallocSplStr(&pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
737 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
738 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
743 // Check if the notify name has changed.
744 if (wcscmp(pJob
->pwszNotifyName
, pJobInfo
->pNotifyName
) != 0)
746 // The new notify name doesn't need to exist, so no additional verification is required.
748 // Use the new value.
749 if (!ReallocSplStr(&pJob
->pwszNotifyName
, pJobInfo
->pNotifyName
))
751 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
752 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
757 // Check if the (optional) Print Processor Parameters have changed.
758 if ((!pJob
->pwszPrintProcessorParameters
&& pJobInfo
->pParameters
) || wcscmp(pJob
->pwszPrintProcessorParameters
, pJobInfo
->pParameters
) != 0)
760 // Use the new value.
761 if (!ReallocSplStr(&pJob
->pwszPrintProcessorParameters
, pJobInfo
->pParameters
))
763 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
764 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
769 // Check if the (optional) Status Message has changed.
770 if ((!pJob
->pwszStatus
&& pJobInfo
->pStatus
) || wcscmp(pJob
->pwszStatus
, pJobInfo
->pStatus
) != 0)
772 // Use the new value.
773 if (!ReallocSplStr(&pJob
->pwszStatus
, pJobInfo
->pStatus
))
775 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
776 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
781 // Check if the user name has changed.
782 if (wcscmp(pJob
->pwszUserName
, pJobInfo
->pUserName
) != 0)
784 // The new user name doesn't need to exist, so no additional verification is required.
786 // Use the new value.
787 if (!ReallocSplStr(&pJob
->pwszUserName
, pJobInfo
->pUserName
))
789 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
790 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
795 // Check if the priority has changed.
796 if (pJob
->dwPriority
!= pJobInfo
->Priority
&& IS_VALID_PRIORITY(pJobInfo
->Priority
))
798 // Set the new priority.
799 pJob
->dwPriority
= pJobInfo
->Priority
;
801 // Remove and reinsert the job in the Printer's Job List.
802 // The Compare function will be used to find the right position now considering the new priority.
803 DeleteElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
804 InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
807 // Check if the status flags have changed.
808 if (pJob
->dwStatus
!= pJobInfo
->Status
)
810 // Only add status flags that make sense.
811 if (pJobInfo
->Status
& JOB_STATUS_PAUSED
)
812 pJob
->dwStatus
|= JOB_STATUS_PAUSED
;
814 if (pJobInfo
->Status
& JOB_STATUS_ERROR
)
815 pJob
->dwStatus
|= JOB_STATUS_ERROR
;
817 if (pJobInfo
->Status
& JOB_STATUS_OFFLINE
)
818 pJob
->dwStatus
|= JOB_STATUS_OFFLINE
;
820 if (pJobInfo
->Status
& JOB_STATUS_PAPEROUT
)
821 pJob
->dwStatus
|= JOB_STATUS_PAPEROUT
;
824 dwErrorCode
= ERROR_SUCCESS
;
831 LocalSetJob(HANDLE hPrinter
, DWORD JobId
, DWORD Level
, PBYTE pJobInfo
, DWORD Command
)
834 PLOCAL_HANDLE pHandle
;
836 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
838 // Check if this is a printer handle.
839 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
840 if (pHandle
->HandleType
!= Printer
)
842 dwErrorCode
= ERROR_INVALID_HANDLE
;
846 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
848 // Get the desired job.
849 pJob
= LookupElementSkiplist(&GlobalJobList
, &JobId
, NULL
);
850 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
852 dwErrorCode
= ERROR_INVALID_PARAMETER
;
856 // Setting job information is handled differently for each level.
860 dwErrorCode
= _LocalSetJobLevel1(pPrinterHandle
, pJob
, (PJOB_INFO_1W
)pJobInfo
);
862 dwErrorCode
= _LocalSetJobLevel2(pPrinterHandle
, pJob
, (PJOB_INFO_2W
)pJobInfo
);
864 dwErrorCode
= ERROR_INVALID_LEVEL
;
867 if (dwErrorCode
!= ERROR_SUCCESS
)
870 // Perform an additional command if desired.
876 dwErrorCode
= ERROR_SUCCESS
;
879 SetLastError(dwErrorCode
);
880 return (dwErrorCode
== ERROR_SUCCESS
);
884 ReadJobShadowFile(PCWSTR pwszFilePath
)
888 HANDLE hFile
= INVALID_HANDLE_VALUE
;
890 PLOCAL_JOB pReturnValue
= NULL
;
891 PLOCAL_PRINTER pPrinter
;
892 PSHD_HEADER pShadowFile
= NULL
;
893 PWSTR pwszPrinterName
;
895 // Try to open the file.
896 hFile
= CreateFileW(pwszFilePath
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
897 if (hFile
== INVALID_HANDLE_VALUE
)
899 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
903 // Get its file size (small enough for a single DWORD) and allocate memory for all of it.
904 cbFileSize
= GetFileSize(hFile
, NULL
);
905 pShadowFile
= DllAllocSplMem(cbFileSize
);
908 ERR("DllAllocSplMem failed with error %lufor file \"%S\"!\n", GetLastError(), pwszFilePath
);
912 // Read the entire file.
913 if (!ReadFile(hFile
, pShadowFile
, cbFileSize
, &cbRead
, NULL
))
915 ERR("ReadFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
919 // Check signature and header size.
920 if (pShadowFile
->dwSignature
!= SHD_WIN2003_SIGNATURE
|| pShadowFile
->cbHeader
!= sizeof(SHD_HEADER
))
922 ERR("Signature or Header Size mismatch for file \"%S\"!\n", pwszFilePath
);
926 // Retrieve the associated printer from the list.
927 pwszPrinterName
= (PWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrinterName
);
928 pPrinter
= LookupElementSkiplist(&PrinterList
, &pwszPrinterName
, NULL
);
931 ERR("Shadow file \"%S\" references a non-existing printer!\n", pwszFilePath
);
935 // Create a new job structure and copy over the relevant fields.
936 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
939 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
943 pJob
->dwJobID
= pShadowFile
->dwJobID
;
944 pJob
->dwTotalPages
= pShadowFile
->dwTotalPages
;
945 pJob
->dwPriority
= pShadowFile
->dwPriority
;
946 pJob
->pPrinter
= pPrinter
;
947 pJob
->pwszDatatype
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDatatype
));
948 pJob
->pwszDocumentName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDocumentName
));
949 pJob
->pwszOutputFile
= NULL
;
950 CopyMemory(&pJob
->stSubmitted
, &pShadowFile
->stSubmitted
, sizeof(SYSTEMTIME
));
951 CopyMemory(&pJob
->DevMode
, (PDEVMODEW
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDevMode
), sizeof(DEVMODEW
));
957 DllFreeSplMem(pShadowFile
);
959 if (hFile
!= INVALID_HANDLE_VALUE
)
966 WriteJobShadowFile(PCWSTR pwszFilePath
, const PLOCAL_JOB pJob
)
968 BOOL bReturnValue
= FALSE
;
970 DWORD cbDocumentName
;
974 DWORD dwCurrentOffset
;
975 HANDLE hFile
= INVALID_HANDLE_VALUE
;
976 PSHD_HEADER pShadowFile
= NULL
;
978 // Try to open the file.
979 hFile
= CreateFileW(pwszFilePath
, GENERIC_WRITE
, 0, NULL
, OPEN_ALWAYS
, 0, NULL
);
980 if (hFile
== INVALID_HANDLE_VALUE
)
982 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
986 // Compute the total size of the shadow file.
987 cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
988 cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
989 cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
990 cbFileSize
= sizeof(SHD_HEADER
) + cbPrinterName
+ cbDatatype
+ cbDocumentName
+ sizeof(DEVMODEW
);
992 // Allocate memory for it.
993 pShadowFile
= DllAllocSplMem(cbFileSize
);
996 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1000 // Fill out the shadow file header information.
1001 pShadowFile
->dwSignature
= SHD_WIN2003_SIGNATURE
;
1002 pShadowFile
->cbHeader
= sizeof(SHD_HEADER
);
1005 pShadowFile
->dwJobID
= pJob
->dwJobID
;
1006 pShadowFile
->dwTotalPages
= pJob
->dwTotalPages
;
1007 pShadowFile
->dwPriority
= pJob
->dwPriority
;
1008 CopyMemory(&pShadowFile
->stSubmitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
1010 // Add the extra values that are stored as offsets in the shadow file.
1011 // The first value begins right after the shadow file header.
1012 dwCurrentOffset
= sizeof(SHD_HEADER
);
1014 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
1015 pShadowFile
->offPrinterName
= dwCurrentOffset
;
1016 dwCurrentOffset
+= cbPrinterName
;
1018 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszDatatype
, cbDatatype
);
1019 pShadowFile
->offDatatype
= dwCurrentOffset
;
1020 dwCurrentOffset
+= cbDatatype
;
1022 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszDocumentName
, cbDocumentName
);
1023 pShadowFile
->offDocumentName
= dwCurrentOffset
;
1024 dwCurrentOffset
+= cbDocumentName
;
1026 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, &pJob
->DevMode
, sizeof(DEVMODEW
));
1027 pShadowFile
->offDevMode
= dwCurrentOffset
;
1028 dwCurrentOffset
+= sizeof(DEVMODEW
);
1031 if (!WriteFile(hFile
, pShadowFile
, cbFileSize
, &cbWritten
, NULL
))
1033 ERR("WriteFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1037 bReturnValue
= TRUE
;
1041 DllFreeSplMem(pShadowFile
);
1043 if (hFile
!= INVALID_HANDLE_VALUE
)
1046 return bReturnValue
;
1050 FreeJob(PLOCAL_JOB pJob
)
1052 ////////// TODO /////////
1054 DllFreeSplStr(pJob
->pwszDatatype
);
1055 DllFreeSplStr(pJob
->pwszDocumentName
);
1056 DllFreeSplStr(pJob
->pwszOutputFile
);
1057 DllFreeSplMem(pJob
);