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
* ppStart
, PBYTE
* ppEnd
, 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 };
373 // A Status Message is optional.
374 if (pJob
->pwszStatus
)
375 cbStatus
= (wcslen(pJob
->pwszStatus
) + 1) * sizeof(WCHAR
);
377 // Check if the supplied buffer is large enough.
378 *pcbNeeded
+= sizeof(JOB_INFO_1W
) + cbDatatype
+ cbDocumentName
+ cbMachineName
+ cbPrinterName
+ cbStatus
+ cbUserName
;
379 if (cbBuf
< *pcbNeeded
)
381 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
385 // Put the strings at the end of the buffer.
386 *ppEnd
-= cbDatatype
;
387 JobInfo1
.pDatatype
= (PWSTR
)*ppEnd
;
388 CopyMemory(*ppEnd
, pJob
->pwszDatatype
, cbDatatype
);
390 *ppEnd
-= cbDocumentName
;
391 JobInfo1
.pDocument
= (PWSTR
)*ppEnd
;
392 CopyMemory(*ppEnd
, pJob
->pwszDocumentName
, cbDocumentName
);
394 *ppEnd
-= cbMachineName
;
395 JobInfo1
.pMachineName
= (PWSTR
)*ppEnd
;
396 CopyMemory(*ppEnd
, pJob
->pwszMachineName
, cbMachineName
);
398 *ppEnd
-= cbPrinterName
;
399 JobInfo1
.pPrinterName
= (PWSTR
)*ppEnd
;
400 CopyMemory(*ppEnd
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
405 JobInfo1
.pStatus
= (PWSTR
)*ppEnd
;
406 CopyMemory(*ppEnd
, pJob
->pwszStatus
, cbStatus
);
409 *ppEnd
-= cbUserName
;
410 JobInfo1
.pUserName
= (PWSTR
)*ppEnd
;
411 CopyMemory(*ppEnd
, pJob
->pwszUserName
, cbUserName
);
413 // Fill the rest of the structure.
414 JobInfo1
.JobId
= pJob
->dwJobID
;
415 JobInfo1
.Priority
= pJob
->dwPriority
;
416 JobInfo1
.Status
= pJob
->dwStatus
;
417 JobInfo1
.TotalPages
= pJob
->dwTotalPages
;
418 CopyMemory(&JobInfo1
.Submitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
420 // Finally copy the structure to the output pointer.
421 CopyMemory(*ppStart
, &JobInfo1
, sizeof(JOB_INFO_1W
));
422 *ppStart
+= sizeof(JOB_INFO_1W
);
423 dwErrorCode
= ERROR_SUCCESS
;
430 _LocalGetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PBYTE
* ppStart
, PBYTE
* ppEnd
, DWORD cbBuf
, PDWORD pcbNeeded
)
432 DWORD cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
433 DWORD cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
434 DWORD cbDriverName
= (wcslen(pJob
->pPrinter
->pwszPrinterDriver
) + 1) * sizeof(WCHAR
);
435 DWORD cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
436 DWORD cbNotifyName
= (wcslen(pJob
->pwszNotifyName
) + 1) * sizeof(WCHAR
);
437 DWORD cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
438 DWORD cbPrintProcessor
= (wcslen(pJob
->pPrintProcessor
->pwszName
) + 1) * sizeof(WCHAR
);
439 DWORD cbPrintProcessorParameters
= 0;
441 DWORD cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
444 FILETIME ftSubmitted
;
445 JOB_INFO_2W JobInfo2
= { 0 };
446 ULARGE_INTEGER uliNow
;
447 ULARGE_INTEGER uliSubmitted
;
449 // Print Processor Parameters and Status Message are optional.
450 if (pJob
->pwszPrintProcessorParameters
)
451 cbPrintProcessorParameters
= (wcslen(pJob
->pwszPrintProcessorParameters
) + 1) * sizeof(WCHAR
);
453 if (pJob
->pwszStatus
)
454 cbStatus
= (wcslen(pJob
->pwszStatus
) + 1) * sizeof(WCHAR
);
456 // Check if the supplied buffer is large enough.
457 *pcbNeeded
+= sizeof(JOB_INFO_2W
) + cbDatatype
+ sizeof(DEVMODEW
) + cbDocumentName
+ cbDriverName
+ cbMachineName
+ cbNotifyName
+ cbPrinterName
+ cbPrintProcessor
+ cbPrintProcessorParameters
+ cbStatus
+ cbUserName
;
458 if (cbBuf
< *pcbNeeded
)
460 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
464 // Put the strings at the end of the buffer.
465 *ppEnd
-= cbDatatype
;
466 JobInfo2
.pDatatype
= (PWSTR
)*ppEnd
;
467 CopyMemory(*ppEnd
, pJob
->pwszDatatype
, cbDatatype
);
469 *ppEnd
-= sizeof(DEVMODEW
);
470 JobInfo2
.pDevMode
= (PDEVMODEW
)*ppEnd
;
471 CopyMemory(*ppEnd
, &pJob
->DevMode
, sizeof(DEVMODEW
));
473 *ppEnd
-= cbDocumentName
;
474 JobInfo2
.pDocument
= (PWSTR
)*ppEnd
;
475 CopyMemory(*ppEnd
, pJob
->pwszDocumentName
, cbDocumentName
);
477 *ppEnd
-= cbDriverName
;
478 JobInfo2
.pDriverName
= (PWSTR
)*ppEnd
;
479 CopyMemory(*ppEnd
, pJob
->pPrinter
->pwszPrinterDriver
, cbDriverName
);
481 *ppEnd
-= cbMachineName
;
482 JobInfo2
.pMachineName
= (PWSTR
)*ppEnd
;
483 CopyMemory(*ppEnd
, pJob
->pwszMachineName
, cbMachineName
);
485 *ppEnd
-= cbNotifyName
;
486 JobInfo2
.pNotifyName
= (PWSTR
)*ppEnd
;
487 CopyMemory(*ppEnd
, pJob
->pwszNotifyName
, cbNotifyName
);
489 *ppEnd
-= cbPrinterName
;
490 JobInfo2
.pPrinterName
= (PWSTR
)*ppEnd
;
491 CopyMemory(*ppEnd
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
493 *ppEnd
-= cbPrintProcessor
;
494 JobInfo2
.pPrintProcessor
= (PWSTR
)*ppEnd
;
495 CopyMemory(*ppEnd
, pJob
->pPrintProcessor
->pwszName
, cbPrintProcessor
);
497 if (cbPrintProcessorParameters
)
499 *ppEnd
-= cbPrintProcessorParameters
;
500 JobInfo2
.pParameters
= (PWSTR
)*ppEnd
;
501 CopyMemory(*ppEnd
, pJob
->pwszPrintProcessorParameters
, cbPrintProcessorParameters
);
507 JobInfo2
.pStatus
= (PWSTR
)*ppEnd
;
508 CopyMemory(*ppEnd
, pJob
->pwszStatus
, cbStatus
);
511 *ppEnd
-= cbUserName
;
512 JobInfo2
.pUserName
= (PWSTR
)*ppEnd
;
513 CopyMemory(*ppEnd
, pJob
->pwszUserName
, cbUserName
);
515 // Time in JOB_INFO_2W is the number of milliseconds elapsed since the job was submitted. Calculate this time.
516 if (!SystemTimeToFileTime(&pJob
->stSubmitted
, &ftSubmitted
))
518 ERR("SystemTimeToFileTime failed with error %lu!\n", GetLastError());
522 GetSystemTimeAsFileTime(&ftNow
);
523 uliSubmitted
.LowPart
= ftSubmitted
.dwLowDateTime
;
524 uliSubmitted
.HighPart
= ftSubmitted
.dwHighDateTime
;
525 uliNow
.LowPart
= ftNow
.dwLowDateTime
;
526 uliNow
.HighPart
= ftNow
.dwHighDateTime
;
527 JobInfo2
.Time
= (DWORD
)((uliNow
.QuadPart
- uliSubmitted
.QuadPart
) / 10000);
529 // Position in JOB_INFO_2W is the 1-based index of the job in the processing queue.
530 // Retrieve this through the element index of the job in the Printer's Job List.
531 if (!LookupElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
, &JobInfo2
.Position
))
533 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
534 ERR("pJob could not be located in the Printer's Job List!\n");
538 // Make the index 1-based.
541 // Fill the rest of the structure.
542 JobInfo2
.JobId
= pJob
->dwJobID
;
543 JobInfo2
.PagesPrinted
= pJob
->dwPagesPrinted
;
544 JobInfo2
.Priority
= pJob
->dwPriority
;
545 JobInfo2
.StartTime
= pJob
->dwStartTime
;
546 JobInfo2
.Status
= pJob
->dwStatus
;
547 JobInfo2
.TotalPages
= pJob
->dwTotalPages
;
548 JobInfo2
.UntilTime
= pJob
->dwUntilTime
;
549 CopyMemory(&JobInfo2
.Submitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
551 // Finally copy the structure to the output pointer.
552 CopyMemory(*ppStart
, &JobInfo2
, sizeof(JOB_INFO_2W
));
553 *ppStart
+= sizeof(JOB_INFO_2W
);
554 dwErrorCode
= ERROR_SUCCESS
;
561 LocalGetJob(HANDLE hPrinter
, DWORD JobId
, DWORD Level
, PBYTE pStart
, DWORD cbBuf
, LPDWORD pcbNeeded
)
564 PBYTE pEnd
= &pStart
[cbBuf
];
565 PLOCAL_HANDLE pHandle
;
567 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
569 // Check if this is a printer handle.
570 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
571 if (pHandle
->HandleType
!= Printer
)
573 dwErrorCode
= ERROR_INVALID_HANDLE
;
577 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
579 // Get the desired job.
580 pJob
= LookupElementSkiplist(&GlobalJobList
, &JobId
, NULL
);
581 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
583 dwErrorCode
= ERROR_INVALID_PARAMETER
;
590 // The function behaves differently for each level.
592 dwErrorCode
= _LocalGetJobLevel1(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
594 dwErrorCode
= _LocalGetJobLevel2(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
596 dwErrorCode
= ERROR_INVALID_LEVEL
;
599 SetLastError(dwErrorCode
);
600 return (dwErrorCode
== ERROR_SUCCESS
);
604 _LocalSetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PJOB_INFO_1W pJobInfo
)
608 // First check the validity of the input before changing anything.
609 if (!FindDatatype(pJob
->pPrintProcessor
, pJobInfo
->pDatatype
))
611 dwErrorCode
= ERROR_INVALID_DATATYPE
;
615 // Check if the datatype has changed.
616 if (wcscmp(pJob
->pwszDatatype
, pJobInfo
->pDatatype
) != 0)
618 // Use the new value.
619 if (!ReallocSplStr(&pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
621 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
622 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
627 // Check if the document name has changed.
628 if (wcscmp(pJob
->pwszDocumentName
, pJobInfo
->pDocument
) != 0)
630 // Use the new value.
631 if (!ReallocSplStr(&pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
633 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
634 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
639 // Check if the status message has changed.
640 if ((!pJob
->pwszStatus
&& pJobInfo
->pStatus
) || wcscmp(pJob
->pwszStatus
, pJobInfo
->pStatus
) != 0)
642 // Use the new value.
643 if (!ReallocSplStr(&pJob
->pwszStatus
, pJobInfo
->pStatus
))
645 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
646 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
651 // Check if the user name has changed.
652 if (wcscmp(pJob
->pwszUserName
, pJobInfo
->pUserName
) != 0)
654 // The new user name doesn't need to exist, so no additional verification is required.
656 // Use the new value.
657 if (!ReallocSplStr(&pJob
->pwszUserName
, pJobInfo
->pUserName
))
659 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
660 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
665 // Check if the priority has changed.
666 if (pJob
->dwPriority
!= pJobInfo
->Priority
&& IS_VALID_PRIORITY(pJobInfo
->Priority
))
668 // Set the new priority.
669 pJob
->dwPriority
= pJobInfo
->Priority
;
671 // Remove and reinsert the job in the Printer's Job List.
672 // The Compare function will be used to find the right position now considering the new priority.
673 DeleteElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
674 InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
677 // Check if the status flags have changed.
678 if (pJob
->dwStatus
!= pJobInfo
->Status
)
680 // Only add status flags that make sense.
681 if (pJobInfo
->Status
& JOB_STATUS_PAUSED
)
682 pJob
->dwStatus
|= JOB_STATUS_PAUSED
;
684 if (pJobInfo
->Status
& JOB_STATUS_ERROR
)
685 pJob
->dwStatus
|= JOB_STATUS_ERROR
;
687 if (pJobInfo
->Status
& JOB_STATUS_OFFLINE
)
688 pJob
->dwStatus
|= JOB_STATUS_OFFLINE
;
690 if (pJobInfo
->Status
& JOB_STATUS_PAPEROUT
)
691 pJob
->dwStatus
|= JOB_STATUS_PAPEROUT
;
694 dwErrorCode
= ERROR_SUCCESS
;
701 _LocalSetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PJOB_INFO_2W pJobInfo
)
704 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
706 // First check the validity of the input before changing anything.
707 pPrintProcessor
= FindPrintProcessor(pJobInfo
->pPrintProcessor
);
708 if (!pPrintProcessor
)
710 dwErrorCode
= ERROR_UNKNOWN_PRINTPROCESSOR
;
714 if (!FindDatatype(pPrintProcessor
, pJobInfo
->pDatatype
))
716 dwErrorCode
= ERROR_INVALID_DATATYPE
;
720 // Check if the datatype has changed.
721 if (wcscmp(pJob
->pwszDatatype
, pJobInfo
->pDatatype
) != 0)
723 // Use the new value.
724 if (!ReallocSplStr(&pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
726 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
727 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
732 // Check if the document name has changed.
733 if (wcscmp(pJob
->pwszDocumentName
, pJobInfo
->pDocument
) != 0)
735 // Use the new value.
736 if (!ReallocSplStr(&pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
738 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
739 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
744 // Check if the notify name has changed.
745 if (wcscmp(pJob
->pwszNotifyName
, pJobInfo
->pNotifyName
) != 0)
747 // The new notify name doesn't need to exist, so no additional verification is required.
749 // Use the new value.
750 if (!ReallocSplStr(&pJob
->pwszNotifyName
, pJobInfo
->pNotifyName
))
752 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
753 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
758 // Check if the (optional) Print Processor Parameters have changed.
759 if ((!pJob
->pwszPrintProcessorParameters
&& pJobInfo
->pParameters
) || wcscmp(pJob
->pwszPrintProcessorParameters
, pJobInfo
->pParameters
) != 0)
761 // Use the new value.
762 if (!ReallocSplStr(&pJob
->pwszPrintProcessorParameters
, pJobInfo
->pParameters
))
764 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
765 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
770 // Check if the (optional) Status Message has changed.
771 if ((!pJob
->pwszStatus
&& pJobInfo
->pStatus
) || wcscmp(pJob
->pwszStatus
, pJobInfo
->pStatus
) != 0)
773 // Use the new value.
774 if (!ReallocSplStr(&pJob
->pwszStatus
, pJobInfo
->pStatus
))
776 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
777 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
782 // Check if the user name has changed.
783 if (wcscmp(pJob
->pwszUserName
, pJobInfo
->pUserName
) != 0)
785 // The new user name doesn't need to exist, so no additional verification is required.
787 // Use the new value.
788 if (!ReallocSplStr(&pJob
->pwszUserName
, pJobInfo
->pUserName
))
790 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
791 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
796 // Check if the priority has changed.
797 if (pJob
->dwPriority
!= pJobInfo
->Priority
&& IS_VALID_PRIORITY(pJobInfo
->Priority
))
799 // Set the new priority.
800 pJob
->dwPriority
= pJobInfo
->Priority
;
802 // Remove and reinsert the job in the Printer's Job List.
803 // The Compare function will be used to find the right position now considering the new priority.
804 DeleteElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
805 InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
808 // Check if the status flags have changed.
809 if (pJob
->dwStatus
!= pJobInfo
->Status
)
811 // Only add status flags that make sense.
812 if (pJobInfo
->Status
& JOB_STATUS_PAUSED
)
813 pJob
->dwStatus
|= JOB_STATUS_PAUSED
;
815 if (pJobInfo
->Status
& JOB_STATUS_ERROR
)
816 pJob
->dwStatus
|= JOB_STATUS_ERROR
;
818 if (pJobInfo
->Status
& JOB_STATUS_OFFLINE
)
819 pJob
->dwStatus
|= JOB_STATUS_OFFLINE
;
821 if (pJobInfo
->Status
& JOB_STATUS_PAPEROUT
)
822 pJob
->dwStatus
|= JOB_STATUS_PAPEROUT
;
825 dwErrorCode
= ERROR_SUCCESS
;
832 LocalSetJob(HANDLE hPrinter
, DWORD JobId
, DWORD Level
, PBYTE pJobInfo
, DWORD Command
)
834 const WCHAR wszFolder
[] = L
"\\PRINTERS\\";
835 const DWORD cchFolder
= _countof(wszFolder
) - 1;
838 PLOCAL_HANDLE pHandle
;
840 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
841 WCHAR wszFullPath
[MAX_PATH
];
843 // Check if this is a printer handle.
844 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
845 if (pHandle
->HandleType
!= Printer
)
847 dwErrorCode
= ERROR_INVALID_HANDLE
;
851 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
853 // Get the desired job.
854 pJob
= LookupElementSkiplist(&GlobalJobList
, &JobId
, NULL
);
855 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
857 dwErrorCode
= ERROR_INVALID_PARAMETER
;
861 // Setting job information is handled differently for each level.
865 dwErrorCode
= _LocalSetJobLevel1(pPrinterHandle
, pJob
, (PJOB_INFO_1W
)pJobInfo
);
867 dwErrorCode
= _LocalSetJobLevel2(pPrinterHandle
, pJob
, (PJOB_INFO_2W
)pJobInfo
);
869 dwErrorCode
= ERROR_INVALID_LEVEL
;
872 if (dwErrorCode
!= ERROR_SUCCESS
)
875 // Construct the full path to the shadow file.
876 CopyMemory(wszFullPath
, wszSpoolDirectory
, cchSpoolDirectory
* sizeof(WCHAR
));
877 CopyMemory(&wszFullPath
[cchSpoolDirectory
], wszFolder
, cchFolder
* sizeof(WCHAR
));
878 swprintf(&wszFullPath
[cchSpoolDirectory
+ cchFolder
], L
"%05lu.SHD", JobId
);
880 // Write the job data into the shadow file.
881 WriteJobShadowFile(wszFullPath
, pJob
);
883 // Perform an additional command if desired.
889 dwErrorCode
= ERROR_SUCCESS
;
892 SetLastError(dwErrorCode
);
893 return (dwErrorCode
== ERROR_SUCCESS
);
897 LocalEnumJobs(HANDLE hPrinter
, DWORD FirstJob
, DWORD NoJobs
, DWORD Level
, PBYTE pStart
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
900 PBYTE pEnd
= &pStart
[cbBuf
];
901 PLOCAL_HANDLE pHandle
;
903 PSKIPLIST_NODE pFirstJobNode
;
904 PSKIPLIST_NODE pNode
;
905 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
907 // Check if this is a printer handle.
908 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
909 if (pHandle
->HandleType
!= Printer
)
911 dwErrorCode
= ERROR_INVALID_HANDLE
;
915 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
920 dwErrorCode
= ERROR_INVALID_LEVEL
;
928 // Lookup the node of the first job requested by the caller in the Printer's Job List.
929 pFirstJobNode
= LookupNodeByIndexSkiplist(&pPrinterHandle
->pPrinter
->JobList
, FirstJob
);
931 // Count the required buffer size and the number of jobs.
932 pNode
= pFirstJobNode
;
934 while (*pcReturned
< NoJobs
&& pNode
)
936 pJob
= (PLOCAL_JOB
)pNode
->Element
;
938 // The function behaves differently for each level.
940 _LocalGetJobLevel1(pPrinterHandle
, pJob
, NULL
, NULL
, 0, pcbNeeded
);
942 _LocalGetJobLevel2(pPrinterHandle
, pJob
, NULL
, NULL
, 0, pcbNeeded
);
944 // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
946 pNode
= pNode
->Next
[0];
949 // Check if the supplied buffer is large enough.
950 if (cbBuf
< *pcbNeeded
)
952 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
956 // Begin counting again and also empty the given buffer.
959 ZeroMemory(pStart
, cbBuf
);
961 // Now call the same functions again to copy the actual data for each job into the buffer.
962 pNode
= pFirstJobNode
;
964 while (*pcReturned
< NoJobs
&& pNode
)
966 pJob
= (PLOCAL_JOB
)pNode
->Element
;
968 // The function behaves differently for each level.
970 dwErrorCode
= _LocalGetJobLevel1(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
972 dwErrorCode
= _LocalGetJobLevel2(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
974 if (dwErrorCode
!= ERROR_SUCCESS
)
977 // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
979 pNode
= pNode
->Next
[0];
982 dwErrorCode
= ERROR_SUCCESS
;
985 SetLastError(dwErrorCode
);
986 return (dwErrorCode
== ERROR_SUCCESS
);
990 ReadJobShadowFile(PCWSTR pwszFilePath
)
994 HANDLE hFile
= INVALID_HANDLE_VALUE
;
996 PLOCAL_JOB pReturnValue
= NULL
;
997 PLOCAL_PRINTER pPrinter
;
998 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
999 PSHD_HEADER pShadowFile
= NULL
;
1000 PWSTR pwszPrinterName
;
1001 PWSTR pwszPrintProcessor
;
1003 // Try to open the file.
1004 hFile
= CreateFileW(pwszFilePath
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
1005 if (hFile
== INVALID_HANDLE_VALUE
)
1007 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1011 // Get its file size (small enough for a single DWORD) and allocate memory for all of it.
1012 cbFileSize
= GetFileSize(hFile
, NULL
);
1013 pShadowFile
= DllAllocSplMem(cbFileSize
);
1016 ERR("DllAllocSplMem failed with error %lufor file \"%S\"!\n", GetLastError(), pwszFilePath
);
1020 // Read the entire file.
1021 if (!ReadFile(hFile
, pShadowFile
, cbFileSize
, &cbRead
, NULL
))
1023 ERR("ReadFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1027 // Check signature and header size.
1028 if (pShadowFile
->dwSignature
!= SHD_WIN2003_SIGNATURE
|| pShadowFile
->cbHeader
!= sizeof(SHD_HEADER
))
1030 ERR("Signature or Header Size mismatch for file \"%S\"!\n", pwszFilePath
);
1034 // Retrieve the associated printer from the list.
1035 pwszPrinterName
= (PWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrinterName
);
1036 pPrinter
= LookupElementSkiplist(&PrinterList
, &pwszPrinterName
, NULL
);
1039 ERR("Shadow file \"%S\" references a non-existing printer \"%S\"!\n", pwszFilePath
, pwszPrinterName
);
1043 // Retrieve the associated Print Processor from the list.
1044 pwszPrintProcessor
= (PWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrintProcessor
);
1045 pPrintProcessor
= FindPrintProcessor(pwszPrintProcessor
);
1046 if (!pPrintProcessor
)
1048 ERR("Shadow file \"%S\" references a non-existing Print Processor \"%S\"!\n", pwszFilePath
, pwszPrintProcessor
);
1052 // Create a new job structure and copy over the relevant fields.
1053 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
1056 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1060 pJob
->dwJobID
= pShadowFile
->dwJobID
;
1061 pJob
->dwPriority
= pShadowFile
->dwPriority
;
1062 pJob
->dwStartTime
= pShadowFile
->dwStartTime
;
1063 pJob
->dwTotalPages
= pShadowFile
->dwTotalPages
;
1064 pJob
->dwUntilTime
= pShadowFile
->dwUntilTime
;
1065 pJob
->pPrinter
= pPrinter
;
1066 pJob
->pPrintProcessor
= pPrintProcessor
;
1067 pJob
->pwszDatatype
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDatatype
));
1068 pJob
->pwszDocumentName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDocumentName
));
1069 pJob
->pwszMachineName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offMachineName
));
1070 pJob
->pwszNotifyName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offNotifyName
));
1072 if (pShadowFile
->offPrintProcessorParameters
)
1073 pJob
->pwszPrintProcessorParameters
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrintProcessorParameters
));
1075 pJob
->pwszUserName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offUserName
));
1077 CopyMemory(&pJob
->stSubmitted
, &pShadowFile
->stSubmitted
, sizeof(SYSTEMTIME
));
1078 CopyMemory(&pJob
->DevMode
, (PDEVMODEW
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDevMode
), sizeof(DEVMODEW
));
1080 pReturnValue
= pJob
;
1084 DllFreeSplMem(pShadowFile
);
1086 if (hFile
!= INVALID_HANDLE_VALUE
)
1089 return pReturnValue
;
1093 WriteJobShadowFile(PWSTR pwszFilePath
, const PLOCAL_JOB pJob
)
1095 BOOL bReturnValue
= FALSE
;
1097 DWORD cbDocumentName
;
1099 DWORD cbMachineName
;
1101 DWORD cbPrinterDriver
;
1102 DWORD cbPrinterName
;
1103 DWORD cbPrintProcessor
;
1104 DWORD cbPrintProcessorParameters
= 0;
1107 DWORD dwCurrentOffset
;
1108 HANDLE hSHDFile
= INVALID_HANDLE_VALUE
;
1109 HANDLE hSPLFile
= INVALID_HANDLE_VALUE
;
1110 PSHD_HEADER pShadowFile
= NULL
;
1112 // Try to open the SHD file.
1113 hSHDFile
= CreateFileW(pwszFilePath
, GENERIC_WRITE
, 0, NULL
, OPEN_ALWAYS
, 0, NULL
);
1114 if (hSHDFile
== INVALID_HANDLE_VALUE
)
1116 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1120 // Compute the total size of the shadow file.
1121 cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
1122 cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
1123 cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
1124 cbNotifyName
= (wcslen(pJob
->pwszNotifyName
) + 1) * sizeof(WCHAR
);
1125 cbPrinterDriver
= (wcslen(pJob
->pPrinter
->pwszPrinterDriver
) + 1) * sizeof(WCHAR
);
1126 cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
1127 cbPrintProcessor
= (wcslen(pJob
->pPrintProcessor
->pwszName
) + 1) * sizeof(WCHAR
);
1128 cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
1130 // Print Processor Parameters are optional.
1131 if (pJob
->pwszPrintProcessorParameters
)
1132 cbPrintProcessorParameters
= (wcslen(pJob
->pwszPrintProcessorParameters
) + 1) * sizeof(WCHAR
);
1134 cbFileSize
= sizeof(SHD_HEADER
) + cbDatatype
+ cbDocumentName
+ sizeof(DEVMODEW
) + cbMachineName
+ cbNotifyName
+ cbPrinterDriver
+ cbPrinterName
+ cbPrintProcessor
+ cbPrintProcessorParameters
+ cbUserName
;
1136 // Allocate memory for it.
1137 pShadowFile
= DllAllocSplMem(cbFileSize
);
1140 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1144 // Fill out the shadow file header information.
1145 pShadowFile
->dwSignature
= SHD_WIN2003_SIGNATURE
;
1146 pShadowFile
->cbHeader
= sizeof(SHD_HEADER
);
1149 pShadowFile
->dwJobID
= pJob
->dwJobID
;
1150 pShadowFile
->dwPriority
= pJob
->dwPriority
;
1151 pShadowFile
->dwStartTime
= pJob
->dwStartTime
;
1152 pShadowFile
->dwTotalPages
= pJob
->dwTotalPages
;
1153 pShadowFile
->dwUntilTime
= pJob
->dwUntilTime
;
1154 CopyMemory(&pShadowFile
->stSubmitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
1156 // Determine the file size of the .SPL file
1157 wcscpy(wcsrchr(pwszFilePath
, L
'.'), L
".SPL");
1158 hSPLFile
= CreateFileW(pwszFilePath
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
1159 if (hSPLFile
!= INVALID_HANDLE_VALUE
)
1160 pShadowFile
->dwSPLSize
= GetFileSize(hSPLFile
, NULL
);
1162 // Add the extra values that are stored as offsets in the shadow file.
1163 // The first value begins right after the shadow file header.
1164 dwCurrentOffset
= sizeof(SHD_HEADER
);
1166 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszDatatype
, cbDatatype
);
1167 pShadowFile
->offDatatype
= dwCurrentOffset
;
1168 dwCurrentOffset
+= cbDatatype
;
1170 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszDocumentName
, cbDocumentName
);
1171 pShadowFile
->offDocumentName
= dwCurrentOffset
;
1172 dwCurrentOffset
+= cbDocumentName
;
1174 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, &pJob
->DevMode
, sizeof(DEVMODEW
));
1175 pShadowFile
->offDevMode
= dwCurrentOffset
;
1176 dwCurrentOffset
+= sizeof(DEVMODEW
);
1178 // offDriverName is only written, but automatically determined through offPrinterName when reading.
1179 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrinter
->pwszPrinterDriver
, cbPrinterDriver
);
1180 pShadowFile
->offDriverName
= dwCurrentOffset
;
1181 dwCurrentOffset
+= cbPrinterDriver
;
1183 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszMachineName
, cbMachineName
);
1184 pShadowFile
->offMachineName
= dwCurrentOffset
;
1185 dwCurrentOffset
+= cbMachineName
;
1187 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszNotifyName
, cbNotifyName
);
1188 pShadowFile
->offNotifyName
= dwCurrentOffset
;
1189 dwCurrentOffset
+= cbNotifyName
;
1191 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
1192 pShadowFile
->offPrinterName
= dwCurrentOffset
;
1193 dwCurrentOffset
+= cbPrinterName
;
1195 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrintProcessor
->pwszName
, cbPrintProcessor
);
1196 pShadowFile
->offPrintProcessor
= dwCurrentOffset
;
1197 dwCurrentOffset
+= cbPrintProcessor
;
1199 if (cbPrintProcessorParameters
)
1201 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszPrintProcessorParameters
, cbPrintProcessorParameters
);
1202 pShadowFile
->offPrintProcessorParameters
= dwCurrentOffset
;
1203 dwCurrentOffset
+= cbPrintProcessorParameters
;
1206 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszUserName
, cbUserName
);
1207 pShadowFile
->offUserName
= dwCurrentOffset
;
1208 dwCurrentOffset
+= cbUserName
;
1211 if (!WriteFile(hSHDFile
, pShadowFile
, cbFileSize
, &cbWritten
, NULL
))
1213 ERR("WriteFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1217 bReturnValue
= TRUE
;
1221 DllFreeSplMem(pShadowFile
);
1223 if (hSHDFile
!= INVALID_HANDLE_VALUE
)
1224 CloseHandle(hSHDFile
);
1226 if (hSPLFile
!= INVALID_HANDLE_VALUE
)
1227 CloseHandle(hSPLFile
);
1229 return bReturnValue
;
1233 FreeJob(PLOCAL_JOB pJob
)
1235 ////////// TODO /////////
1237 DllFreeSplStr(pJob
->pwszDatatype
);
1238 DllFreeSplStr(pJob
->pwszDocumentName
);
1239 DllFreeSplStr(pJob
->pwszOutputFile
);
1240 DllFreeSplMem(pJob
);