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";
49 PLOCAL_PRINTER pPrinter
= NULL
;
50 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
51 PWSTR pwszPort
= NULL
;
52 PWSTR pwszPrintProcessor
= NULL
;
53 WCHAR wszPrinterName
[MAX_PRINTER_NAME
+ 1];
55 // Initialize an empty list for our printers.
56 InitializeSkiplist(&PrinterList
, DllAllocSplMem
, _PrinterListCompareRoutine
, (PSKIPLIST_FREE_ROUTINE
)DllFreeSplMem
);
58 // Open our printers registry key. Each subkey is a local printer there.
59 dwErrorCode
= (DWORD
)RegOpenKeyExW(HKEY_LOCAL_MACHINE
, wszPrintersKey
, 0, KEY_READ
, &hKey
);
60 if (dwErrorCode
!= ERROR_SUCCESS
)
62 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode
);
66 // Get the number of subkeys.
67 dwErrorCode
= (DWORD
)RegQueryInfoKeyW(hKey
, NULL
, NULL
, NULL
, &dwSubKeys
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
68 if (dwErrorCode
!= ERROR_SUCCESS
)
70 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode
);
74 // Loop through all available local printers.
75 for (i
= 0; i
< dwSubKeys
; i
++)
77 // Cleanup tasks from the previous run
86 if (pPrinter
->pDefaultDevMode
)
87 DllFreeSplMem(pPrinter
->pDefaultDevMode
);
89 if (pPrinter
->pwszDefaultDatatype
)
90 DllFreeSplStr(pPrinter
->pwszDefaultDatatype
);
92 if (pPrinter
->pwszDescription
)
93 DllFreeSplStr(pPrinter
->pwszDescription
);
95 if (pPrinter
->pwszPrinterDriver
)
96 DllFreeSplStr(pPrinter
->pwszPrinterDriver
);
98 if (pPrinter
->pwszPrinterName
)
99 DllFreeSplStr(pPrinter
->pwszPrinterName
);
101 DllFreeSplMem(pPrinter
);
105 if (pwszPrintProcessor
)
107 DllFreeSplStr(pwszPrintProcessor
);
108 pwszPrintProcessor
= NULL
;
111 // Get the name of this printer.
112 cchPrinterName
= _countof(wszPrinterName
);
113 dwErrorCode
= (DWORD
)RegEnumKeyExW(hKey
, i
, wszPrinterName
, &cchPrinterName
, NULL
, NULL
, NULL
, NULL
);
114 if (dwErrorCode
== ERROR_MORE_DATA
)
116 // This printer name exceeds the maximum length and is invalid.
119 else if (dwErrorCode
!= ERROR_SUCCESS
)
121 ERR("RegEnumKeyExW failed for iteration %lu with status %lu!\n", i
, dwErrorCode
);
125 // Open this Printer's registry key.
126 dwErrorCode
= (DWORD
)RegOpenKeyExW(hKey
, wszPrinterName
, 0, KEY_READ
, &hSubKey
);
127 if (dwErrorCode
!= ERROR_SUCCESS
)
129 ERR("RegOpenKeyExW failed for Printer \"%S\" with status %lu!\n", wszPrinterName
, dwErrorCode
);
133 // Get the Print Processor.
134 pwszPrintProcessor
= AllocAndRegQueryWSZ(hSubKey
, L
"Print Processor");
135 if (!pwszPrintProcessor
)
138 // Try to find it in the Print Processor List.
139 pPrintProcessor
= FindPrintProcessor(pwszPrintProcessor
);
140 if (!pPrintProcessor
)
142 ERR("Invalid Print Processor \"%S\" for Printer \"%S\"!\n", pwszPrintProcessor
, wszPrinterName
);
147 pwszPort
= AllocAndRegQueryWSZ(hSubKey
, L
"Port");
151 // Try to find it in the Port List.
152 pPort
= FindPort(pwszPort
);
155 ERR("Invalid Port \"%S\" for Printer \"%S\"!\n", pwszPort
, wszPrinterName
);
159 // Create a new LOCAL_PRINTER structure for it.
160 pPrinter
= DllAllocSplMem(sizeof(LOCAL_PRINTER
));
163 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
164 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
168 pPrinter
->pwszPrinterName
= AllocSplStr(wszPrinterName
);
169 pPrinter
->pPrintProcessor
= pPrintProcessor
;
170 pPrinter
->pPort
= pPort
;
171 InitializePrinterJobList(pPrinter
);
173 // Get the printer driver.
174 pPrinter
->pwszPrinterDriver
= AllocAndRegQueryWSZ(hSubKey
, L
"Printer Driver");
175 if (!pPrinter
->pwszPrinterDriver
)
178 // Get the description.
179 pPrinter
->pwszDescription
= AllocAndRegQueryWSZ(hSubKey
, L
"Description");
180 if (!pPrinter
->pwszDescription
)
183 // Get the default datatype.
184 pPrinter
->pwszDefaultDatatype
= AllocAndRegQueryWSZ(hSubKey
, L
"Datatype");
185 if (!pPrinter
->pwszDefaultDatatype
)
188 // Verify that it's valid.
189 if (!FindDatatype(pPrintProcessor
, pPrinter
->pwszDefaultDatatype
))
191 ERR("Invalid default datatype \"%S\" for Printer \"%S\"!\n", pPrinter
->pwszDefaultDatatype
, wszPrinterName
);
195 // Determine the size of the DevMode.
196 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Default DevMode", NULL
, NULL
, NULL
, &cbData
);
197 if (dwErrorCode
!= ERROR_SUCCESS
)
199 ERR("Couldn't query the size of the DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName
, dwErrorCode
, cbData
);
203 // Allocate enough memory for the DevMode.
204 pPrinter
->pDefaultDevMode
= DllAllocSplMem(cbData
);
205 if (!pPrinter
->pDefaultDevMode
)
207 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
208 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
212 // Get the default DevMode.
213 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Default DevMode", NULL
, NULL
, (PBYTE
)pPrinter
->pDefaultDevMode
, &cbData
);
214 if (dwErrorCode
!= ERROR_SUCCESS
)
216 ERR("Couldn't query a DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName
, dwErrorCode
, cbData
);
220 // Get the Attributes.
221 cbData
= sizeof(DWORD
);
222 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Attributes", NULL
, NULL
, (PBYTE
)&pPrinter
->dwAttributes
, &cbData
);
223 if (dwErrorCode
!= ERROR_SUCCESS
)
225 ERR("Couldn't query Attributes for Printer \"%S\", status is %lu!\n", wszPrinterName
, dwErrorCode
);
230 cbData
= sizeof(DWORD
);
231 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Status", NULL
, NULL
, (PBYTE
)&pPrinter
->dwStatus
, &cbData
);
232 if (dwErrorCode
!= ERROR_SUCCESS
)
234 ERR("Couldn't query Status for Printer \"%S\", status is %lu!\n", wszPrinterName
, dwErrorCode
);
238 // Add this printer to the printer list.
239 if (!InsertElementSkiplist(&PrinterList
, pPrinter
))
241 ERR("InsertElementSkiplist failed for Printer \"%S\"!\n", pPrinter
->pwszPrinterName
);
245 // Don't let the cleanup routines free this.
249 dwErrorCode
= ERROR_SUCCESS
;
254 RegCloseKey(hSubKey
);
258 if (pPrinter
->pDefaultDevMode
)
259 DllFreeSplMem(pPrinter
->pDefaultDevMode
);
261 if (pPrinter
->pwszDefaultDatatype
)
262 DllFreeSplStr(pPrinter
->pwszDefaultDatatype
);
264 if (pPrinter
->pwszDescription
)
265 DllFreeSplStr(pPrinter
->pwszDescription
);
267 if (pPrinter
->pwszPrinterDriver
)
268 DllFreeSplStr(pPrinter
->pwszPrinterDriver
);
270 if (pPrinter
->pwszPrinterName
)
271 DllFreeSplStr(pPrinter
->pwszPrinterName
);
273 DllFreeSplMem(pPrinter
);
276 if (pwszPrintProcessor
)
277 DllFreeSplStr(pwszPrintProcessor
);
283 SetLastError(dwErrorCode
);
284 return (dwErrorCode
== ERROR_SUCCESS
);
289 _LocalEnumPrintersLevel1(DWORD Flags
, LPWSTR Name
, LPBYTE pPrinterEnum
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
291 const WCHAR wszComma
[] = L
",";
296 DWORD cchComputerName
= 0;
300 PBYTE pPrinterString
;
301 PSKIPLIST_NODE pNode
;
302 PLOCAL_PRINTER pPrinter
;
303 PRINTER_INFO_1W PrinterInfo1
;
304 WCHAR wszComputerName
[2 + MAX_COMPUTERNAME_LENGTH
+ 1 + 1];
306 DWORD dwOffsets
[] = {
307 FIELD_OFFSET(PRINTER_INFO_1W
, pName
),
308 FIELD_OFFSET(PRINTER_INFO_1W
, pDescription
),
309 FIELD_OFFSET(PRINTER_INFO_1W
, pComment
),
313 if (Flags
& PRINTER_ENUM_NAME
)
317 // The user supplied a Computer Name (with leading double backslashes) or Print Provider Name.
318 // Only process what's directed at us and dismiss every other request with ERROR_INVALID_NAME.
319 if (Name
[0] == L
'\\' && Name
[1] == L
'\\')
321 // Prepend slashes to the computer name.
322 wszComputerName
[0] = L
'\\';
323 wszComputerName
[1] = L
'\\';
325 // Get the local computer name for comparison.
326 cchComputerName
= MAX_COMPUTERNAME_LENGTH
+ 1;
327 if (!GetComputerNameW(&wszComputerName
[2], &cchComputerName
))
329 dwErrorCode
= GetLastError();
330 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode
);
334 // Add the leading slashes to the total length.
335 cchComputerName
+= 2;
337 // Now compare this with the local computer name and reject if it doesn't match.
338 if (wcsicmp(&Name
[2], &wszComputerName
[2]) != 0)
340 dwErrorCode
= ERROR_INVALID_NAME
;
344 // Add a trailing backslash to wszComputerName, which will later be prepended in front of the printer names.
345 wszComputerName
[cchComputerName
++] = L
'\\';
346 wszComputerName
[cchComputerName
] = 0;
348 else if (wcsicmp(Name
, wszPrintProviderInfo
[0]) != 0)
350 // The user supplied a name that cannot be processed by the local print provider.
351 dwErrorCode
= ERROR_INVALID_NAME
;
357 // The caller wants information about this Print Provider.
358 // spoolss packs this into an array of information about all Print Providers.
359 *pcbNeeded
= sizeof(PRINTER_INFO_1W
);
361 for (i
= 0; i
< 3; i
++)
362 *pcbNeeded
+= (wcslen(wszPrintProviderInfo
[i
]) + 1) * sizeof(WCHAR
);
364 // Check if the supplied buffer is large enough.
365 if (cbBuf
< *pcbNeeded
)
367 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
371 // Copy over the print processor information.
372 ((PPRINTER_INFO_1W
)pPrinterEnum
)->Flags
= 0;
373 PackStrings(wszPrintProviderInfo
, pPrinterEnum
, dwOffsets
, &pPrinterEnum
[*pcbNeeded
]);
375 dwErrorCode
= ERROR_SUCCESS
;
380 // Count the required buffer size and the number of printers.
383 for (pNode
= PrinterList
.Head
.Next
[0]; pNode
; pNode
= pNode
->Next
[0])
385 pPrinter
= (PLOCAL_PRINTER
)pNode
->Element
;
387 // This looks wrong, but is totally right. PRINTER_INFO_1W has three members pName, pComment and pDescription.
388 // But pComment equals the "Description" registry value while pDescription is concatenated out of pName and pComment.
389 // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
390 cbName
= (wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
391 cbComment
= (wcslen(pPrinter
->pwszDescription
) + 1) * sizeof(WCHAR
);
392 cbDescription
= cchComputerName
* sizeof(WCHAR
) + cbName
+ cbComment
+ sizeof(WCHAR
);
394 *pcbNeeded
+= sizeof(PRINTER_INFO_1W
) + cchComputerName
* sizeof(WCHAR
) + cbName
+ cbComment
+ cbDescription
;
398 // Check if the supplied buffer is large enough.
399 if (cbBuf
< *pcbNeeded
)
401 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
405 // Put the strings right after the last PRINTER_INFO_1W structure.
406 // Due to all the required string processing, we can't just use PackStrings here :(
407 pPrinterInfo
= pPrinterEnum
;
408 pPrinterString
= pPrinterEnum
+ i
* sizeof(PRINTER_INFO_1W
);
410 // Copy over the printer information.
411 for (pNode
= PrinterList
.Head
.Next
[0]; pNode
; pNode
= pNode
->Next
[0])
413 pPrinter
= (PLOCAL_PRINTER
)pNode
->Element
;
415 // FIXME: As for now, the Flags member returns no information.
416 PrinterInfo1
.Flags
= 0;
418 // Copy the printer name.
419 PrinterInfo1
.pName
= (PWSTR
)pPrinterString
;
420 CopyMemory(pPrinterString
, wszComputerName
, cchComputerName
* sizeof(WCHAR
));
421 pPrinterString
+= cchComputerName
* sizeof(WCHAR
);
422 cbName
= (wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
423 CopyMemory(pPrinterString
, pPrinter
->pwszPrinterName
, cbName
);
424 pPrinterString
+= cbName
;
426 // Copy the printer comment (equals the "Description" registry value).
427 PrinterInfo1
.pComment
= (PWSTR
)pPrinterString
;
428 cbComment
= (wcslen(pPrinter
->pwszDescription
) + 1) * sizeof(WCHAR
);
429 CopyMemory(pPrinterString
, pPrinter
->pwszDescription
, cbComment
);
430 pPrinterString
+= cbComment
;
432 // Copy the description, which for PRINTER_INFO_1W has the form "Name,Comment,"
433 PrinterInfo1
.pDescription
= (PWSTR
)pPrinterString
;
434 CopyMemory(pPrinterString
, wszComputerName
, cchComputerName
* sizeof(WCHAR
));
435 pPrinterString
+= cchComputerName
* sizeof(WCHAR
);
436 CopyMemory(pPrinterString
, pPrinter
->pwszPrinterName
, cbName
- sizeof(WCHAR
));
437 pPrinterString
+= cbName
- sizeof(WCHAR
);
438 CopyMemory(pPrinterString
, wszComma
, sizeof(WCHAR
));
439 pPrinterString
+= sizeof(WCHAR
);
440 CopyMemory(pPrinterString
, pPrinter
->pwszDescription
, cbComment
- sizeof(WCHAR
));
441 pPrinterString
+= cbComment
- sizeof(WCHAR
);
442 CopyMemory(pPrinterString
, wszComma
, sizeof(wszComma
));
443 pPrinterString
+= sizeof(wszComma
);
445 // Finally copy the structure and advance to the next one in the output buffer.
446 CopyMemory(pPrinterInfo
, &PrinterInfo1
, sizeof(PRINTER_INFO_1W
));
447 pPrinterInfo
+= sizeof(PRINTER_INFO_1W
);
451 dwErrorCode
= ERROR_SUCCESS
;
458 LocalEnumPrinters(DWORD Flags
, LPWSTR Name
, DWORD Level
, LPBYTE pPrinterEnum
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
462 // Do no sanity checks here. This is verified by localspl_apitest!
469 // Treat it as success if the caller queried no information and we don't need to return any.
470 dwErrorCode
= ERROR_SUCCESS
;
472 if (Flags
& PRINTER_ENUM_LOCAL
)
474 // The function behaves quite differently for each level.
477 dwErrorCode
= _LocalEnumPrintersLevel1(Flags
, Name
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
481 // TODO: Handle other levels.
482 // The caller supplied an invalid level.
483 dwErrorCode
= ERROR_INVALID_LEVEL
;
489 SetLastError(dwErrorCode
);
490 return (dwErrorCode
== ERROR_SUCCESS
);
494 LocalOpenPrinter(PWSTR lpPrinterName
, HANDLE
* phPrinter
, PPRINTER_DEFAULTSW pDefault
)
497 DWORD cchComputerName
;
498 DWORD cchFirstParameter
;
501 HANDLE hExternalHandle
;
502 PWSTR p
= lpPrinterName
;
503 PWSTR pwszFirstParameter
= NULL
;
504 PWSTR pwszSecondParameter
= NULL
;
506 PLOCAL_HANDLE pHandle
= NULL
;
508 PLOCAL_PORT_HANDLE pPortHandle
= NULL
;
509 PLOCAL_PRINT_MONITOR pPrintMonitor
;
510 PLOCAL_PRINTER pPrinter
;
511 PLOCAL_PRINTER_HANDLE pPrinterHandle
= NULL
;
512 PLOCAL_XCV_HANDLE pXcvHandle
= NULL
;
513 WCHAR wszComputerName
[MAX_COMPUTERNAME_LENGTH
+ 1];
514 WCHAR wszFullPath
[MAX_PATH
];
516 // TODO: lpPrinterName == NULL is supported and means access to the local printer server.
517 // Not sure yet if that is passed down to localspl.dll or processed in advance.
520 if (!lpPrinterName
|| !phPrinter
)
522 dwErrorCode
= ERROR_INVALID_PARAMETER
;
528 // Skip any server name in the first parameter.
529 // Does lpPrinterName begin with two backslashes to indicate a server name?
530 if (lpPrinterName
[0] == L
'\\' && lpPrinterName
[1] == L
'\\')
532 // Skip these two backslashes.
535 // Look for the closing backslash.
536 p
= wcschr(lpPrinterName
, L
'\\');
539 // We didn't get a proper server name.
540 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
544 // Get the local computer name for comparison.
545 cchComputerName
= _countof(wszComputerName
);
546 if (!GetComputerNameW(wszComputerName
, &cchComputerName
))
548 dwErrorCode
= GetLastError();
549 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode
);
553 // Now compare this string excerpt with the local computer name.
554 // The input parameter may not be writable, so we can't null-terminate the input string at this point.
555 // This print provider only supports local printers, so both strings have to match.
556 if (p
- lpPrinterName
!= cchComputerName
|| _wcsnicmp(lpPrinterName
, wszComputerName
, cchComputerName
) != 0)
558 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
562 // We have checked the server name and don't need it anymore.
563 lpPrinterName
= p
+ 1;
566 // Look for a comma. If it exists, it indicates the end of the first parameter.
567 pwszSecondParameter
= wcschr(lpPrinterName
, L
',');
568 if (pwszSecondParameter
)
569 cchFirstParameter
= pwszSecondParameter
- p
;
571 cchFirstParameter
= wcslen(lpPrinterName
);
573 // We must have at least one parameter.
574 if (!cchFirstParameter
&& !pwszSecondParameter
)
576 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
580 // Do we have a first parameter?
581 if (cchFirstParameter
)
584 // No null-termination is necessary here, because DllAllocSplMem returns a zero-initialized buffer.
585 pwszFirstParameter
= DllAllocSplMem((cchFirstParameter
+ 1) * sizeof(WCHAR
));
586 CopyMemory(pwszFirstParameter
, lpPrinterName
, cchFirstParameter
* sizeof(WCHAR
));
589 // Do we have a second parameter?
590 if (pwszSecondParameter
)
592 // Yes, skip the comma at the beginning.
593 ++pwszSecondParameter
;
595 // Skip whitespace as well.
596 while (*pwszSecondParameter
== L
' ')
597 ++pwszSecondParameter
;
600 // Create a new handle.
601 pHandle
= DllAllocSplMem(sizeof(LOCAL_HANDLE
));
604 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
605 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
609 // Now we can finally check the type of handle actually requested.
610 if (pwszFirstParameter
&& pwszSecondParameter
&& wcsncmp(pwszSecondParameter
, L
"Port", 4) == 0)
612 // The caller wants a port handle and provided a string like:
614 // "\\COMPUTERNAME\LPT1:, Port"
616 // Look for this port in our Print Monitor Port list.
617 pPort
= FindPort(pwszFirstParameter
);
620 // The supplied port is unknown to all our Print Monitors.
621 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
625 pPrintMonitor
= pPort
->pPrintMonitor
;
627 // Call the monitor's OpenPort function.
628 if (pPrintMonitor
->bIsLevel2
)
629 bReturnValue
= ((PMONITOR2
)pPrintMonitor
->pMonitor
)->pfnOpenPort(pPrintMonitor
->hMonitor
, pwszFirstParameter
, &hExternalHandle
);
631 bReturnValue
= ((LPMONITOREX
)pPrintMonitor
->pMonitor
)->Monitor
.pfnOpenPort(pwszFirstParameter
, &hExternalHandle
);
635 // The OpenPort function failed. Return its last error.
636 dwErrorCode
= GetLastError();
640 // Create a new LOCAL_PORT_HANDLE.
641 pPortHandle
= DllAllocSplMem(sizeof(LOCAL_PORT_HANDLE
));
644 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
645 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
649 pPortHandle
->hPort
= hExternalHandle
;
650 pPortHandle
->pPort
= pPort
;
652 // Return the Port handle through our general handle.
653 pHandle
->HandleType
= HandleType_Port
;
654 pHandle
->pSpecificHandle
= pPortHandle
;
656 else if (!pwszFirstParameter
&& pwszSecondParameter
&& wcsncmp(pwszSecondParameter
, L
"Xcv", 3) == 0)
658 // The caller wants an Xcv handle and provided a string like:
659 // ", XcvMonitor Local Port"
660 // "\\COMPUTERNAME\, XcvMonitor Local Port"
662 // "\\COMPUTERNAME\, XcvPort LPT1:"
664 // Skip the "Xcv" string.
665 pwszSecondParameter
+= 3;
667 // Is XcvMonitor or XcvPort requested?
668 if (wcsncmp(pwszSecondParameter
, L
"Monitor ", 8) == 0)
670 // Skip the "Monitor " string.
671 pwszSecondParameter
+= 8;
673 // Look for this monitor in our Print Monitor list.
674 pPrintMonitor
= FindPrintMonitor(pwszSecondParameter
);
677 // The caller supplied a non-existing Monitor name.
678 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
682 else if (wcsncmp(pwszSecondParameter
, L
"Port ", 5) == 0)
684 // Skip the "Port " string.
685 pwszSecondParameter
+= 5;
687 // Look for this port in our Print Monitor Port list.
688 pPort
= FindPort(pwszFirstParameter
);
691 // The supplied port is unknown to all our Print Monitors.
692 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
696 pPrintMonitor
= pPort
->pPrintMonitor
;
700 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
704 // Call the monitor's XcvOpenPort function.
705 if (pPrintMonitor
->bIsLevel2
)
706 bReturnValue
= ((PMONITOR2
)pPrintMonitor
->pMonitor
)->pfnXcvOpenPort(pPrintMonitor
->hMonitor
, pwszSecondParameter
, SERVER_EXECUTE
, &hExternalHandle
);
708 bReturnValue
= ((LPMONITOREX
)pPrintMonitor
->pMonitor
)->Monitor
.pfnXcvOpenPort(pwszSecondParameter
, SERVER_EXECUTE
, &hExternalHandle
);
712 // The XcvOpenPort function failed. Return its last error.
713 dwErrorCode
= GetLastError();
717 // Create a new LOCAL_XCV_HANDLE.
718 pXcvHandle
= DllAllocSplMem(sizeof(LOCAL_XCV_HANDLE
));
721 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
722 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
726 pXcvHandle
->hXcv
= hExternalHandle
;
727 pXcvHandle
->pPrintMonitor
= pPrintMonitor
;
729 // Return the Xcv handle through our general handle.
730 pHandle
->HandleType
= HandleType_Xcv
;
731 pHandle
->pSpecificHandle
= pXcvHandle
;
735 // The caller wants a Printer or Printer Job handle and provided a string like:
737 // "\\COMPUTERNAME\HP DeskJet"
738 // "HP DeskJet, Job 5"
739 // "\\COMPUTERNAME\HP DeskJet, Job 5"
741 // Retrieve the printer from the list.
742 pPrinter
= LookupElementSkiplist(&PrinterList
, &pwszFirstParameter
, NULL
);
745 // The printer does not exist.
746 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
750 // Create a new LOCAL_PRINTER_HANDLE.
751 pPrinterHandle
= DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE
));
754 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
755 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
759 pPrinterHandle
->hSPLFile
= INVALID_HANDLE_VALUE
;
760 pPrinterHandle
->pPrinter
= pPrinter
;
762 // Check if a datatype was given.
763 if (pDefault
&& pDefault
->pDatatype
)
765 // Use the datatype if it's valid.
766 if (!FindDatatype(pPrinter
->pPrintProcessor
, pDefault
->pDatatype
))
768 dwErrorCode
= ERROR_INVALID_DATATYPE
;
772 pPrinterHandle
->pwszDatatype
= AllocSplStr(pDefault
->pDatatype
);
776 // Use the default datatype.
777 pPrinterHandle
->pwszDatatype
= AllocSplStr(pPrinter
->pwszDefaultDatatype
);
780 // Check if a DevMode was given, otherwise use the default.
781 if (pDefault
&& pDefault
->pDevMode
)
782 pPrinterHandle
->pDevMode
= DuplicateDevMode(pDefault
->pDevMode
);
784 pPrinterHandle
->pDevMode
= DuplicateDevMode(pPrinter
->pDefaultDevMode
);
786 // Check if the caller wants a handle to an existing Print Job.
787 if (pwszSecondParameter
)
789 // The "Job " string has to follow now.
790 if (wcsncmp(pwszSecondParameter
, L
"Job ", 4) != 0)
792 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
796 // Skip the "Job " string.
797 pwszSecondParameter
+= 4;
799 // Skip even more whitespace.
800 while (*pwszSecondParameter
== ' ')
801 ++pwszSecondParameter
;
803 // Finally extract the desired Job ID.
804 dwJobID
= wcstoul(pwszSecondParameter
, NULL
, 10);
805 if (!IS_VALID_JOB_ID(dwJobID
))
807 // The user supplied an invalid Job ID.
808 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
812 // Look for this job in the Global Job List.
813 pJob
= LookupElementSkiplist(&GlobalJobList
, &dwJobID
, NULL
);
814 if (!pJob
|| pJob
->pPrinter
!= pPrinter
)
816 // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
817 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
821 // Try to open its SPL file.
822 GetJobFilePath(L
"SPL", dwJobID
, wszFullPath
);
823 pPrinterHandle
->hSPLFile
= CreateFileW(wszFullPath
, GENERIC_READ
| GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
824 if (pPrinterHandle
->hSPLFile
== INVALID_HANDLE_VALUE
)
826 dwErrorCode
= GetLastError();
827 ERR("CreateFileW failed with error %lu for \"%S\"!", dwErrorCode
, wszFullPath
);
831 // Associate the job to our Printer Handle, but don't set bStartedDoc.
832 // This prevents the caller from doing further StartDocPrinter, WritePrinter, etc. calls on it.
833 pPrinterHandle
->pJob
= pJob
;
836 // Return the Printer handle through our general handle.
837 pHandle
->HandleType
= HandleType_Printer
;
838 pHandle
->pSpecificHandle
= pPrinterHandle
;
841 // We were successful! Return the handle.
842 *phPrinter
= (HANDLE
)pHandle
;
843 dwErrorCode
= ERROR_SUCCESS
;
845 // Don't let the cleanup routines free this.
847 pPrinterHandle
= NULL
;
851 DllFreeSplMem(pHandle
);
855 if (pPrinterHandle
->pwszDatatype
)
856 DllFreeSplStr(pPrinterHandle
->pwszDatatype
);
858 if (pPrinterHandle
->pDevMode
)
859 DllFreeSplMem(pPrinterHandle
->pDevMode
);
861 DllFreeSplMem(pPrinterHandle
);
864 if (pwszFirstParameter
)
865 DllFreeSplMem(pwszFirstParameter
);
867 SetLastError(dwErrorCode
);
868 return (dwErrorCode
== ERROR_SUCCESS
);
872 LocalReadPrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pNoBytesRead
)
876 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
877 PLOCAL_PORT_HANDLE pPortHandle
;
878 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
883 dwErrorCode
= ERROR_INVALID_HANDLE
;
887 // Port handles are an entirely different thing.
888 if (pHandle
->HandleType
== HandleType_Port
)
890 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
892 // Call the monitor's ReadPort function.
893 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
894 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnReadPort(pPortHandle
->hPort
, pBuf
, cbBuf
, pNoBytesRead
);
896 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnReadPort(pPortHandle
->hPort
, pBuf
, cbBuf
, pNoBytesRead
);
900 // The ReadPort function failed. Return its last error.
901 dwErrorCode
= GetLastError();
905 // We were successful!
906 dwErrorCode
= ERROR_SUCCESS
;
910 // The remaining function deals with Printer handles only.
911 if (pHandle
->HandleType
!= HandleType_Printer
)
913 dwErrorCode
= ERROR_INVALID_HANDLE
;
917 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
919 // ReadPrinter needs an opened SPL file to work.
920 // This only works if a Printer Job Handle was requested in OpenPrinter.
921 if (pPrinterHandle
->hSPLFile
== INVALID_HANDLE_VALUE
)
923 dwErrorCode
= ERROR_INVALID_HANDLE
;
927 // Pass the parameters to ReadFile.
928 if (!ReadFile(pPrinterHandle
->hSPLFile
, pBuf
, cbBuf
, pNoBytesRead
, NULL
))
930 dwErrorCode
= GetLastError();
931 ERR("ReadFile failed with error %lu!\n", dwErrorCode
);
935 dwErrorCode
= ERROR_SUCCESS
;
938 SetLastError(dwErrorCode
);
939 return (dwErrorCode
== ERROR_SUCCESS
);
943 LocalStartDocPrinter(HANDLE hPrinter
, DWORD Level
, PBYTE pDocInfo
)
947 DWORD dwReturnValue
= 0;
948 PDOC_INFO_1W pDocInfo1
= (PDOC_INFO_1W
)pDocInfo
;
950 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
951 PLOCAL_PORT_HANDLE pPortHandle
;
952 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
957 dwErrorCode
= ERROR_INVALID_HANDLE
;
961 // Port handles are an entirely different thing.
962 if (pHandle
->HandleType
== HandleType_Port
)
964 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
966 // This call should come from a Print Processor and the job this port is going to print was assigned to us before opening the Print Processor.
967 // Claim it exclusively for this port handle.
968 pJob
= pPortHandle
->pPort
->pNextJobToProcess
;
969 pPortHandle
->pPort
->pNextJobToProcess
= NULL
;
972 // Call the monitor's StartDocPort function.
973 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
974 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnStartDocPort(pPortHandle
->hPort
, pJob
->pPrinter
->pwszPrinterName
, pJob
->dwJobID
, Level
, pDocInfo
);
976 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnStartDocPort(pPortHandle
->hPort
, pJob
->pPrinter
->pwszPrinterName
, pJob
->dwJobID
, Level
, pDocInfo
);
980 // The StartDocPort function failed. Return its last error.
981 dwErrorCode
= GetLastError();
985 // We were successful!
986 dwErrorCode
= ERROR_SUCCESS
;
987 dwReturnValue
= pJob
->dwJobID
;
991 // The remaining function deals with Printer handles only.
992 if (pHandle
->HandleType
!= HandleType_Printer
)
994 dwErrorCode
= ERROR_INVALID_HANDLE
;
1000 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1004 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1006 // pJob may already be occupied if this is a Print Job handle. In this case, StartDocPrinter has to fail.
1007 if (pPrinterHandle
->pJob
)
1009 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1013 // Check the validity of the datatype if we got one.
1014 if (pDocInfo1
->pDatatype
&& !FindDatatype(pPrinterHandle
->pJob
->pPrintProcessor
, pDocInfo1
->pDatatype
))
1016 dwErrorCode
= ERROR_INVALID_DATATYPE
;
1020 // Check if this is the right document information level.
1023 dwErrorCode
= ERROR_INVALID_LEVEL
;
1027 // All requirements are met - create a new job.
1028 dwErrorCode
= CreateJob(pPrinterHandle
);
1029 if (dwErrorCode
!= ERROR_SUCCESS
)
1032 // Use any given datatype.
1033 if (pDocInfo1
->pDatatype
&& !ReallocSplStr(&pPrinterHandle
->pJob
->pwszDatatype
, pDocInfo1
->pDatatype
))
1035 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1036 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1040 // Use any given document name.
1041 if (pDocInfo1
->pDocName
&& !ReallocSplStr(&pPrinterHandle
->pJob
->pwszDocumentName
, pDocInfo1
->pDocName
))
1043 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1044 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1048 // We were successful!
1049 dwErrorCode
= ERROR_SUCCESS
;
1050 dwReturnValue
= pPrinterHandle
->pJob
->dwJobID
;
1053 SetLastError(dwErrorCode
);
1054 return dwReturnValue
;
1058 LocalStartPagePrinter(HANDLE hPrinter
)
1061 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1062 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1065 if (!pHandle
|| pHandle
->HandleType
!= HandleType_Printer
)
1067 dwErrorCode
= ERROR_INVALID_HANDLE
;
1071 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1073 // We require StartDocPrinter or AddJob to be called first.
1074 if (!pPrinterHandle
->bStartedDoc
)
1076 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
1080 // Increase the page count.
1081 ++pPrinterHandle
->pJob
->dwTotalPages
;
1082 dwErrorCode
= ERROR_SUCCESS
;
1085 SetLastError(dwErrorCode
);
1086 return (dwErrorCode
== ERROR_SUCCESS
);
1090 LocalWritePrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pcWritten
)
1094 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1095 PLOCAL_PORT_HANDLE pPortHandle
;
1096 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1101 dwErrorCode
= ERROR_INVALID_HANDLE
;
1105 // Port handles are an entirely different thing.
1106 if (pHandle
->HandleType
== HandleType_Port
)
1108 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1110 // Call the monitor's WritePort function.
1111 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1112 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnWritePort(pPortHandle
->hPort
, pBuf
, cbBuf
, pcWritten
);
1114 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnWritePort(pPortHandle
->hPort
, pBuf
, cbBuf
, pcWritten
);
1118 // The WritePort function failed. Return its last error.
1119 dwErrorCode
= GetLastError();
1123 // We were successful!
1124 dwErrorCode
= ERROR_SUCCESS
;
1128 // The remaining function deals with Printer handles only.
1129 if (pHandle
->HandleType
!= HandleType_Printer
)
1131 dwErrorCode
= ERROR_INVALID_HANDLE
;
1135 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1137 // We require StartDocPrinter or AddJob to be called first.
1138 if (!pPrinterHandle
->bStartedDoc
)
1140 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
1144 // TODO: This function is only called when doing non-spooled printing.
1145 // This needs to be investigated further. We can't just use pPrinterHandle->hSPLFile here, because that's currently reserved for Printer Job handles (see LocalReadPrinter).
1147 // Pass the parameters to WriteFile.
1148 if (!WriteFile(SOME_SPOOL_FILE_HANDLE
, pBuf
, cbBuf
, pcWritten
, NULL
))
1150 dwErrorCode
= GetLastError();
1151 ERR("WriteFile failed with error %lu!\n", GetLastError());
1156 dwErrorCode
= ERROR_SUCCESS
;
1159 SetLastError(dwErrorCode
);
1160 return (dwErrorCode
== ERROR_SUCCESS
);
1164 LocalEndPagePrinter(HANDLE hPrinter
)
1167 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1170 if (!pHandle
|| pHandle
->HandleType
!= HandleType_Printer
)
1172 dwErrorCode
= ERROR_INVALID_HANDLE
;
1176 // This function doesn't do anything else for now.
1177 dwErrorCode
= ERROR_SUCCESS
;
1180 SetLastError(dwErrorCode
);
1181 return (dwErrorCode
== ERROR_SUCCESS
);
1185 LocalEndDocPrinter(HANDLE hPrinter
)
1189 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1190 PLOCAL_PORT_HANDLE pPortHandle
;
1191 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1196 dwErrorCode
= ERROR_INVALID_HANDLE
;
1200 // Port handles are an entirely different thing.
1201 if (pHandle
->HandleType
== HandleType_Port
)
1203 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1205 // Call the monitor's EndDocPort function.
1206 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1207 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnEndDocPort(pPortHandle
->hPort
);
1209 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnEndDocPort(pPortHandle
->hPort
);
1213 // The EndDocPort function failed. Return its last error.
1214 dwErrorCode
= GetLastError();
1218 // We were successful!
1219 dwErrorCode
= ERROR_SUCCESS
;
1223 // The remaining function deals with Printer handles only.
1224 if (pHandle
->HandleType
!= HandleType_Printer
)
1226 dwErrorCode
= ERROR_INVALID_HANDLE
;
1230 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1232 // We require StartDocPrinter or AddJob to be called first.
1233 if (!pPrinterHandle
->bStartedDoc
)
1235 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
1239 // TODO: Something like ScheduleJob
1242 pPrinterHandle
->bStartedDoc
= FALSE
;
1243 pPrinterHandle
->pJob
= NULL
;
1244 dwErrorCode
= ERROR_SUCCESS
;
1247 SetLastError(dwErrorCode
);
1248 return (dwErrorCode
== ERROR_SUCCESS
);
1252 LocalClosePrinter(HANDLE hPrinter
)
1254 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1255 PLOCAL_PORT_HANDLE pPortHandle
;
1256 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1257 PLOCAL_XCV_HANDLE pXcvHandle
;
1261 SetLastError(ERROR_INVALID_HANDLE
);
1265 if (pHandle
->HandleType
== HandleType_Port
)
1267 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1269 // Call the monitor's ClosePort function.
1270 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1271 ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnClosePort(pPortHandle
->hPort
);
1273 ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnClosePort(pPortHandle
->hPort
);
1275 else if (pHandle
->HandleType
== HandleType_Printer
)
1277 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1279 // Terminate any started job.
1280 if (pPrinterHandle
->pJob
)
1281 FreeJob(pPrinterHandle
->pJob
);
1283 // Free memory for the fields.
1284 DllFreeSplMem(pPrinterHandle
->pDevMode
);
1285 DllFreeSplStr(pPrinterHandle
->pwszDatatype
);
1287 else if (pHandle
->HandleType
== HandleType_Xcv
)
1289 pXcvHandle
= (PLOCAL_XCV_HANDLE
)pHandle
->pSpecificHandle
;
1291 // Call the monitor's XcvClosePort function.
1292 if (pXcvHandle
->pPrintMonitor
->bIsLevel2
)
1293 ((PMONITOR2
)pXcvHandle
->pPrintMonitor
->pMonitor
)->pfnXcvClosePort(pXcvHandle
->hXcv
);
1295 ((LPMONITOREX
)pXcvHandle
->pPrintMonitor
->pMonitor
)->Monitor
.pfnXcvClosePort(pXcvHandle
->hXcv
);
1298 // Free memory for the handle and the specific handle.
1299 DllFreeSplMem(pHandle
->pSpecificHandle
);
1300 DllFreeSplMem(pHandle
);