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
)
481 DWORD cchComputerName
;
482 DWORD cchFirstParameter
;
485 HANDLE hExternalHandle
;
486 PWSTR p
= lpPrinterName
;
487 PWSTR pwszFirstParameter
= NULL
;
488 PWSTR pwszSecondParameter
= NULL
;
490 PLOCAL_HANDLE pHandle
= NULL
;
491 PLOCAL_PRINT_MONITOR pPrintMonitor
;
492 PLOCAL_PRINTER pPrinter
;
493 PLOCAL_PRINTER_HANDLE pPrinterHandle
= NULL
;
494 WCHAR wszComputerName
[MAX_COMPUTERNAME_LENGTH
+ 1];
496 // TODO: lpPrinterName == NULL is supported and means access to the local printer server.
497 // Not sure yet if that is passed down to localspl.dll or processed in advance.
500 if (!lpPrinterName
|| !phPrinter
)
502 dwErrorCode
= ERROR_INVALID_PARAMETER
;
506 // Skip any server name in the first parameter.
507 // Does lpPrinterName begin with two backslashes to indicate a server name?
508 if (lpPrinterName
[0] == L
'\\' && lpPrinterName
[1] == L
'\\')
510 // Skip these two backslashes.
513 // Look for the closing backslash.
514 p
= wcschr(lpPrinterName
, L
'\\');
517 // We didn't get a proper server name.
518 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
522 // Get the local computer name for comparison.
523 cchComputerName
= _countof(wszComputerName
);
524 if (!GetComputerNameW(wszComputerName
, &cchComputerName
))
526 dwErrorCode
= GetLastError();
527 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode
);
531 // Now compare this string excerpt with the local computer name.
532 // The input parameter may not be writable, so we can't null-terminate the input string at this point.
533 // This print provider only supports local printers, so both strings have to match.
534 if (p
- lpPrinterName
!= cchComputerName
|| _wcsnicmp(lpPrinterName
, wszComputerName
, cchComputerName
) != 0)
536 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
540 // We have checked the server name and don't need it anymore.
541 lpPrinterName
= p
+ 1;
544 // Look for a comma. If it exists, it indicates the end of the first parameter.
545 pwszSecondParameter
= wcschr(lpPrinterName
, L
',');
546 if (pwszSecondParameter
)
547 cchFirstParameter
= pwszSecondParameter
- p
;
549 cchFirstParameter
= wcslen(lpPrinterName
);
551 // We must have at least one parameter.
552 if (!cchFirstParameter
&& !pwszSecondParameter
)
554 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
558 // Do we have a first parameter?
559 if (cchFirstParameter
)
562 pwszFirstParameter
= DllAllocSplMem((cchFirstParameter
+ 1) * sizeof(WCHAR
));
563 CopyMemory(pwszFirstParameter
, lpPrinterName
, cchFirstParameter
* sizeof(WCHAR
));
564 pwszFirstParameter
[cchFirstParameter
] = 0;
567 // Do we have a second parameter?
568 if (pwszSecondParameter
)
570 // Yes, skip the comma at the beginning.
571 ++pwszSecondParameter
;
573 // Skip whitespace as well.
574 while (*pwszSecondParameter
== L
' ')
575 ++pwszSecondParameter
;
578 // Create a new handle.
579 pHandle
= DllAllocSplMem(sizeof(LOCAL_HANDLE
));
582 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
583 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
587 // Now we can finally check the type of handle actually requested.
588 if (pwszFirstParameter
&& pwszSecondParameter
&& wcsncmp(pwszSecondParameter
, L
"Port", 4) == 0)
590 // The caller wants a port handle and provided a string like:
592 // "\\COMPUTERNAME\LPT1:, Port"
594 // Look for this port in our Print Monitor Port list.
595 pPrintMonitor
= FindPrintMonitorByPort(pwszFirstParameter
);
598 // The supplied port is unknown to all our Print Monitors.
599 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
603 // Call the monitor's OpenPort function.
604 if (pPrintMonitor
->bIsLevel2
)
605 bReturnValue
= ((PMONITOR2
)pPrintMonitor
->pMonitor
)->pfnOpenPort(pPrintMonitor
->hMonitor
, pwszFirstParameter
, &hExternalHandle
);
607 bReturnValue
= ((LPMONITOREX
)pPrintMonitor
->pMonitor
)->Monitor
.pfnOpenPort(pwszFirstParameter
, &hExternalHandle
);
611 // The OpenPort function failed. Return its last error.
612 dwErrorCode
= GetLastError();
616 // Return the Port handle through our general handle.
617 pHandle
->HandleType
= HandleType_Port
;
618 pHandle
->pSpecificHandle
= hExternalHandle
;
620 else if (!pwszFirstParameter
&& pwszSecondParameter
&& wcsncmp(pwszSecondParameter
, L
"Xcv", 3) == 0)
622 // The caller wants an Xcv handle and provided a string like:
623 // ", XcvMonitor Local Port"
624 // "\\COMPUTERNAME\, XcvMonitor Local Port"
626 // "\\COMPUTERNAME\, XcvPort LPT1:"
628 // Skip the "Xcv" string.
629 pwszSecondParameter
+= 3;
631 // Is XcvMonitor or XcvPort requested?
632 if (wcsncmp(pwszSecondParameter
, L
"Monitor ", 8) == 0)
634 // Skip the "Monitor " string.
635 pwszSecondParameter
+= 8;
637 // Look for this monitor in our Print Monitor list.
638 pPrintMonitor
= FindPrintMonitor(pwszSecondParameter
);
641 // The caller supplied a non-existing Monitor name.
642 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
646 else if (wcsncmp(pwszSecondParameter
, L
"Port ", 5) == 0)
648 // Skip the "Port " string.
649 pwszSecondParameter
+= 5;
651 // Look for this port in our Print Monitor Port list.
652 pPrintMonitor
= FindPrintMonitorByPort(pwszSecondParameter
);
655 // The supplied port is unknown to all our Print Monitors.
656 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
662 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
666 // Call the monitor's XcvOpenPort function.
667 if (pPrintMonitor
->bIsLevel2
)
668 bReturnValue
= ((PMONITOR2
)pPrintMonitor
->pMonitor
)->pfnXcvOpenPort(pPrintMonitor
->hMonitor
, pwszSecondParameter
, SERVER_EXECUTE
, &hExternalHandle
);
670 bReturnValue
= ((LPMONITOREX
)pPrintMonitor
->pMonitor
)->Monitor
.pfnXcvOpenPort(pwszSecondParameter
, SERVER_EXECUTE
, &hExternalHandle
);
674 // The XcvOpenPort function failed. Return its last error.
675 dwErrorCode
= GetLastError();
679 // Return the Xcv handle through our general handle.
680 pHandle
->HandleType
= HandleType_Xcv
;
681 pHandle
->pSpecificHandle
= hExternalHandle
;
685 // The caller wants a Printer or Printer Job handle and provided a string like:
687 // "\\COMPUTERNAME\HP DeskJet"
688 // "HP DeskJet, Job 5"
689 // "\\COMPUTERNAME\HP DeskJet, Job 5"
691 // Retrieve the printer from the list.
692 pPrinter
= LookupElementSkiplist(&PrinterList
, &pwszFirstParameter
, NULL
);
695 // The printer does not exist.
696 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
700 // Create a new printer handle.
701 pPrinterHandle
= DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE
));
704 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
705 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
709 pPrinterHandle
->pPrinter
= pPrinter
;
711 // Check if a datatype was given.
712 if (pDefault
&& pDefault
->pDatatype
)
714 // Use the datatype if it's valid.
715 if (!FindDatatype(pPrinter
->pPrintProcessor
, pDefault
->pDatatype
))
717 dwErrorCode
= ERROR_INVALID_DATATYPE
;
721 pPrinterHandle
->pwszDatatype
= AllocSplStr(pDefault
->pDatatype
);
725 // Use the default datatype.
726 pPrinterHandle
->pwszDatatype
= AllocSplStr(pPrinter
->pwszDefaultDatatype
);
729 // Check if a DevMode was given, otherwise use the default.
730 if (pDefault
&& pDefault
->pDevMode
)
731 pPrinterHandle
->pDevMode
= DuplicateDevMode(pDefault
->pDevMode
);
733 pPrinterHandle
->pDevMode
= DuplicateDevMode(pPrinter
->pDefaultDevMode
);
735 // Check if the caller wants a handle to an existing job.
736 if (pwszSecondParameter
)
738 // The "Job " string has to follow now.
739 if (wcsncmp(pwszSecondParameter
, L
"Job ", 4) != 0)
741 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
745 // Skip the "Job " string.
746 pwszSecondParameter
+= 4;
748 // Skip even more whitespace.
749 while (*pwszSecondParameter
== ' ')
750 ++pwszSecondParameter
;
752 // Finally extract the desired Job ID.
753 dwJobID
= wcstoul(pwszSecondParameter
, NULL
, 10);
754 if (!IS_VALID_JOB_ID(dwJobID
))
756 // The user supplied an invalid Job ID.
757 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
761 // Look for this job in the Global Job List.
762 pJob
= LookupElementSkiplist(&GlobalJobList
, &dwJobID
, NULL
);
763 if (!pJob
|| pJob
->pPrinter
!= pPrinter
)
765 // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
766 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
770 pPrinterHandle
->pStartedJob
= pJob
;
773 // Return the Printer handle through our general handle.
774 pHandle
->HandleType
= HandleType_Printer
;
775 pHandle
->pSpecificHandle
= pPrinterHandle
;
778 // We were successful! Return the handle.
779 *phPrinter
= (HANDLE
)pHandle
;
780 dwErrorCode
= ERROR_SUCCESS
;
782 // Don't let the cleanup routines free this.
784 pPrinterHandle
= NULL
;
788 DllFreeSplMem(pHandle
);
792 if (pPrinterHandle
->pwszDatatype
)
793 DllFreeSplStr(pPrinterHandle
->pwszDatatype
);
795 if (pPrinterHandle
->pDevMode
)
796 DllFreeSplMem(pPrinterHandle
->pDevMode
);
798 DllFreeSplMem(pPrinterHandle
);
801 if (pwszFirstParameter
)
802 DllFreeSplMem(pwszFirstParameter
);
804 SetLastError(dwErrorCode
);
805 return (dwErrorCode
== ERROR_SUCCESS
);
809 LocalStartDocPrinter(HANDLE hPrinter
, DWORD Level
, LPBYTE pDocInfo
)
812 DWORD dwReturnValue
= 0;
813 PDOC_INFO_1W pDocumentInfo1
;
814 PLOCAL_HANDLE pHandle
;
816 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
821 dwErrorCode
= ERROR_INVALID_PARAMETER
;
827 dwErrorCode
= ERROR_INVALID_HANDLE
;
831 // Check if this is a printer handle.
832 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
833 if (pHandle
->HandleType
!= HandleType_Printer
)
835 dwErrorCode
= ERROR_INVALID_HANDLE
;
839 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
841 // Check if this is the right document information level.
844 dwErrorCode
= ERROR_INVALID_LEVEL
;
848 pDocumentInfo1
= (PDOC_INFO_1W
)pDocInfo
;
851 pJob
= DllAllocSplMem(sizeof(LOCAL_JOB
));
852 pJob
->pPrinter
= pPrinterHandle
->pPrinter
;
853 pJob
->dwPriority
= DEF_PRIORITY
;
855 // Check if a datatype was given.
856 if (pDocumentInfo1
->pDatatype
)
858 // Use the datatype if it's valid.
859 if (!FindDatatype(pJob
->pPrintProcessor
, pDocumentInfo1
->pDatatype
))
861 dwErrorCode
= ERROR_INVALID_DATATYPE
;
865 pJob
->pwszDatatype
= AllocSplStr(pDocumentInfo1
->pDatatype
);
869 // Use the printer handle datatype.
870 pJob
->pwszDatatype
= AllocSplStr(pPrinterHandle
->pwszDatatype
);
873 // Copy over printer defaults.
874 pJob
->pDevMode
= DuplicateDevMode(pPrinterHandle
->pDevMode
);
876 // Copy over supplied information.
877 if (pDocumentInfo1
->pDocName
)
878 pJob
->pwszDocumentName
= AllocSplStr(pDocumentInfo1
->pDocName
);
880 if (pDocumentInfo1
->pOutputFile
)
881 pJob
->pwszOutputFile
= AllocSplStr(pDocumentInfo1
->pOutputFile
);
883 // Get an ID for the new job.
884 if (!GetNextJobID(&pJob
->dwJobID
))
886 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
890 // Add the job to the Global Job List.
891 if (!InsertElementSkiplist(&GlobalJobList
, pJob
))
893 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
894 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob
->dwJobID
);
898 // Add the job at the end of the Printer's Job List.
899 // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
900 if (!InsertTailElementSkiplist(&pJob
->pPrinter
->JobList
, pJob
))
902 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
903 ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob
->dwJobID
);
907 pPrinterHandle
->pStartedJob
= pJob
;
908 dwErrorCode
= ERROR_SUCCESS
;
909 dwReturnValue
= pJob
->dwJobID
;
912 SetLastError(dwErrorCode
);
913 return dwReturnValue
;
917 LocalStartPagePrinter(HANDLE hPrinter
)
919 ///////////// TODO /////////////////////
924 LocalWritePrinter(HANDLE hPrinter
, LPVOID pBuf
, DWORD cbBuf
, LPDWORD pcWritten
)
926 ///////////// TODO /////////////////////
931 LocalEndPagePrinter(HANDLE hPrinter
)
933 ///////////// TODO /////////////////////
938 LocalEndDocPrinter(HANDLE hPrinter
)
940 ///////////// TODO /////////////////////
945 LocalClosePrinter(HANDLE hPrinter
)
947 PLOCAL_HANDLE pHandle
;
951 SetLastError(ERROR_INVALID_HANDLE
);
955 pHandle
= (PLOCAL_HANDLE
)hPrinter
;
957 ///////////// TODO /////////////////////
958 /// Check the handle type, do thoroughful checks on all data fields and clean them.
959 ////////////////////////////////////////
961 DllFreeSplMem(pHandle
);