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
->pDefaultDevMode
)
85 DllFreeSplMem(pPrinter
->pDefaultDevMode
);
87 if (pPrinter
->pwszDefaultDatatype
)
88 DllFreeSplStr(pPrinter
->pwszDefaultDatatype
);
90 if (pPrinter
->pwszDescription
)
91 DllFreeSplStr(pPrinter
->pwszDescription
);
93 if (pPrinter
->pwszPrinterDriver
)
94 DllFreeSplStr(pPrinter
->pwszPrinterDriver
);
96 if (pPrinter
->pwszPrinterName
)
97 DllFreeSplStr(pPrinter
->pwszPrinterName
);
99 DllFreeSplMem(pPrinter
);
103 if (pwszPrintProcessor
)
105 DllFreeSplStr(pwszPrintProcessor
);
106 pwszPrintProcessor
= NULL
;
109 // Get the name of this printer.
110 cchPrinterName
= _countof(wszPrinterName
);
111 dwErrorCode
= (DWORD
)RegEnumKeyExW(hKey
, i
, wszPrinterName
, &cchPrinterName
, NULL
, NULL
, NULL
, NULL
);
112 if (dwErrorCode
== ERROR_MORE_DATA
)
114 // This printer name exceeds the maximum length and is invalid.
117 else if (dwErrorCode
!= ERROR_SUCCESS
)
119 ERR("RegEnumKeyExW failed for iteration %lu with status %lu!\n", i
, dwErrorCode
);
123 // Open this Printer's registry key.
124 dwErrorCode
= (DWORD
)RegOpenKeyExW(hKey
, wszPrinterName
, 0, KEY_READ
, &hSubKey
);
125 if (dwErrorCode
!= ERROR_SUCCESS
)
127 ERR("RegOpenKeyExW failed for Printer \"%S\" with status %lu!\n", wszPrinterName
, dwErrorCode
);
131 // Get the Print Processor.
132 pwszPrintProcessor
= AllocAndRegQueryWSZ(hSubKey
, L
"Print Processor");
133 if (!pwszPrintProcessor
)
136 // Try to find it in the Print Processor List.
137 pPrintProcessor
= FindPrintProcessor(pwszPrintProcessor
);
138 if (!pPrintProcessor
)
140 ERR("Invalid Print Processor \"%S\" for Printer \"%S\"!\n", pwszPrintProcessor
, wszPrinterName
);
144 // Create a new LOCAL_PRINTER structure for it.
145 pPrinter
= DllAllocSplMem(sizeof(LOCAL_PRINTER
));
148 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
149 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
153 pPrinter
->pwszPrinterName
= AllocSplStr(wszPrinterName
);
154 pPrinter
->pPrintProcessor
= pPrintProcessor
;
155 InitializePrinterJobList(pPrinter
);
157 // Get the printer driver.
158 pPrinter
->pwszPrinterDriver
= AllocAndRegQueryWSZ(hSubKey
, L
"Printer Driver");
159 if (!pPrinter
->pwszPrinterDriver
)
162 // Get the description.
163 pPrinter
->pwszDescription
= AllocAndRegQueryWSZ(hSubKey
, L
"Description");
164 if (!pPrinter
->pwszDescription
)
167 // Get the default datatype.
168 pPrinter
->pwszDefaultDatatype
= AllocAndRegQueryWSZ(hSubKey
, L
"Datatype");
169 if (!pPrinter
->pwszDefaultDatatype
)
172 // Verify that it's valid.
173 if (!FindDatatype(pPrintProcessor
, pPrinter
->pwszDefaultDatatype
))
175 ERR("Invalid default datatype \"%S\" for Printer \"%S\"!\n", pPrinter
->pwszDefaultDatatype
, wszPrinterName
);
179 // Determine the size of the DevMode.
180 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Default DevMode", NULL
, NULL
, NULL
, &cbData
);
181 if (dwErrorCode
!= ERROR_SUCCESS
)
183 ERR("Couldn't query the size of the DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName
, dwErrorCode
, cbData
);
187 // Allocate enough memory for the DevMode.
188 pPrinter
->pDefaultDevMode
= DllAllocSplMem(cbData
);
189 if (!pPrinter
->pDefaultDevMode
)
191 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
192 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
196 // Get the default DevMode.
197 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Default DevMode", NULL
, NULL
, (PBYTE
)pPrinter
->pDefaultDevMode
, &cbData
);
198 if (dwErrorCode
!= ERROR_SUCCESS
)
200 ERR("Couldn't query a DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName
, dwErrorCode
, cbData
);
204 // Get the Attributes.
205 cbData
= sizeof(DWORD
);
206 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Attributes", NULL
, NULL
, (PBYTE
)&pPrinter
->dwAttributes
, &cbData
);
207 if (dwErrorCode
!= ERROR_SUCCESS
)
209 ERR("Couldn't query Attributes for Printer \"%S\", status is %lu!\n", wszPrinterName
, dwErrorCode
);
214 cbData
= sizeof(DWORD
);
215 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Status", NULL
, NULL
, (PBYTE
)&pPrinter
->dwStatus
, &cbData
);
216 if (dwErrorCode
!= ERROR_SUCCESS
)
218 ERR("Couldn't query Status for Printer \"%S\", status is %lu!\n", wszPrinterName
, dwErrorCode
);
222 // Add this printer to the printer list.
223 if (!InsertElementSkiplist(&PrinterList
, pPrinter
))
225 ERR("InsertElementSkiplist failed for Printer \"%S\"!\n", pPrinter
->pwszPrinterName
);
229 // Don't let the cleanup routines free this.
233 dwErrorCode
= ERROR_SUCCESS
;
238 RegCloseKey(hSubKey
);
242 if (pPrinter
->pDefaultDevMode
)
243 DllFreeSplMem(pPrinter
->pDefaultDevMode
);
245 if (pPrinter
->pwszDefaultDatatype
)
246 DllFreeSplStr(pPrinter
->pwszDefaultDatatype
);
248 if (pPrinter
->pwszDescription
)
249 DllFreeSplStr(pPrinter
->pwszDescription
);
251 if (pPrinter
->pwszPrinterDriver
)
252 DllFreeSplStr(pPrinter
->pwszPrinterDriver
);
254 if (pPrinter
->pwszPrinterName
)
255 DllFreeSplStr(pPrinter
->pwszPrinterName
);
257 DllFreeSplMem(pPrinter
);
260 if (pwszPrintProcessor
)
261 DllFreeSplStr(pwszPrintProcessor
);
267 SetLastError(dwErrorCode
);
268 return (dwErrorCode
== ERROR_SUCCESS
);
273 _LocalEnumPrintersLevel1(DWORD Flags
, LPWSTR Name
, LPBYTE pPrinterEnum
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
275 const WCHAR wszComma
[] = L
",";
280 DWORD cchComputerName
= 0;
284 PBYTE pPrinterString
;
285 PSKIPLIST_NODE pNode
;
286 PLOCAL_PRINTER pPrinter
;
287 PRINTER_INFO_1W PrinterInfo1
;
288 WCHAR wszComputerName
[2 + MAX_COMPUTERNAME_LENGTH
+ 1 + 1];
290 DWORD dwOffsets
[] = {
291 FIELD_OFFSET(PRINTER_INFO_1W
, pName
),
292 FIELD_OFFSET(PRINTER_INFO_1W
, pDescription
),
293 FIELD_OFFSET(PRINTER_INFO_1W
, pComment
),
297 if (Flags
& PRINTER_ENUM_NAME
)
301 // The user supplied a Computer Name (with leading double backslashes) or Print Provider Name.
302 // Only process what's directed at us and dismiss every other request with ERROR_INVALID_NAME.
303 if (Name
[0] == L
'\\' && Name
[1] == L
'\\')
305 // Prepend slashes to the computer name.
306 wszComputerName
[0] = L
'\\';
307 wszComputerName
[1] = L
'\\';
309 // Get the local computer name for comparison.
310 cchComputerName
= MAX_COMPUTERNAME_LENGTH
+ 1;
311 if (!GetComputerNameW(&wszComputerName
[2], &cchComputerName
))
313 dwErrorCode
= GetLastError();
314 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode
);
318 // Add the leading slashes to the total length.
319 cchComputerName
+= 2;
321 // Now compare this with the local computer name and reject if it doesn't match.
322 if (wcsicmp(&Name
[2], &wszComputerName
[2]) != 0)
324 dwErrorCode
= ERROR_INVALID_NAME
;
328 // Add a trailing backslash to wszComputerName, which will later be prepended in front of the printer names.
329 wszComputerName
[cchComputerName
++] = L
'\\';
330 wszComputerName
[cchComputerName
] = 0;
332 else if (wcsicmp(Name
, wszPrintProviderInfo
[0]) != 0)
334 // The user supplied a name that cannot be processed by the local print provider.
335 dwErrorCode
= ERROR_INVALID_NAME
;
341 // The caller wants information about this Print Provider.
342 // spoolss packs this into an array of information about all Print Providers.
343 *pcbNeeded
= sizeof(PRINTER_INFO_1W
);
345 for (i
= 0; i
< 3; i
++)
346 *pcbNeeded
+= (wcslen(wszPrintProviderInfo
[i
]) + 1) * sizeof(WCHAR
);
348 // Check if the supplied buffer is large enough.
349 if (cbBuf
< *pcbNeeded
)
351 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
355 // Copy over the print processor information.
356 ((PPRINTER_INFO_1W
)pPrinterEnum
)->Flags
= 0;
357 PackStrings(wszPrintProviderInfo
, pPrinterEnum
, dwOffsets
, &pPrinterEnum
[*pcbNeeded
]);
359 dwErrorCode
= ERROR_SUCCESS
;
364 // Count the required buffer size and the number of printers.
367 for (pNode
= PrinterList
.Head
.Next
[0]; pNode
; pNode
= pNode
->Next
[0])
369 pPrinter
= (PLOCAL_PRINTER
)pNode
->Element
;
371 // This looks wrong, but is totally right. PRINTER_INFO_1W has three members pName, pComment and pDescription.
372 // But pComment equals the "Description" registry value while pDescription is concatenated out of pName and pComment.
373 // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
374 cbName
= (wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
375 cbComment
= (wcslen(pPrinter
->pwszDescription
) + 1) * sizeof(WCHAR
);
376 cbDescription
= cchComputerName
* sizeof(WCHAR
) + cbName
+ cbComment
+ sizeof(WCHAR
);
378 *pcbNeeded
+= sizeof(PRINTER_INFO_1W
) + cchComputerName
* sizeof(WCHAR
) + cbName
+ cbComment
+ cbDescription
;
382 // Check if the supplied buffer is large enough.
383 if (cbBuf
< *pcbNeeded
)
385 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
389 // Put the strings right after the last PRINTER_INFO_1W structure.
390 // Due to all the required string processing, we can't just use PackStrings here :(
391 pPrinterInfo
= pPrinterEnum
;
392 pPrinterString
= pPrinterEnum
+ i
* sizeof(PRINTER_INFO_1W
);
394 // Copy over the printer information.
395 for (pNode
= PrinterList
.Head
.Next
[0]; pNode
; pNode
= pNode
->Next
[0])
397 pPrinter
= (PLOCAL_PRINTER
)pNode
->Element
;
399 // FIXME: As for now, the Flags member returns no information.
400 PrinterInfo1
.Flags
= 0;
402 // Copy the printer name.
403 PrinterInfo1
.pName
= (PWSTR
)pPrinterString
;
404 CopyMemory(pPrinterString
, wszComputerName
, cchComputerName
* sizeof(WCHAR
));
405 pPrinterString
+= cchComputerName
* sizeof(WCHAR
);
406 cbName
= (wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
407 CopyMemory(pPrinterString
, pPrinter
->pwszPrinterName
, cbName
);
408 pPrinterString
+= cbName
;
410 // Copy the printer comment (equals the "Description" registry value).
411 PrinterInfo1
.pComment
= (PWSTR
)pPrinterString
;
412 cbComment
= (wcslen(pPrinter
->pwszDescription
) + 1) * sizeof(WCHAR
);
413 CopyMemory(pPrinterString
, pPrinter
->pwszDescription
, cbComment
);
414 pPrinterString
+= cbComment
;
416 // Copy the description, which for PRINTER_INFO_1W has the form "Name,Comment,"
417 PrinterInfo1
.pDescription
= (PWSTR
)pPrinterString
;
418 CopyMemory(pPrinterString
, wszComputerName
, cchComputerName
* sizeof(WCHAR
));
419 pPrinterString
+= cchComputerName
* sizeof(WCHAR
);
420 CopyMemory(pPrinterString
, pPrinter
->pwszPrinterName
, cbName
- sizeof(WCHAR
));
421 pPrinterString
+= cbName
- sizeof(WCHAR
);
422 CopyMemory(pPrinterString
, wszComma
, sizeof(WCHAR
));
423 pPrinterString
+= sizeof(WCHAR
);
424 CopyMemory(pPrinterString
, pPrinter
->pwszDescription
, cbComment
- sizeof(WCHAR
));
425 pPrinterString
+= cbComment
- sizeof(WCHAR
);
426 CopyMemory(pPrinterString
, wszComma
, sizeof(wszComma
));
427 pPrinterString
+= sizeof(wszComma
);
429 // Finally copy the structure and advance to the next one in the output buffer.
430 CopyMemory(pPrinterInfo
, &PrinterInfo1
, sizeof(PRINTER_INFO_1W
));
431 pPrinterInfo
+= sizeof(PRINTER_INFO_1W
);
435 dwErrorCode
= ERROR_SUCCESS
;
442 LocalEnumPrinters(DWORD Flags
, LPWSTR Name
, DWORD Level
, LPBYTE pPrinterEnum
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
446 // Do no sanity checks here. This is verified by localspl_apitest!
453 // Treat it as success if the caller queried no information and we don't need to return any.
454 dwErrorCode
= ERROR_SUCCESS
;
456 if (Flags
& PRINTER_ENUM_LOCAL
)
458 // The function behaves quite differently for each level.
461 dwErrorCode
= _LocalEnumPrintersLevel1(Flags
, Name
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
465 // TODO: Handle other levels.
466 // The caller supplied an invalid level.
467 dwErrorCode
= ERROR_INVALID_LEVEL
;
473 SetLastError(dwErrorCode
);
474 return (dwErrorCode
== ERROR_SUCCESS
);
478 LocalOpenPrinter(PWSTR lpPrinterName
, HANDLE
* phPrinter
, PPRINTER_DEFAULTSW pDefault
)
480 DWORD cchComputerName
;
481 DWORD cchPrinterName
;
484 PWSTR p
= lpPrinterName
;
485 PWSTR pwszPrinterName
= NULL
;
487 PLOCAL_HANDLE pHandle
;
488 PLOCAL_PRINTER pPrinter
;
489 PLOCAL_PRINTER_HANDLE pPrinterHandle
= NULL
;
490 WCHAR wszComputerName
[MAX_COMPUTERNAME_LENGTH
+ 1];
493 if (!lpPrinterName
|| !phPrinter
)
495 dwErrorCode
= ERROR_INVALID_PARAMETER
;
499 // Does lpPrinterName begin with two backslashes to indicate a server name?
500 if (lpPrinterName
[0] == L
'\\' && lpPrinterName
[1] == L
'\\')
502 // Skip these two backslashes.
505 // Look for the closing backslash.
506 p
= wcschr(lpPrinterName
, L
'\\');
509 // We didn't get a proper server name.
510 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
514 // Null-terminate the string here to enable comparison.
517 // Get the local computer name for comparison.
518 cchComputerName
= _countof(wszComputerName
);
519 if (!GetComputerNameW(wszComputerName
, &cchComputerName
))
521 dwErrorCode
= GetLastError();
522 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode
);
526 // Now compare this with the local computer name and reject if it doesn't match, because this print provider only supports local printers.
527 if (wcsicmp(lpPrinterName
, wszComputerName
) != 0)
529 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
533 // We have checked the server name and don't need it anymore.
534 lpPrinterName
= p
+ 1;
537 // Look for a comma. If it exists, it indicates the end of the printer name.
538 p
= wcschr(lpPrinterName
, L
',');
540 cchPrinterName
= p
- lpPrinterName
;
542 cchPrinterName
= wcslen(lpPrinterName
);
544 // No printer name and no comma? This is invalid!
545 if (!cchPrinterName
&& !p
)
547 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
551 // Do we have a printer name?
555 pwszPrinterName
= DllAllocSplMem((cchPrinterName
+ 1) * sizeof(WCHAR
));
556 CopyMemory(pwszPrinterName
, lpPrinterName
, cchPrinterName
* sizeof(WCHAR
));
557 pwszPrinterName
[cchPrinterName
] = 0;
559 // Retrieve the associated printer from the list.
560 pPrinter
= LookupElementSkiplist(&PrinterList
, &pwszPrinterName
, NULL
);
563 // The printer does not exist.
564 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
568 // Create a new printer handle.
569 pPrinterHandle
= DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE
));
570 pPrinterHandle
->pPrinter
= pPrinter
;
572 // Check if a datatype was given.
573 if (pDefault
&& pDefault
->pDatatype
)
575 // Use the datatype if it's valid.
576 if (!FindDatatype(pPrinter
->pPrintProcessor
, pDefault
->pDatatype
))
578 dwErrorCode
= ERROR_INVALID_DATATYPE
;
582 pPrinterHandle
->pwszDatatype
= AllocSplStr(pDefault
->pDatatype
);
586 // Use the default datatype.
587 pPrinterHandle
->pwszDatatype
= AllocSplStr(pPrinter
->pwszDefaultDatatype
);
590 // Check if a DevMode was given, otherwise use the default.
591 if (pDefault
&& pDefault
->pDevMode
)
592 pPrinterHandle
->pDevMode
= DuplicateDevMode(pDefault
->pDevMode
);
594 pPrinterHandle
->pDevMode
= DuplicateDevMode(pPrinter
->pDefaultDevMode
);
596 // Did we have a comma? Then the user may want a handle to an existing job instead of creating a new job.
608 // The "Job " string has to follow now.
609 if (wcscmp(p
, L
"Job ") != 0)
611 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
615 // Skip the "Job " string.
616 p
+= sizeof("Job ") - 1;
618 // Skip even more whitespace.
622 // Finally extract the desired Job ID.
623 dwJobID
= wcstoul(p
, NULL
, 10);
624 if (!IS_VALID_JOB_ID(dwJobID
))
626 // The user supplied an invalid Job ID.
627 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
631 // Look for this job in the Global Job List.
632 pJob
= LookupElementSkiplist(&GlobalJobList
, &dwJobID
, NULL
);
633 if (!pJob
|| pJob
->pPrinter
!= pPrinter
)
635 // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
636 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
640 pPrinterHandle
->pStartedJob
= pJob
;
643 // Create a new handle that references a printer.
644 pHandle
= DllAllocSplMem(sizeof(LOCAL_HANDLE
));
645 pHandle
->HandleType
= Printer
;
646 pHandle
->pSpecificHandle
= pPrinterHandle
;
650 // No printer name, but we have a comma!
651 // This may be a request to a XcvMonitor or XcvPort handle.
661 // Check if this is a request to a XcvMonitor.
662 if (wcscmp(p
, L
"XcvMonitor ") == 0)
664 // Skip the "XcvMonitor " string.
665 p
+= sizeof("XcvMonitor ") - 1;
667 ///////////// TODO /////////////////////
668 pHandle
= DllAllocSplMem(sizeof(LOCAL_HANDLE
));
669 pHandle
->HandleType
= Monitor
;
670 //pHandle->pSpecificHandle = pMonitorHandle;
672 else if (wcscmp(p
, L
"XcvPort ") == 0)
674 // Skip the "XcvPort " string.
675 p
+= sizeof("XcvPort ") - 1;
677 //////////// TODO //////////////////////
678 pHandle
= DllAllocSplMem(sizeof(LOCAL_HANDLE
));
679 pHandle
->HandleType
= Port
;
680 //pHandle->pSpecificHandle = pPortHandle;
684 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
689 *phPrinter
= (HANDLE
)pHandle
;
690 dwErrorCode
= ERROR_SUCCESS
;
692 // Don't let the cleanup routines free this.
693 pPrinterHandle
= NULL
;
694 pwszPrinterName
= NULL
;
699 if (pPrinterHandle
->pwszDatatype
)
700 DllFreeSplStr(pPrinterHandle
->pwszDatatype
);
702 DllFreeSplMem(pPrinterHandle
);
706 DllFreeSplMem(pwszPrinterName
);
708 SetLastError(dwErrorCode
);
709 return (dwErrorCode
== ERROR_SUCCESS
);
713 LocalStartDocPrinter(HANDLE hPrinter
, DWORD Level
, LPBYTE pDocInfo
)
716 DWORD dwReturnValue
= 0;
717 PDOC_INFO_1W pDocumentInfo1
;
718 PLOCAL_HANDLE pHandle
;
720 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
725 dwErrorCode
= ERROR_INVALID_PARAMETER
;
731 dwErrorCode
= ERROR_INVALID_HANDLE
;
735 // Check if this is a printer handle.
736 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
737 if (pHandle
->HandleType
!= Printer
)
739 dwErrorCode
= ERROR_INVALID_HANDLE
;
743 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
745 // Check if this is the right document information level.
748 dwErrorCode
= ERROR_INVALID_LEVEL
;
752 pDocumentInfo1
= (PDOC_INFO_1W
)pDocInfo
;
755 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
756 pJob
->pPrinter
= pPrinterHandle
->pPrinter
;
757 pJob
->dwPriority
= DEF_PRIORITY
;
759 // Check if a datatype was given.
760 if (pDocumentInfo1
->pDatatype
)
762 // Use the datatype if it's valid.
763 if (!FindDatatype(pJob
->pPrintProcessor
, pDocumentInfo1
->pDatatype
))
765 dwErrorCode
= ERROR_INVALID_DATATYPE
;
769 pJob
->pwszDatatype
= AllocSplStr(pDocumentInfo1
->pDatatype
);
773 // Use the printer handle datatype.
774 pJob
->pwszDatatype
= AllocSplStr(pPrinterHandle
->pwszDatatype
);
777 // Copy over printer defaults.
778 pJob
->pDevMode
= DuplicateDevMode(pPrinterHandle
->pDevMode
);
780 // Copy over supplied information.
781 if (pDocumentInfo1
->pDocName
)
782 pJob
->pwszDocumentName
= AllocSplStr(pDocumentInfo1
->pDocName
);
784 if (pDocumentInfo1
->pOutputFile
)
785 pJob
->pwszOutputFile
= AllocSplStr(pDocumentInfo1
->pOutputFile
);
787 // Get an ID for the new job.
788 if (!GetNextJobID(&pJob
->dwJobID
))
790 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
794 // Add the job to the Global Job List.
795 if (!InsertElementSkiplist(&GlobalJobList
, pJob
))
797 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
798 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob
->dwJobID
);
802 // Add the job at the end of the Printer's Job List.
803 // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
804 if (!InsertTailElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
))
806 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
807 ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob
->dwJobID
);
811 pPrinterHandle
->pStartedJob
= pJob
;
812 dwErrorCode
= ERROR_SUCCESS
;
813 dwReturnValue
= pJob
->dwJobID
;
816 SetLastError(dwErrorCode
);
817 return dwReturnValue
;
821 LocalStartPagePrinter(HANDLE hPrinter
)
823 ///////////// TODO /////////////////////
828 LocalWritePrinter(HANDLE hPrinter
, LPVOID pBuf
, DWORD cbBuf
, LPDWORD pcWritten
)
830 ///////////// TODO /////////////////////
835 LocalEndPagePrinter(HANDLE hPrinter
)
837 ///////////// TODO /////////////////////
842 LocalEndDocPrinter(HANDLE hPrinter
)
844 ///////////// TODO /////////////////////
849 LocalClosePrinter(HANDLE hPrinter
)
851 PLOCAL_HANDLE pHandle
;
855 SetLastError(ERROR_INVALID_HANDLE
);
859 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
861 ///////////// TODO /////////////////////
862 /// Check the handle type, do thoroughful checks on all data fields and clean them.
863 ////////////////////////////////////////
865 DllFreeSplMem(pHandle
);