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;
116 PLOCAL_JOB pJob
= NULL
;
118 WCHAR wszFullPath
[MAX_PATH
];
119 WIN32_FIND_DATAW FindData
;
121 // This one is incremented in GetNextJobID.
124 // Initialize an empty list for all jobs of all local printers.
125 // We will search it by Job ID (supply a pointer to a DWORD in LookupElementSkiplist).
126 InitializeSkiplist(&GlobalJobList
, DllAllocSplMem
, _GlobalJobListCompareRoutine
, (PSKIPLIST_FREE_ROUTINE
)DllFreeSplMem
);
128 // Construct the full path search pattern.
129 CopyMemory(wszFullPath
, wszSpoolDirectory
, cchSpoolDirectory
* sizeof(WCHAR
));
130 CopyMemory(&wszFullPath
[cchSpoolDirectory
], wszPath
, (cchPath
+ 1) * sizeof(WCHAR
));
132 // Use the search pattern to look for unfinished jobs serialized in shadow files (.SHD)
133 hFind
= FindFirstFileW(wszFullPath
, &FindData
);
134 if (hFind
== INVALID_HANDLE_VALUE
)
136 // No unfinished jobs found.
137 dwErrorCode
= ERROR_SUCCESS
;
143 // Skip possible subdirectories.
144 if (FindData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
147 // Extract the Job ID and verify the file name format at the same time.
148 dwJobID
= wcstoul(FindData
.cFileName
, &p
, 10);
149 if (!IS_VALID_JOB_ID(dwJobID
))
152 if (wcsicmp(p
, L
".SHD") != 0)
155 // This shadow file has a valid name. Construct the full path and try to load it.
156 CopyMemory(&wszFullPath
[cchSpoolDirectory
+ cchFolders
], FindData
.cFileName
, cchPattern
);
157 pJob
= ReadJobShadowFile(wszFullPath
);
161 // Add it to the Global Job List.
162 if (!InsertElementSkiplist(&GlobalJobList
, pJob
))
164 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
165 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob
->dwJobID
);
169 // Add it to the Printer's Job List.
170 if (!InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
))
172 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
173 ERR("InsertElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob
->dwJobID
);
177 while (FindNextFileW(hFind
, &FindData
));
179 dwErrorCode
= ERROR_SUCCESS
;
186 SetLastError(dwErrorCode
);
187 return (dwErrorCode
== ERROR_SUCCESS
);
191 InitializePrinterJobList(PLOCAL_PRINTER pPrinter
)
193 // Initialize an empty list for this printer's jobs.
194 // This one is only for sorting the jobs. If you need to lookup a job, search the GlobalJobList by Job ID.
195 InitializeSkiplist(&pPrinter
->JobList
, DllAllocSplMem
, _PrinterJobListCompareRoutine
, (PSKIPLIST_FREE_ROUTINE
)DllFreeSplMem
);
199 LocalAddJob(HANDLE hPrinter
, DWORD Level
, LPBYTE pData
, DWORD cbBuf
, LPDWORD pcbNeeded
)
201 const WCHAR wszDoubleBackslash
[] = L
"\\";
202 const DWORD cchDoubleBackslash
= _countof(wszDoubleBackslash
) - 1;
203 const WCHAR wszPrintersPath
[] = L
"\\PRINTERS\\";
204 const DWORD cchPrintersPath
= _countof(wszPrintersPath
) - 1;
205 const DWORD cchSpl
= _countof("?????.SPL") - 1;
207 ADDJOB_INFO_1W AddJobInfo1
;
208 DWORD cchMachineName
;
212 PLOCAL_HANDLE pHandle
;
214 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
215 RPC_BINDING_HANDLE hServerBinding
= NULL
;
216 RPC_WSTR pwszBinding
= NULL
;
217 RPC_WSTR pwszMachineName
= NULL
;
219 // Check if this is a printer handle.
220 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
221 if (pHandle
->HandleType
!= Printer
)
223 dwErrorCode
= ERROR_INVALID_HANDLE
;
227 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
229 // This handle must not have started a job yet!
230 if (pPrinterHandle
->pStartedJob
)
232 dwErrorCode
= ERROR_INVALID_HANDLE
;
236 // Check if this is the right structure level.
239 dwErrorCode
= ERROR_INVALID_LEVEL
;
243 // Check if the printer is set to do direct printing.
244 // The Job List isn't used in this case.
245 if (pPrinterHandle
->pPrinter
->dwAttributes
& PRINTER_ATTRIBUTE_DIRECT
)
247 dwErrorCode
= ERROR_INVALID_ACCESS
;
251 // Check if the supplied buffer is large enough.
252 *pcbNeeded
= sizeof(ADDJOB_INFO_1W
) + (cchSpoolDirectory
+ cchPrintersPath
+ cchSpl
+ 1) * sizeof(WCHAR
);
253 if (cbBuf
< *pcbNeeded
)
255 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
260 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
263 dwErrorCode
= GetLastError();
264 ERR("DllAllocSplMem failed with error %lu!\n", dwErrorCode
);
268 // Reserve an ID for this job.
269 if (!GetNextJobID(&pJob
->dwJobID
))
271 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
275 // Copy over defaults to the LOCAL_JOB structure.
276 pJob
->pPrinter
= pPrinterHandle
->pPrinter
;
277 pJob
->pPrintProcessor
= pPrinterHandle
->pPrinter
->pPrintProcessor
;
278 pJob
->dwPriority
= DEF_PRIORITY
;
279 pJob
->dwStatus
= JOB_STATUS_SPOOLING
;
280 pJob
->pwszDatatype
= AllocSplStr(pPrinterHandle
->pwszDatatype
);
281 pJob
->pwszDocumentName
= AllocSplStr(wszDefaultDocumentName
);
282 pJob
->pDevMode
= DuplicateDevMode(pPrinterHandle
->pDevMode
);
283 GetSystemTime(&pJob
->stSubmitted
);
285 // Get the user name for the Job.
286 cchUserName
= UNLEN
+ 1;
287 pJob
->pwszUserName
= DllAllocSplMem(cchUserName
* sizeof(WCHAR
));
288 if (!GetUserNameW(pJob
->pwszUserName
, &cchUserName
))
290 dwErrorCode
= GetLastError();
291 ERR("GetUserNameW failed with error %lu!\n", dwErrorCode
);
295 // FIXME: For now, pwszNotifyName equals pwszUserName.
296 pJob
->pwszNotifyName
= AllocSplStr(pJob
->pwszUserName
);
298 // Get the name of the machine that submitted the Job over RPC.
299 dwErrorCode
= RpcBindingServerFromClient(NULL
, &hServerBinding
);
300 if (dwErrorCode
!= RPC_S_OK
)
302 ERR("RpcBindingServerFromClient failed with status %lu!\n", dwErrorCode
);
306 dwErrorCode
= RpcBindingToStringBindingW(hServerBinding
, &pwszBinding
);
307 if (dwErrorCode
!= RPC_S_OK
)
309 ERR("RpcBindingToStringBindingW failed with status %lu!\n", dwErrorCode
);
313 dwErrorCode
= RpcStringBindingParseW(pwszBinding
, NULL
, NULL
, &pwszMachineName
, NULL
, NULL
);
314 if (dwErrorCode
!= RPC_S_OK
)
316 ERR("RpcStringBindingParseW failed with status %lu!\n", dwErrorCode
);
320 cchMachineName
= wcslen(pwszMachineName
);
321 pJob
->pwszMachineName
= DllAllocSplMem((cchMachineName
+ cchDoubleBackslash
+ 1) * sizeof(WCHAR
));
322 CopyMemory(pJob
->pwszMachineName
, wszDoubleBackslash
, cchDoubleBackslash
* sizeof(WCHAR
));
323 CopyMemory(pJob
->pwszMachineName
+ cchDoubleBackslash
, pwszMachineName
, (cchMachineName
+ 1) * sizeof(WCHAR
));
325 // Add the job to the Global Job List.
326 if (!InsertElementSkiplist(&GlobalJobList
, pJob
))
328 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
329 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob
->dwJobID
);
333 // Add the job at the end of the Printer's Job List.
334 // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
335 if (!InsertTailElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
))
337 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
338 ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob
->dwJobID
);
342 // Return a proper ADDJOB_INFO_1W structure.
343 AddJobInfo1
.JobId
= pJob
->dwJobID
;
344 AddJobInfo1
.Path
= (PWSTR
)(pData
+ sizeof(ADDJOB_INFO_1W
));
346 CopyMemory(p
, &AddJobInfo1
, sizeof(ADDJOB_INFO_1W
));
347 p
+= sizeof(ADDJOB_INFO_1W
);
348 CopyMemory(p
, wszSpoolDirectory
, cchSpoolDirectory
);
349 p
+= cchSpoolDirectory
;
350 CopyMemory(p
, wszPrintersPath
, cchPrintersPath
);
351 p
+= cchPrintersPath
;
352 swprintf((PWSTR
)p
, L
"%05lu.SPL", pJob
->dwJobID
);
354 dwErrorCode
= ERROR_SUCCESS
;
358 RpcStringFreeW(&pwszMachineName
);
361 RpcStringFreeW(&pwszBinding
);
364 RpcBindingFree(&hServerBinding
);
366 SetLastError(dwErrorCode
);
367 return (dwErrorCode
== ERROR_SUCCESS
);
372 _LocalGetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PBYTE
* ppStart
, PBYTE
* ppEnd
, DWORD cbBuf
, PDWORD pcbNeeded
)
374 DWORD cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
375 DWORD cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
376 DWORD cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
377 DWORD cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
379 DWORD cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
381 JOB_INFO_1W JobInfo1
= { 0 };
383 // A Status Message is optional.
384 if (pJob
->pwszStatus
)
385 cbStatus
= (wcslen(pJob
->pwszStatus
) + 1) * sizeof(WCHAR
);
387 // Check if the supplied buffer is large enough.
388 *pcbNeeded
+= sizeof(JOB_INFO_1W
) + cbDatatype
+ cbDocumentName
+ cbMachineName
+ cbPrinterName
+ cbStatus
+ cbUserName
;
389 if (cbBuf
< *pcbNeeded
)
391 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
395 // Put the strings at the end of the buffer.
396 *ppEnd
-= cbDatatype
;
397 JobInfo1
.pDatatype
= (PWSTR
)*ppEnd
;
398 CopyMemory(*ppEnd
, pJob
->pwszDatatype
, cbDatatype
);
400 *ppEnd
-= cbDocumentName
;
401 JobInfo1
.pDocument
= (PWSTR
)*ppEnd
;
402 CopyMemory(*ppEnd
, pJob
->pwszDocumentName
, cbDocumentName
);
404 *ppEnd
-= cbMachineName
;
405 JobInfo1
.pMachineName
= (PWSTR
)*ppEnd
;
406 CopyMemory(*ppEnd
, pJob
->pwszMachineName
, cbMachineName
);
408 *ppEnd
-= cbPrinterName
;
409 JobInfo1
.pPrinterName
= (PWSTR
)*ppEnd
;
410 CopyMemory(*ppEnd
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
415 JobInfo1
.pStatus
= (PWSTR
)*ppEnd
;
416 CopyMemory(*ppEnd
, pJob
->pwszStatus
, cbStatus
);
419 *ppEnd
-= cbUserName
;
420 JobInfo1
.pUserName
= (PWSTR
)*ppEnd
;
421 CopyMemory(*ppEnd
, pJob
->pwszUserName
, cbUserName
);
423 // Fill the rest of the structure.
424 JobInfo1
.JobId
= pJob
->dwJobID
;
425 JobInfo1
.Priority
= pJob
->dwPriority
;
426 JobInfo1
.Status
= pJob
->dwStatus
;
427 JobInfo1
.TotalPages
= pJob
->dwTotalPages
;
428 CopyMemory(&JobInfo1
.Submitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
430 // Finally copy the structure to the output pointer.
431 CopyMemory(*ppStart
, &JobInfo1
, sizeof(JOB_INFO_1W
));
432 *ppStart
+= sizeof(JOB_INFO_1W
);
433 dwErrorCode
= ERROR_SUCCESS
;
440 _LocalGetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PBYTE
* ppStart
, PBYTE
* ppEnd
, DWORD cbBuf
, PDWORD pcbNeeded
)
442 DWORD cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
443 DWORD cbDevMode
= pJob
->pDevMode
->dmSize
+ pJob
->pDevMode
->dmDriverExtra
;
444 DWORD cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
445 DWORD cbDriverName
= (wcslen(pJob
->pPrinter
->pwszPrinterDriver
) + 1) * sizeof(WCHAR
);
446 DWORD cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
447 DWORD cbNotifyName
= (wcslen(pJob
->pwszNotifyName
) + 1) * sizeof(WCHAR
);
448 DWORD cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
449 DWORD cbPrintProcessor
= (wcslen(pJob
->pPrintProcessor
->pwszName
) + 1) * sizeof(WCHAR
);
450 DWORD cbPrintProcessorParameters
= 0;
452 DWORD cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
455 FILETIME ftSubmitted
;
456 JOB_INFO_2W JobInfo2
= { 0 };
457 ULARGE_INTEGER uliNow
;
458 ULARGE_INTEGER uliSubmitted
;
460 // Print Processor Parameters and Status Message are optional.
461 if (pJob
->pwszPrintProcessorParameters
)
462 cbPrintProcessorParameters
= (wcslen(pJob
->pwszPrintProcessorParameters
) + 1) * sizeof(WCHAR
);
464 if (pJob
->pwszStatus
)
465 cbStatus
= (wcslen(pJob
->pwszStatus
) + 1) * sizeof(WCHAR
);
467 // Check if the supplied buffer is large enough.
468 *pcbNeeded
+= sizeof(JOB_INFO_2W
) + cbDatatype
+ cbDevMode
+ cbDocumentName
+ cbDriverName
+ cbMachineName
+ cbNotifyName
+ cbPrinterName
+ cbPrintProcessor
+ cbPrintProcessorParameters
+ cbStatus
+ cbUserName
;
469 if (cbBuf
< *pcbNeeded
)
471 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
475 // Put the strings at the end of the buffer.
476 *ppEnd
-= cbDatatype
;
477 JobInfo2
.pDatatype
= (PWSTR
)*ppEnd
;
478 CopyMemory(*ppEnd
, pJob
->pwszDatatype
, cbDatatype
);
481 JobInfo2
.pDevMode
= (PDEVMODEW
)*ppEnd
;
482 CopyMemory(*ppEnd
, pJob
->pDevMode
, cbDevMode
);
484 *ppEnd
-= cbDocumentName
;
485 JobInfo2
.pDocument
= (PWSTR
)*ppEnd
;
486 CopyMemory(*ppEnd
, pJob
->pwszDocumentName
, cbDocumentName
);
488 *ppEnd
-= cbDriverName
;
489 JobInfo2
.pDriverName
= (PWSTR
)*ppEnd
;
490 CopyMemory(*ppEnd
, pJob
->pPrinter
->pwszPrinterDriver
, cbDriverName
);
492 *ppEnd
-= cbMachineName
;
493 JobInfo2
.pMachineName
= (PWSTR
)*ppEnd
;
494 CopyMemory(*ppEnd
, pJob
->pwszMachineName
, cbMachineName
);
496 *ppEnd
-= cbNotifyName
;
497 JobInfo2
.pNotifyName
= (PWSTR
)*ppEnd
;
498 CopyMemory(*ppEnd
, pJob
->pwszNotifyName
, cbNotifyName
);
500 *ppEnd
-= cbPrinterName
;
501 JobInfo2
.pPrinterName
= (PWSTR
)*ppEnd
;
502 CopyMemory(*ppEnd
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
504 *ppEnd
-= cbPrintProcessor
;
505 JobInfo2
.pPrintProcessor
= (PWSTR
)*ppEnd
;
506 CopyMemory(*ppEnd
, pJob
->pPrintProcessor
->pwszName
, cbPrintProcessor
);
508 if (cbPrintProcessorParameters
)
510 *ppEnd
-= cbPrintProcessorParameters
;
511 JobInfo2
.pParameters
= (PWSTR
)*ppEnd
;
512 CopyMemory(*ppEnd
, pJob
->pwszPrintProcessorParameters
, cbPrintProcessorParameters
);
518 JobInfo2
.pStatus
= (PWSTR
)*ppEnd
;
519 CopyMemory(*ppEnd
, pJob
->pwszStatus
, cbStatus
);
522 *ppEnd
-= cbUserName
;
523 JobInfo2
.pUserName
= (PWSTR
)*ppEnd
;
524 CopyMemory(*ppEnd
, pJob
->pwszUserName
, cbUserName
);
526 // Time in JOB_INFO_2W is the number of milliseconds elapsed since the job was submitted. Calculate this time.
527 if (!SystemTimeToFileTime(&pJob
->stSubmitted
, &ftSubmitted
))
529 ERR("SystemTimeToFileTime failed with error %lu!\n", GetLastError());
533 GetSystemTimeAsFileTime(&ftNow
);
534 uliSubmitted
.LowPart
= ftSubmitted
.dwLowDateTime
;
535 uliSubmitted
.HighPart
= ftSubmitted
.dwHighDateTime
;
536 uliNow
.LowPart
= ftNow
.dwLowDateTime
;
537 uliNow
.HighPart
= ftNow
.dwHighDateTime
;
538 JobInfo2
.Time
= (DWORD
)((uliNow
.QuadPart
- uliSubmitted
.QuadPart
) / 10000);
540 // Position in JOB_INFO_2W is the 1-based index of the job in the processing queue.
541 // Retrieve this through the element index of the job in the Printer's Job List.
542 if (!LookupElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
, &JobInfo2
.Position
))
544 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
545 ERR("pJob could not be located in the Printer's Job List!\n");
549 // Make the index 1-based.
552 // Fill the rest of the structure.
553 JobInfo2
.JobId
= pJob
->dwJobID
;
554 JobInfo2
.PagesPrinted
= pJob
->dwPagesPrinted
;
555 JobInfo2
.Priority
= pJob
->dwPriority
;
556 JobInfo2
.StartTime
= pJob
->dwStartTime
;
557 JobInfo2
.Status
= pJob
->dwStatus
;
558 JobInfo2
.TotalPages
= pJob
->dwTotalPages
;
559 JobInfo2
.UntilTime
= pJob
->dwUntilTime
;
560 CopyMemory(&JobInfo2
.Submitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
562 // Finally copy the structure to the output pointer.
563 CopyMemory(*ppStart
, &JobInfo2
, sizeof(JOB_INFO_2W
));
564 *ppStart
+= sizeof(JOB_INFO_2W
);
565 dwErrorCode
= ERROR_SUCCESS
;
572 LocalGetJob(HANDLE hPrinter
, DWORD JobId
, DWORD Level
, PBYTE pStart
, DWORD cbBuf
, LPDWORD pcbNeeded
)
575 PBYTE pEnd
= &pStart
[cbBuf
];
576 PLOCAL_HANDLE pHandle
;
578 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
580 // Check if this is a printer handle.
581 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
582 if (pHandle
->HandleType
!= Printer
)
584 dwErrorCode
= ERROR_INVALID_HANDLE
;
588 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
590 // Get the desired job.
591 pJob
= LookupElementSkiplist(&GlobalJobList
, &JobId
, NULL
);
592 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
594 dwErrorCode
= ERROR_INVALID_PARAMETER
;
601 // The function behaves differently for each level.
603 dwErrorCode
= _LocalGetJobLevel1(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
605 dwErrorCode
= _LocalGetJobLevel2(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
607 dwErrorCode
= ERROR_INVALID_LEVEL
;
610 SetLastError(dwErrorCode
);
611 return (dwErrorCode
== ERROR_SUCCESS
);
615 _LocalSetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PJOB_INFO_1W pJobInfo
)
619 // First check the validity of the input before changing anything.
620 if (!FindDatatype(pJob
->pPrintProcessor
, pJobInfo
->pDatatype
))
622 dwErrorCode
= ERROR_INVALID_DATATYPE
;
626 // Check if the datatype has changed.
627 if (wcscmp(pJob
->pwszDatatype
, pJobInfo
->pDatatype
) != 0)
629 // Use the new value.
630 if (!ReallocSplStr(&pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
632 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
633 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
638 // Check if the document name has changed.
639 if (wcscmp(pJob
->pwszDocumentName
, pJobInfo
->pDocument
) != 0)
641 // Use the new value.
642 if (!ReallocSplStr(&pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
644 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
645 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
650 // Check if the status message has changed.
651 if ((!pJob
->pwszStatus
&& pJobInfo
->pStatus
) || wcscmp(pJob
->pwszStatus
, pJobInfo
->pStatus
) != 0)
653 // Use the new value.
654 if (!ReallocSplStr(&pJob
->pwszStatus
, pJobInfo
->pStatus
))
656 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
657 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
662 // Check if the user name has changed.
663 if (wcscmp(pJob
->pwszUserName
, pJobInfo
->pUserName
) != 0)
665 // The new user name doesn't need to exist, so no additional verification is required.
667 // Use the new value.
668 if (!ReallocSplStr(&pJob
->pwszUserName
, pJobInfo
->pUserName
))
670 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
671 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
676 // Check if the priority has changed.
677 if (pJob
->dwPriority
!= pJobInfo
->Priority
&& IS_VALID_PRIORITY(pJobInfo
->Priority
))
679 // Set the new priority.
680 pJob
->dwPriority
= pJobInfo
->Priority
;
682 // Remove and reinsert the job in the Printer's Job List.
683 // The Compare function will be used to find the right position now considering the new priority.
684 DeleteElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
685 InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
688 // Check if the status flags have changed.
689 if (pJob
->dwStatus
!= pJobInfo
->Status
)
691 // Only add status flags that make sense.
692 if (pJobInfo
->Status
& JOB_STATUS_PAUSED
)
693 pJob
->dwStatus
|= JOB_STATUS_PAUSED
;
695 if (pJobInfo
->Status
& JOB_STATUS_ERROR
)
696 pJob
->dwStatus
|= JOB_STATUS_ERROR
;
698 if (pJobInfo
->Status
& JOB_STATUS_OFFLINE
)
699 pJob
->dwStatus
|= JOB_STATUS_OFFLINE
;
701 if (pJobInfo
->Status
& JOB_STATUS_PAPEROUT
)
702 pJob
->dwStatus
|= JOB_STATUS_PAPEROUT
;
705 dwErrorCode
= ERROR_SUCCESS
;
712 _LocalSetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PJOB_INFO_2W pJobInfo
)
715 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
717 // First check the validity of the input before changing anything.
718 pPrintProcessor
= FindPrintProcessor(pJobInfo
->pPrintProcessor
);
719 if (!pPrintProcessor
)
721 dwErrorCode
= ERROR_UNKNOWN_PRINTPROCESSOR
;
725 if (!FindDatatype(pPrintProcessor
, pJobInfo
->pDatatype
))
727 dwErrorCode
= ERROR_INVALID_DATATYPE
;
731 // Check if the datatype has changed.
732 if (wcscmp(pJob
->pwszDatatype
, pJobInfo
->pDatatype
) != 0)
734 // Use the new value.
735 if (!ReallocSplStr(&pJob
->pwszDatatype
, pJobInfo
->pDatatype
))
737 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
738 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
743 // Check if the document name has changed.
744 if (wcscmp(pJob
->pwszDocumentName
, pJobInfo
->pDocument
) != 0)
746 // Use the new value.
747 if (!ReallocSplStr(&pJob
->pwszDocumentName
, pJobInfo
->pDocument
))
749 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
750 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
755 // Check if the notify name has changed.
756 if (wcscmp(pJob
->pwszNotifyName
, pJobInfo
->pNotifyName
) != 0)
758 // The new notify name doesn't need to exist, so no additional verification is required.
760 // Use the new value.
761 if (!ReallocSplStr(&pJob
->pwszNotifyName
, pJobInfo
->pNotifyName
))
763 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
764 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
769 // Check if the (optional) Print Processor Parameters have changed.
770 if ((!pJob
->pwszPrintProcessorParameters
&& pJobInfo
->pParameters
) || wcscmp(pJob
->pwszPrintProcessorParameters
, pJobInfo
->pParameters
) != 0)
772 // Use the new value.
773 if (!ReallocSplStr(&pJob
->pwszPrintProcessorParameters
, pJobInfo
->pParameters
))
775 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
776 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
781 // Check if the (optional) Status Message has changed.
782 if ((!pJob
->pwszStatus
&& pJobInfo
->pStatus
) || wcscmp(pJob
->pwszStatus
, pJobInfo
->pStatus
) != 0)
784 // Use the new value.
785 if (!ReallocSplStr(&pJob
->pwszStatus
, pJobInfo
->pStatus
))
787 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
788 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
793 // Check if the user name has changed.
794 if (wcscmp(pJob
->pwszUserName
, pJobInfo
->pUserName
) != 0)
796 // The new user name doesn't need to exist, so no additional verification is required.
798 // Use the new value.
799 if (!ReallocSplStr(&pJob
->pwszUserName
, pJobInfo
->pUserName
))
801 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
802 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
807 // Check if the priority has changed.
808 if (pJob
->dwPriority
!= pJobInfo
->Priority
&& IS_VALID_PRIORITY(pJobInfo
->Priority
))
810 // Set the new priority.
811 pJob
->dwPriority
= pJobInfo
->Priority
;
813 // Remove and reinsert the job in the Printer's Job List.
814 // The Compare function will be used to find the right position now considering the new priority.
815 DeleteElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
816 InsertElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
);
819 // Check if the status flags have changed.
820 if (pJob
->dwStatus
!= pJobInfo
->Status
)
822 // Only add status flags that make sense.
823 if (pJobInfo
->Status
& JOB_STATUS_PAUSED
)
824 pJob
->dwStatus
|= JOB_STATUS_PAUSED
;
826 if (pJobInfo
->Status
& JOB_STATUS_ERROR
)
827 pJob
->dwStatus
|= JOB_STATUS_ERROR
;
829 if (pJobInfo
->Status
& JOB_STATUS_OFFLINE
)
830 pJob
->dwStatus
|= JOB_STATUS_OFFLINE
;
832 if (pJobInfo
->Status
& JOB_STATUS_PAPEROUT
)
833 pJob
->dwStatus
|= JOB_STATUS_PAPEROUT
;
836 dwErrorCode
= ERROR_SUCCESS
;
843 LocalSetJob(HANDLE hPrinter
, DWORD JobId
, DWORD Level
, PBYTE pJobInfo
, DWORD Command
)
845 const WCHAR wszFolder
[] = L
"\\PRINTERS\\";
846 const DWORD cchFolder
= _countof(wszFolder
) - 1;
849 PLOCAL_HANDLE pHandle
;
851 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
852 WCHAR wszFullPath
[MAX_PATH
];
854 // Check if this is a printer handle.
855 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
856 if (pHandle
->HandleType
!= Printer
)
858 dwErrorCode
= ERROR_INVALID_HANDLE
;
862 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
864 // Get the desired job.
865 pJob
= LookupElementSkiplist(&GlobalJobList
, &JobId
, NULL
);
866 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
868 dwErrorCode
= ERROR_INVALID_PARAMETER
;
872 // Setting job information is handled differently for each level.
876 dwErrorCode
= _LocalSetJobLevel1(pPrinterHandle
, pJob
, (PJOB_INFO_1W
)pJobInfo
);
878 dwErrorCode
= _LocalSetJobLevel2(pPrinterHandle
, pJob
, (PJOB_INFO_2W
)pJobInfo
);
880 dwErrorCode
= ERROR_INVALID_LEVEL
;
883 if (dwErrorCode
!= ERROR_SUCCESS
)
886 // Construct the full path to the shadow file.
887 CopyMemory(wszFullPath
, wszSpoolDirectory
, cchSpoolDirectory
* sizeof(WCHAR
));
888 CopyMemory(&wszFullPath
[cchSpoolDirectory
], wszFolder
, cchFolder
* sizeof(WCHAR
));
889 swprintf(&wszFullPath
[cchSpoolDirectory
+ cchFolder
], L
"%05lu.SHD", JobId
);
891 // Write the job data into the shadow file.
892 WriteJobShadowFile(wszFullPath
, pJob
);
894 // Perform an additional command if desired.
900 dwErrorCode
= ERROR_SUCCESS
;
903 SetLastError(dwErrorCode
);
904 return (dwErrorCode
== ERROR_SUCCESS
);
908 LocalEnumJobs(HANDLE hPrinter
, DWORD FirstJob
, DWORD NoJobs
, DWORD Level
, PBYTE pStart
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
912 PBYTE pEnd
= &pStart
[cbBuf
];
913 PLOCAL_HANDLE pHandle
;
915 PSKIPLIST_NODE pFirstJobNode
;
916 PSKIPLIST_NODE pNode
;
917 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
919 // Check if this is a printer handle.
920 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
921 if (pHandle
->HandleType
!= Printer
)
923 dwErrorCode
= ERROR_INVALID_HANDLE
;
927 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
932 dwErrorCode
= ERROR_INVALID_LEVEL
;
940 // Lookup the node of the first job requested by the caller in the Printer's Job List.
941 pFirstJobNode
= LookupNodeByIndexSkiplist(&pPrinterHandle
->pPrinter
->JobList
, FirstJob
);
943 // Count the required buffer size and the number of jobs.
945 pNode
= pFirstJobNode
;
947 while (i
< NoJobs
&& pNode
)
949 pJob
= (PLOCAL_JOB
)pNode
->Element
;
951 // The function behaves differently for each level.
953 _LocalGetJobLevel1(pPrinterHandle
, pJob
, NULL
, NULL
, 0, pcbNeeded
);
955 _LocalGetJobLevel2(pPrinterHandle
, pJob
, NULL
, NULL
, 0, pcbNeeded
);
957 // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
959 pNode
= pNode
->Next
[0];
962 // Check if the supplied buffer is large enough.
963 if (cbBuf
< *pcbNeeded
)
965 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
969 // Begin counting again and also empty the given buffer.
971 ZeroMemory(pStart
, cbBuf
);
973 // Now call the same functions again to copy the actual data for each job into the buffer.
975 pNode
= pFirstJobNode
;
977 while (i
< NoJobs
&& pNode
)
979 pJob
= (PLOCAL_JOB
)pNode
->Element
;
981 // The function behaves differently for each level.
983 dwErrorCode
= _LocalGetJobLevel1(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
985 dwErrorCode
= _LocalGetJobLevel2(pPrinterHandle
, pJob
, &pStart
, &pEnd
, cbBuf
, pcbNeeded
);
987 if (dwErrorCode
!= ERROR_SUCCESS
)
990 // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
992 pNode
= pNode
->Next
[0];
996 dwErrorCode
= ERROR_SUCCESS
;
999 SetLastError(dwErrorCode
);
1000 return (dwErrorCode
== ERROR_SUCCESS
);
1004 LocalScheduleJob(HANDLE hPrinter
, DWORD dwJobID
)
1006 const WCHAR wszFolder
[] = L
"\\PRINTERS\\";
1007 const DWORD cchFolder
= _countof(wszFolder
) - 1;
1011 PLOCAL_HANDLE pHandle
;
1013 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1014 WCHAR wszFullPath
[MAX_PATH
];
1016 // Check if this is a printer handle.
1017 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1018 if (pHandle
->HandleType
!= Printer
)
1020 dwErrorCode
= ERROR_INVALID_HANDLE
;
1024 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1026 // Check if the Job ID is valid.
1027 pJob
= LookupElementSkiplist(&GlobalJobList
, &dwJobID
, NULL
);
1028 if (!pJob
|| pJob
->pPrinter
!= pPrinterHandle
->pPrinter
)
1030 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1034 // Construct the full path to the spool file.
1035 CopyMemory(wszFullPath
, wszSpoolDirectory
, cchSpoolDirectory
* sizeof(WCHAR
));
1036 CopyMemory(&wszFullPath
[cchSpoolDirectory
], wszFolder
, cchFolder
* sizeof(WCHAR
));
1037 swprintf(&wszFullPath
[cchSpoolDirectory
+ cchFolder
], L
"%05lu.SPL", dwJobID
);
1039 // Check if it exists.
1040 dwAttributes
= GetFileAttributesW(wszFullPath
);
1041 if (dwAttributes
== INVALID_FILE_ATTRIBUTES
|| dwAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1043 dwErrorCode
= ERROR_SPOOL_FILE_NOT_FOUND
;
1047 // Switch from spooling to printing.
1048 pJob
->dwStatus
&= ~JOB_STATUS_SPOOLING
;
1049 pJob
->dwStatus
|= JOB_STATUS_PRINTING
;
1051 // Write the job data into the shadow file.
1052 wcscpy(wcsrchr(wszFullPath
, L
'.'), L
".SHD");
1053 WriteJobShadowFile(wszFullPath
, pJob
);
1055 dwErrorCode
= ERROR_SUCCESS
;
1058 SetLastError(dwErrorCode
);
1059 return (dwErrorCode
== ERROR_SUCCESS
);
1063 ReadJobShadowFile(PCWSTR pwszFilePath
)
1067 HANDLE hFile
= INVALID_HANDLE_VALUE
;
1069 PLOCAL_JOB pReturnValue
= NULL
;
1070 PLOCAL_PRINTER pPrinter
;
1071 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
1072 PSHD_HEADER pShadowFile
= NULL
;
1073 PWSTR pwszPrinterName
;
1074 PWSTR pwszPrintProcessor
;
1076 // Try to open the file.
1077 hFile
= CreateFileW(pwszFilePath
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
1078 if (hFile
== INVALID_HANDLE_VALUE
)
1080 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1084 // Get its file size (small enough for a single DWORD) and allocate memory for all of it.
1085 cbFileSize
= GetFileSize(hFile
, NULL
);
1086 pShadowFile
= DllAllocSplMem(cbFileSize
);
1089 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1093 // Read the entire file.
1094 if (!ReadFile(hFile
, pShadowFile
, cbFileSize
, &cbRead
, NULL
))
1096 ERR("ReadFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1100 // Check signature and header size.
1101 if (pShadowFile
->dwSignature
!= SHD_WIN2003_SIGNATURE
|| pShadowFile
->cbHeader
!= sizeof(SHD_HEADER
))
1103 ERR("Signature or Header Size mismatch for file \"%S\"!\n", pwszFilePath
);
1107 // Retrieve the associated printer from the list.
1108 pwszPrinterName
= (PWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrinterName
);
1109 pPrinter
= LookupElementSkiplist(&PrinterList
, &pwszPrinterName
, NULL
);
1112 ERR("Shadow file \"%S\" references a non-existing printer \"%S\"!\n", pwszFilePath
, pwszPrinterName
);
1116 // Retrieve the associated Print Processor from the list.
1117 pwszPrintProcessor
= (PWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrintProcessor
);
1118 pPrintProcessor
= FindPrintProcessor(pwszPrintProcessor
);
1119 if (!pPrintProcessor
)
1121 ERR("Shadow file \"%S\" references a non-existing Print Processor \"%S\"!\n", pwszFilePath
, pwszPrintProcessor
);
1125 // Create a new job structure and copy over the relevant fields.
1126 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
1129 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1133 pJob
->dwJobID
= pShadowFile
->dwJobID
;
1134 pJob
->dwPriority
= pShadowFile
->dwPriority
;
1135 pJob
->dwStartTime
= pShadowFile
->dwStartTime
;
1136 pJob
->dwTotalPages
= pShadowFile
->dwTotalPages
;
1137 pJob
->dwUntilTime
= pShadowFile
->dwUntilTime
;
1138 pJob
->pPrinter
= pPrinter
;
1139 pJob
->pPrintProcessor
= pPrintProcessor
;
1140 pJob
->pDevMode
= DuplicateDevMode((PDEVMODEW
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDevMode
));
1141 pJob
->pwszDatatype
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDatatype
));
1142 pJob
->pwszDocumentName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDocumentName
));
1143 pJob
->pwszMachineName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offMachineName
));
1144 pJob
->pwszNotifyName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offNotifyName
));
1146 if (pShadowFile
->offPrintProcessorParameters
)
1147 pJob
->pwszPrintProcessorParameters
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrintProcessorParameters
));
1149 pJob
->pwszUserName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offUserName
));
1150 CopyMemory(&pJob
->stSubmitted
, &pShadowFile
->stSubmitted
, sizeof(SYSTEMTIME
));
1152 pReturnValue
= pJob
;
1156 DllFreeSplMem(pShadowFile
);
1158 if (hFile
!= INVALID_HANDLE_VALUE
)
1161 return pReturnValue
;
1165 WriteJobShadowFile(PWSTR pwszFilePath
, const PLOCAL_JOB pJob
)
1167 BOOL bReturnValue
= FALSE
;
1170 DWORD cbDocumentName
;
1172 DWORD cbMachineName
;
1174 DWORD cbPrinterDriver
;
1175 DWORD cbPrinterName
;
1176 DWORD cbPrintProcessor
;
1177 DWORD cbPrintProcessorParameters
= 0;
1180 DWORD dwCurrentOffset
;
1181 HANDLE hSHDFile
= INVALID_HANDLE_VALUE
;
1182 HANDLE hSPLFile
= INVALID_HANDLE_VALUE
;
1183 PSHD_HEADER pShadowFile
= NULL
;
1185 // Try to open the SHD file.
1186 hSHDFile
= CreateFileW(pwszFilePath
, GENERIC_WRITE
, FILE_SHARE_READ
, NULL
, CREATE_ALWAYS
, 0, NULL
);
1187 if (hSHDFile
== INVALID_HANDLE_VALUE
)
1189 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1193 // Compute the total size of the shadow file.
1194 cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
1195 cbDevMode
= pJob
->pDevMode
->dmSize
+ pJob
->pDevMode
->dmDriverExtra
;
1196 cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
1197 cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
1198 cbNotifyName
= (wcslen(pJob
->pwszNotifyName
) + 1) * sizeof(WCHAR
);
1199 cbPrinterDriver
= (wcslen(pJob
->pPrinter
->pwszPrinterDriver
) + 1) * sizeof(WCHAR
);
1200 cbPrinterName
= (wcslen(pJob
->pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
1201 cbPrintProcessor
= (wcslen(pJob
->pPrintProcessor
->pwszName
) + 1) * sizeof(WCHAR
);
1202 cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
1204 // Print Processor Parameters are optional.
1205 if (pJob
->pwszPrintProcessorParameters
)
1206 cbPrintProcessorParameters
= (wcslen(pJob
->pwszPrintProcessorParameters
) + 1) * sizeof(WCHAR
);
1208 cbFileSize
= sizeof(SHD_HEADER
) + cbDatatype
+ cbDocumentName
+ cbDevMode
+ cbMachineName
+ cbNotifyName
+ cbPrinterDriver
+ cbPrinterName
+ cbPrintProcessor
+ cbPrintProcessorParameters
+ cbUserName
;
1210 // Allocate memory for it.
1211 pShadowFile
= DllAllocSplMem(cbFileSize
);
1214 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1218 // Fill out the shadow file header information.
1219 pShadowFile
->dwSignature
= SHD_WIN2003_SIGNATURE
;
1220 pShadowFile
->cbHeader
= sizeof(SHD_HEADER
);
1223 pShadowFile
->dwJobID
= pJob
->dwJobID
;
1224 pShadowFile
->dwPriority
= pJob
->dwPriority
;
1225 pShadowFile
->dwStartTime
= pJob
->dwStartTime
;
1226 pShadowFile
->dwTotalPages
= pJob
->dwTotalPages
;
1227 pShadowFile
->dwUntilTime
= pJob
->dwUntilTime
;
1228 CopyMemory(&pShadowFile
->stSubmitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
1230 // Determine the file size of the .SPL file
1231 wcscpy(wcsrchr(pwszFilePath
, L
'.'), L
".SPL");
1232 hSPLFile
= CreateFileW(pwszFilePath
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
1233 if (hSPLFile
!= INVALID_HANDLE_VALUE
)
1234 pShadowFile
->dwSPLSize
= GetFileSize(hSPLFile
, NULL
);
1236 // Add the extra values that are stored as offsets in the shadow file.
1237 // The first value begins right after the shadow file header.
1238 dwCurrentOffset
= sizeof(SHD_HEADER
);
1240 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszDatatype
, cbDatatype
);
1241 pShadowFile
->offDatatype
= dwCurrentOffset
;
1242 dwCurrentOffset
+= cbDatatype
;
1244 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszDocumentName
, cbDocumentName
);
1245 pShadowFile
->offDocumentName
= dwCurrentOffset
;
1246 dwCurrentOffset
+= cbDocumentName
;
1248 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pDevMode
, cbDevMode
);
1249 pShadowFile
->offDevMode
= dwCurrentOffset
;
1250 dwCurrentOffset
+= cbDevMode
;
1252 // offDriverName is only written, but automatically determined through offPrinterName when reading.
1253 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrinter
->pwszPrinterDriver
, cbPrinterDriver
);
1254 pShadowFile
->offDriverName
= dwCurrentOffset
;
1255 dwCurrentOffset
+= cbPrinterDriver
;
1257 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszMachineName
, cbMachineName
);
1258 pShadowFile
->offMachineName
= dwCurrentOffset
;
1259 dwCurrentOffset
+= cbMachineName
;
1261 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszNotifyName
, cbNotifyName
);
1262 pShadowFile
->offNotifyName
= dwCurrentOffset
;
1263 dwCurrentOffset
+= cbNotifyName
;
1265 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrinter
->pwszPrinterName
, cbPrinterName
);
1266 pShadowFile
->offPrinterName
= dwCurrentOffset
;
1267 dwCurrentOffset
+= cbPrinterName
;
1269 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pPrintProcessor
->pwszName
, cbPrintProcessor
);
1270 pShadowFile
->offPrintProcessor
= dwCurrentOffset
;
1271 dwCurrentOffset
+= cbPrintProcessor
;
1273 if (cbPrintProcessorParameters
)
1275 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszPrintProcessorParameters
, cbPrintProcessorParameters
);
1276 pShadowFile
->offPrintProcessorParameters
= dwCurrentOffset
;
1277 dwCurrentOffset
+= cbPrintProcessorParameters
;
1280 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszUserName
, cbUserName
);
1281 pShadowFile
->offUserName
= dwCurrentOffset
;
1282 dwCurrentOffset
+= cbUserName
;
1285 if (!WriteFile(hSHDFile
, pShadowFile
, cbFileSize
, &cbWritten
, NULL
))
1287 ERR("WriteFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
1291 bReturnValue
= TRUE
;
1295 DllFreeSplMem(pShadowFile
);
1297 if (hSHDFile
!= INVALID_HANDLE_VALUE
)
1298 CloseHandle(hSHDFile
);
1300 if (hSPLFile
!= INVALID_HANDLE_VALUE
)
1301 CloseHandle(hSPLFile
);
1303 return bReturnValue
;
1307 FreeJob(PLOCAL_JOB pJob
)
1309 ////////// TODO /////////
1311 DllFreeSplStr(pJob
->pwszDatatype
);
1312 DllFreeSplStr(pJob
->pwszDocumentName
);
1313 DllFreeSplStr(pJob
->pwszOutputFile
);
1314 DllFreeSplMem(pJob
);