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 related to Printers and printing
5 * COPYRIGHT: Copyright 2015 Colin Finck <colin@reactos.org>
15 * @name _PrinterListCompareRoutine
17 * SKIPLIST_COMPARE_ROUTINE for the Printer List.
18 * Does a case-insensitive comparison, because e.g. LocalOpenPrinter doesn't match the case when looking for Printers.
21 _PrinterListCompareRoutine(PVOID FirstStruct
, PVOID SecondStruct
)
23 PLOCAL_PRINTER A
= (PLOCAL_PRINTER
)FirstStruct
;
24 PLOCAL_PRINTER B
= (PLOCAL_PRINTER
)SecondStruct
;
26 return wcsicmp(A
->pwszPrinterName
, B
->pwszPrinterName
);
30 * @name InitializePrinterList
32 * Initializes a list of locally available Printers.
33 * The list is searchable by name and returns information about the printers, including their job queues.
34 * During this process, the job queues are also initialized.
37 InitializePrinterList()
39 const WCHAR wszPrintersKey
[] = L
"SYSTEM\\CurrentControlSet\\Control\\Print\\Printers";
48 PLOCAL_PRINTER pPrinter
= NULL
;
49 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
50 PWSTR pwszPrintProcessor
= NULL
;
51 WCHAR wszPrinterName
[MAX_PRINTER_NAME
+ 1];
53 // Initialize an empty list for our printers.
54 InitializeSkiplist(&PrinterList
, DllAllocSplMem
, _PrinterListCompareRoutine
, (PSKIPLIST_FREE_ROUTINE
)DllFreeSplMem
);
56 // Open our printers registry key. Each subkey is a local printer there.
57 lStatus
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, wszPrintersKey
, 0, KEY_READ
, &hKey
);
58 if (lStatus
!= ERROR_SUCCESS
)
60 ERR("RegOpenKeyExW failed with status %ld!\n", lStatus
);
64 // Get the number of subkeys.
65 lStatus
= RegQueryInfoKeyW(hKey
, NULL
, NULL
, NULL
, &dwSubKeys
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
66 if (lStatus
!= ERROR_SUCCESS
)
68 ERR("RegQueryInfoKeyW failed with status %ld!\n", lStatus
);
72 // Loop through all available local printers.
73 for (i
= 0; i
< dwSubKeys
; i
++)
75 // Cleanup tasks from the previous run
84 if (pPrinter
->pwszDefaultDatatype
)
85 DllFreeSplStr(pPrinter
->pwszDefaultDatatype
);
87 if (pPrinter
->pwszDescription
)
88 DllFreeSplStr(pPrinter
->pwszDescription
);
90 if (pPrinter
->pwszPrinterDriver
)
91 DllFreeSplStr(pPrinter
->pwszPrinterDriver
);
93 if (pPrinter
->pwszPrinterName
)
94 DllFreeSplStr(pPrinter
->pwszPrinterName
);
96 DllFreeSplMem(pPrinter
);
100 if (pwszPrintProcessor
)
102 DllFreeSplStr(pwszPrintProcessor
);
103 pwszPrintProcessor
= NULL
;
106 // Get the name of this printer.
107 cchPrinterName
= _countof(wszPrinterName
);
108 lStatus
= RegEnumKeyExW(hKey
, i
, wszPrinterName
, &cchPrinterName
, NULL
, NULL
, NULL
, NULL
);
109 if (lStatus
== ERROR_MORE_DATA
)
111 // This printer name exceeds the maximum length and is invalid.
114 else if (lStatus
!= ERROR_SUCCESS
)
116 ERR("RegEnumKeyExW failed for iteration %lu with status %ld!\n", i
, lStatus
);
120 // Open this Printer's registry key.
121 lStatus
= RegOpenKeyExW(hKey
, wszPrinterName
, 0, KEY_READ
, &hSubKey
);
122 if (lStatus
!= ERROR_SUCCESS
)
124 ERR("RegOpenKeyExW failed for Printer \"%S\" with status %ld!\n", wszPrinterName
, lStatus
);
128 // Get the Print Processor.
129 pwszPrintProcessor
= AllocAndRegQueryWSZ(hSubKey
, L
"Print Processor");
130 if (!pwszPrintProcessor
)
133 // Try to find it in the Print Processor List.
134 pPrintProcessor
= FindPrintProcessor(pwszPrintProcessor
);
135 if (!pPrintProcessor
)
137 ERR("Invalid Print Processor \"%S\" for Printer \"%S\"!\n", pwszPrintProcessor
, wszPrinterName
);
141 // Create a new LOCAL_PRINTER structure for it.
142 pPrinter
= DllAllocSplMem(sizeof(LOCAL_PRINTER
));
145 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
149 pPrinter
->pwszPrinterName
= AllocSplStr(wszPrinterName
);
150 pPrinter
->pPrintProcessor
= pPrintProcessor
;
151 InitializePrinterJobList(pPrinter
);
153 // Get the printer driver.
154 pPrinter
->pwszPrinterDriver
= AllocAndRegQueryWSZ(hSubKey
, L
"Printer Driver");
155 if (!pPrinter
->pwszPrinterDriver
)
158 // Get the description.
159 pPrinter
->pwszDescription
= AllocAndRegQueryWSZ(hSubKey
, L
"Description");
160 if (!pPrinter
->pwszDescription
)
163 // Get the default datatype.
164 pPrinter
->pwszDefaultDatatype
= AllocAndRegQueryWSZ(hSubKey
, L
"Datatype");
165 if (!pPrinter
->pwszDefaultDatatype
)
168 // Verify that it's valid.
169 if (!FindDatatype(pPrintProcessor
, pPrinter
->pwszDefaultDatatype
))
171 ERR("Invalid default datatype \"%S\" for Printer \"%S\"!\n", pPrinter
->pwszDefaultDatatype
, wszPrinterName
);
175 // Get the default DevMode.
176 cbData
= sizeof(DEVMODEW
);
177 lStatus
= RegQueryValueExW(hSubKey
, L
"Default DevMode", NULL
, NULL
, (PBYTE
)&pPrinter
->DefaultDevMode
, &cbData
);
178 if (lStatus
!= ERROR_SUCCESS
|| cbData
!= sizeof(DEVMODEW
))
180 ERR("Couldn't query a valid DevMode for Printer \"%S\", status is %ld, cbData is %lu!\n", wszPrinterName
, lStatus
, cbData
);
184 // Get the Attributes.
185 cbData
= sizeof(DWORD
);
186 lStatus
= RegQueryValueExW(hSubKey
, L
"Attributes", NULL
, NULL
, (PBYTE
)&pPrinter
->dwAttributes
, &cbData
);
187 if (lStatus
!= ERROR_SUCCESS
)
189 ERR("Couldn't query Attributes for Printer \"%S\", status is %ld!\n", wszPrinterName
, lStatus
);
194 cbData
= sizeof(DWORD
);
195 lStatus
= RegQueryValueExW(hSubKey
, L
"Status", NULL
, NULL
, (PBYTE
)&pPrinter
->dwStatus
, &cbData
);
196 if (lStatus
!= ERROR_SUCCESS
)
198 ERR("Couldn't query Status for Printer \"%S\", status is %ld!\n", wszPrinterName
, lStatus
);
202 // Add this printer to the printer list.
203 if (!InsertElementSkiplist(&PrinterList
, pPrinter
))
205 ERR("InsertElementSkiplist failed for Printer \"%S\"!\n", pPrinter
->pwszPrinterName
);
209 // Don't let the cleanup routines free this.
216 RegCloseKey(hSubKey
);
220 if (pPrinter
->pwszDefaultDatatype
)
221 DllFreeSplStr(pPrinter
->pwszDefaultDatatype
);
223 if (pPrinter
->pwszDescription
)
224 DllFreeSplStr(pPrinter
->pwszDescription
);
226 if (pPrinter
->pwszPrinterDriver
)
227 DllFreeSplStr(pPrinter
->pwszPrinterDriver
);
229 if (pPrinter
->pwszPrinterName
)
230 DllFreeSplStr(pPrinter
->pwszPrinterName
);
232 DllFreeSplMem(pPrinter
);
235 if (pwszPrintProcessor
)
236 DllFreeSplStr(pwszPrintProcessor
);
245 _LocalEnumPrintersLevel1(DWORD Flags
, LPWSTR Name
, LPBYTE pPrinterEnum
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
247 const WCHAR wszComma
[] = L
",";
252 DWORD cchComputerName
= 0;
256 PBYTE pPrinterString
;
257 PSKIPLIST_NODE pNode
;
258 PLOCAL_PRINTER pPrinter
;
259 PRINTER_INFO_1W PrinterInfo1
;
260 WCHAR wszComputerName
[2 + MAX_COMPUTERNAME_LENGTH
+ 1 + 1];
262 DWORD dwOffsets
[] = {
263 FIELD_OFFSET(PRINTER_INFO_1W
, pName
),
264 FIELD_OFFSET(PRINTER_INFO_1W
, pDescription
),
265 FIELD_OFFSET(PRINTER_INFO_1W
, pComment
),
269 if (Flags
& PRINTER_ENUM_NAME
)
273 // The user supplied a Computer Name (with leading double backslashes) or Print Provider Name.
274 // Only process what's directed at us and dismiss every other request with ERROR_INVALID_NAME.
275 if (Name
[0] == L
'\\' && Name
[1] == L
'\\')
277 // Prepend slashes to the computer name.
278 wszComputerName
[0] = L
'\\';
279 wszComputerName
[1] = L
'\\';
281 // Get the local computer name for comparison.
282 cchComputerName
= MAX_COMPUTERNAME_LENGTH
+ 1;
283 if (!GetComputerNameW(&wszComputerName
[2], &cchComputerName
))
285 dwErrorCode
= GetLastError();
286 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode
);
290 // Add the leading slashes to the total length.
291 cchComputerName
+= 2;
293 // Now compare this with the local computer name and reject if it doesn't match.
294 if (wcsicmp(&Name
[2], &wszComputerName
[2]) != 0)
296 dwErrorCode
= ERROR_INVALID_NAME
;
300 // Add a trailing backslash to wszComputerName, which will later be prepended in front of the printer names.
301 wszComputerName
[cchComputerName
++] = L
'\\';
302 wszComputerName
[cchComputerName
] = 0;
304 else if (wcsicmp(Name
, wszPrintProviderInfo
[0]) != 0)
306 // The user supplied a name that cannot be processed by the local print provider.
307 dwErrorCode
= ERROR_INVALID_NAME
;
313 // The caller wants information about this Print Provider.
314 // spoolss packs this into an array of information about all Print Providers.
315 *pcbNeeded
= sizeof(PRINTER_INFO_1W
);
317 for (i
= 0; i
< 3; i
++)
318 *pcbNeeded
+= (wcslen(wszPrintProviderInfo
[i
]) + 1) * sizeof(WCHAR
);
322 // Check if the supplied buffer is large enough.
323 if (cbBuf
< *pcbNeeded
)
325 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
329 // Copy over the print processor information.
330 ((PPRINTER_INFO_1W
)pPrinterEnum
)->Flags
= 0;
331 PackStrings(wszPrintProviderInfo
, pPrinterEnum
, dwOffsets
, &pPrinterEnum
[*pcbNeeded
]);
332 dwErrorCode
= ERROR_SUCCESS
;
337 // Count the required buffer size and the number of printers.
338 for (pNode
= PrinterList
.Head
.Next
[0]; pNode
; pNode
= pNode
->Next
[0])
340 pPrinter
= (PLOCAL_PRINTER
)pNode
->Element
;
342 // This looks wrong, but is totally right. PRINTER_INFO_1W has three members pName, pComment and pDescription.
343 // But pComment equals the "Description" registry value while pDescription is concatenated out of pName and pComment.
344 // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
345 cbName
= (wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
346 cbComment
= (wcslen(pPrinter
->pwszDescription
) + 1) * sizeof(WCHAR
);
347 cbDescription
= cchComputerName
* sizeof(WCHAR
) + cbName
+ cbComment
+ sizeof(WCHAR
);
349 *pcbNeeded
+= sizeof(PRINTER_INFO_1W
) + cchComputerName
* sizeof(WCHAR
) + cbName
+ cbComment
+ cbDescription
;
353 // Check if the supplied buffer is large enough.
354 if (cbBuf
< *pcbNeeded
)
356 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
360 // Put the strings right after the last PRINTER_INFO_1W structure.
361 // Due to all the required string processing, we can't just use PackStrings here :(
362 pPrinterInfo
= pPrinterEnum
;
363 pPrinterString
= pPrinterEnum
+ *pcReturned
* sizeof(PRINTER_INFO_1W
);
365 // Copy over the printer information.
366 for (pNode
= PrinterList
.Head
.Next
[0]; pNode
; pNode
= pNode
->Next
[0])
368 pPrinter
= (PLOCAL_PRINTER
)pNode
->Element
;
370 // FIXME: As for now, the Flags member returns no information.
371 PrinterInfo1
.Flags
= 0;
373 // Copy the printer name.
374 PrinterInfo1
.pName
= (PWSTR
)pPrinterString
;
375 CopyMemory(pPrinterString
, wszComputerName
, cchComputerName
* sizeof(WCHAR
));
376 pPrinterString
+= cchComputerName
* sizeof(WCHAR
);
377 cbName
= (wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
378 CopyMemory(pPrinterString
, pPrinter
->pwszPrinterName
, cbName
);
379 pPrinterString
+= cbName
;
381 // Copy the printer comment (equals the "Description" registry value).
382 PrinterInfo1
.pComment
= (PWSTR
)pPrinterString
;
383 cbComment
= (wcslen(pPrinter
->pwszDescription
) + 1) * sizeof(WCHAR
);
384 CopyMemory(pPrinterString
, pPrinter
->pwszDescription
, cbComment
);
385 pPrinterString
+= cbComment
;
387 // Copy the description, which for PRINTER_INFO_1W has the form "Name,Comment,"
388 PrinterInfo1
.pDescription
= (PWSTR
)pPrinterString
;
389 CopyMemory(pPrinterString
, wszComputerName
, cchComputerName
* sizeof(WCHAR
));
390 pPrinterString
+= cchComputerName
* sizeof(WCHAR
);
391 CopyMemory(pPrinterString
, pPrinter
->pwszPrinterName
, cbName
- sizeof(WCHAR
));
392 pPrinterString
+= cbName
- sizeof(WCHAR
);
393 CopyMemory(pPrinterString
, wszComma
, sizeof(WCHAR
));
394 pPrinterString
+= sizeof(WCHAR
);
395 CopyMemory(pPrinterString
, pPrinter
->pwszDescription
, cbComment
- sizeof(WCHAR
));
396 pPrinterString
+= cbComment
- sizeof(WCHAR
);
397 CopyMemory(pPrinterString
, wszComma
, sizeof(wszComma
));
398 pPrinterString
+= sizeof(wszComma
);
400 // Finally copy the structure and advance to the next one in the output buffer.
401 CopyMemory(pPrinterInfo
, &PrinterInfo1
, sizeof(PRINTER_INFO_1W
));
402 pPrinterInfo
+= sizeof(PRINTER_INFO_1W
);
405 dwErrorCode
= ERROR_SUCCESS
;
412 LocalEnumPrinters(DWORD Flags
, LPWSTR Name
, DWORD Level
, LPBYTE pPrinterEnum
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
416 // Do no sanity checks here. This is verified by localspl_apitest!
423 // Treat it as success if the caller queried no information and we don't need to return any.
424 dwErrorCode
= ERROR_SUCCESS
;
426 if (Flags
& PRINTER_ENUM_LOCAL
)
428 // The function behaves quite differently for each level.
431 dwErrorCode
= _LocalEnumPrintersLevel1(Flags
, Name
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
435 // TODO: Handle other levels.
436 // The caller supplied an invalid level.
437 dwErrorCode
= ERROR_INVALID_LEVEL
;
443 SetLastError(dwErrorCode
);
444 return (dwErrorCode
== ERROR_SUCCESS
);
448 LocalOpenPrinter(PWSTR lpPrinterName
, HANDLE
* phPrinter
, PPRINTER_DEFAULTSW pDefault
)
450 DWORD cchComputerName
;
451 DWORD cchPrinterName
;
454 PWSTR p
= lpPrinterName
;
455 PWSTR pwszPrinterName
= NULL
;
457 PLOCAL_HANDLE pHandle
;
458 PLOCAL_PRINTER pPrinter
;
459 PLOCAL_PRINTER_HANDLE pPrinterHandle
= NULL
;
460 WCHAR wszComputerName
[MAX_COMPUTERNAME_LENGTH
+ 1];
463 if (!lpPrinterName
|| !phPrinter
)
465 dwErrorCode
= ERROR_INVALID_PARAMETER
;
469 // Does lpPrinterName begin with two backslashes to indicate a server name?
470 if (lpPrinterName
[0] == L
'\\' && lpPrinterName
[1] == L
'\\')
472 // Skip these two backslashes.
475 // Look for the closing backslash.
476 p
= wcschr(lpPrinterName
, L
'\\');
479 // We didn't get a proper server name.
480 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
484 // Null-terminate the string here to enable comparison.
487 // Get the local computer name for comparison.
488 cchComputerName
= _countof(wszComputerName
);
489 if (!GetComputerNameW(wszComputerName
, &cchComputerName
))
491 dwErrorCode
= GetLastError();
492 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode
);
496 // Now compare this with the local computer name and reject if it doesn't match, because this print provider only supports local printers.
497 if (wcsicmp(lpPrinterName
, wszComputerName
) != 0)
499 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
503 // We have checked the server name and don't need it anymore.
504 lpPrinterName
= p
+ 1;
507 // Look for a comma. If it exists, it indicates the end of the printer name.
508 p
= wcschr(lpPrinterName
, L
',');
510 cchPrinterName
= p
- lpPrinterName
;
512 cchPrinterName
= wcslen(lpPrinterName
);
514 // No printer name and no comma? This is invalid!
515 if (!cchPrinterName
&& !p
)
517 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
521 // Do we have a printer name?
525 pwszPrinterName
= DllAllocSplMem((cchPrinterName
+ 1) * sizeof(WCHAR
));
526 CopyMemory(pwszPrinterName
, lpPrinterName
, cchPrinterName
* sizeof(WCHAR
));
527 pwszPrinterName
[cchPrinterName
] = 0;
529 // Retrieve the associated printer from the list.
530 pPrinter
= LookupElementSkiplist(&PrinterList
, &pwszPrinterName
, NULL
);
533 // The printer does not exist.
534 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
538 // Create a new printer handle.
539 pPrinterHandle
= DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE
));
540 pPrinterHandle
->pPrinter
= pPrinter
;
542 // Check if a datatype was given.
543 if (pDefault
&& pDefault
->pDatatype
)
545 // Use the datatype if it's valid.
546 if (!FindDatatype(pPrinter
->pPrintProcessor
, pDefault
->pDatatype
))
548 dwErrorCode
= ERROR_INVALID_DATATYPE
;
552 pPrinterHandle
->pwszDatatype
= AllocSplStr(pDefault
->pDatatype
);
556 // Use the default datatype.
557 pPrinterHandle
->pwszDatatype
= AllocSplStr(pPrinter
->pwszDefaultDatatype
);
560 // Check if a DevMode was given, otherwise use the default.
561 if (pDefault
&& pDefault
->pDevMode
)
562 CopyMemory(&pPrinterHandle
->DevMode
, pDefault
->pDevMode
, sizeof(DEVMODEW
));
564 CopyMemory(&pPrinterHandle
->DevMode
, &pPrinter
->DefaultDevMode
, sizeof(DEVMODEW
));
566 // Did we have a comma? Then the user may want a handle to an existing job instead of creating a new job.
578 // The "Job " string has to follow now.
579 if (wcscmp(p
, L
"Job ") != 0)
581 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
585 // Skip the "Job " string.
586 p
+= sizeof("Job ") - 1;
588 // Skip even more whitespace.
592 // Finally extract the desired Job ID.
593 dwJobID
= wcstoul(p
, NULL
, 10);
594 if (!IS_VALID_JOB_ID(dwJobID
))
596 // The user supplied an invalid Job ID.
597 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
601 // Look for this job in the Global Job List.
602 pJob
= LookupElementSkiplist(&GlobalJobList
, &dwJobID
, NULL
);
603 if (!pJob
|| pJob
->pPrinter
!= pPrinter
)
605 // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
606 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
610 pPrinterHandle
->pStartedJob
= pJob
;
613 // Create a new handle that references a printer.
614 pHandle
= DllAllocSplMem(sizeof(LOCAL_HANDLE
));
615 pHandle
->HandleType
= Printer
;
616 pHandle
->pSpecificHandle
= pPrinterHandle
;
620 // No printer name, but we have a comma!
621 // This may be a request to a XcvMonitor or XcvPort handle.
631 // Check if this is a request to a XcvMonitor.
632 if (wcscmp(p
, L
"XcvMonitor ") == 0)
634 // Skip the "XcvMonitor " string.
635 p
+= sizeof("XcvMonitor ") - 1;
637 ///////////// TODO /////////////////////
638 pHandle
= DllAllocSplMem(sizeof(LOCAL_HANDLE
));
639 pHandle
->HandleType
= Monitor
;
640 //pHandle->pSpecificHandle = pMonitorHandle;
642 else if (wcscmp(p
, L
"XcvPort ") == 0)
644 // Skip the "XcvPort " string.
645 p
+= sizeof("XcvPort ") - 1;
647 //////////// TODO //////////////////////
648 pHandle
= DllAllocSplMem(sizeof(LOCAL_HANDLE
));
649 pHandle
->HandleType
= Port
;
650 //pHandle->pSpecificHandle = pPortHandle;
654 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
659 *phPrinter
= (HANDLE
)pHandle
;
660 dwErrorCode
= ERROR_SUCCESS
;
662 // Don't let the cleanup routines free this.
663 pPrinterHandle
= NULL
;
664 pwszPrinterName
= NULL
;
669 if (pPrinterHandle
->pwszDatatype
)
670 DllFreeSplStr(pPrinterHandle
->pwszDatatype
);
672 DllFreeSplMem(pPrinterHandle
);
676 DllFreeSplMem(pwszPrinterName
);
678 SetLastError(dwErrorCode
);
679 return (dwErrorCode
== ERROR_SUCCESS
);
683 LocalStartDocPrinter(HANDLE hPrinter
, DWORD Level
, LPBYTE pDocInfo
)
686 DWORD dwReturnValue
= 0;
687 PDOC_INFO_1W pDocumentInfo1
;
688 PLOCAL_HANDLE pHandle
;
690 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
695 dwErrorCode
= ERROR_INVALID_PARAMETER
;
701 dwErrorCode
= ERROR_INVALID_HANDLE
;
705 // Check if this is a printer handle.
706 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
707 if (pHandle
->HandleType
!= Printer
)
709 dwErrorCode
= ERROR_INVALID_HANDLE
;
713 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
715 // Check if this is the right document information level.
718 dwErrorCode
= ERROR_INVALID_LEVEL
;
722 pDocumentInfo1
= (PDOC_INFO_1W
)pDocInfo
;
725 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
726 pJob
->pPrinter
= pPrinterHandle
->pPrinter
;
727 pJob
->dwPriority
= DEF_PRIORITY
;
729 // Check if a datatype was given.
730 if (pDocumentInfo1
->pDatatype
)
732 // Use the datatype if it's valid.
733 if (!FindDatatype(pJob
->pPrintProcessor
, pDocumentInfo1
->pDatatype
))
735 dwErrorCode
= ERROR_INVALID_DATATYPE
;
739 pJob
->pwszDatatype
= AllocSplStr(pDocumentInfo1
->pDatatype
);
743 // Use the printer handle datatype.
744 pJob
->pwszDatatype
= AllocSplStr(pPrinterHandle
->pwszDatatype
);
747 // Copy over printer defaults.
748 CopyMemory(&pJob
->DevMode
, &pPrinterHandle
->DevMode
, sizeof(DEVMODEW
));
750 // Copy over supplied information.
751 if (pDocumentInfo1
->pDocName
)
752 pJob
->pwszDocumentName
= AllocSplStr(pDocumentInfo1
->pDocName
);
754 if (pDocumentInfo1
->pOutputFile
)
755 pJob
->pwszOutputFile
= AllocSplStr(pDocumentInfo1
->pOutputFile
);
757 // Get an ID for the new job.
758 if (!GetNextJobID(&pJob
->dwJobID
))
760 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
764 // Add the job to the Global Job List.
765 if (!InsertElementSkiplist(&GlobalJobList
, pJob
))
767 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
768 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob
->dwJobID
);
772 // Add the job at the end of the Printer's Job List.
773 // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
774 if (!InsertTailElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
))
776 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
777 ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob
->dwJobID
);
781 pPrinterHandle
->pStartedJob
= pJob
;
782 dwErrorCode
= ERROR_SUCCESS
;
783 dwReturnValue
= pJob
->dwJobID
;
786 SetLastError(dwErrorCode
);
787 return dwReturnValue
;
791 LocalStartPagePrinter(HANDLE hPrinter
)
793 ///////////// TODO /////////////////////
798 LocalWritePrinter(HANDLE hPrinter
, LPVOID pBuf
, DWORD cbBuf
, LPDWORD pcWritten
)
800 ///////////// TODO /////////////////////
805 LocalEndPagePrinter(HANDLE hPrinter
)
807 ///////////// TODO /////////////////////
812 LocalEndDocPrinter(HANDLE hPrinter
)
814 ///////////// TODO /////////////////////
819 LocalClosePrinter(HANDLE hPrinter
)
821 PLOCAL_HANDLE pHandle
;
825 SetLastError(ERROR_INVALID_HANDLE
);
829 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
831 ///////////// TODO /////////////////////
832 /// Check the handle type, do thoroughful checks on all data fields and clean them.
833 ////////////////////////////////////////
835 DllFreeSplMem(pHandle
);