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 dwErrorCode
= (DWORD
)RegOpenKeyExW(HKEY_LOCAL_MACHINE
, wszPrintersKey
, 0, KEY_READ
, &hKey
);
58 if (dwErrorCode
!= ERROR_SUCCESS
)
60 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode
);
64 // Get the number of subkeys.
65 dwErrorCode
= (DWORD
)RegQueryInfoKeyW(hKey
, NULL
, NULL
, NULL
, &dwSubKeys
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
66 if (dwErrorCode
!= ERROR_SUCCESS
)
68 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode
);
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 dwErrorCode
= (DWORD
)RegEnumKeyExW(hKey
, i
, wszPrinterName
, &cchPrinterName
, NULL
, NULL
, NULL
, NULL
);
109 if (dwErrorCode
== ERROR_MORE_DATA
)
111 // This printer name exceeds the maximum length and is invalid.
114 else if (dwErrorCode
!= ERROR_SUCCESS
)
116 ERR("RegEnumKeyExW failed for iteration %lu with status %lu!\n", i
, dwErrorCode
);
120 // Open this Printer's registry key.
121 dwErrorCode
= (DWORD
)RegOpenKeyExW(hKey
, wszPrinterName
, 0, KEY_READ
, &hSubKey
);
122 if (dwErrorCode
!= ERROR_SUCCESS
)
124 ERR("RegOpenKeyExW failed for Printer \"%S\" with status %lu!\n", wszPrinterName
, dwErrorCode
);
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 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
146 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
150 pPrinter
->pwszPrinterName
= AllocSplStr(wszPrinterName
);
151 pPrinter
->pPrintProcessor
= pPrintProcessor
;
152 InitializePrinterJobList(pPrinter
);
154 // Get the printer driver.
155 pPrinter
->pwszPrinterDriver
= AllocAndRegQueryWSZ(hSubKey
, L
"Printer Driver");
156 if (!pPrinter
->pwszPrinterDriver
)
159 // Get the description.
160 pPrinter
->pwszDescription
= AllocAndRegQueryWSZ(hSubKey
, L
"Description");
161 if (!pPrinter
->pwszDescription
)
164 // Get the default datatype.
165 pPrinter
->pwszDefaultDatatype
= AllocAndRegQueryWSZ(hSubKey
, L
"Datatype");
166 if (!pPrinter
->pwszDefaultDatatype
)
169 // Verify that it's valid.
170 if (!FindDatatype(pPrintProcessor
, pPrinter
->pwszDefaultDatatype
))
172 ERR("Invalid default datatype \"%S\" for Printer \"%S\"!\n", pPrinter
->pwszDefaultDatatype
, wszPrinterName
);
176 // Get the default DevMode.
177 cbData
= sizeof(DEVMODEW
);
178 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Default DevMode", NULL
, NULL
, (PBYTE
)&pPrinter
->DefaultDevMode
, &cbData
);
179 if (dwErrorCode
!= ERROR_SUCCESS
|| cbData
!= sizeof(DEVMODEW
))
181 ERR("Couldn't query a valid DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName
, dwErrorCode
, cbData
);
185 // Get the Attributes.
186 cbData
= sizeof(DWORD
);
187 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Attributes", NULL
, NULL
, (PBYTE
)&pPrinter
->dwAttributes
, &cbData
);
188 if (dwErrorCode
!= ERROR_SUCCESS
)
190 ERR("Couldn't query Attributes for Printer \"%S\", status is %lu!\n", wszPrinterName
, dwErrorCode
);
195 cbData
= sizeof(DWORD
);
196 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Status", NULL
, NULL
, (PBYTE
)&pPrinter
->dwStatus
, &cbData
);
197 if (dwErrorCode
!= ERROR_SUCCESS
)
199 ERR("Couldn't query Status for Printer \"%S\", status is %lu!\n", wszPrinterName
, dwErrorCode
);
203 // Add this printer to the printer list.
204 if (!InsertElementSkiplist(&PrinterList
, pPrinter
))
206 ERR("InsertElementSkiplist failed for Printer \"%S\"!\n", pPrinter
->pwszPrinterName
);
210 // Don't let the cleanup routines free this.
214 dwErrorCode
= ERROR_SUCCESS
;
219 RegCloseKey(hSubKey
);
223 if (pPrinter
->pwszDefaultDatatype
)
224 DllFreeSplStr(pPrinter
->pwszDefaultDatatype
);
226 if (pPrinter
->pwszDescription
)
227 DllFreeSplStr(pPrinter
->pwszDescription
);
229 if (pPrinter
->pwszPrinterDriver
)
230 DllFreeSplStr(pPrinter
->pwszPrinterDriver
);
232 if (pPrinter
->pwszPrinterName
)
233 DllFreeSplStr(pPrinter
->pwszPrinterName
);
235 DllFreeSplMem(pPrinter
);
238 if (pwszPrintProcessor
)
239 DllFreeSplStr(pwszPrintProcessor
);
245 SetLastError(dwErrorCode
);
246 return (dwErrorCode
== ERROR_SUCCESS
);
251 _LocalEnumPrintersLevel1(DWORD Flags
, LPWSTR Name
, LPBYTE pPrinterEnum
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
253 const WCHAR wszComma
[] = L
",";
258 DWORD cchComputerName
= 0;
262 PBYTE pPrinterString
;
263 PSKIPLIST_NODE pNode
;
264 PLOCAL_PRINTER pPrinter
;
265 PRINTER_INFO_1W PrinterInfo1
;
266 WCHAR wszComputerName
[2 + MAX_COMPUTERNAME_LENGTH
+ 1 + 1];
268 DWORD dwOffsets
[] = {
269 FIELD_OFFSET(PRINTER_INFO_1W
, pName
),
270 FIELD_OFFSET(PRINTER_INFO_1W
, pDescription
),
271 FIELD_OFFSET(PRINTER_INFO_1W
, pComment
),
275 if (Flags
& PRINTER_ENUM_NAME
)
279 // The user supplied a Computer Name (with leading double backslashes) or Print Provider Name.
280 // Only process what's directed at us and dismiss every other request with ERROR_INVALID_NAME.
281 if (Name
[0] == L
'\\' && Name
[1] == L
'\\')
283 // Prepend slashes to the computer name.
284 wszComputerName
[0] = L
'\\';
285 wszComputerName
[1] = L
'\\';
287 // Get the local computer name for comparison.
288 cchComputerName
= MAX_COMPUTERNAME_LENGTH
+ 1;
289 if (!GetComputerNameW(&wszComputerName
[2], &cchComputerName
))
291 dwErrorCode
= GetLastError();
292 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode
);
296 // Add the leading slashes to the total length.
297 cchComputerName
+= 2;
299 // Now compare this with the local computer name and reject if it doesn't match.
300 if (wcsicmp(&Name
[2], &wszComputerName
[2]) != 0)
302 dwErrorCode
= ERROR_INVALID_NAME
;
306 // Add a trailing backslash to wszComputerName, which will later be prepended in front of the printer names.
307 wszComputerName
[cchComputerName
++] = L
'\\';
308 wszComputerName
[cchComputerName
] = 0;
310 else if (wcsicmp(Name
, wszPrintProviderInfo
[0]) != 0)
312 // The user supplied a name that cannot be processed by the local print provider.
313 dwErrorCode
= ERROR_INVALID_NAME
;
319 // The caller wants information about this Print Provider.
320 // spoolss packs this into an array of information about all Print Providers.
321 *pcbNeeded
= sizeof(PRINTER_INFO_1W
);
323 for (i
= 0; i
< 3; i
++)
324 *pcbNeeded
+= (wcslen(wszPrintProviderInfo
[i
]) + 1) * sizeof(WCHAR
);
326 // Check if the supplied buffer is large enough.
327 if (cbBuf
< *pcbNeeded
)
329 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
333 // Copy over the print processor information.
334 ((PPRINTER_INFO_1W
)pPrinterEnum
)->Flags
= 0;
335 PackStrings(wszPrintProviderInfo
, pPrinterEnum
, dwOffsets
, &pPrinterEnum
[*pcbNeeded
]);
337 dwErrorCode
= ERROR_SUCCESS
;
342 // Count the required buffer size and the number of printers.
345 for (pNode
= PrinterList
.Head
.Next
[0]; pNode
; pNode
= pNode
->Next
[0])
347 pPrinter
= (PLOCAL_PRINTER
)pNode
->Element
;
349 // This looks wrong, but is totally right. PRINTER_INFO_1W has three members pName, pComment and pDescription.
350 // But pComment equals the "Description" registry value while pDescription is concatenated out of pName and pComment.
351 // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
352 cbName
= (wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
353 cbComment
= (wcslen(pPrinter
->pwszDescription
) + 1) * sizeof(WCHAR
);
354 cbDescription
= cchComputerName
* sizeof(WCHAR
) + cbName
+ cbComment
+ sizeof(WCHAR
);
356 *pcbNeeded
+= sizeof(PRINTER_INFO_1W
) + cchComputerName
* sizeof(WCHAR
) + cbName
+ cbComment
+ cbDescription
;
360 // Check if the supplied buffer is large enough.
361 if (cbBuf
< *pcbNeeded
)
363 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
367 // Put the strings right after the last PRINTER_INFO_1W structure.
368 // Due to all the required string processing, we can't just use PackStrings here :(
369 pPrinterInfo
= pPrinterEnum
;
370 pPrinterString
= pPrinterEnum
+ i
* sizeof(PRINTER_INFO_1W
);
372 // Copy over the printer information.
373 for (pNode
= PrinterList
.Head
.Next
[0]; pNode
; pNode
= pNode
->Next
[0])
375 pPrinter
= (PLOCAL_PRINTER
)pNode
->Element
;
377 // FIXME: As for now, the Flags member returns no information.
378 PrinterInfo1
.Flags
= 0;
380 // Copy the printer name.
381 PrinterInfo1
.pName
= (PWSTR
)pPrinterString
;
382 CopyMemory(pPrinterString
, wszComputerName
, cchComputerName
* sizeof(WCHAR
));
383 pPrinterString
+= cchComputerName
* sizeof(WCHAR
);
384 cbName
= (wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
385 CopyMemory(pPrinterString
, pPrinter
->pwszPrinterName
, cbName
);
386 pPrinterString
+= cbName
;
388 // Copy the printer comment (equals the "Description" registry value).
389 PrinterInfo1
.pComment
= (PWSTR
)pPrinterString
;
390 cbComment
= (wcslen(pPrinter
->pwszDescription
) + 1) * sizeof(WCHAR
);
391 CopyMemory(pPrinterString
, pPrinter
->pwszDescription
, cbComment
);
392 pPrinterString
+= cbComment
;
394 // Copy the description, which for PRINTER_INFO_1W has the form "Name,Comment,"
395 PrinterInfo1
.pDescription
= (PWSTR
)pPrinterString
;
396 CopyMemory(pPrinterString
, wszComputerName
, cchComputerName
* sizeof(WCHAR
));
397 pPrinterString
+= cchComputerName
* sizeof(WCHAR
);
398 CopyMemory(pPrinterString
, pPrinter
->pwszPrinterName
, cbName
- sizeof(WCHAR
));
399 pPrinterString
+= cbName
- sizeof(WCHAR
);
400 CopyMemory(pPrinterString
, wszComma
, sizeof(WCHAR
));
401 pPrinterString
+= sizeof(WCHAR
);
402 CopyMemory(pPrinterString
, pPrinter
->pwszDescription
, cbComment
- sizeof(WCHAR
));
403 pPrinterString
+= cbComment
- sizeof(WCHAR
);
404 CopyMemory(pPrinterString
, wszComma
, sizeof(wszComma
));
405 pPrinterString
+= sizeof(wszComma
);
407 // Finally copy the structure and advance to the next one in the output buffer.
408 CopyMemory(pPrinterInfo
, &PrinterInfo1
, sizeof(PRINTER_INFO_1W
));
409 pPrinterInfo
+= sizeof(PRINTER_INFO_1W
);
413 dwErrorCode
= ERROR_SUCCESS
;
420 LocalEnumPrinters(DWORD Flags
, LPWSTR Name
, DWORD Level
, LPBYTE pPrinterEnum
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
424 // Do no sanity checks here. This is verified by localspl_apitest!
431 // Treat it as success if the caller queried no information and we don't need to return any.
432 dwErrorCode
= ERROR_SUCCESS
;
434 if (Flags
& PRINTER_ENUM_LOCAL
)
436 // The function behaves quite differently for each level.
439 dwErrorCode
= _LocalEnumPrintersLevel1(Flags
, Name
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
443 // TODO: Handle other levels.
444 // The caller supplied an invalid level.
445 dwErrorCode
= ERROR_INVALID_LEVEL
;
451 SetLastError(dwErrorCode
);
452 return (dwErrorCode
== ERROR_SUCCESS
);
456 LocalOpenPrinter(PWSTR lpPrinterName
, HANDLE
* phPrinter
, PPRINTER_DEFAULTSW pDefault
)
458 DWORD cchComputerName
;
459 DWORD cchPrinterName
;
462 PWSTR p
= lpPrinterName
;
463 PWSTR pwszPrinterName
= NULL
;
465 PLOCAL_HANDLE pHandle
;
466 PLOCAL_PRINTER pPrinter
;
467 PLOCAL_PRINTER_HANDLE pPrinterHandle
= NULL
;
468 WCHAR wszComputerName
[MAX_COMPUTERNAME_LENGTH
+ 1];
471 if (!lpPrinterName
|| !phPrinter
)
473 dwErrorCode
= ERROR_INVALID_PARAMETER
;
477 // Does lpPrinterName begin with two backslashes to indicate a server name?
478 if (lpPrinterName
[0] == L
'\\' && lpPrinterName
[1] == L
'\\')
480 // Skip these two backslashes.
483 // Look for the closing backslash.
484 p
= wcschr(lpPrinterName
, L
'\\');
487 // We didn't get a proper server name.
488 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
492 // Null-terminate the string here to enable comparison.
495 // Get the local computer name for comparison.
496 cchComputerName
= _countof(wszComputerName
);
497 if (!GetComputerNameW(wszComputerName
, &cchComputerName
))
499 dwErrorCode
= GetLastError();
500 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode
);
504 // Now compare this with the local computer name and reject if it doesn't match, because this print provider only supports local printers.
505 if (wcsicmp(lpPrinterName
, wszComputerName
) != 0)
507 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
511 // We have checked the server name and don't need it anymore.
512 lpPrinterName
= p
+ 1;
515 // Look for a comma. If it exists, it indicates the end of the printer name.
516 p
= wcschr(lpPrinterName
, L
',');
518 cchPrinterName
= p
- lpPrinterName
;
520 cchPrinterName
= wcslen(lpPrinterName
);
522 // No printer name and no comma? This is invalid!
523 if (!cchPrinterName
&& !p
)
525 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
529 // Do we have a printer name?
533 pwszPrinterName
= DllAllocSplMem((cchPrinterName
+ 1) * sizeof(WCHAR
));
534 CopyMemory(pwszPrinterName
, lpPrinterName
, cchPrinterName
* sizeof(WCHAR
));
535 pwszPrinterName
[cchPrinterName
] = 0;
537 // Retrieve the associated printer from the list.
538 pPrinter
= LookupElementSkiplist(&PrinterList
, &pwszPrinterName
, NULL
);
541 // The printer does not exist.
542 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
546 // Create a new printer handle.
547 pPrinterHandle
= DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE
));
548 pPrinterHandle
->pPrinter
= pPrinter
;
550 // Check if a datatype was given.
551 if (pDefault
&& pDefault
->pDatatype
)
553 // Use the datatype if it's valid.
554 if (!FindDatatype(pPrinter
->pPrintProcessor
, pDefault
->pDatatype
))
556 dwErrorCode
= ERROR_INVALID_DATATYPE
;
560 pPrinterHandle
->pwszDatatype
= AllocSplStr(pDefault
->pDatatype
);
564 // Use the default datatype.
565 pPrinterHandle
->pwszDatatype
= AllocSplStr(pPrinter
->pwszDefaultDatatype
);
568 // Check if a DevMode was given, otherwise use the default.
569 if (pDefault
&& pDefault
->pDevMode
)
570 CopyMemory(&pPrinterHandle
->DevMode
, pDefault
->pDevMode
, sizeof(DEVMODEW
));
572 CopyMemory(&pPrinterHandle
->DevMode
, &pPrinter
->DefaultDevMode
, sizeof(DEVMODEW
));
574 // Did we have a comma? Then the user may want a handle to an existing job instead of creating a new job.
586 // The "Job " string has to follow now.
587 if (wcscmp(p
, L
"Job ") != 0)
589 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
593 // Skip the "Job " string.
594 p
+= sizeof("Job ") - 1;
596 // Skip even more whitespace.
600 // Finally extract the desired Job ID.
601 dwJobID
= wcstoul(p
, NULL
, 10);
602 if (!IS_VALID_JOB_ID(dwJobID
))
604 // The user supplied an invalid Job ID.
605 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
609 // Look for this job in the Global Job List.
610 pJob
= LookupElementSkiplist(&GlobalJobList
, &dwJobID
, NULL
);
611 if (!pJob
|| pJob
->pPrinter
!= pPrinter
)
613 // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
614 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
618 pPrinterHandle
->pStartedJob
= pJob
;
621 // Create a new handle that references a printer.
622 pHandle
= DllAllocSplMem(sizeof(LOCAL_HANDLE
));
623 pHandle
->HandleType
= Printer
;
624 pHandle
->pSpecificHandle
= pPrinterHandle
;
628 // No printer name, but we have a comma!
629 // This may be a request to a XcvMonitor or XcvPort handle.
639 // Check if this is a request to a XcvMonitor.
640 if (wcscmp(p
, L
"XcvMonitor ") == 0)
642 // Skip the "XcvMonitor " string.
643 p
+= sizeof("XcvMonitor ") - 1;
645 ///////////// TODO /////////////////////
646 pHandle
= DllAllocSplMem(sizeof(LOCAL_HANDLE
));
647 pHandle
->HandleType
= Monitor
;
648 //pHandle->pSpecificHandle = pMonitorHandle;
650 else if (wcscmp(p
, L
"XcvPort ") == 0)
652 // Skip the "XcvPort " string.
653 p
+= sizeof("XcvPort ") - 1;
655 //////////// TODO //////////////////////
656 pHandle
= DllAllocSplMem(sizeof(LOCAL_HANDLE
));
657 pHandle
->HandleType
= Port
;
658 //pHandle->pSpecificHandle = pPortHandle;
662 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
667 *phPrinter
= (HANDLE
)pHandle
;
668 dwErrorCode
= ERROR_SUCCESS
;
670 // Don't let the cleanup routines free this.
671 pPrinterHandle
= NULL
;
672 pwszPrinterName
= NULL
;
677 if (pPrinterHandle
->pwszDatatype
)
678 DllFreeSplStr(pPrinterHandle
->pwszDatatype
);
680 DllFreeSplMem(pPrinterHandle
);
684 DllFreeSplMem(pwszPrinterName
);
686 SetLastError(dwErrorCode
);
687 return (dwErrorCode
== ERROR_SUCCESS
);
691 LocalStartDocPrinter(HANDLE hPrinter
, DWORD Level
, LPBYTE pDocInfo
)
694 DWORD dwReturnValue
= 0;
695 PDOC_INFO_1W pDocumentInfo1
;
696 PLOCAL_HANDLE pHandle
;
698 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
703 dwErrorCode
= ERROR_INVALID_PARAMETER
;
709 dwErrorCode
= ERROR_INVALID_HANDLE
;
713 // Check if this is a printer handle.
714 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
715 if (pHandle
->HandleType
!= Printer
)
717 dwErrorCode
= ERROR_INVALID_HANDLE
;
721 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
723 // Check if this is the right document information level.
726 dwErrorCode
= ERROR_INVALID_LEVEL
;
730 pDocumentInfo1
= (PDOC_INFO_1W
)pDocInfo
;
733 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
734 pJob
->pPrinter
= pPrinterHandle
->pPrinter
;
735 pJob
->dwPriority
= DEF_PRIORITY
;
737 // Check if a datatype was given.
738 if (pDocumentInfo1
->pDatatype
)
740 // Use the datatype if it's valid.
741 if (!FindDatatype(pJob
->pPrintProcessor
, pDocumentInfo1
->pDatatype
))
743 dwErrorCode
= ERROR_INVALID_DATATYPE
;
747 pJob
->pwszDatatype
= AllocSplStr(pDocumentInfo1
->pDatatype
);
751 // Use the printer handle datatype.
752 pJob
->pwszDatatype
= AllocSplStr(pPrinterHandle
->pwszDatatype
);
755 // Copy over printer defaults.
756 CopyMemory(&pJob
->DevMode
, &pPrinterHandle
->DevMode
, sizeof(DEVMODEW
));
758 // Copy over supplied information.
759 if (pDocumentInfo1
->pDocName
)
760 pJob
->pwszDocumentName
= AllocSplStr(pDocumentInfo1
->pDocName
);
762 if (pDocumentInfo1
->pOutputFile
)
763 pJob
->pwszOutputFile
= AllocSplStr(pDocumentInfo1
->pOutputFile
);
765 // Get an ID for the new job.
766 if (!GetNextJobID(&pJob
->dwJobID
))
768 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
772 // Add the job to the Global Job List.
773 if (!InsertElementSkiplist(&GlobalJobList
, pJob
))
775 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
776 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob
->dwJobID
);
780 // Add the job at the end of the Printer's Job List.
781 // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
782 if (!InsertTailElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
))
784 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
785 ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob
->dwJobID
);
789 pPrinterHandle
->pStartedJob
= pJob
;
790 dwErrorCode
= ERROR_SUCCESS
;
791 dwReturnValue
= pJob
->dwJobID
;
794 SetLastError(dwErrorCode
);
795 return dwReturnValue
;
799 LocalStartPagePrinter(HANDLE hPrinter
)
801 ///////////// TODO /////////////////////
806 LocalWritePrinter(HANDLE hPrinter
, LPVOID pBuf
, DWORD cbBuf
, LPDWORD pcWritten
)
808 ///////////// TODO /////////////////////
813 LocalEndPagePrinter(HANDLE hPrinter
)
815 ///////////// TODO /////////////////////
820 LocalEndDocPrinter(HANDLE hPrinter
)
822 ///////////// TODO /////////////////////
827 LocalClosePrinter(HANDLE hPrinter
)
829 PLOCAL_HANDLE pHandle
;
833 SetLastError(ERROR_INVALID_HANDLE
);
837 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
839 ///////////// TODO /////////////////////
840 /// Check the handle type, do thoroughful checks on all data fields and clean them.
841 ////////////////////////////////////////
843 DllFreeSplMem(pHandle
);