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
->Printer
->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 BOOL bReturnValue
= FALSE
;
200 DWORD cchMachineName
;
203 PLOCAL_HANDLE pHandle
;
205 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
206 RPC_BINDING_HANDLE hServerBinding
= NULL
;
208 RPC_WSTR pwszBinding
= NULL
;
209 RPC_WSTR pwszMachineName
= NULL
;
211 // Check if this is a printer handle.
212 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
213 if (pHandle
->HandleType
!= Printer
)
216 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->SpecificHandle
;
218 // Check if this is the right structure level.
222 // FIXME: This needs to fail if the printer is set to do direct printing.
224 // Check if the supplied buffer is large enough.
225 *pcbNeeded
= sizeof(ADDJOB_INFO_1W
) + (cchSpoolDirectory
+ cchPrintersPath
+ cchSpl
+ 1) * sizeof(WCHAR
);
226 if (cbBuf
< *pcbNeeded
)
228 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
233 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
236 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
240 // Reserve an ID for this job.
241 if (!GetNextJobID(&pJob
->dwJobID
))
244 // Copy over defaults to the LOCAL_JOB structure.
245 pJob
->Printer
= pPrinterHandle
->Printer
;
246 pJob
->dwPriority
= DEF_PRIORITY
;
247 pJob
->pwszDatatype
= AllocSplStr(pPrinterHandle
->pwszDatatype
);
248 pJob
->pwszDocumentName
= AllocSplStr(wszDefaultDocumentName
);
249 CopyMemory(&pJob
->DevMode
, &pPrinterHandle
->DevMode
, sizeof(DEVMODEW
));
250 GetSystemTime(&pJob
->stSubmitted
);
252 // Get the user name for the Job.
253 cchUserName
= UNLEN
+ 1;
254 pJob
->pwszUserName
= DllAllocSplMem(cchUserName
* sizeof(WCHAR
));
255 if (!GetUserNameW(pJob
->pwszUserName
, &cchUserName
))
257 ERR("GetUserNameW failed with error %lu!\n", GetLastError());
261 // FIXME: For now, pwszNotifyName equals pwszUserName.
262 pJob
->pwszNotifyName
= AllocSplStr(pJob
->pwszUserName
);
264 // Get the name of the machine that submitted the Job over RPC.
265 Status
= RpcBindingServerFromClient(NULL
, &hServerBinding
);
266 if (Status
!= RPC_S_OK
)
268 ERR("RpcBindingServerFromClient failed with status %lu!\n", Status
);
272 Status
= RpcBindingToStringBindingW(hServerBinding
, &pwszBinding
);
273 if (Status
!= RPC_S_OK
)
275 ERR("RpcBindingToStringBindingW failed with status %lu!\n", Status
);
279 Status
= RpcStringBindingParseW(pwszBinding
, NULL
, NULL
, &pwszMachineName
, NULL
, NULL
);
280 if (Status
!= RPC_S_OK
)
282 ERR("RpcStringBindingParseW failed with status %lu!\n", Status
);
286 cchMachineName
= wcslen(pwszMachineName
);
287 pJob
->pwszMachineName
= DllAllocSplMem((cchMachineName
+ cchDoubleBackslash
+ 1) * sizeof(WCHAR
));
288 CopyMemory(pJob
->pwszMachineName
, wszDoubleBackslash
, cchDoubleBackslash
* sizeof(WCHAR
));
289 CopyMemory(pJob
->pwszMachineName
+ cchDoubleBackslash
, pwszMachineName
, (cchMachineName
+ 1) * sizeof(WCHAR
));
291 // Add the job to the Global Job List.
292 if (!InsertElementSkiplist(&GlobalJobList
, pJob
))
294 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob
->dwJobID
);
298 // Add the job at the end of the Printer's Job List.
299 // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
300 if (!InsertTailElementSkiplist(&pJob
->Printer
->JobList
, pJob
))
302 ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob
->dwJobID
);
306 // Return a proper ADDJOB_INFO_1W structure.
307 AddJobInfo1
.JobId
= pJob
->dwJobID
;
308 AddJobInfo1
.Path
= (PWSTR
)(pData
+ sizeof(ADDJOB_INFO_1W
));
310 CopyMemory(p
, &AddJobInfo1
, sizeof(ADDJOB_INFO_1W
));
311 p
+= sizeof(ADDJOB_INFO_1W
);
312 CopyMemory(p
, wszSpoolDirectory
, cchSpoolDirectory
);
313 p
+= cchSpoolDirectory
;
314 CopyMemory(p
, wszPrintersPath
, cchPrintersPath
);
315 p
+= cchPrintersPath
;
316 swprintf((PWSTR
)p
, L
"%05lu.SPL", pJob
->dwJobID
);
322 RpcStringFreeW(&pwszMachineName
);
325 RpcStringFreeW(&pwszBinding
);
328 RpcBindingFree(&hServerBinding
);
335 _LocalGetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PBYTE pOutput
, DWORD cbBuf
, PDWORD pcbNeeded
)
337 DWORD cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
338 DWORD cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
339 DWORD cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
340 DWORD cbPrinterName
= (wcslen(pJob
->Printer
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
341 DWORD cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
342 JOB_INFO_1W JobInfo1
= { 0 };
345 // Check if the supplied buffer is large enough.
346 *pcbNeeded
= sizeof(JOB_INFO_1W
) + cbDatatype
+ cbDocumentName
+ cbMachineName
+ cbPrinterName
+ cbUserName
;
347 if (cbBuf
< *pcbNeeded
)
349 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
353 // Put the strings right after the JOB_INFO_1W structure.
354 pString
= pOutput
+ sizeof(JOB_INFO_1W
);
356 JobInfo1
.pDatatype
= (PWSTR
)pString
;
357 CopyMemory(pString
, pJob
->pwszDatatype
, cbDatatype
);
358 pString
+= cbDatatype
;
360 JobInfo1
.pDocument
= (PWSTR
)pString
;
361 CopyMemory(pString
, pJob
->pwszDocumentName
, cbDocumentName
);
362 pString
+= cbDocumentName
;
364 JobInfo1
.pMachineName
= (PWSTR
)pString
;
365 CopyMemory(pString
, pJob
->pwszMachineName
, cbMachineName
);
366 pString
+= cbMachineName
;
368 JobInfo1
.pPrinterName
= (PWSTR
)pString
;
369 CopyMemory(pString
, pJob
->Printer
->pwszPrinterName
, cbPrinterName
);
370 pString
+= cbPrinterName
;
372 JobInfo1
.pUserName
= (PWSTR
)pString
;
373 CopyMemory(pString
, pJob
->pwszUserName
, cbUserName
);
374 pString
+= cbUserName
;
376 // Fill the structure and copy it as well.
377 JobInfo1
.JobId
= pJob
->dwJobID
;
378 JobInfo1
.Priority
= pJob
->dwPriority
;
379 JobInfo1
.Status
= pJob
->dwStatus
;
380 JobInfo1
.TotalPages
= pJob
->dwTotalPages
;
381 CopyMemory(&JobInfo1
.Submitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
382 CopyMemory(pOutput
, &JobInfo1
, sizeof(JOB_INFO_1W
));
388 _LocalGetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PLOCAL_JOB pJob
, PBYTE pOutput
, DWORD cbBuf
, PDWORD pcbNeeded
)
390 DWORD cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
391 DWORD cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
392 DWORD cbDriverName
= (wcslen(pJob
->Printer
->pwszPrinterDriver
) + 1) * sizeof(WCHAR
);
393 DWORD cbMachineName
= (wcslen(pJob
->pwszMachineName
) + 1) * sizeof(WCHAR
);
394 DWORD cbNotifyName
= (wcslen(pJob
->pwszNotifyName
) + 1) * sizeof(WCHAR
);
395 DWORD cbPrinterName
= (wcslen(pJob
->Printer
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
396 DWORD cbPrintProcessor
= (wcslen(pJob
->Printer
->pPrintProcessor
->pwszName
) + 1) * sizeof(WCHAR
);
397 DWORD cbUserName
= (wcslen(pJob
->pwszUserName
) + 1) * sizeof(WCHAR
);
399 FILETIME ftSubmitted
;
400 JOB_INFO_2W JobInfo2
= { 0 };
402 ULARGE_INTEGER uliNow
;
403 ULARGE_INTEGER uliSubmitted
;
405 // Check if the supplied buffer is large enough.
406 *pcbNeeded
= sizeof(JOB_INFO_2W
) + cbDatatype
+ sizeof(DEVMODEW
) + cbDocumentName
+ cbDriverName
+ cbMachineName
+ cbNotifyName
+ cbPrinterName
+ cbPrintProcessor
+ cbUserName
;
407 if (cbBuf
< *pcbNeeded
)
409 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
413 // Put the strings right after the JOB_INFO_2W structure.
414 pString
= pOutput
+ sizeof(JOB_INFO_2W
);
416 JobInfo2
.pDatatype
= (PWSTR
)pString
;
417 CopyMemory(pString
, pJob
->pwszDatatype
, cbDatatype
);
418 pString
+= cbDatatype
;
420 JobInfo2
.pDevMode
= (PDEVMODEW
)pString
;
421 CopyMemory(pString
, &pJob
->DevMode
, sizeof(DEVMODEW
));
422 pString
+= sizeof(DEVMODEW
);
424 JobInfo2
.pDocument
= (PWSTR
)pString
;
425 CopyMemory(pString
, pJob
->pwszDocumentName
, cbDocumentName
);
426 pString
+= cbDocumentName
;
428 JobInfo2
.pDriverName
= (PWSTR
)pString
;
429 CopyMemory(pString
, pJob
->Printer
->pwszPrinterDriver
, cbDriverName
);
430 pString
+= cbDriverName
;
432 JobInfo2
.pMachineName
= (PWSTR
)pString
;
433 CopyMemory(pString
, pJob
->pwszMachineName
, cbMachineName
);
434 pString
+= cbMachineName
;
436 JobInfo2
.pNotifyName
= (PWSTR
)pString
;
437 CopyMemory(pString
, pJob
->pwszNotifyName
, cbNotifyName
);
438 pString
+= cbNotifyName
;
440 JobInfo2
.pPrinterName
= (PWSTR
)pString
;
441 CopyMemory(pString
, pJob
->Printer
->pwszPrinterName
, cbPrinterName
);
442 pString
+= cbPrinterName
;
444 JobInfo2
.pPrintProcessor
= (PWSTR
)pString
;
445 CopyMemory(pString
, pJob
->Printer
->pPrintProcessor
->pwszName
, cbPrintProcessor
);
446 pString
+= cbPrintProcessor
;
448 JobInfo2
.pUserName
= (PWSTR
)pString
;
449 CopyMemory(pString
, pJob
->pwszUserName
, cbUserName
);
450 pString
+= cbUserName
;
452 // Time in JOB_INFO_2W is the number of milliseconds elapsed since the job was submitted. Calculate this time.
453 if (!SystemTimeToFileTime(&pJob
->stSubmitted
, &ftSubmitted
))
455 ERR("SystemTimeToFileTime failed with error %lu!\n", GetLastError());
459 GetSystemTimeAsFileTime(&ftNow
);
460 uliSubmitted
.LowPart
= ftSubmitted
.dwLowDateTime
;
461 uliSubmitted
.HighPart
= ftSubmitted
.dwHighDateTime
;
462 uliNow
.LowPart
= ftNow
.dwLowDateTime
;
463 uliNow
.HighPart
= ftNow
.dwHighDateTime
;
464 JobInfo2
.Time
= (DWORD
)((uliNow
.QuadPart
- uliSubmitted
.QuadPart
) / 10000);
466 // Position in JOB_INFO_2W is the 1-based index of the job in the processing queue.
467 // Retrieve this through the element index of the job in the Printer's Job List.
468 if (!LookupElementSkiplist(&pJob
->Printer
->JobList
, pJob
, &JobInfo2
.Position
))
470 ERR("pJob could not be located in the Printer's Job List!\n");
474 // Make the index 1-based.
477 // Fill the rest of the structure.
478 JobInfo2
.JobId
= pJob
->dwJobID
;
479 JobInfo2
.PagesPrinted
= pJob
->dwPagesPrinted
;
480 JobInfo2
.Priority
= pJob
->dwPriority
;
481 JobInfo2
.StartTime
= pJob
->dwStartTime
;
482 JobInfo2
.Status
= pJob
->dwStatus
;
483 JobInfo2
.TotalPages
= pJob
->dwTotalPages
;
484 JobInfo2
.UntilTime
= pJob
->dwUntilTime
;
485 CopyMemory(&JobInfo2
.Submitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
487 // Finally copy the structure to the output pointer.
488 CopyMemory(pOutput
, &JobInfo2
, sizeof(JOB_INFO_2W
));
493 LocalGetJob(HANDLE hPrinter
, DWORD JobId
, DWORD Level
, LPBYTE pOutput
, DWORD cbBuf
, LPDWORD pcbNeeded
)
495 PLOCAL_HANDLE pHandle
;
497 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
499 // Check if this is a printer handle.
500 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
501 if (pHandle
->HandleType
!= Printer
)
504 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->SpecificHandle
;
506 // Get the desired job.
507 pJob
= LookupElementSkiplist(&GlobalJobList
, &JobId
, NULL
);
508 if (!pJob
|| pJob
->Printer
!= pPrinterHandle
->Printer
)
510 SetLastError(ERROR_INVALID_PARAMETER
);
514 // The function behaves differently for each level.
516 return _LocalGetJobLevel1(pPrinterHandle
, pJob
, pOutput
, cbBuf
, pcbNeeded
);
518 return _LocalGetJobLevel2(pPrinterHandle
, pJob
, pOutput
, cbBuf
, pcbNeeded
);
524 ReadJobShadowFile(PCWSTR pwszFilePath
)
528 HANDLE hFile
= INVALID_HANDLE_VALUE
;
530 PLOCAL_JOB pReturnValue
= NULL
;
531 PLOCAL_PRINTER pPrinter
;
532 PSHD_HEADER pShadowFile
= NULL
;
533 PWSTR pwszPrinterName
;
535 // Try to open the file.
536 hFile
= CreateFileW(pwszFilePath
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
537 if (hFile
== INVALID_HANDLE_VALUE
)
539 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
543 // Get its file size (small enough for a single DWORD) and allocate memory for all of it.
544 cbFileSize
= GetFileSize(hFile
, NULL
);
545 pShadowFile
= DllAllocSplMem(cbFileSize
);
548 ERR("DllAllocSplMem failed with error %lufor file \"%S\"!\n", GetLastError(), pwszFilePath
);
552 // Read the entire file.
553 if (!ReadFile(hFile
, pShadowFile
, cbFileSize
, &cbRead
, NULL
))
555 ERR("ReadFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
559 // Check signature and header size.
560 if (pShadowFile
->dwSignature
!= SHD_WIN2003_SIGNATURE
|| pShadowFile
->cbHeader
!= sizeof(SHD_HEADER
))
562 ERR("Signature or Header Size mismatch for file \"%S\"!\n", pwszFilePath
);
566 // Retrieve the associated printer from the list.
567 pwszPrinterName
= (PWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offPrinterName
);
568 pPrinter
= LookupElementSkiplist(&PrinterList
, &pwszPrinterName
, NULL
);
571 ERR("Shadow file \"%S\" references a non-existing printer!\n", pwszFilePath
);
575 // Create a new job structure and copy over the relevant fields.
576 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
579 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
583 pJob
->dwJobID
= pShadowFile
->dwJobID
;
584 pJob
->dwTotalPages
= pShadowFile
->dwTotalPages
;
585 pJob
->dwPriority
= pShadowFile
->dwPriority
;
586 pJob
->Printer
= pPrinter
;
587 pJob
->pwszDatatype
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDatatype
));
588 pJob
->pwszDocumentName
= AllocSplStr((PCWSTR
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDocumentName
));
589 pJob
->pwszOutputFile
= NULL
;
590 CopyMemory(&pJob
->stSubmitted
, &pShadowFile
->stSubmitted
, sizeof(SYSTEMTIME
));
591 CopyMemory(&pJob
->DevMode
, (PDEVMODEW
)((ULONG_PTR
)pShadowFile
+ pShadowFile
->offDevMode
), sizeof(DEVMODEW
));
597 DllFreeSplMem(pShadowFile
);
599 if (hFile
!= INVALID_HANDLE_VALUE
)
606 WriteJobShadowFile(PCWSTR pwszFilePath
, const PLOCAL_JOB pJob
)
608 BOOL bReturnValue
= FALSE
;
610 DWORD cbDocumentName
;
614 DWORD dwCurrentOffset
;
615 HANDLE hFile
= INVALID_HANDLE_VALUE
;
616 PSHD_HEADER pShadowFile
= NULL
;
618 // Try to open the file.
619 hFile
= CreateFileW(pwszFilePath
, GENERIC_WRITE
, 0, NULL
, OPEN_ALWAYS
, 0, NULL
);
620 if (hFile
== INVALID_HANDLE_VALUE
)
622 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
626 // Compute the total size of the shadow file.
627 cbPrinterName
= (wcslen(pJob
->Printer
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
628 cbDatatype
= (wcslen(pJob
->pwszDatatype
) + 1) * sizeof(WCHAR
);
629 cbDocumentName
= (wcslen(pJob
->pwszDocumentName
) + 1) * sizeof(WCHAR
);
630 cbFileSize
= sizeof(SHD_HEADER
) + cbPrinterName
+ cbDatatype
+ cbDocumentName
+ sizeof(DEVMODEW
);
632 // Allocate memory for it.
633 pShadowFile
= DllAllocSplMem(cbFileSize
);
636 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
640 // Fill out the shadow file header information.
641 pShadowFile
->dwSignature
= SHD_WIN2003_SIGNATURE
;
642 pShadowFile
->cbHeader
= sizeof(SHD_HEADER
);
645 pShadowFile
->dwJobID
= pJob
->dwJobID
;
646 pShadowFile
->dwTotalPages
= pJob
->dwTotalPages
;
647 pShadowFile
->dwPriority
= pJob
->dwPriority
;
648 CopyMemory(&pShadowFile
->stSubmitted
, &pJob
->stSubmitted
, sizeof(SYSTEMTIME
));
650 // Add the extra values that are stored as offsets in the shadow file.
651 // The first value begins right after the shadow file header.
652 dwCurrentOffset
= sizeof(SHD_HEADER
);
654 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->Printer
->pwszPrinterName
, cbPrinterName
);
655 pShadowFile
->offPrinterName
= dwCurrentOffset
;
656 dwCurrentOffset
+= cbPrinterName
;
658 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszDatatype
, cbDatatype
);
659 pShadowFile
->offDatatype
= dwCurrentOffset
;
660 dwCurrentOffset
+= cbDatatype
;
662 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, pJob
->pwszDocumentName
, cbDocumentName
);
663 pShadowFile
->offDocumentName
= dwCurrentOffset
;
664 dwCurrentOffset
+= cbDocumentName
;
666 CopyMemory((PBYTE
)pShadowFile
+ dwCurrentOffset
, &pJob
->DevMode
, sizeof(DEVMODEW
));
667 pShadowFile
->offDevMode
= dwCurrentOffset
;
668 dwCurrentOffset
+= sizeof(DEVMODEW
);
671 if (!WriteFile(hFile
, pShadowFile
, cbFileSize
, &cbWritten
, NULL
))
673 ERR("WriteFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath
);
681 DllFreeSplMem(pShadowFile
);
683 if (hFile
!= INVALID_HANDLE_VALUE
)
690 FreeJob(PLOCAL_JOB pJob
)
692 ////////// TODO /////////
694 DllFreeSplStr(pJob
->pwszDatatype
);
695 DllFreeSplStr(pJob
->pwszDocumentName
);
696 DllFreeSplStr(pJob
->pwszOutputFile
);