ReactOS  0.4.11-dev-791-gf6f1255
jobs.c
Go to the documentation of this file.
1 /*
2  * PROJECT: ReactOS Local Spooler
3  * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE: Functions for managing print jobs
5  * COPYRIGHT: Copyright 2015-2017 Colin Finck (colin@reactos.org)
6  */
7 
8 #include "precomp.h"
9 
10 // Global Variables
12 
13 // Local Variables
15 
16 // Local Constants
18  FIELD_OFFSET(JOB_INFO_1W, pPrinterName),
19  FIELD_OFFSET(JOB_INFO_1W, pMachineName),
20  FIELD_OFFSET(JOB_INFO_1W, pUserName),
21  FIELD_OFFSET(JOB_INFO_1W, pDocument),
22  FIELD_OFFSET(JOB_INFO_1W, pDatatype),
23  FIELD_OFFSET(JOB_INFO_1W, pStatus),
24  MAXDWORD
25 };
26 
28  FIELD_OFFSET(JOB_INFO_2W, pPrinterName),
29  FIELD_OFFSET(JOB_INFO_2W, pMachineName),
30  FIELD_OFFSET(JOB_INFO_2W, pUserName),
31  FIELD_OFFSET(JOB_INFO_2W, pDocument),
32  FIELD_OFFSET(JOB_INFO_2W, pNotifyName),
33  FIELD_OFFSET(JOB_INFO_2W, pDatatype),
34  FIELD_OFFSET(JOB_INFO_2W, pPrintProcessor),
35  FIELD_OFFSET(JOB_INFO_2W, pParameters),
36  FIELD_OFFSET(JOB_INFO_2W, pDriverName),
37  FIELD_OFFSET(JOB_INFO_2W, pStatus),
38  MAXDWORD
39 };
40 
41 
57 static __inline BOOL
59 {
60  if (!pwszA && !pwszB)
61  return TRUE;
62 
63  if (pwszA && !pwszB)
64  return FALSE;
65 
66  if (!pwszA && pwszB)
67  return FALSE;
68 
69  return (wcscmp(pwszA, pwszB) == 0);
70 }
71 
72 static BOOL
74 {
75  ++_dwLastJobID;
76 
77  while (LookupElementSkiplist(&GlobalJobList, &_dwLastJobID, NULL))
78  {
79  // This ID is already taken. Try the next one.
80  ++_dwLastJobID;
81  }
82 
84  {
85  ERR("Job ID %lu isn't valid!\n", _dwLastJobID);
86  return FALSE;
87  }
88 
89  *dwJobID = _dwLastJobID;
90  return TRUE;
91 }
92 
99 static int WINAPI
101 {
102  PLOCAL_JOB A = (PLOCAL_JOB)FirstStruct;
103  PLOCAL_JOB B = (PLOCAL_JOB)SecondStruct;
104 
105  return A->dwJobID - B->dwJobID;
106 }
107 
114 static int WINAPI
116 {
117  PLOCAL_JOB A = (PLOCAL_JOB)FirstStruct;
118  PLOCAL_JOB B = (PLOCAL_JOB)SecondStruct;
119  int iComparison;
120  FILETIME ftSubmittedA;
121  FILETIME ftSubmittedB;
122 
123  // First compare the priorities to determine the order.
124  // The job with a higher priority shall come first.
125  iComparison = A->dwPriority - B->dwPriority;
126  if (iComparison != 0)
127  return iComparison;
128 
129  // Both have the same priority, so go by creation time.
130  if (!SystemTimeToFileTime(&A->stSubmitted, &ftSubmittedA))
131  {
132  ERR("SystemTimeToFileTime failed for A with error %lu!\n", GetLastError());
133  return 0;
134  }
135 
136  if (!SystemTimeToFileTime(&B->stSubmitted, &ftSubmittedB))
137  {
138  ERR("SystemTimeToFileTime failed for B with error %lu!\n", GetLastError());
139  return 0;
140  }
141 
142  return CompareFileTime(&ftSubmittedA, &ftSubmittedB);
143 }
144 
145 DWORD
146 GetJobFilePath(PCWSTR pwszExtension, DWORD dwJobID, PWSTR pwszOutput)
147 {
148  TRACE("GetJobFilePath(%S, %lu, %p)\n", pwszExtension, dwJobID, pwszOutput);
149 
150  if (pwszOutput)
151  {
152  CopyMemory(pwszOutput, wszJobDirectory, cchJobDirectory * sizeof(WCHAR));
153  swprintf(&pwszOutput[cchJobDirectory], L"\\%05lu.%s", dwJobID, pwszExtension);
154  }
155 
156  // pwszExtension may be L"SPL" or L"SHD", same length for both!
157  return (cchJobDirectory + sizeof("\\?????.SPL")) * sizeof(WCHAR);
158 }
159 
160 BOOL
162 {
163  const WCHAR wszPath[] = L"\\?????.SHD";
164  const DWORD cchPath = _countof(wszPath) - 1;
165 
166  DWORD dwErrorCode;
167  DWORD dwJobID;
168  HANDLE hFind;
169  PLOCAL_JOB pJob = NULL;
170  PWSTR p;
171  WCHAR wszFullPath[MAX_PATH];
172  WIN32_FIND_DATAW FindData;
173 
174  TRACE("InitializeGlobalJobList()\n");
175 
176  // This one is incremented in _GetNextJobID.
177  _dwLastJobID = 0;
178 
179  // Initialize an empty list for all jobs of all local printers.
180  // We will search it by Job ID (supply a pointer to a DWORD in LookupElementSkiplist).
182 
183  // Construct the full path search pattern.
184  CopyMemory(wszFullPath, wszJobDirectory, cchJobDirectory * sizeof(WCHAR));
185  CopyMemory(&wszFullPath[cchJobDirectory], wszPath, (cchPath + 1) * sizeof(WCHAR));
186 
187  // Use the search pattern to look for unfinished jobs serialized in shadow files (.SHD)
188  hFind = FindFirstFileW(wszFullPath, &FindData);
189  if (hFind == INVALID_HANDLE_VALUE)
190  {
191  // No unfinished jobs found.
192  dwErrorCode = ERROR_SUCCESS;
193  goto Cleanup;
194  }
195 
196  do
197  {
198  // Skip possible subdirectories.
199  if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
200  continue;
201 
202  // Extract the Job ID and verify the file name format at the same time.
203  // This includes all valid names (like "00005.SHD") and excludes invalid ones (like "10ABC.SHD").
204  dwJobID = wcstoul(FindData.cFileName, &p, 10);
205  if (!IS_VALID_JOB_ID(dwJobID))
206  continue;
207 
208  if (wcsicmp(p, L".SHD") != 0)
209  continue;
210 
211  // This shadow file has a valid name. Construct the full path and try to load it.
212  GetJobFilePath(L"SHD", dwJobID, wszFullPath);
213  pJob = ReadJobShadowFile(wszFullPath);
214  if (!pJob)
215  continue;
216 
217  // Add it to the Global Job List.
218  if (!InsertElementSkiplist(&GlobalJobList, pJob))
219  {
220  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
221  ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
222  goto Cleanup;
223  }
224 
225  // Add it to the Printer's Job List.
226  if (!InsertElementSkiplist(&pJob->pPrinter->JobList, pJob))
227  {
228  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
229  ERR("InsertElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
230  goto Cleanup;
231  }
232  }
233  while (FindNextFileW(hFind, &FindData));
234 
235  dwErrorCode = ERROR_SUCCESS;
236 
237 Cleanup:
238  // Outside the loop
239  if (hFind)
240  FindClose(hFind);
241 
242  SetLastError(dwErrorCode);
243  return (dwErrorCode == ERROR_SUCCESS);
244 }
245 
246 void
248 {
249  TRACE("InitializePrinterJobList(%p)\n", pPrinter);
250 
251  // Initialize an empty list for this printer's jobs.
252  // This one is only for sorting the jobs. If you need to lookup a job, search the GlobalJobList by Job ID.
254 }
255 
258 {
259  const WCHAR wszDoubleBackslash[] = L"\\";
260  const DWORD cchDoubleBackslash = _countof(wszDoubleBackslash) - 1;
261 
262  DWORD cchMachineName;
263  DWORD cchUserName;
264  DWORD dwErrorCode;
265  PLOCAL_JOB pJob;
266  RPC_BINDING_HANDLE hServerBinding = NULL;
267  RPC_WSTR pwszBinding = NULL;
268  RPC_WSTR pwszMachineName = NULL;
269 
270  TRACE("CreateJob(%p)\n", pPrinterHandle);
271 
272  // Create a new job.
273  pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
274  if (!pJob)
275  {
276  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
277  ERR("DllAllocSplMem failed!\n");
278  goto Cleanup;
279  }
280 
281  // Reserve an ID for this job.
282  if (!_GetNextJobID(&pJob->dwJobID))
283  {
284  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
285  goto Cleanup;
286  }
287 
288  // Copy over defaults to the LOCAL_JOB structure.
289  pJob->pPrinter = pPrinterHandle->pPrinter;
290  pJob->pPrintProcessor = pPrinterHandle->pPrinter->pPrintProcessor;
291  pJob->dwPriority = DEF_PRIORITY;
293  pJob->pwszDatatype = AllocSplStr(pPrinterHandle->pwszDatatype);
295  pJob->pDevMode = DuplicateDevMode(pPrinterHandle->pDevMode);
296  GetSystemTime(&pJob->stSubmitted);
297 
298  // Get the user name for the Job.
299  cchUserName = UNLEN + 1;
300  pJob->pwszUserName = DllAllocSplMem(cchUserName * sizeof(WCHAR));
301  if (!GetUserNameW(pJob->pwszUserName, &cchUserName))
302  {
303  dwErrorCode = GetLastError();
304  ERR("GetUserNameW failed with error %lu!\n", dwErrorCode);
305  goto Cleanup;
306  }
307 
308  // FIXME: For now, pwszNotifyName equals pwszUserName.
309  pJob->pwszNotifyName = AllocSplStr(pJob->pwszUserName);
310 
311  // Get the name of the machine that submitted the Job over RPC.
312  dwErrorCode = RpcBindingServerFromClient(NULL, &hServerBinding);
313  if (dwErrorCode != RPC_S_OK)
314  {
315  ERR("RpcBindingServerFromClient failed with status %lu!\n", dwErrorCode);
316  goto Cleanup;
317  }
318 
319  dwErrorCode = RpcBindingToStringBindingW(hServerBinding, &pwszBinding);
320  if (dwErrorCode != RPC_S_OK)
321  {
322  ERR("RpcBindingToStringBindingW failed with status %lu!\n", dwErrorCode);
323  goto Cleanup;
324  }
325 
326  dwErrorCode = RpcStringBindingParseW(pwszBinding, NULL, NULL, &pwszMachineName, NULL, NULL);
327  if (dwErrorCode != RPC_S_OK)
328  {
329  ERR("RpcStringBindingParseW failed with status %lu!\n", dwErrorCode);
330  goto Cleanup;
331  }
332 
333  cchMachineName = wcslen(pwszMachineName);
334  pJob->pwszMachineName = DllAllocSplMem((cchMachineName + cchDoubleBackslash + 1) * sizeof(WCHAR));
335  CopyMemory(pJob->pwszMachineName, wszDoubleBackslash, cchDoubleBackslash * sizeof(WCHAR));
336  CopyMemory(&pJob->pwszMachineName[cchDoubleBackslash], pwszMachineName, (cchMachineName + 1) * sizeof(WCHAR));
337 
338  // Add the job to the Global Job List.
339  if (!InsertElementSkiplist(&GlobalJobList, pJob))
340  {
341  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
342  ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
343  goto Cleanup;
344  }
345 
346  // Add the job at the end of the Printer's Job List.
347  // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
348  if (!InsertTailElementSkiplist(&pJob->pPrinter->JobList, pJob))
349  {
350  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
351  ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
352  goto Cleanup;
353  }
354 
355  // We were successful!
356  pPrinterHandle->bStartedDoc = TRUE;
357  pPrinterHandle->pJob = pJob;
358  dwErrorCode = ERROR_SUCCESS;
359 
360  // Don't let the cleanup routine free this.
361  pJob = NULL;
362 
363 Cleanup:
364  if (pJob)
365  DllFreeSplMem(pJob);
366 
367  if (pwszMachineName)
368  RpcStringFreeW(&pwszMachineName);
369 
370  if (pwszBinding)
371  RpcStringFreeW(&pwszBinding);
372 
373  if (hServerBinding)
374  RpcBindingFree(&hServerBinding);
375 
376  return dwErrorCode;
377 }
378 
379 BOOL WINAPI
381 {
382  ADDJOB_INFO_1W AddJobInfo1;
383  DWORD dwErrorCode;
384  PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
385  PLOCAL_PRINTER_HANDLE pPrinterHandle;
386 
387  TRACE("LocalAddJob(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pData, cbBuf, pcbNeeded);
388 
389  // Check if this is a printer handle.
390  if (pHandle->HandleType != HandleType_Printer)
391  {
392  dwErrorCode = ERROR_INVALID_HANDLE;
393  goto Cleanup;
394  }
395 
396  pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
397 
398  // This handle must not have started a job yet!
399  if (pPrinterHandle->pJob)
400  {
401  dwErrorCode = ERROR_INVALID_HANDLE;
402  goto Cleanup;
403  }
404 
405  // Check if this is the right structure level.
406  if (Level != 1)
407  {
408  dwErrorCode = ERROR_INVALID_LEVEL;
409  goto Cleanup;
410  }
411 
412  // Check if the printer is set to do direct printing.
413  // The Job List isn't used in this case.
414  if (pPrinterHandle->pPrinter->dwAttributes & PRINTER_ATTRIBUTE_DIRECT)
415  {
416  dwErrorCode = ERROR_INVALID_ACCESS;
417  goto Cleanup;
418  }
419 
420  // Check if the supplied buffer is large enough.
421  *pcbNeeded = sizeof(ADDJOB_INFO_1W) + GetJobFilePath(L"SPL", 0, NULL);
422  if (cbBuf < *pcbNeeded)
423  {
424  dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
425  goto Cleanup;
426  }
427 
428  // All requirements are met - create a new job.
429  dwErrorCode = CreateJob(pPrinterHandle);
430  if (dwErrorCode != ERROR_SUCCESS)
431  goto Cleanup;
432 
433  // Mark that this job was started with AddJob (so that it can be scheduled for printing with ScheduleJob).
434  pPrinterHandle->pJob->bAddedJob = TRUE;
435 
436  // Return a proper ADDJOB_INFO_1W structure.
437  AddJobInfo1.JobId = pPrinterHandle->pJob->dwJobID;
438  AddJobInfo1.Path = (PWSTR)(pData + sizeof(ADDJOB_INFO_1W));
439 
440  CopyMemory(pData, &AddJobInfo1, sizeof(ADDJOB_INFO_1W));
441  GetJobFilePath(L"SPL", AddJobInfo1.JobId, AddJobInfo1.Path);
442 
443 Cleanup:
444  SetLastError(dwErrorCode);
445  return (dwErrorCode == ERROR_SUCCESS);
446 }
447 
448 
449 static void
451 {
452  DWORD cbDatatype;
453  DWORD cbDocumentName = 0;
454  DWORD cbMachineName;
455  DWORD cbPrinterName;
456  DWORD cbStatus = 0;
457  DWORD cbUserName = 0;
458  PWSTR pwszStrings[6];
459 
460  // Calculate the string lengths.
461  if (!ppJobInfo)
462  {
463  cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
464  cbMachineName = (wcslen(pJob->pwszMachineName) + 1) * sizeof(WCHAR);
465  cbPrinterName = (wcslen(pJob->pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
466 
467  // These values are optional.
468  if (pJob->pwszDocumentName)
469  cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
470 
471  if (pJob->pwszStatus)
472  cbStatus = (wcslen(pJob->pwszStatus) + 1) * sizeof(WCHAR);
473 
474  if (pJob->pwszUserName)
475  cbUserName = (wcslen(pJob->pwszUserName) + 1) * sizeof(WCHAR);
476 
477  *pcbNeeded += sizeof(JOB_INFO_1W) + cbDatatype + cbDocumentName + cbMachineName + cbPrinterName + cbStatus + cbUserName;
478  return;
479  }
480 
481  // Set the general fields.
482  (*ppJobInfo)->JobId = pJob->dwJobID;
483  (*ppJobInfo)->Status = pJob->dwStatus;
484  (*ppJobInfo)->Priority = pJob->dwPriority;
485  (*ppJobInfo)->TotalPages = pJob->dwTotalPages;
486  (*ppJobInfo)->PagesPrinted = pJob->dwPagesPrinted;
487  CopyMemory(&(*ppJobInfo)->Submitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
488 
489  // Position in JOB_INFO_1W is the 1-based index of the job in the processing queue.
490  // Retrieve this through the element index of the job in the Printer's Job List.
491  if (!LookupElementSkiplist(&pJob->pPrinter->JobList, pJob, &(*ppJobInfo)->Position))
492  {
493  ERR("pJob could not be located in the Printer's Job List!\n");
494  return;
495  }
496 
497  // Make the index 1-based.
498  ++(*ppJobInfo)->Position;
499 
500  // Set the pPrinterName field.
501  pwszStrings[0] = pJob->pPrinter->pwszPrinterName;
502 
503  // Set the pMachineName field.
504  pwszStrings[1] = pJob->pwszMachineName;
505 
506  // Set the pUserName field.
507  pwszStrings[2] = pJob->pwszUserName;
508 
509  // Set the pDocument field.
510  pwszStrings[3] = pJob->pwszDocumentName;
511 
512  // Set the pDatatype field.
513  pwszStrings[4] = pJob->pwszDatatype;
514 
515  // Set the pStatus field.
516  pwszStrings[5] = pJob->pwszStatus;
517 
518  // Finally copy the structure and advance to the next one in the output buffer.
519  *ppJobInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppJobInfo), dwJobInfo1Offsets, *ppJobInfoEnd);
520  (*ppJobInfo)++;
521 }
522 
523 static void
525 {
526  DWORD cbDatatype;
527  DWORD cbDevMode;
528  DWORD cbDocumentName = 0;
529  DWORD cbDriverName;
530  DWORD cbMachineName;
531  DWORD cbNotifyName = 0;
532  DWORD cbPrinterName;
533  DWORD cbPrintProcessor;
534  DWORD cbPrintProcessorParameters = 0;
535  DWORD cbStatus = 0;
536  DWORD cbUserName = 0;
537  FILETIME ftNow;
538  FILETIME ftSubmitted;
539  PWSTR pwszStrings[10];
540  ULARGE_INTEGER uliNow;
541  ULARGE_INTEGER uliSubmitted;
542 
543  // Calculate the string lengths.
544  cbDevMode = pJob->pDevMode->dmSize + pJob->pDevMode->dmDriverExtra;
545 
546  if (!ppJobInfo)
547  {
548  cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
549  cbDriverName = (wcslen(pJob->pPrinter->pwszPrinterDriver) + 1) * sizeof(WCHAR);
550  cbMachineName = (wcslen(pJob->pwszMachineName) + 1) * sizeof(WCHAR);
551  cbPrinterName = (wcslen(pJob->pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
552  cbPrintProcessor = (wcslen(pJob->pPrintProcessor->pwszName) + 1) * sizeof(WCHAR);
553 
554  // These values are optional.
555  if (pJob->pwszDocumentName)
556  cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
557 
558  if (pJob->pwszNotifyName)
559  cbNotifyName = (wcslen(pJob->pwszNotifyName) + 1) * sizeof(WCHAR);
560 
562  cbPrintProcessorParameters = (wcslen(pJob->pwszPrintProcessorParameters) + 1) * sizeof(WCHAR);
563 
564  if (pJob->pwszStatus)
565  cbStatus = (wcslen(pJob->pwszStatus) + 1) * sizeof(WCHAR);
566 
567  if (pJob->pwszUserName)
568  cbUserName = (wcslen(pJob->pwszUserName) + 1) * sizeof(WCHAR);
569 
570  *pcbNeeded += sizeof(JOB_INFO_2W) + cbDatatype + cbDevMode + cbDocumentName + cbDriverName + cbMachineName + cbNotifyName + cbPrinterName + cbPrintProcessor + cbPrintProcessorParameters + cbStatus + cbUserName;
571  return;
572  }
573 
574  // Set the general fields.
575  (*ppJobInfo)->JobId = pJob->dwJobID;
576  (*ppJobInfo)->Status = pJob->dwStatus;
577  (*ppJobInfo)->Priority = pJob->dwPriority;
578  (*ppJobInfo)->StartTime = pJob->dwStartTime;
579  (*ppJobInfo)->UntilTime = pJob->dwUntilTime;
580  (*ppJobInfo)->TotalPages = pJob->dwTotalPages;
581  (*ppJobInfo)->PagesPrinted = pJob->dwPagesPrinted;
582  CopyMemory(&(*ppJobInfo)->Submitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
583 
584  // Time in JOB_INFO_2W is the number of milliseconds elapsed since the job was submitted. Calculate this time.
585  if (!SystemTimeToFileTime(&pJob->stSubmitted, &ftSubmitted))
586  {
587  ERR("SystemTimeToFileTime failed with error %lu!\n", GetLastError());
588  return;
589  }
590 
591  GetSystemTimeAsFileTime(&ftNow);
592  uliSubmitted.LowPart = ftSubmitted.dwLowDateTime;
593  uliSubmitted.HighPart = ftSubmitted.dwHighDateTime;
594  uliNow.LowPart = ftNow.dwLowDateTime;
595  uliNow.HighPart = ftNow.dwHighDateTime;
596  (*ppJobInfo)->Time = (DWORD)((uliNow.QuadPart - uliSubmitted.QuadPart) / 10000);
597 
598  // Position in JOB_INFO_2W is the 1-based index of the job in the processing queue.
599  // Retrieve this through the element index of the job in the Printer's Job List.
600  if (!LookupElementSkiplist(&pJob->pPrinter->JobList, pJob, &(*ppJobInfo)->Position))
601  {
602  ERR("pJob could not be located in the Printer's Job List!\n");
603  return;
604  }
605 
606  // Make the index 1-based.
607  ++(*ppJobInfo)->Position;
608 
609  // FIXME!
610  FIXME("Setting pSecurityDescriptor and Size to 0 for now!\n");
611  (*ppJobInfo)->pSecurityDescriptor = NULL;
612  (*ppJobInfo)->Size = 0;
613 
614  // Set the pDevMode field (and copy the DevMode).
615  *ppJobInfoEnd -= cbDevMode;
616  CopyMemory(*ppJobInfoEnd, pJob->pDevMode, cbDevMode);
617  (*ppJobInfo)->pDevMode = (PDEVMODEW)(*ppJobInfoEnd);
618 
619  // Set the pPrinterName field.
620  pwszStrings[0] = pJob->pPrinter->pwszPrinterName;
621 
622  // Set the pMachineName field.
623  pwszStrings[1] = pJob->pwszMachineName;
624 
625  // Set the pUserName field.
626  pwszStrings[2] = pJob->pwszUserName;
627 
628  // Set the pDocument field.
629  pwszStrings[3] = pJob->pwszDocumentName;
630 
631  // Set the pNotifyName field.
632  pwszStrings[4] = pJob->pwszNotifyName;
633 
634  // Set the pDatatype field.
635  pwszStrings[5] = pJob->pwszDatatype;
636 
637  // Set the pPrintProcessor field.
638  pwszStrings[6] = pJob->pPrintProcessor->pwszName;
639 
640  // Set the pParameters field.
641  pwszStrings[7] = pJob->pwszPrintProcessorParameters;
642 
643  // Set the pDriverName field.
644  pwszStrings[8] = pJob->pPrinter->pwszPrinterDriver;
645 
646  // Set the pStatus field.
647  pwszStrings[9] = pJob->pwszStatus;
648 
649  // Finally copy the structure and advance to the next one in the output buffer.
650  *ppJobInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppJobInfo), dwJobInfo2Offsets, *ppJobInfoEnd);
651  (*ppJobInfo)++;
652 }
653 
654 BOOL WINAPI
655 LocalGetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pStart, DWORD cbBuf, LPDWORD pcbNeeded)
656 {
657  DWORD dwErrorCode;
658  PBYTE pEnd = &pStart[cbBuf];
659  PLOCAL_HANDLE pHandle;
660  PLOCAL_JOB pJob;
661  PLOCAL_PRINTER_HANDLE pPrinterHandle;
662 
663  TRACE("LocalGetJob(%p, %lu, %lu, %p, %lu, %p)\n", hPrinter, JobId, Level, pStart, cbBuf, pcbNeeded);
664 
665  // Check if this is a printer handle.
666  pHandle = (PLOCAL_HANDLE)hPrinter;
667  if (pHandle->HandleType != HandleType_Printer)
668  {
669  dwErrorCode = ERROR_INVALID_HANDLE;
670  goto Cleanup;
671  }
672 
673  pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
674 
675  // Get the desired job.
676  pJob = LookupElementSkiplist(&GlobalJobList, &JobId, NULL);
677  if (!pJob || pJob->pPrinter != pPrinterHandle->pPrinter)
678  {
679  dwErrorCode = ERROR_INVALID_PARAMETER;
680  goto Cleanup;
681  }
682 
683  if (Level > 2)
684  {
685  // The caller supplied an invalid level for GetJob.
686  dwErrorCode = ERROR_INVALID_LEVEL;
687  goto Cleanup;
688  }
689 
690  // Count the required buffer size.
691  *pcbNeeded = 0;
692 
693  if (Level == 1)
694  _LocalGetJobLevel1(pJob, NULL, NULL, pcbNeeded);
695  else if (Level == 2)
696  _LocalGetJobLevel2(pJob, NULL, NULL, pcbNeeded);
697 
698  // Check if the supplied buffer is large enough.
699  if (cbBuf < *pcbNeeded)
700  {
701  dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
702  goto Cleanup;
703  }
704 
705  // Copy over the Job information.
706  pEnd = &pStart[*pcbNeeded];
707 
708  if (Level == 1)
709  _LocalGetJobLevel1(pJob, (PJOB_INFO_1W*)&pStart, &pEnd, NULL);
710  else if (Level == 2)
711  _LocalGetJobLevel2(pJob, (PJOB_INFO_2W*)&pStart, &pEnd, NULL);
712 
713  dwErrorCode = ERROR_SUCCESS;
714 
715 Cleanup:
716  SetLastError(dwErrorCode);
717  return (dwErrorCode == ERROR_SUCCESS);
718 }
719 
720 static DWORD
722 {
723  DWORD dwErrorCode;
724 
725  // First check the validity of the input before changing anything.
726  if (!FindDatatype(pJob->pPrintProcessor, pJobInfo->pDatatype))
727  {
728  dwErrorCode = ERROR_INVALID_DATATYPE;
729  goto Cleanup;
730  }
731 
732  // Check if the datatype has changed.
733  if (!_EqualStrings(pJob->pwszDatatype, pJobInfo->pDatatype))
734  {
735  // Use the new value.
736  if (!ReallocSplStr(&pJob->pwszDatatype, pJobInfo->pDatatype))
737  {
738  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
739  ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
740  goto Cleanup;
741  }
742  }
743 
744  // Check if the document name has changed. An empty string is permitted here!
745  if (!_EqualStrings(pJob->pwszDocumentName, pJobInfo->pDocument))
746  {
747  // Use the new value.
748  if (!ReallocSplStr(&pJob->pwszDocumentName, pJobInfo->pDocument))
749  {
750  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
751  ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
752  goto Cleanup;
753  }
754  }
755 
756  // Check if the status message has changed. An empty string is permitted here!
757  if (!_EqualStrings(pJob->pwszStatus, pJobInfo->pStatus))
758  {
759  // Use the new value.
760  if (!ReallocSplStr(&pJob->pwszStatus, pJobInfo->pStatus))
761  {
762  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
763  ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
764  goto Cleanup;
765  }
766  }
767 
768  // Check if the user name has changed. An empty string is permitted here!
769  if (!_EqualStrings(pJob->pwszUserName, pJobInfo->pUserName))
770  {
771  // The new user name doesn't need to exist, so no additional verification is required.
772 
773  // Use the new value.
774  if (!ReallocSplStr(&pJob->pwszUserName, pJobInfo->pUserName))
775  {
776  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
777  ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
778  goto Cleanup;
779  }
780  }
781 
782  // Check if the priority has changed.
783  if (pJob->dwPriority != pJobInfo->Priority && IS_VALID_PRIORITY(pJobInfo->Priority))
784  {
785  // Set the new priority.
786  pJob->dwPriority = pJobInfo->Priority;
787 
788  // Remove and reinsert the job in the Printer's Job List.
789  // The Compare function will be used to find the right position now considering the new priority.
790  DeleteElementSkiplist(&pJob->pPrinter->JobList, pJob);
791  InsertElementSkiplist(&pJob->pPrinter->JobList, pJob);
792  }
793 
794  // Check if the status flags have changed.
795  if (pJob->dwStatus != pJobInfo->Status)
796  {
797  // Only add status flags that make sense.
798  if (pJobInfo->Status & JOB_STATUS_PAUSED)
799  pJob->dwStatus |= JOB_STATUS_PAUSED;
800 
801  if (pJobInfo->Status & JOB_STATUS_ERROR)
802  pJob->dwStatus |= JOB_STATUS_ERROR;
803 
804  if (pJobInfo->Status & JOB_STATUS_OFFLINE)
805  pJob->dwStatus |= JOB_STATUS_OFFLINE;
806 
807  if (pJobInfo->Status & JOB_STATUS_PAPEROUT)
808  pJob->dwStatus |= JOB_STATUS_PAPEROUT;
809  }
810 
811  dwErrorCode = ERROR_SUCCESS;
812 
813 Cleanup:
814  return dwErrorCode;
815 }
816 
817 static DWORD
819 {
820  DWORD dwErrorCode;
821  PLOCAL_PRINT_PROCESSOR pPrintProcessor;
822 
823  // First check the validity of the input before changing anything.
824  pPrintProcessor = FindPrintProcessor(pJobInfo->pPrintProcessor);
825  if (!pPrintProcessor)
826  {
827  dwErrorCode = ERROR_UNKNOWN_PRINTPROCESSOR;
828  goto Cleanup;
829  }
830 
831  if (!FindDatatype(pPrintProcessor, pJobInfo->pDatatype))
832  {
833  dwErrorCode = ERROR_INVALID_DATATYPE;
834  goto Cleanup;
835  }
836 
837  // Check if the datatype has changed.
838  if (!_EqualStrings(pJob->pwszDatatype, pJobInfo->pDatatype))
839  {
840  // Use the new value.
841  if (!ReallocSplStr(&pJob->pwszDatatype, pJobInfo->pDatatype))
842  {
843  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
844  ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
845  goto Cleanup;
846  }
847  }
848 
849  // Check if the document name has changed. An empty string is permitted here!
850  if (!_EqualStrings(pJob->pwszDocumentName, pJobInfo->pDocument))
851  {
852  // Use the new value.
853  if (!ReallocSplStr(&pJob->pwszDocumentName, pJobInfo->pDocument))
854  {
855  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
856  ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
857  goto Cleanup;
858  }
859  }
860 
861  // Check if the notify name has changed. An empty string is permitted here!
862  if (!_EqualStrings(pJob->pwszNotifyName, pJobInfo->pNotifyName))
863  {
864  // The new notify name doesn't need to exist, so no additional verification is required.
865 
866  // Use the new value.
867  if (!ReallocSplStr(&pJob->pwszNotifyName, pJobInfo->pNotifyName))
868  {
869  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
870  ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
871  goto Cleanup;
872  }
873  }
874 
875  // Check if the Print Processor Parameters have changed. An empty string is permitted here!
877  {
878  // Use the new value.
880  {
881  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
882  ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
883  goto Cleanup;
884  }
885  }
886 
887  // Check if the Status Message has changed. An empty string is permitted here!
888  if (!_EqualStrings(pJob->pwszStatus, pJobInfo->pStatus))
889  {
890  // Use the new value.
891  if (!ReallocSplStr(&pJob->pwszStatus, pJobInfo->pStatus))
892  {
893  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
894  ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
895  goto Cleanup;
896  }
897  }
898 
899  // Check if the user name has changed. An empty string is permitted here!
900  if (!_EqualStrings(pJob->pwszUserName, pJobInfo->pUserName))
901  {
902  // The new user name doesn't need to exist, so no additional verification is required.
903 
904  // Use the new value.
905  if (!ReallocSplStr(&pJob->pwszUserName, pJobInfo->pUserName))
906  {
907  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
908  ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
909  goto Cleanup;
910  }
911  }
912 
913  // Check if the priority has changed.
914  if (pJob->dwPriority != pJobInfo->Priority && IS_VALID_PRIORITY(pJobInfo->Priority))
915  {
916  // Set the new priority.
917  pJob->dwPriority = pJobInfo->Priority;
918 
919  // Remove and reinsert the job in the Printer's Job List.
920  // The Compare function will be used to find the right position now considering the new priority.
921  DeleteElementSkiplist(&pJob->pPrinter->JobList, pJob);
922  InsertElementSkiplist(&pJob->pPrinter->JobList, pJob);
923  }
924 
925  // Check if the status flags have changed.
926  if (pJob->dwStatus != pJobInfo->Status)
927  {
928  // Only add status flags that make sense.
929  if (pJobInfo->Status & JOB_STATUS_PAUSED)
930  pJob->dwStatus |= JOB_STATUS_PAUSED;
931 
932  if (pJobInfo->Status & JOB_STATUS_ERROR)
933  pJob->dwStatus |= JOB_STATUS_ERROR;
934 
935  if (pJobInfo->Status & JOB_STATUS_OFFLINE)
936  pJob->dwStatus |= JOB_STATUS_OFFLINE;
937 
938  if (pJobInfo->Status & JOB_STATUS_PAPEROUT)
939  pJob->dwStatus |= JOB_STATUS_PAPEROUT;
940  }
941 
942  dwErrorCode = ERROR_SUCCESS;
943 
944 Cleanup:
945  return dwErrorCode;
946 }
947 
948 BOOL WINAPI
949 LocalSetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command)
950 {
951  DWORD dwErrorCode = ERROR_SUCCESS;
952  PLOCAL_HANDLE pHandle;
953  PLOCAL_JOB pJob;
954  PLOCAL_PRINTER_HANDLE pPrinterHandle;
955  WCHAR wszFullPath[MAX_PATH];
956 
957  TRACE("LocalSetJob(%p, %lu, %lu, %p, %lu)\n", hPrinter, JobId, Level, pJobInfo, Command);
958 
959  // Check if this is a printer handle.
960  pHandle = (PLOCAL_HANDLE)hPrinter;
961  if (!pHandle || pHandle->HandleType != HandleType_Printer)
962  {
963  dwErrorCode = ERROR_INVALID_HANDLE;
964  goto Cleanup;
965  }
966 
967  pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
968 
969  // Get the desired job.
970  pJob = LookupElementSkiplist(&GlobalJobList, &JobId, NULL);
971  if (!pJob || pJob->pPrinter != pPrinterHandle->pPrinter)
972  {
973  dwErrorCode = ERROR_INVALID_PARAMETER;
974  goto Cleanup;
975  }
976 
977  // Set new job information if a valid level was given.
978  if (Level == 1)
979  dwErrorCode = _LocalSetJobLevel1(pPrinterHandle, pJob, (PJOB_INFO_1W)pJobInfo);
980  else if (Level == 2)
981  dwErrorCode = _LocalSetJobLevel2(pPrinterHandle, pJob, (PJOB_INFO_2W)pJobInfo);
982 
983  if (dwErrorCode != ERROR_SUCCESS)
984  goto Cleanup;
985 
986  // If we do spooled printing, the job information is written down into a shadow file.
987  if (!(pPrinterHandle->pPrinter->dwAttributes & PRINTER_ATTRIBUTE_DIRECT))
988  {
989  // Write the job data into the shadow file.
990  GetJobFilePath(L"SHD", JobId, wszFullPath);
991  WriteJobShadowFile(wszFullPath, pJob);
992  }
993 
994  // Perform an additional command if desired.
995  if (Command)
996  {
997  if (Command == JOB_CONTROL_SENT_TO_PRINTER)
998  {
999  // This indicates the end of the Print Job.
1000 
1001  // Cancel the Job at the Print Processor.
1002  if (pJob->hPrintProcessor)
1004 
1005  FreeJob(pJob);
1006 
1007  // TODO: All open handles associated with the job need to be invalidated.
1008  // This certainly needs handle tracking...
1009  }
1010  else
1011  {
1012  ERR("Unimplemented SetJob Command: %lu!\n", Command);
1013  }
1014  }
1015 
1016 Cleanup:
1017  SetLastError(dwErrorCode);
1018  return (dwErrorCode == ERROR_SUCCESS);
1019 }
1020 
1021 BOOL WINAPI
1022 LocalEnumJobs(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pStart, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
1023 {
1024  DWORD dwErrorCode;
1025  DWORD i;
1026  PBYTE pEnd;
1027  PLOCAL_HANDLE pHandle;
1028  PLOCAL_JOB pJob;
1029  PSKIPLIST_NODE pFirstJobNode;
1030  PSKIPLIST_NODE pNode;
1031  PLOCAL_PRINTER_HANDLE pPrinterHandle;
1032 
1033  TRACE("LocalEnumJobs(%p, %lu, %lu, %lu, %p, %lu, %p, %p)\n", hPrinter, FirstJob, NoJobs, Level, pStart, cbBuf, pcbNeeded, pcReturned);
1034 
1035  // Check if this is a printer handle.
1036  pHandle = (PLOCAL_HANDLE)hPrinter;
1037  if (pHandle->HandleType != HandleType_Printer)
1038  {
1039  dwErrorCode = ERROR_INVALID_HANDLE;
1040  goto Cleanup;
1041  }
1042 
1043  pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1044 
1045  // Check the level.
1046  if (Level > 2)
1047  {
1048  dwErrorCode = ERROR_INVALID_LEVEL;
1049  goto Cleanup;
1050  }
1051 
1052  // Begin counting.
1053  *pcbNeeded = 0;
1054  *pcReturned = 0;
1055 
1056  // Lookup the node of the first job requested by the caller in the Printer's Job List.
1057  pFirstJobNode = LookupNodeByIndexSkiplist(&pPrinterHandle->pPrinter->JobList, FirstJob);
1058 
1059  // Count the required buffer size and the number of jobs.
1060  i = 0;
1061  pNode = pFirstJobNode;
1062 
1063  while (i < NoJobs && pNode)
1064  {
1065  pJob = (PLOCAL_JOB)pNode->Element;
1066 
1067  if (Level == 1)
1068  _LocalGetJobLevel1(pJob, NULL, NULL, pcbNeeded);
1069  else if (Level == 2)
1070  _LocalGetJobLevel2(pJob, NULL, NULL, pcbNeeded);
1071 
1072  // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
1073  i++;
1074  pNode = pNode->Next[0];
1075  }
1076 
1077  // Check if the supplied buffer is large enough.
1078  if (cbBuf < *pcbNeeded)
1079  {
1080  dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
1081  goto Cleanup;
1082  }
1083 
1084  // Copy over the Job information.
1085  i = 0;
1086  pNode = pFirstJobNode;
1087  pEnd = &pStart[*pcbNeeded];
1088 
1089  while (i < NoJobs && pNode)
1090  {
1091  pJob = (PLOCAL_JOB)pNode->Element;
1092 
1093  if (Level == 1)
1094  _LocalGetJobLevel1(pJob, (PJOB_INFO_1W*)&pStart, &pEnd, NULL);
1095  else if (Level == 2)
1096  _LocalGetJobLevel2(pJob, (PJOB_INFO_2W*)&pStart, &pEnd, NULL);
1097 
1098  // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
1099  i++;
1100  pNode = pNode->Next[0];
1101  }
1102 
1103  *pcReturned = i;
1104  dwErrorCode = ERROR_SUCCESS;
1105 
1106 Cleanup:
1107  SetLastError(dwErrorCode);
1108  return (dwErrorCode == ERROR_SUCCESS);
1109 }
1110 
1111 BOOL WINAPI
1112 LocalScheduleJob(HANDLE hPrinter, DWORD dwJobID)
1113 {
1115  DWORD dwErrorCode;
1116  HANDLE hThread;
1117  PLOCAL_JOB pJob;
1118  PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1119  PLOCAL_PRINTER_HANDLE pPrinterHandle;
1120  WCHAR wszFullPath[MAX_PATH];
1121 
1122  TRACE("LocalScheduleJob(%p, %lu)\n", hPrinter, dwJobID);
1123 
1124  // Check if this is a printer handle.
1125  if (pHandle->HandleType != HandleType_Printer)
1126  {
1127  dwErrorCode = ERROR_INVALID_HANDLE;
1128  goto Cleanup;
1129  }
1130 
1131  pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1132 
1133  // Check if the Job ID is valid.
1134  pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL);
1135  if (!pJob || pJob->pPrinter != pPrinterHandle->pPrinter)
1136  {
1137  dwErrorCode = ERROR_INVALID_PARAMETER;
1138  goto Cleanup;
1139  }
1140 
1141  // Check if this Job was started with AddJob.
1142  if (!pJob->bAddedJob)
1143  {
1144  dwErrorCode = ERROR_SPL_NO_ADDJOB;
1145  goto Cleanup;
1146  }
1147 
1148  // Construct the full path to the spool file.
1149  GetJobFilePath(L"SPL", dwJobID, wszFullPath);
1150 
1151  // Check if it exists.
1152  dwAttributes = GetFileAttributesW(wszFullPath);
1153  if (dwAttributes == INVALID_FILE_ATTRIBUTES || dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
1154  {
1155  dwErrorCode = ERROR_SPOOL_FILE_NOT_FOUND;
1156  goto Cleanup;
1157  }
1158 
1159  // Spooling is finished at this point.
1160  pJob->dwStatus &= ~JOB_STATUS_SPOOLING;
1161 
1162  // Write the job data into the shadow file.
1163  wcscpy(wcsrchr(wszFullPath, L'.'), L".SHD");
1164  WriteJobShadowFile(wszFullPath, pJob);
1165 
1166  // Create the thread for performing the printing process.
1168  if (!hThread)
1169  {
1170  dwErrorCode = GetLastError();
1171  ERR("CreateThread failed with error %lu!\n", dwErrorCode);
1172  goto Cleanup;
1173  }
1174 
1175  // We don't need the thread handle. Keeping it open blocks the thread from terminating.
1176  CloseHandle(hThread);
1177 
1178  // ScheduleJob has done its job. The rest happens inside the thread.
1179  dwErrorCode = ERROR_SUCCESS;
1180 
1181 Cleanup:
1182  SetLastError(dwErrorCode);
1183  return (dwErrorCode == ERROR_SUCCESS);
1184 }
1185 
1186 PLOCAL_JOB
1188 {
1189  DWORD cbFileSize;
1190  DWORD cbRead;
1192  PLOCAL_JOB pJob;
1193  PLOCAL_JOB pReturnValue = NULL;
1194  PLOCAL_PRINTER pPrinter;
1195  PLOCAL_PRINT_PROCESSOR pPrintProcessor;
1196  PSHD_HEADER pShadowFile = NULL;
1197  PWSTR pwszPrinterName;
1198  PWSTR pwszPrintProcessor;
1199 
1200  TRACE("ReadJobShadowFile(%S)\n", pwszFilePath);
1201 
1202  // Try to open the file.
1203  hFile = CreateFileW(pwszFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1204  if (hFile == INVALID_HANDLE_VALUE)
1205  {
1206  ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
1207  goto Cleanup;
1208  }
1209 
1210  // Get its file size (small enough for a single DWORD) and allocate memory for all of it.
1211  cbFileSize = GetFileSize(hFile, NULL);
1212  pShadowFile = DllAllocSplMem(cbFileSize);
1213  if (!pShadowFile)
1214  {
1215  ERR("DllAllocSplMem failed for file \"%S\"!\n", pwszFilePath);
1216  goto Cleanup;
1217  }
1218 
1219  // Read the entire file.
1220  if (!ReadFile(hFile, pShadowFile, cbFileSize, &cbRead, NULL))
1221  {
1222  ERR("ReadFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
1223  goto Cleanup;
1224  }
1225 
1226  // Check signature and header size.
1227  if (pShadowFile->dwSignature != SHD_WIN2003_SIGNATURE || pShadowFile->cbHeader != sizeof(SHD_HEADER))
1228  {
1229  ERR("Signature or Header Size mismatch for file \"%S\"!\n", pwszFilePath);
1230  goto Cleanup;
1231  }
1232 
1233  // Retrieve the associated printer from the list.
1234  pwszPrinterName = (PWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offPrinterName);
1235  pPrinter = LookupElementSkiplist(&PrinterList, &pwszPrinterName, NULL);
1236  if (!pPrinter)
1237  {
1238  ERR("Shadow file \"%S\" references a non-existing printer \"%S\"!\n", pwszFilePath, pwszPrinterName);
1239  goto Cleanup;
1240  }
1241 
1242  // Retrieve the associated Print Processor from the list.
1243  pwszPrintProcessor = (PWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offPrintProcessor);
1244  pPrintProcessor = FindPrintProcessor(pwszPrintProcessor);
1245  if (!pPrintProcessor)
1246  {
1247  ERR("Shadow file \"%S\" references a non-existing Print Processor \"%S\"!\n", pwszFilePath, pwszPrintProcessor);
1248  goto Cleanup;
1249  }
1250 
1251  // Create a new job structure and copy over the relevant fields.
1252  pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
1253  if (!pJob)
1254  {
1255  ERR("DllAllocSplMem failed for file \"%S\"!\n", pwszFilePath);
1256  goto Cleanup;
1257  }
1258 
1259  pJob->dwJobID = pShadowFile->dwJobID;
1260  pJob->dwPriority = pShadowFile->dwPriority;
1261  pJob->dwStartTime = pShadowFile->dwStartTime;
1262  pJob->dwTotalPages = pShadowFile->dwTotalPages;
1263  pJob->dwUntilTime = pShadowFile->dwUntilTime;
1264  pJob->pPrinter = pPrinter;
1265  pJob->pPrintProcessor = pPrintProcessor;
1266  pJob->pDevMode = DuplicateDevMode((PDEVMODEW)((ULONG_PTR)pShadowFile + pShadowFile->offDevMode));
1267  pJob->pwszDatatype = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offDatatype));
1268  pJob->pwszMachineName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offMachineName));
1269  CopyMemory(&pJob->stSubmitted, &pShadowFile->stSubmitted, sizeof(SYSTEMTIME));
1270 
1271  // Copy the optional values.
1272  if (pShadowFile->offDocumentName)
1273  pJob->pwszDocumentName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offDocumentName));
1274 
1275  if (pShadowFile->offNotifyName)
1276  pJob->pwszNotifyName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offNotifyName));
1277 
1278  if (pShadowFile->offPrintProcessorParameters)
1280 
1281  if (pShadowFile->offUserName)
1282  pJob->pwszUserName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offUserName));
1283 
1284  // Jobs read from shadow files were always added using AddJob.
1285  pJob->bAddedJob = TRUE;
1286 
1287  pReturnValue = pJob;
1288 
1289 Cleanup:
1290  if (pShadowFile)
1291  DllFreeSplMem(pShadowFile);
1292 
1293  if (hFile != INVALID_HANDLE_VALUE)
1294  CloseHandle(hFile);
1295 
1296  return pReturnValue;
1297 }
1298 
1299 BOOL
1300 WriteJobShadowFile(PWSTR pwszFilePath, const PLOCAL_JOB pJob)
1301 {
1302  BOOL bReturnValue = FALSE;
1303  DWORD cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
1304  DWORD cbDevMode = pJob->pDevMode->dmSize + pJob->pDevMode->dmDriverExtra;
1305  DWORD cbDocumentName = 0;
1306  DWORD cbFileSize;
1307  DWORD cbMachineName = (wcslen(pJob->pwszMachineName) + 1) * sizeof(WCHAR);
1308  DWORD cbNotifyName = 0;
1309  DWORD cbPrinterDriver = (wcslen(pJob->pPrinter->pwszPrinterDriver) + 1) * sizeof(WCHAR);
1310  DWORD cbPrinterName = (wcslen(pJob->pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
1311  DWORD cbPrintProcessor = (wcslen(pJob->pPrintProcessor->pwszName) + 1) * sizeof(WCHAR);
1312  DWORD cbPrintProcessorParameters = 0;
1313  DWORD cbUserName = 0;
1314  DWORD cbWritten;
1315  DWORD dwCurrentOffset;
1316  HANDLE hSHDFile = INVALID_HANDLE_VALUE;
1317  HANDLE hSPLFile = INVALID_HANDLE_VALUE;
1318  PSHD_HEADER pShadowFile = NULL;
1319 
1320  TRACE("WriteJobShadowFile(%S, %p)\n", pwszFilePath, pJob);
1321 
1322  // Try to open the SHD file.
1323  hSHDFile = CreateFileW(pwszFilePath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
1324  if (hSHDFile == INVALID_HANDLE_VALUE)
1325  {
1326  ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
1327  goto Cleanup;
1328  }
1329 
1330  // Calculate the lengths of the optional values and the total size of the shadow file.
1331  if (pJob->pwszDocumentName)
1332  cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
1333 
1334  if (pJob->pwszNotifyName)
1335  cbNotifyName = (wcslen(pJob->pwszNotifyName) + 1) * sizeof(WCHAR);
1336 
1337  if (pJob->pwszPrintProcessorParameters)
1338  cbPrintProcessorParameters = (wcslen(pJob->pwszPrintProcessorParameters) + 1) * sizeof(WCHAR);
1339 
1340  if (pJob->pwszUserName)
1341  cbUserName = (wcslen(pJob->pwszUserName) + 1) * sizeof(WCHAR);
1342 
1343  cbFileSize = sizeof(SHD_HEADER) + cbDatatype + cbDocumentName + cbDevMode + cbMachineName + cbNotifyName + cbPrinterDriver + cbPrinterName + cbPrintProcessor + cbPrintProcessorParameters + cbUserName;
1344 
1345  // Allocate memory for it.
1346  pShadowFile = DllAllocSplMem(cbFileSize);
1347  if (!pShadowFile)
1348  {
1349  ERR("DllAllocSplMem failed for file \"%S\"!\n", pwszFilePath);
1350  goto Cleanup;
1351  }
1352 
1353  // Fill out the shadow file header information.
1354  pShadowFile->dwSignature = SHD_WIN2003_SIGNATURE;
1355  pShadowFile->cbHeader = sizeof(SHD_HEADER);
1356 
1357  // Copy the values.
1358  pShadowFile->dwJobID = pJob->dwJobID;
1359  pShadowFile->dwPriority = pJob->dwPriority;
1360  pShadowFile->dwStartTime = pJob->dwStartTime;
1361  pShadowFile->dwTotalPages = pJob->dwTotalPages;
1362  pShadowFile->dwUntilTime = pJob->dwUntilTime;
1363  CopyMemory(&pShadowFile->stSubmitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
1364 
1365  // Determine the file size of the .SPL file
1366  wcscpy(wcsrchr(pwszFilePath, L'.'), L".SPL");
1367  hSPLFile = CreateFileW(pwszFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1368  if (hSPLFile != INVALID_HANDLE_VALUE)
1369  pShadowFile->dwSPLSize = GetFileSize(hSPLFile, NULL);
1370 
1371  // Add the extra values that are stored as offsets in the shadow file.
1372  // The first value begins right after the shadow file header.
1373  dwCurrentOffset = sizeof(SHD_HEADER);
1374 
1375  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszDatatype, cbDatatype);
1376  pShadowFile->offDatatype = dwCurrentOffset;
1377  dwCurrentOffset += cbDatatype;
1378 
1379  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pDevMode, cbDevMode);
1380  pShadowFile->offDevMode = dwCurrentOffset;
1381  dwCurrentOffset += cbDevMode;
1382 
1383  // offDriverName is only written, but automatically determined through offPrinterName when reading.
1384  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pPrinter->pwszPrinterDriver, cbPrinterDriver);
1385  pShadowFile->offDriverName = dwCurrentOffset;
1386  dwCurrentOffset += cbPrinterDriver;
1387 
1388  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszMachineName, cbMachineName);
1389  pShadowFile->offMachineName = dwCurrentOffset;
1390  dwCurrentOffset += cbMachineName;
1391 
1392  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pPrinter->pwszPrinterName, cbPrinterName);
1393  pShadowFile->offPrinterName = dwCurrentOffset;
1394  dwCurrentOffset += cbPrinterName;
1395 
1396  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pPrintProcessor->pwszName, cbPrintProcessor);
1397  pShadowFile->offPrintProcessor = dwCurrentOffset;
1398  dwCurrentOffset += cbPrintProcessor;
1399 
1400  // Copy the optional values.
1401  if (cbDocumentName)
1402  {
1403  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszDocumentName, cbDocumentName);
1404  pShadowFile->offDocumentName = dwCurrentOffset;
1405  dwCurrentOffset += cbDocumentName;
1406  }
1407 
1408  if (cbNotifyName)
1409  {
1410  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszNotifyName, cbNotifyName);
1411  pShadowFile->offNotifyName = dwCurrentOffset;
1412  dwCurrentOffset += cbNotifyName;
1413  }
1414 
1415  if (cbPrintProcessorParameters)
1416  {
1417  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszPrintProcessorParameters, cbPrintProcessorParameters);
1418  pShadowFile->offPrintProcessorParameters = dwCurrentOffset;
1419  dwCurrentOffset += cbPrintProcessorParameters;
1420  }
1421 
1422  if (cbUserName)
1423  {
1424  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszUserName, cbUserName);
1425  pShadowFile->offUserName = dwCurrentOffset;
1426  dwCurrentOffset += cbUserName;
1427  }
1428 
1429  // Write the file.
1430  if (!WriteFile(hSHDFile, pShadowFile, cbFileSize, &cbWritten, NULL))
1431  {
1432  ERR("WriteFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
1433  goto Cleanup;
1434  }
1435 
1436  bReturnValue = TRUE;
1437 
1438 Cleanup:
1439  if (pShadowFile)
1440  DllFreeSplMem(pShadowFile);
1441 
1442  if (hSHDFile != INVALID_HANDLE_VALUE)
1443  CloseHandle(hSHDFile);
1444 
1445  if (hSPLFile != INVALID_HANDLE_VALUE)
1446  CloseHandle(hSPLFile);
1447 
1448  return bReturnValue;
1449 }
1450 
1451 void
1453 {
1454  PWSTR pwszSHDFile;
1455 
1456  TRACE("FreeJob(%p)\n", pJob);
1457 
1458  // Remove the Job from both Job Lists.
1459  DeleteElementSkiplist(&pJob->pPrinter->JobList, pJob);
1460  DeleteElementSkiplist(&GlobalJobList, pJob);
1461 
1462  // Try to delete the corresponding .SHD file.
1463  pwszSHDFile = DllAllocSplMem(GetJobFilePath(L"SHD", 0, NULL));
1464  if (pwszSHDFile && GetJobFilePath(L"SHD", pJob->dwJobID, pwszSHDFile))
1465  DeleteFileW(pwszSHDFile);
1466 
1467  // Free memory for the mandatory fields.
1468  DllFreeSplMem(pJob->pDevMode);
1469  DllFreeSplStr(pJob->pwszDatatype);
1473  DllFreeSplStr(pJob->pwszUserName);
1474 
1475  // Free memory for the optional fields if they are present.
1476  if (pJob->pwszOutputFile)
1478 
1479  if (pJob->pwszPrintProcessorParameters)
1481 
1482  if (pJob->pwszStatus)
1483  DllFreeSplStr(pJob->pwszStatus);
1484 
1485  // Finally free the job structure itself.
1486  DllFreeSplMem(pJob);
1487 }
LPWSTR pUserName
Definition: winspool.h:298
static DWORD _LocalSetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PJOB_INFO_2W pJobInfo)
Definition: jobs.c:818
DWORD *typedef PVOID
Definition: winlogon.h:61
BOOL WINAPI LocalEnumJobs(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pStart, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
Definition: jobs.c:1022
#define ERROR_SPOOL_FILE_NOT_FOUND
Definition: winerror.h:1207
#define ERROR_INVALID_DATATYPE
Definition: winerror.h:1111
#define ERROR_INVALID_PARAMETER
Definition: compat.h:91
SYSTEMTIME stSubmitted
Definition: precomp.h:143
static __inline BOOL _EqualStrings(PCWSTR pwszA, PCWSTR pwszB)
Definition: jobs.c:58
BOOL WINAPI FindNextFileW(IN HANDLE hFindFile, OUT LPWIN32_FIND_DATAW lpFindFileData)
Definition: find.c:382
PDEVMODEW pDevMode
Definition: precomp.h:170
DWORD offDocumentName
Definition: precomp.h:222
PWSTR WINAPI AllocSplStr(PCWSTR pwszInput)
Definition: memory.c:56
VOID WINAPI GetSystemTimeAsFileTime(OUT PFILETIME lpFileTime)
Definition: time.c:128
DWORD dwStartTime
Definition: precomp.h:231
RPC_STATUS WINAPI RpcBindingFree(RPC_BINDING_HANDLE *Binding)
Definition: rpc_binding.c:784
#define TRUE
Definition: types.h:120
DWORD offNotifyName
Definition: precomp.h:221
#define CloseHandle
Definition: compat.h:398
BOOL InitializeGlobalJobList(void)
Definition: jobs.c:161
DWORD offDriverName
Definition: precomp.h:225
#define ERROR_SUCCESS
Definition: deptool.c:10
#define JOB_STATUS_PAUSED
Definition: winspool.h:338
BOOL bAddedJob
Definition: precomp.h:138
BOOL InsertTailElementSkiplist(PSKIPLIST Skiplist, PVOID Element)
Definition: skiplist.c:308
DWORD offUserName
Definition: precomp.h:220
RPC_STATUS WINAPI RpcStringFreeW(RPC_WSTR *String)
Definition: rpcrt4_main.c:177
DWORD dwStartTime
Definition: precomp.h:153
PWSTR pwszOutputFile
Definition: precomp.h:148
__wchar_t WCHAR
Definition: xmlstorage.h:180
struct _devicemodeW * PDEVMODEW
Definition: gdi32p.h:167
static BOOL _GetNextJobID(PDWORD dwJobID)
Definition: jobs.c:73
LPWSTR pDatatype
Definition: winspool.h:254
#define _countof(array)
Definition: fontsub.cpp:30
#define B(row, col)
Definition: m_matrix.c:146
DWORD dwAttributes
Definition: precomp.h:117
PWSTR pwszPrinterDriver
Definition: precomp.h:120
BOOL WINAPI DllFreeSplMem(PVOID pMem)
Definition: memory.c:112
#define ERROR_INVALID_HANDLE
Definition: compat.h:88
PWSTR pwszMachineName
Definition: precomp.h:156
PVOID *typedef PWSTR
Definition: winlogon.h:66
#define JOB_STATUS_PAPEROUT
Definition: winspool.h:344
struct _ADDJOB_INFO_1W ADDJOB_INFO_1W
WORD dmDriverExtra
Definition: wingdi.h:1598
#define JOB_CONTROL_CANCEL
Definition: winspool.h:332
#define ERROR_NOT_ENOUGH_MEMORY
Definition: dderror.h:7
#define INVALID_HANDLE_VALUE
Definition: compat.h:391
DWORD WINAPI GetLastError(VOID)
Definition: except.c:1059
$ULONG LowPart
Definition: ntbasedef.h:576
DWORD WINAPI PrintingThreadProc(PLOCAL_JOB pJob)
SKIPLIST JobList
Definition: precomp.h:126
_IRQL_requires_same_ typedef _In_ ULONG _In_ UCHAR Level
Definition: wmitypes.h:55
#define DEF_PRIORITY
Definition: winspool.h:230
static DWORD dwJobInfo2Offsets[]
Definition: jobs.c:27
Definition: shell.h:41
_Check_return_ unsigned long __cdecl wcstoul(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t **_EndPtr, _In_ int _Radix)
#define FILE_SHARE_WRITE
Definition: nt_native.h:681
unsigned short * RPC_WSTR
Definition: rpcdce.h:46
void FreeJob(PLOCAL_JOB pJob)
Definition: jobs.c:1452
#define WCHAR
Definition: msvc.h:43
PSKIPLIST_NODE LookupNodeByIndexSkiplist(PSKIPLIST Skiplist, DWORD ElementIndex)
Definition: skiplist.c:412
void(WINAPI * PSKIPLIST_FREE_ROUTINE)(PVOID)
Definition: skiplist.h:24
#define DWORD
Definition: msvc.h:34
DWORD dwPriority
Definition: precomp.h:142
PDEVMODEW pDevMode
Definition: precomp.h:157
DWORD DWORD
Definition: winlogon.h:84
DWORD dwAttributes
Definition: vdmdbg.h:34
#define FILE_SHARE_READ
Definition: compat.h:125
#define JOB_STATUS_SPOOLING
Definition: winspool.h:341
LPWSTR pUserName
Definition: winspool.h:252
uint32_t ULONG_PTR
Definition: typedefs.h:63
BOOL WINAPI ReallocSplStr(PWSTR *ppwszString, PCWSTR pwszInput)
Definition: memory.c:192
void InitializeSkiplist(PSKIPLIST Skiplist, PSKIPLIST_ALLOCATE_ROUTINE AllocateRoutine, PSKIPLIST_COMPARE_ROUTINE CompareRoutine, PSKIPLIST_FREE_ROUTINE FreeRoutine)
Definition: skiplist.c:220
DWORD dwSignature
Definition: precomp.h:214
void InitializePrinterJobList(PLOCAL_PRINTER pPrinter)
Definition: jobs.c:247
DWORD dwHighDateTime
Definition: mapidefs.h:66
static DWORD _dwLastJobID
Definition: jobs.c:14
GLenum GLclampf GLint i
Definition: glfuncs.h:14
DWORD dwJobID
Definition: precomp.h:136
LPWSTR pDocument
Definition: winspool.h:299
struct _LOCAL_HANDLE * PLOCAL_HANDLE
Definition: precomp.h:54
LPWSTR pDatatype
Definition: winspool.h:301
ULONGLONG QuadPart
Definition: ms-dtyp.idl:185
#define FALSE
Definition: types.h:117
PLOCAL_JOB ReadJobShadowFile(PCWSTR pwszFilePath)
Definition: jobs.c:1187
DWORD WINAPI GetFileAttributesW(LPCWSTR lpFileName)
Definition: fileinfo.c:802
#define GENERIC_WRITE
Definition: nt_native.h:90
BOOL InsertElementSkiplist(PSKIPLIST Skiplist, PVOID Element)
Definition: skiplist.c:250
LPWSTR pStatus
Definition: winspool.h:306
#define FIXME(fmt,...)
Definition: debug.h:110
BOOL WINAPI DeleteFileW(IN LPCWSTR lpFileName)
Definition: delete.c:39
BOOL WINAPI LocalScheduleJob(HANDLE hPrinter, DWORD dwJobID)
Definition: jobs.c:1112
PWSTR pwszDatatype
Definition: precomp.h:147
#define JOB_STATUS_OFFLINE
Definition: winspool.h:343
HANDLE WINAPI DECLSPEC_HOTPATCH CreateThread(IN LPSECURITY_ATTRIBUTES lpThreadAttributes, IN DWORD dwStackSize, IN LPTHREAD_START_ROUTINE lpStartAddress, IN LPVOID lpParameter, IN DWORD dwCreationFlags, OUT LPDWORD lpThreadId)
Definition: thread.c:112
PLOCAL_PRINT_PROCESSOR pPrintProcessor
Definition: precomp.h:124
smooth NULL
Definition: ftsmooth.c:416
DWORD dwUntilTime
Definition: precomp.h:154
PVOID pSpecificHandle
Definition: precomp.h:204
#define MAXDWORD
_Check_return_ _CRTIMP _CONST_RETURN wchar_t *__cdecl wcsrchr(_In_z_ const wchar_t *_Str, _In_ wchar_t _Ch)
DWORD GetJobFilePath(PCWSTR pwszExtension, DWORD dwJobID, PWSTR pwszOutput)
Definition: jobs.c:146
static void _LocalGetJobLevel1(PLOCAL_JOB pJob, PJOB_INFO_1W *ppJobInfo, PBYTE *ppJobInfoEnd, PDWORD pcbNeeded)
Definition: jobs.c:450
LPWSTR pNotifyName
Definition: winspool.h:300
DWORD(WINAPI * LPTHREAD_START_ROUTINE)(LPVOID)
Definition: winbase.h:707
DWORD offPrintProcessor
Definition: precomp.h:227
BOOL WINAPI LocalGetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pStart, DWORD cbBuf, LPDWORD pcbNeeded)
Definition: jobs.c:655
#define OPEN_EXISTING
Definition: compat.h:426
BOOL WriteJobShadowFile(PWSTR pwszFilePath, const PLOCAL_JOB pJob)
Definition: jobs.c:1300
RPCRTAPI RPC_STATUS RPC_ENTRY RpcBindingServerFromClient(RPC_BINDING_HANDLE ClientBinding, RPC_BINDING_HANDLE *ServerBinding)
Definition: rpc_binding.c:1643
PWSTR pwszPrintProcessorParameters
Definition: precomp.h:149
#define FILE_ATTRIBUTE_DIRECTORY
Definition: nt_native.h:705
BOOL WINAPI LocalAddJob(HANDLE hPrinter, DWORD Level, PBYTE pData, DWORD cbBuf, PDWORD pcbNeeded)
Definition: jobs.c:380
BOOL WINAPI WriteFile(IN HANDLE hFile, IN LPCVOID lpBuffer, IN DWORD nNumberOfBytesToWrite OPTIONAL, OUT LPDWORD lpNumberOfBytesWritten OPTIONAL, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: rw.c:24
static void _LocalGetJobLevel2(PLOCAL_JOB pJob, PJOB_INFO_2W *ppJobInfo, PBYTE *ppJobInfoEnd, PDWORD pcbNeeded)
Definition: jobs.c:524
DWORD offDevMode
Definition: precomp.h:226
struct _SHD_HEADER SHD_HEADER
Definition: precomp.h:63
DWORD dwStatus
Definition: precomp.h:155
VOID WINAPI GetSystemTime(OUT LPSYSTEMTIME lpSystemTime)
Definition: time.c:317
RPC_STATUS WINAPI RpcStringBindingParseW(RPC_WSTR StringBinding, RPC_WSTR *ObjUuid, RPC_WSTR *Protseq, RPC_WSTR *NetworkAddr, RPC_WSTR *Endpoint, RPC_WSTR *Options)
Definition: rpc_binding.c:675
DWORD Priority
Definition: winspool.h:309
BOOL WINAPI GetUserNameW(LPWSTR lpszName, LPDWORD lpSize)
Definition: misc.c:291
#define TRACE(s)
Definition: solgame.cpp:4
RPC_STATUS WINAPI RpcBindingToStringBindingW(RPC_BINDING_HANDLE Binding, RPC_WSTR *StringBinding)
Definition: rpc_binding.c:944
$ULONG HighPart
Definition: ntbasedef.h:577
unsigned int BOOL
Definition: ntddk_ex.h:94
LONG WINAPI CompareFileTime(IN CONST FILETIME *lpFileTime1, IN CONST FILETIME *lpFileTime2)
Definition: time.c:106
PBYTE WINAPI PackStrings(PWSTR *pSource, PBYTE pDest, const DWORD *DestOffsets, PBYTE pEnd)
Definition: tools.c:39
_IRQL_requires_same_ _In_ PVOID _In_ PVOID SecondStruct
Definition: rtltypes.h:379
if(!(yy_init))
Definition: macro.lex.yy.c:717
DWORD Priority
Definition: winspool.h:257
static int WINAPI _PrinterJobListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
Definition: jobs.c:115
PLOCAL_PRINTER pPrinter
Definition: precomp.h:140
DWORD dwTotalPages
Definition: precomp.h:151
#define SHD_WIN2003_SIGNATURE
Definition: precomp.h:41
WORD dmSize
Definition: wingdi.h:1597
#define MAX_PATH
Definition: compat.h:26
#define swprintf(buf, format,...)
Definition: sprintf.c:56
struct _SKIPLIST_NODE * Next[SKIPLIST_LEVELS]
Definition: skiplist.h:30
BOOL FindDatatype(const PLOCAL_PRINT_PROCESSOR pPrintProcessor, PCWSTR pwszDatatype)
#define CopyMemory
Definition: winbase.h:1633
SKIPLIST PrinterList
Definition: printers.c:11
DWORD cchJobDirectory
Definition: main.c:14
#define PRINTER_ATTRIBUTE_DIRECT
Definition: winspool.h:209
DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh)
Definition: fileinfo.c:481
#define SetLastError(x)
Definition: compat.h:409
DWORD dwSPLSize
Definition: precomp.h:241
#define INVALID_FILE_ATTRIBUTES
Definition: vfdcmd.c:23
Definition: ttei1.cpp:12
_CRTIMP wchar_t *__cdecl wcscpy(_Out_writes_z_(_String_length_(_Source)+1) wchar_t *_Dest, _In_z_ const wchar_t *_Source)
PWSTR pwszNotifyName
Definition: precomp.h:145
static const WCHAR L[]
Definition: oid.c:1087
DWORD offDatatype
Definition: precomp.h:228
LPWSTR pParameters
Definition: winspool.h:303
#define JOB_CONTROL_SENT_TO_PRINTER
Definition: winspool.h:335
_IRQL_requires_same_ _In_ PVOID FirstStruct
Definition: rtltypes.h:379
#define ERROR_INVALID_ACCESS
Definition: winerror.h:115
DWORD dwPriority
Definition: precomp.h:219
#define GENERIC_READ
Definition: compat.h:124
LPWSTR pPrintProcessor
Definition: winspool.h:302
static const WCHAR Cleanup[]
Definition: register.c:80
_In_ HANDLE hFile
Definition: mswsock.h:90
#define WINAPI
Definition: msvc.h:20
#define wcsicmp
Definition: string.h:1152
LPWSTR pDocument
Definition: winspool.h:253
struct _LOCAL_JOB * PLOCAL_JOB
Definition: precomp.h:55
DWORD WINAPI CreateJob(PLOCAL_PRINTER_HANDLE pPrinterHandle)
Definition: jobs.c:257
#define ERR(fmt,...)
Definition: debug.h:109
PVOID LookupElementSkiplist(PSKIPLIST Skiplist, PVOID Element, PDWORD ElementIndex)
Definition: skiplist.c:357
_Check_return_ _CRTIMP int __cdecl wcscmp(_In_z_ const wchar_t *_Str1, _In_z_ const wchar_t *_Str2)
LPWSTR pStatus
Definition: winspool.h:255
#define ERROR_UNKNOWN_PRINTPROCESSOR
Definition: winerror.h:1105
#define CREATE_ALWAYS
Definition: disk.h:72
BOOL WINAPI SystemTimeToFileTime(IN CONST SYSTEMTIME *lpSystemTime, OUT LPFILETIME lpFileTime)
Definition: time.c:148
DWORD *typedef HANDLE
Definition: winlogon.h:61
SYSTEMTIME stSubmitted
Definition: precomp.h:230
enum _LOCAL_HANDLE::@4114 HandleType
_In_ DWORD _Out_ PDWORD pcbNeeded
Definition: winddi.h:3827
DWORD dwTotalPages
Definition: precomp.h:234
PWSTR pwszDocumentName
Definition: precomp.h:146
#define IS_VALID_JOB_ID(ID)
Definition: precomp.h:37
HANDLE hPrintProcessor
Definition: precomp.h:139
#define UNLEN
Definition: sspi.c:28
PDEVMODEW DuplicateDevMode(PDEVMODEW pInput)
Definition: tools.c:61
DWORD Status
Definition: winspool.h:308
WCHAR wszJobDirectory[MAX_PATH]
Definition: main.c:13
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:254
DWORD offMachineName
Definition: precomp.h:240
PVOID Element
Definition: skiplist.h:31
static int WINAPI _GlobalJobListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
Definition: jobs.c:100
HANDLE hThread
Definition: wizard.c:27
DWORD * PDWORD
Definition: pedump.c:68
BOOL WINAPI DllFreeSplStr(PWSTR pwszString)
Definition: memory.c:129
#define CreateFileW
Definition: compat.h:400
#define ERROR_SPL_NO_ADDJOB
Definition: winerror.h:1209
PLOCAL_JOB pJob
Definition: precomp.h:168
DWORD dwPagesPrinted
Definition: precomp.h:152
PLOCAL_PRINT_PROCESSOR pPrintProcessor
Definition: precomp.h:141
#define IS_VALID_PRIORITY(P)
Definition: precomp.h:38
uint32_t * LPDWORD
Definition: typedefs.h:57
DWORD cbHeader
Definition: precomp.h:215
PVOID DeleteElementSkiplist(PSKIPLIST Skiplist, PVOID Element)
Definition: skiplist.c:146
struct _LOCAL_PRINTER_HANDLE * PLOCAL_PRINTER_HANDLE
Definition: precomp.h:61
PVOID WINAPI DllAllocSplMem(DWORD dwBytes)
Definition: memory.c:95
const uint16_t * PCWSTR
Definition: typedefs.h:55
BOOL WINAPI LocalSetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command)
Definition: jobs.c:949
#define ERROR_INVALID_LEVEL
Definition: winerror.h:196
PControlPrintProcessor pfnControlPrintProcessor
Definition: precomp.h:101
GLfloat GLfloat p
Definition: glext.h:8902
DWORD dwUntilTime
Definition: precomp.h:232
PLOCAL_PRINT_PROCESSOR FindPrintProcessor(PCWSTR pwszName)
PLOCAL_PRINTER pPrinter
Definition: precomp.h:167
TW_UINT32 TW_UINT16 TW_UINT16 TW_MEMREF pData
Definition: twain.h:1827
static DWORD _LocalSetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PJOB_INFO_1W pJobInfo)
Definition: jobs.c:721
BOOL WINAPI ReadFile(IN HANDLE hFile, IN LPVOID lpBuffer, IN DWORD nNumberOfBytesToRead, OUT LPDWORD lpNumberOfBytesRead OPTIONAL, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: rw.c:123
PWSTR pwszStatus
Definition: precomp.h:150
BYTE * PBYTE
Definition: pedump.c:66
struct _JOB_INFO_2W JOB_INFO_2W
const WCHAR wszDefaultDocumentName[]
Definition: main.c:25
PWSTR pwszPrinterName
Definition: precomp.h:115
DWORD Status
Definition: winspool.h:256
size_t __cdecl wcslen(_In_z_ const wchar_t *_Str)
SKIPLIST GlobalJobList
Definition: jobs.c:11
DWORD dwLowDateTime
Definition: mapidefs.h:65
#define JOB_STATUS_ERROR
Definition: winspool.h:339
PWSTR pwszUserName
Definition: precomp.h:144
DWORD offPrinterName
Definition: precomp.h:224
#define RPC_S_OK
Definition: rpcnterr.h:22
static DWORD dwJobInfo1Offsets[]
Definition: jobs.c:17
struct _JOB_INFO_1W JOB_INFO_1W
DWORD offPrintProcessorParameters
Definition: precomp.h:229
HANDLE WINAPI FindFirstFileW(IN LPCWSTR lpFileName, OUT LPWIN32_FIND_DATAW lpFindFileData)
Definition: find.c:320
#define ERROR_INSUFFICIENT_BUFFER
Definition: dderror.h:10
BOOL WINAPI FindClose(HANDLE hFindFile)
Definition: find.c:502
DWORD dwJobID
Definition: precomp.h:218