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-2017 Colin Finck <colin@reactos.org>
14 static DWORD dwPrinterInfo1Offsets
[] = {
15 FIELD_OFFSET(PRINTER_INFO_1W
, pName
),
16 FIELD_OFFSET(PRINTER_INFO_1W
, pComment
),
17 FIELD_OFFSET(PRINTER_INFO_1W
, pDescription
),
22 * @name _PrinterListCompareRoutine
24 * SKIPLIST_COMPARE_ROUTINE for the Printer List.
25 * Does a case-insensitive comparison, because e.g. LocalOpenPrinter doesn't match the case when looking for Printers.
28 _PrinterListCompareRoutine(PVOID FirstStruct
, PVOID SecondStruct
)
30 PLOCAL_PRINTER A
= (PLOCAL_PRINTER
)FirstStruct
;
31 PLOCAL_PRINTER B
= (PLOCAL_PRINTER
)SecondStruct
;
33 return wcsicmp(A
->pwszPrinterName
, B
->pwszPrinterName
);
37 * @name InitializePrinterList
39 * Initializes a list of locally available Printers.
40 * The list is searchable by name and returns information about the printers, including their job queues.
41 * During this process, the job queues are also initialized.
44 InitializePrinterList()
46 const WCHAR wszPrintersKey
[] = L
"SYSTEM\\CurrentControlSet\\Control\\Print\\Printers";
56 PLOCAL_PRINTER pPrinter
= NULL
;
57 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
58 PWSTR pwszPort
= NULL
;
59 PWSTR pwszPrintProcessor
= NULL
;
60 WCHAR wszPrinterName
[MAX_PRINTER_NAME
+ 1];
62 // Initialize an empty list for our printers.
63 InitializeSkiplist(&PrinterList
, DllAllocSplMem
, _PrinterListCompareRoutine
, (PSKIPLIST_FREE_ROUTINE
)DllFreeSplMem
);
65 // Open our printers registry key. Each subkey is a local printer there.
66 dwErrorCode
= (DWORD
)RegOpenKeyExW(HKEY_LOCAL_MACHINE
, wszPrintersKey
, 0, KEY_READ
, &hKey
);
67 if (dwErrorCode
!= ERROR_SUCCESS
)
69 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode
);
73 // Get the number of subkeys.
74 dwErrorCode
= (DWORD
)RegQueryInfoKeyW(hKey
, NULL
, NULL
, NULL
, &dwSubKeys
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
75 if (dwErrorCode
!= ERROR_SUCCESS
)
77 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode
);
81 // Loop through all available local printers.
82 for (i
= 0; i
< dwSubKeys
; i
++)
84 // Cleanup tasks from the previous run
93 if (pPrinter
->pDefaultDevMode
)
94 DllFreeSplMem(pPrinter
->pDefaultDevMode
);
96 if (pPrinter
->pwszDefaultDatatype
)
97 DllFreeSplStr(pPrinter
->pwszDefaultDatatype
);
99 if (pPrinter
->pwszDescription
)
100 DllFreeSplStr(pPrinter
->pwszDescription
);
102 if (pPrinter
->pwszPrinterDriver
)
103 DllFreeSplStr(pPrinter
->pwszPrinterDriver
);
105 if (pPrinter
->pwszPrinterName
)
106 DllFreeSplStr(pPrinter
->pwszPrinterName
);
108 DllFreeSplMem(pPrinter
);
112 if (pwszPrintProcessor
)
114 DllFreeSplStr(pwszPrintProcessor
);
115 pwszPrintProcessor
= NULL
;
118 // Get the name of this printer.
119 cchPrinterName
= _countof(wszPrinterName
);
120 dwErrorCode
= (DWORD
)RegEnumKeyExW(hKey
, i
, wszPrinterName
, &cchPrinterName
, NULL
, NULL
, NULL
, NULL
);
121 if (dwErrorCode
== ERROR_MORE_DATA
)
123 // This printer name exceeds the maximum length and is invalid.
126 else if (dwErrorCode
!= ERROR_SUCCESS
)
128 ERR("RegEnumKeyExW failed for iteration %lu with status %lu!\n", i
, dwErrorCode
);
132 // Open this Printer's registry key.
133 dwErrorCode
= (DWORD
)RegOpenKeyExW(hKey
, wszPrinterName
, 0, KEY_READ
, &hSubKey
);
134 if (dwErrorCode
!= ERROR_SUCCESS
)
136 ERR("RegOpenKeyExW failed for Printer \"%S\" with status %lu!\n", wszPrinterName
, dwErrorCode
);
140 // Get the Print Processor.
141 pwszPrintProcessor
= AllocAndRegQueryWSZ(hSubKey
, L
"Print Processor");
142 if (!pwszPrintProcessor
)
145 // Try to find it in the Print Processor List.
146 pPrintProcessor
= FindPrintProcessor(pwszPrintProcessor
);
147 if (!pPrintProcessor
)
149 ERR("Invalid Print Processor \"%S\" for Printer \"%S\"!\n", pwszPrintProcessor
, wszPrinterName
);
154 pwszPort
= AllocAndRegQueryWSZ(hSubKey
, L
"Port");
158 // Try to find it in the Port List.
159 pPort
= FindPort(pwszPort
);
162 ERR("Invalid Port \"%S\" for Printer \"%S\"!\n", pwszPort
, wszPrinterName
);
166 // Create a new LOCAL_PRINTER structure for it.
167 pPrinter
= DllAllocSplMem(sizeof(LOCAL_PRINTER
));
170 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
171 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
175 pPrinter
->pwszPrinterName
= AllocSplStr(wszPrinterName
);
176 pPrinter
->pPrintProcessor
= pPrintProcessor
;
177 pPrinter
->pPort
= pPort
;
178 InitializePrinterJobList(pPrinter
);
181 pPrinter
->pwszLocation
= AllocAndRegQueryWSZ(hSubKey
, L
"Location");
182 if (!pPrinter
->pwszLocation
)
185 // Get the printer driver.
186 pPrinter
->pwszPrinterDriver
= AllocAndRegQueryWSZ(hSubKey
, L
"Printer Driver");
187 if (!pPrinter
->pwszPrinterDriver
)
190 // Get the description.
191 pPrinter
->pwszDescription
= AllocAndRegQueryWSZ(hSubKey
, L
"Description");
192 if (!pPrinter
->pwszDescription
)
195 // Get the default datatype.
196 pPrinter
->pwszDefaultDatatype
= AllocAndRegQueryWSZ(hSubKey
, L
"Datatype");
197 if (!pPrinter
->pwszDefaultDatatype
)
200 // Verify that it's valid.
201 if (!FindDatatype(pPrintProcessor
, pPrinter
->pwszDefaultDatatype
))
203 ERR("Invalid default datatype \"%S\" for Printer \"%S\"!\n", pPrinter
->pwszDefaultDatatype
, wszPrinterName
);
207 // Determine the size of the DevMode.
208 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Default DevMode", NULL
, NULL
, NULL
, &cbData
);
209 if (dwErrorCode
!= ERROR_SUCCESS
)
211 ERR("Couldn't query the size of the DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName
, dwErrorCode
, cbData
);
215 // Allocate enough memory for the DevMode.
216 pPrinter
->pDefaultDevMode
= DllAllocSplMem(cbData
);
217 if (!pPrinter
->pDefaultDevMode
)
219 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
220 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
224 // Get the default DevMode.
225 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Default DevMode", NULL
, NULL
, (PBYTE
)pPrinter
->pDefaultDevMode
, &cbData
);
226 if (dwErrorCode
!= ERROR_SUCCESS
)
228 ERR("Couldn't query a DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName
, dwErrorCode
, cbData
);
232 // Get the Attributes.
233 cbData
= sizeof(DWORD
);
234 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Attributes", NULL
, NULL
, (PBYTE
)&pPrinter
->dwAttributes
, &cbData
);
235 if (dwErrorCode
!= ERROR_SUCCESS
)
237 ERR("Couldn't query Attributes for Printer \"%S\", status is %lu!\n", wszPrinterName
, dwErrorCode
);
242 cbData
= sizeof(DWORD
);
243 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Status", NULL
, NULL
, (PBYTE
)&pPrinter
->dwStatus
, &cbData
);
244 if (dwErrorCode
!= ERROR_SUCCESS
)
246 ERR("Couldn't query Status for Printer \"%S\", status is %lu!\n", wszPrinterName
, dwErrorCode
);
250 // Add this printer to the printer list.
251 if (!InsertElementSkiplist(&PrinterList
, pPrinter
))
253 ERR("InsertElementSkiplist failed for Printer \"%S\"!\n", pPrinter
->pwszPrinterName
);
257 // Don't let the cleanup routines free this.
261 dwErrorCode
= ERROR_SUCCESS
;
266 RegCloseKey(hSubKey
);
270 if (pPrinter
->pDefaultDevMode
)
271 DllFreeSplMem(pPrinter
->pDefaultDevMode
);
273 if (pPrinter
->pwszDefaultDatatype
)
274 DllFreeSplStr(pPrinter
->pwszDefaultDatatype
);
276 if (pPrinter
->pwszDescription
)
277 DllFreeSplStr(pPrinter
->pwszDescription
);
279 if (pPrinter
->pwszPrinterDriver
)
280 DllFreeSplStr(pPrinter
->pwszPrinterDriver
);
282 if (pPrinter
->pwszPrinterName
)
283 DllFreeSplStr(pPrinter
->pwszPrinterName
);
285 DllFreeSplMem(pPrinter
);
288 if (pwszPrintProcessor
)
289 DllFreeSplStr(pwszPrintProcessor
);
295 SetLastError(dwErrorCode
);
296 return (dwErrorCode
== ERROR_SUCCESS
);
300 * @name _LocalEnumPrintersCheckName
302 * Checks the Name parameter supplied to a call to EnumPrinters.
305 * Flags parameter of EnumPrinters.
308 * Name parameter of EnumPrinters to check.
310 * @param pwszComputerName
311 * Pointer to a string able to hold 2 + MAX_COMPUTERNAME_LENGTH + 1 + 1 characters.
312 * On return, it may contain a computer name to prepend in EnumPrinters depending on the case.
314 * @param pcchComputerName
315 * If a string to prepend is returned, this pointer receives its length in characters.
318 * ERROR_SUCCESS if processing in EnumPrinters can be continued.
319 * ERROR_INVALID_NAME if the Name parameter is invalid for the given flags and this Print Provider.
320 * Any other error code if GetComputerNameW fails. Error codes indicating failure should then be returned by EnumPrinters.
323 _LocalEnumPrintersCheckName(DWORD Flags
, PCWSTR Name
, PWSTR pwszComputerName
, PDWORD pcchComputerName
)
326 PCWSTR pComputerName
;
328 // If there is no Name parameter to check, we can just continue in EnumPrinters.
330 return ERROR_SUCCESS
;
332 // Check if Name does not begin with two backslashes (required for specifying Computer Names).
333 if (Name
[0] != L
'\\' || Name
[1] != L
'\\')
335 if (Flags
& PRINTER_ENUM_NAME
)
337 // If PRINTER_ENUM_NAME is specified, any given Name parameter may only contain the
338 // Print Provider Name or the local Computer Name.
340 // Compare with the Print Provider Name.
341 if (wcsicmp(Name
, wszPrintProviderInfo
[0]) == 0)
342 return ERROR_SUCCESS
;
344 // Dismiss anything else.
345 return ERROR_INVALID_NAME
;
349 // If PRINTER_ENUM_NAME is not specified, we just ignore anything that is not a Computer Name.
350 return ERROR_SUCCESS
;
354 // Prepend the backslashes to the output computer name.
355 pwszComputerName
[0] = L
'\\';
356 pwszComputerName
[1] = L
'\\';
358 // Get the local computer name for comparison.
359 *pcchComputerName
= MAX_COMPUTERNAME_LENGTH
+ 1;
360 if (!GetComputerNameW(&pwszComputerName
[2], pcchComputerName
))
362 ERR("GetComputerNameW failed with error %lu!\n", GetLastError());
363 return GetLastError();
366 // Add the leading slashes to the total length.
367 *pcchComputerName
+= 2;
369 // Compare both names.
370 pComputerName
= &pwszComputerName
[2];
374 // Are we at the end of the local Computer Name string?
377 // Are we also at the end of the supplied Name parameter?
378 // A terminating NUL character and a backslash are both treated as the end, but they are treated differently.
381 // If both names match and Name ends with a NUL character, the computer name will be prepended in EnumPrinters.
382 // Add a trailing backslash for that.
383 pwszComputerName
[(*pcchComputerName
)++] = L
'\\';
384 pwszComputerName
[*pcchComputerName
] = 0;
385 return ERROR_SUCCESS
;
387 else if (*pName
== L
'\\')
389 if (Flags
& PRINTER_ENUM_NAME
)
391 // If PRINTER_ENUM_NAME is specified and a Name parameter is given, it must be exactly the local
392 // Computer Name with two backslashes prepended. Anything else (like "\\COMPUTERNAME\") is dismissed.
393 return ERROR_INVALID_NAME
;
397 // If PRINTER_ENUM_NAME is not specified and a Name parameter is given, it may also end with a backslash.
398 // Only the Computer Name between the backslashes is checked then.
399 // This is largely undocumented, but verified by tests (see winspool_apitest).
400 // In this case, no computer name is prepended in EnumPrinters though.
401 *pwszComputerName
= 0;
402 *pcchComputerName
= 0;
403 return ERROR_SUCCESS
;
408 // Compare both Computer Names case-insensitively and reject with ERROR_INVALID_NAME if they don't match.
409 if (towlower(*pName
) != towlower(*pComputerName
))
410 return ERROR_INVALID_NAME
;
418 _DumpLevel1PrintProviderInformation(PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
)
422 // Count the needed bytes for Print Provider information.
423 *pcbNeeded
= sizeof(PRINTER_INFO_1W
);
425 for (i
= 0; i
< 3; i
++)
426 *pcbNeeded
+= (wcslen(wszPrintProviderInfo
[i
]) + 1) * sizeof(WCHAR
);
428 // Check if the supplied buffer is large enough.
429 if (cbBuf
< *pcbNeeded
)
430 return ERROR_INSUFFICIENT_BUFFER
;
432 // Copy over the Print Provider information.
433 ((PPRINTER_INFO_1W
)pPrinterEnum
)->Flags
= 0;
434 PackStrings(wszPrintProviderInfo
, pPrinterEnum
, dwPrinterInfo1Offsets
, &pPrinterEnum
[*pcbNeeded
]);
437 return ERROR_SUCCESS
;
441 _LocalEnumPrintersLevel0(DWORD Flags
, PCWSTR Name
, PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
, DWORD cchComputerName
, PWSTR wszComputerName
)
443 return ERROR_INVALID_LEVEL
;
447 _LocalEnumPrintersLevel1(DWORD Flags
, PCWSTR Name
, PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
, DWORD cchComputerName
, PWSTR wszComputerName
)
449 const WCHAR wszComma
[] = L
",";
453 size_t cbDescription
;
457 PBYTE pPrinterStrings
;
458 PSKIPLIST_NODE pNode
;
459 PLOCAL_PRINTER pPrinter
;
461 PWSTR pwszStrings
[3];
463 if (Flags
& PRINTER_ENUM_NAME
&& !Name
)
465 // The caller wants information about this Print Provider.
466 // spoolss packs this into an array of information about all Print Providers.
467 dwErrorCode
= _DumpLevel1PrintProviderInformation(pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
471 // Count the required buffer size and the number of printers.
474 for (pNode
= PrinterList
.Head
.Next
[0]; pNode
; pNode
= pNode
->Next
[0])
476 pPrinter
= (PLOCAL_PRINTER
)pNode
->Element
;
478 // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
479 if (Flags
& PRINTER_ENUM_SHARED
)
482 // Attention: pComment equals the "Description" registry value while pDescription is concatenated out of several strings.
483 // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
484 cbName
= (cchComputerName
+ wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
485 cbComment
= (wcslen(pPrinter
->pwszDescription
) + 1) * sizeof(WCHAR
);
486 cbDescription
= cbName
+ (wcslen(pPrinter
->pwszPrinterDriver
) + 1 + wcslen(pPrinter
->pwszLocation
) + 1) * sizeof(WCHAR
);
488 *pcbNeeded
+= sizeof(PRINTER_INFO_1W
) + cbName
+ cbComment
+ cbDescription
;
492 // Check if the supplied buffer is large enough.
493 if (cbBuf
< *pcbNeeded
)
495 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
499 // Initialize the variables for filling the output buffer using PackStrings.
500 pPrinterInfo
= pPrinterEnum
;
501 pPrinterStrings
= &pPrinterEnum
[*pcbNeeded
];
503 // Copy over the Printer information.
504 for (pNode
= PrinterList
.Head
.Next
[0]; pNode
; pNode
= pNode
->Next
[0])
506 pPrinter
= (PLOCAL_PRINTER
)pNode
->Element
;
508 // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
509 if (Flags
& PRINTER_ENUM_SHARED
)
512 // Indicate that this is a Printer.
513 ((PPRINTER_INFO_1W
)pPrinterInfo
)->Flags
= PRINTER_ENUM_ICON8
;
515 // Calculate the string lengths.
516 cbName
= (cchComputerName
+ wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
517 cbComment
= (wcslen(pPrinter
->pwszDescription
) + 1) * sizeof(WCHAR
);
518 cbDescription
= cbName
+ (wcslen(pPrinter
->pwszPrinterDriver
) + 1 + wcslen(pPrinter
->pwszLocation
) + 1) * sizeof(WCHAR
);
520 // Copy the Printer Name.
521 pwszStrings
[0] = DllAllocSplMem(cbName
);
523 StringCbCopyExW(p
, cbName
, wszComputerName
, &p
, &cbName
, 0);
524 StringCbCopyExW(p
, cbName
, pPrinter
->pwszPrinterName
, &p
, &cbName
, 0);
526 // Copy the Printer comment (equals the "Description" registry value).
527 pwszStrings
[1] = pPrinter
->pwszDescription
;
529 // Copy the description, which for PRINTER_INFO_1W has the form "Name,Printer Driver,Location"
530 pwszStrings
[2] = DllAllocSplMem(cbDescription
);
532 StringCbCopyExW(p
, cbDescription
, wszComputerName
, &p
, &cbDescription
, 0);
533 StringCbCopyExW(p
, cbDescription
, pPrinter
->pwszPrinterName
, &p
, &cbDescription
, 0);
534 StringCbCopyExW(p
, cbDescription
, wszComma
, &p
, &cbDescription
, 0);
535 StringCbCopyExW(p
, cbDescription
, pPrinter
->pwszPrinterDriver
, &p
, &cbDescription
, 0);
536 StringCbCopyExW(p
, cbDescription
, wszComma
, &p
, &cbDescription
, 0);
537 StringCbCopyExW(p
, cbDescription
, pPrinter
->pwszLocation
, &p
, &cbDescription
, 0);
539 // Finally copy the structure and advance to the next one in the output buffer.
540 pPrinterStrings
= PackStrings(pwszStrings
, pPrinterInfo
, dwPrinterInfo1Offsets
, pPrinterStrings
);
541 pPrinterInfo
+= sizeof(PRINTER_INFO_1W
);
543 // Free the memory for temporary strings.
544 DllFreeSplMem(pwszStrings
[0]);
545 DllFreeSplMem(pwszStrings
[2]);
549 dwErrorCode
= ERROR_SUCCESS
;
556 _LocalEnumPrintersLevel2(DWORD Flags
, PCWSTR Name
, PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
, DWORD cchComputerName
, PWSTR wszComputerName
)
558 return ERROR_INVALID_LEVEL
;
562 _LocalEnumPrintersLevel4(DWORD Flags
, PCWSTR Name
, PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
, DWORD cchComputerName
, PWSTR wszComputerName
)
564 return ERROR_INVALID_LEVEL
;
568 _LocalEnumPrintersLevel5(DWORD Flags
, PCWSTR Name
, PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
, DWORD cchComputerName
, PWSTR wszComputerName
)
570 return ERROR_INVALID_LEVEL
;
574 LocalEnumPrinters(DWORD Flags
, LPWSTR Name
, DWORD Level
, LPBYTE pPrinterEnum
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
576 DWORD cchComputerName
= 0;
578 WCHAR wszComputerName
[2 + MAX_COMPUTERNAME_LENGTH
+ 1 + 1] = { 0 };
587 if (Flags
& PRINTER_ENUM_CONNECTIONS
|| Flags
& PRINTER_ENUM_REMOTE
|| Flags
& PRINTER_ENUM_NETWORK
)
589 // If the flags for the Network Print Provider are given, bail out with ERROR_INVALID_NAME.
590 // This is the internal way for a Print Provider to signal that it doesn't handle this request.
591 dwErrorCode
= ERROR_INVALID_NAME
;
595 if (!(Flags
& PRINTER_ENUM_LOCAL
|| Flags
& PRINTER_ENUM_NAME
))
597 // The Local Print Provider is the right destination for the request, but without any of these flags,
598 // there is no information that can be returned.
599 // So just signal a successful request.
600 dwErrorCode
= ERROR_SUCCESS
;
604 // Check the supplied Name parameter (if any).
605 // This may return a Computer Name string we later prepend to the output.
606 dwErrorCode
= _LocalEnumPrintersCheckName(Flags
, Name
, wszComputerName
, &cchComputerName
);
607 if (dwErrorCode
!= ERROR_SUCCESS
)
612 dwErrorCode
= _LocalEnumPrintersLevel0(Flags
, Name
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
, cchComputerName
, wszComputerName
);
616 dwErrorCode
= _LocalEnumPrintersLevel1(Flags
, Name
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
, cchComputerName
, wszComputerName
);
620 dwErrorCode
= _LocalEnumPrintersLevel2(Flags
, Name
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
, cchComputerName
, wszComputerName
);
624 dwErrorCode
= _LocalEnumPrintersLevel4(Flags
, Name
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
, cchComputerName
, wszComputerName
);
628 dwErrorCode
= _LocalEnumPrintersLevel5(Flags
, Name
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
, cchComputerName
, wszComputerName
);
632 // The caller supplied an invalid level.
633 dwErrorCode
= ERROR_INVALID_LEVEL
;
637 SetLastError(dwErrorCode
);
638 return (dwErrorCode
== ERROR_SUCCESS
);
642 LocalOpenPrinter(PWSTR lpPrinterName
, HANDLE
* phPrinter
, PPRINTER_DEFAULTSW pDefault
)
645 DWORD cchComputerName
;
646 DWORD cchFirstParameter
;
649 HANDLE hExternalHandle
;
650 PWSTR p
= lpPrinterName
;
651 PWSTR pwszFirstParameter
= NULL
;
652 PWSTR pwszSecondParameter
= NULL
;
654 PLOCAL_HANDLE pHandle
= NULL
;
656 PLOCAL_PORT_HANDLE pPortHandle
= NULL
;
657 PLOCAL_PRINT_MONITOR pPrintMonitor
;
658 PLOCAL_PRINTER pPrinter
;
659 PLOCAL_PRINTER_HANDLE pPrinterHandle
= NULL
;
660 PLOCAL_XCV_HANDLE pXcvHandle
= NULL
;
661 WCHAR wszComputerName
[MAX_COMPUTERNAME_LENGTH
+ 1];
662 WCHAR wszFullPath
[MAX_PATH
];
664 // TODO: lpPrinterName == NULL is supported and means access to the local printer server.
665 // Not sure yet if that is passed down to localspl.dll or processed in advance.
668 if (!lpPrinterName
|| !phPrinter
)
670 dwErrorCode
= ERROR_INVALID_PARAMETER
;
676 // Skip any server name in the first parameter.
677 // Does lpPrinterName begin with two backslashes to indicate a server name?
678 if (lpPrinterName
[0] == L
'\\' && lpPrinterName
[1] == L
'\\')
680 // Skip these two backslashes.
683 // Look for the closing backslash.
684 p
= wcschr(lpPrinterName
, L
'\\');
687 // We didn't get a proper server name.
688 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
692 // Get the local computer name for comparison.
693 cchComputerName
= _countof(wszComputerName
);
694 if (!GetComputerNameW(wszComputerName
, &cchComputerName
))
696 dwErrorCode
= GetLastError();
697 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode
);
701 // Now compare this string excerpt with the local computer name.
702 // The input parameter may not be writable, so we can't null-terminate the input string at this point.
703 // This print provider only supports local printers, so both strings have to match.
704 if (p
- lpPrinterName
!= cchComputerName
|| _wcsnicmp(lpPrinterName
, wszComputerName
, cchComputerName
) != 0)
706 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
710 // We have checked the server name and don't need it anymore.
711 lpPrinterName
= p
+ 1;
714 // Look for a comma. If it exists, it indicates the end of the first parameter.
715 pwszSecondParameter
= wcschr(lpPrinterName
, L
',');
716 if (pwszSecondParameter
)
717 cchFirstParameter
= pwszSecondParameter
- p
;
719 cchFirstParameter
= wcslen(lpPrinterName
);
721 // We must have at least one parameter.
722 if (!cchFirstParameter
&& !pwszSecondParameter
)
724 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
728 // Do we have a first parameter?
729 if (cchFirstParameter
)
732 // No null-termination is necessary here, because DllAllocSplMem returns a zero-initialized buffer.
733 pwszFirstParameter
= DllAllocSplMem((cchFirstParameter
+ 1) * sizeof(WCHAR
));
734 CopyMemory(pwszFirstParameter
, lpPrinterName
, cchFirstParameter
* sizeof(WCHAR
));
737 // Do we have a second parameter?
738 if (pwszSecondParameter
)
740 // Yes, skip the comma at the beginning.
741 ++pwszSecondParameter
;
743 // Skip whitespace as well.
744 while (*pwszSecondParameter
== L
' ')
745 ++pwszSecondParameter
;
748 // Create a new handle.
749 pHandle
= DllAllocSplMem(sizeof(LOCAL_HANDLE
));
752 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
753 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
757 // Now we can finally check the type of handle actually requested.
758 if (pwszFirstParameter
&& pwszSecondParameter
&& wcsncmp(pwszSecondParameter
, L
"Port", 4) == 0)
760 // The caller wants a port handle and provided a string like:
762 // "\\COMPUTERNAME\LPT1:, Port"
764 // Look for this port in our Print Monitor Port list.
765 pPort
= FindPort(pwszFirstParameter
);
768 // The supplied port is unknown to all our Print Monitors.
769 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
773 pPrintMonitor
= pPort
->pPrintMonitor
;
775 // Call the monitor's OpenPort function.
776 if (pPrintMonitor
->bIsLevel2
)
777 bReturnValue
= ((PMONITOR2
)pPrintMonitor
->pMonitor
)->pfnOpenPort(pPrintMonitor
->hMonitor
, pwszFirstParameter
, &hExternalHandle
);
779 bReturnValue
= ((LPMONITOREX
)pPrintMonitor
->pMonitor
)->Monitor
.pfnOpenPort(pwszFirstParameter
, &hExternalHandle
);
783 // The OpenPort function failed. Return its last error.
784 dwErrorCode
= GetLastError();
788 // Create a new LOCAL_PORT_HANDLE.
789 pPortHandle
= DllAllocSplMem(sizeof(LOCAL_PORT_HANDLE
));
792 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
793 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
797 pPortHandle
->hPort
= hExternalHandle
;
798 pPortHandle
->pPort
= pPort
;
800 // Return the Port handle through our general handle.
801 pHandle
->HandleType
= HandleType_Port
;
802 pHandle
->pSpecificHandle
= pPortHandle
;
804 else if (!pwszFirstParameter
&& pwszSecondParameter
&& wcsncmp(pwszSecondParameter
, L
"Xcv", 3) == 0)
806 // The caller wants an Xcv handle and provided a string like:
807 // ", XcvMonitor Local Port"
808 // "\\COMPUTERNAME\, XcvMonitor Local Port"
810 // "\\COMPUTERNAME\, XcvPort LPT1:"
812 // Skip the "Xcv" string.
813 pwszSecondParameter
+= 3;
815 // Is XcvMonitor or XcvPort requested?
816 if (wcsncmp(pwszSecondParameter
, L
"Monitor ", 8) == 0)
818 // Skip the "Monitor " string.
819 pwszSecondParameter
+= 8;
821 // Look for this monitor in our Print Monitor list.
822 pPrintMonitor
= FindPrintMonitor(pwszSecondParameter
);
825 // The caller supplied a non-existing Monitor name.
826 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
830 else if (wcsncmp(pwszSecondParameter
, L
"Port ", 5) == 0)
832 // Skip the "Port " string.
833 pwszSecondParameter
+= 5;
835 // Look for this port in our Print Monitor Port list.
836 pPort
= FindPort(pwszFirstParameter
);
839 // The supplied port is unknown to all our Print Monitors.
840 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
844 pPrintMonitor
= pPort
->pPrintMonitor
;
848 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
852 // Call the monitor's XcvOpenPort function.
853 if (pPrintMonitor
->bIsLevel2
)
854 bReturnValue
= ((PMONITOR2
)pPrintMonitor
->pMonitor
)->pfnXcvOpenPort(pPrintMonitor
->hMonitor
, pwszSecondParameter
, SERVER_EXECUTE
, &hExternalHandle
);
856 bReturnValue
= ((LPMONITOREX
)pPrintMonitor
->pMonitor
)->Monitor
.pfnXcvOpenPort(pwszSecondParameter
, SERVER_EXECUTE
, &hExternalHandle
);
860 // The XcvOpenPort function failed. Return its last error.
861 dwErrorCode
= GetLastError();
865 // Create a new LOCAL_XCV_HANDLE.
866 pXcvHandle
= DllAllocSplMem(sizeof(LOCAL_XCV_HANDLE
));
869 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
870 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
874 pXcvHandle
->hXcv
= hExternalHandle
;
875 pXcvHandle
->pPrintMonitor
= pPrintMonitor
;
877 // Return the Xcv handle through our general handle.
878 pHandle
->HandleType
= HandleType_Xcv
;
879 pHandle
->pSpecificHandle
= pXcvHandle
;
883 // The caller wants a Printer or Printer Job handle and provided a string like:
885 // "\\COMPUTERNAME\HP DeskJet"
886 // "HP DeskJet, Job 5"
887 // "\\COMPUTERNAME\HP DeskJet, Job 5"
889 // Retrieve the printer from the list.
890 pPrinter
= LookupElementSkiplist(&PrinterList
, &pwszFirstParameter
, NULL
);
893 // The printer does not exist.
894 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
898 // Create a new LOCAL_PRINTER_HANDLE.
899 pPrinterHandle
= DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE
));
902 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
903 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
907 pPrinterHandle
->hSPLFile
= INVALID_HANDLE_VALUE
;
908 pPrinterHandle
->pPrinter
= pPrinter
;
910 // Check if a datatype was given.
911 if (pDefault
&& pDefault
->pDatatype
)
913 // Use the datatype if it's valid.
914 if (!FindDatatype(pPrinter
->pPrintProcessor
, pDefault
->pDatatype
))
916 dwErrorCode
= ERROR_INVALID_DATATYPE
;
920 pPrinterHandle
->pwszDatatype
= AllocSplStr(pDefault
->pDatatype
);
924 // Use the default datatype.
925 pPrinterHandle
->pwszDatatype
= AllocSplStr(pPrinter
->pwszDefaultDatatype
);
928 // Check if a DevMode was given, otherwise use the default.
929 if (pDefault
&& pDefault
->pDevMode
)
930 pPrinterHandle
->pDevMode
= DuplicateDevMode(pDefault
->pDevMode
);
932 pPrinterHandle
->pDevMode
= DuplicateDevMode(pPrinter
->pDefaultDevMode
);
934 // Check if the caller wants a handle to an existing Print Job.
935 if (pwszSecondParameter
)
937 // The "Job " string has to follow now.
938 if (wcsncmp(pwszSecondParameter
, L
"Job ", 4) != 0)
940 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
944 // Skip the "Job " string.
945 pwszSecondParameter
+= 4;
947 // Skip even more whitespace.
948 while (*pwszSecondParameter
== ' ')
949 ++pwszSecondParameter
;
951 // Finally extract the desired Job ID.
952 dwJobID
= wcstoul(pwszSecondParameter
, NULL
, 10);
953 if (!IS_VALID_JOB_ID(dwJobID
))
955 // The user supplied an invalid Job ID.
956 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
960 // Look for this job in the Global Job List.
961 pJob
= LookupElementSkiplist(&GlobalJobList
, &dwJobID
, NULL
);
962 if (!pJob
|| pJob
->pPrinter
!= pPrinter
)
964 // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
965 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
969 // Try to open its SPL file.
970 GetJobFilePath(L
"SPL", dwJobID
, wszFullPath
);
971 pPrinterHandle
->hSPLFile
= CreateFileW(wszFullPath
, GENERIC_READ
| GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
972 if (pPrinterHandle
->hSPLFile
== INVALID_HANDLE_VALUE
)
974 dwErrorCode
= GetLastError();
975 ERR("CreateFileW failed with error %lu for \"%S\"!", dwErrorCode
, wszFullPath
);
979 // Associate the job to our Printer Handle, but don't set bStartedDoc.
980 // This prevents the caller from doing further StartDocPrinter, WritePrinter, etc. calls on it.
981 pPrinterHandle
->pJob
= pJob
;
984 // Return the Printer handle through our general handle.
985 pHandle
->HandleType
= HandleType_Printer
;
986 pHandle
->pSpecificHandle
= pPrinterHandle
;
989 // We were successful! Return the handle.
990 *phPrinter
= (HANDLE
)pHandle
;
991 dwErrorCode
= ERROR_SUCCESS
;
993 // Don't let the cleanup routines free this.
995 pPrinterHandle
= NULL
;
999 DllFreeSplMem(pHandle
);
1003 if (pPrinterHandle
->pwszDatatype
)
1004 DllFreeSplStr(pPrinterHandle
->pwszDatatype
);
1006 if (pPrinterHandle
->pDevMode
)
1007 DllFreeSplMem(pPrinterHandle
->pDevMode
);
1009 DllFreeSplMem(pPrinterHandle
);
1012 if (pwszFirstParameter
)
1013 DllFreeSplMem(pwszFirstParameter
);
1015 SetLastError(dwErrorCode
);
1016 return (dwErrorCode
== ERROR_SUCCESS
);
1020 LocalReadPrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pNoBytesRead
)
1024 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1025 PLOCAL_PORT_HANDLE pPortHandle
;
1026 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1031 dwErrorCode
= ERROR_INVALID_HANDLE
;
1035 // Port handles are an entirely different thing.
1036 if (pHandle
->HandleType
== HandleType_Port
)
1038 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1040 // Call the monitor's ReadPort function.
1041 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1042 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnReadPort(pPortHandle
->hPort
, pBuf
, cbBuf
, pNoBytesRead
);
1044 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnReadPort(pPortHandle
->hPort
, pBuf
, cbBuf
, pNoBytesRead
);
1048 // The ReadPort function failed. Return its last error.
1049 dwErrorCode
= GetLastError();
1053 // We were successful!
1054 dwErrorCode
= ERROR_SUCCESS
;
1058 // The remaining function deals with Printer handles only.
1059 if (pHandle
->HandleType
!= HandleType_Printer
)
1061 dwErrorCode
= ERROR_INVALID_HANDLE
;
1065 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1067 // ReadPrinter needs an opened SPL file to work.
1068 // This only works if a Printer Job Handle was requested in OpenPrinter.
1069 if (pPrinterHandle
->hSPLFile
== INVALID_HANDLE_VALUE
)
1071 dwErrorCode
= ERROR_INVALID_HANDLE
;
1075 // Pass the parameters to ReadFile.
1076 if (!ReadFile(pPrinterHandle
->hSPLFile
, pBuf
, cbBuf
, pNoBytesRead
, NULL
))
1078 dwErrorCode
= GetLastError();
1079 ERR("ReadFile failed with error %lu!\n", dwErrorCode
);
1083 dwErrorCode
= ERROR_SUCCESS
;
1086 SetLastError(dwErrorCode
);
1087 return (dwErrorCode
== ERROR_SUCCESS
);
1091 LocalStartDocPrinter(HANDLE hPrinter
, DWORD Level
, PBYTE pDocInfo
)
1095 DWORD dwReturnValue
= 0;
1096 PDOC_INFO_1W pDocInfo1
= (PDOC_INFO_1W
)pDocInfo
;
1098 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1099 PLOCAL_PORT_HANDLE pPortHandle
;
1100 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1105 dwErrorCode
= ERROR_INVALID_HANDLE
;
1109 // Port handles are an entirely different thing.
1110 if (pHandle
->HandleType
== HandleType_Port
)
1112 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1114 // 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.
1115 // Claim it exclusively for this port handle.
1116 pJob
= pPortHandle
->pPort
->pNextJobToProcess
;
1117 pPortHandle
->pPort
->pNextJobToProcess
= NULL
;
1120 // Call the monitor's StartDocPort function.
1121 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1122 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnStartDocPort(pPortHandle
->hPort
, pJob
->pPrinter
->pwszPrinterName
, pJob
->dwJobID
, Level
, pDocInfo
);
1124 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnStartDocPort(pPortHandle
->hPort
, pJob
->pPrinter
->pwszPrinterName
, pJob
->dwJobID
, Level
, pDocInfo
);
1128 // The StartDocPort function failed. Return its last error.
1129 dwErrorCode
= GetLastError();
1133 // We were successful!
1134 dwErrorCode
= ERROR_SUCCESS
;
1135 dwReturnValue
= pJob
->dwJobID
;
1139 // The remaining function deals with Printer handles only.
1140 if (pHandle
->HandleType
!= HandleType_Printer
)
1142 dwErrorCode
= ERROR_INVALID_HANDLE
;
1148 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1152 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1154 // pJob may already be occupied if this is a Print Job handle. In this case, StartDocPrinter has to fail.
1155 if (pPrinterHandle
->pJob
)
1157 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1161 // Check the validity of the datatype if we got one.
1162 if (pDocInfo1
->pDatatype
&& !FindDatatype(pPrinterHandle
->pJob
->pPrintProcessor
, pDocInfo1
->pDatatype
))
1164 dwErrorCode
= ERROR_INVALID_DATATYPE
;
1168 // Check if this is the right document information level.
1171 dwErrorCode
= ERROR_INVALID_LEVEL
;
1175 // All requirements are met - create a new job.
1176 dwErrorCode
= CreateJob(pPrinterHandle
);
1177 if (dwErrorCode
!= ERROR_SUCCESS
)
1180 // Use any given datatype.
1181 if (pDocInfo1
->pDatatype
&& !ReallocSplStr(&pPrinterHandle
->pJob
->pwszDatatype
, pDocInfo1
->pDatatype
))
1183 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1184 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1188 // Use any given document name.
1189 if (pDocInfo1
->pDocName
&& !ReallocSplStr(&pPrinterHandle
->pJob
->pwszDocumentName
, pDocInfo1
->pDocName
))
1191 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1192 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1196 // We were successful!
1197 dwErrorCode
= ERROR_SUCCESS
;
1198 dwReturnValue
= pPrinterHandle
->pJob
->dwJobID
;
1201 SetLastError(dwErrorCode
);
1202 return dwReturnValue
;
1206 LocalStartPagePrinter(HANDLE hPrinter
)
1209 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1210 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1213 if (!pHandle
|| pHandle
->HandleType
!= HandleType_Printer
)
1215 dwErrorCode
= ERROR_INVALID_HANDLE
;
1219 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1221 // We require StartDocPrinter or AddJob to be called first.
1222 if (!pPrinterHandle
->bStartedDoc
)
1224 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
1228 // Increase the page count.
1229 ++pPrinterHandle
->pJob
->dwTotalPages
;
1230 dwErrorCode
= ERROR_SUCCESS
;
1233 SetLastError(dwErrorCode
);
1234 return (dwErrorCode
== ERROR_SUCCESS
);
1238 LocalWritePrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pcWritten
)
1242 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1243 PLOCAL_PORT_HANDLE pPortHandle
;
1244 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1249 dwErrorCode
= ERROR_INVALID_HANDLE
;
1253 // Port handles are an entirely different thing.
1254 if (pHandle
->HandleType
== HandleType_Port
)
1256 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1258 // Call the monitor's WritePort function.
1259 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1260 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnWritePort(pPortHandle
->hPort
, pBuf
, cbBuf
, pcWritten
);
1262 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnWritePort(pPortHandle
->hPort
, pBuf
, cbBuf
, pcWritten
);
1266 // The WritePort function failed. Return its last error.
1267 dwErrorCode
= GetLastError();
1271 // We were successful!
1272 dwErrorCode
= ERROR_SUCCESS
;
1276 // The remaining function deals with Printer handles only.
1277 if (pHandle
->HandleType
!= HandleType_Printer
)
1279 dwErrorCode
= ERROR_INVALID_HANDLE
;
1283 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1285 // We require StartDocPrinter or AddJob to be called first.
1286 if (!pPrinterHandle
->bStartedDoc
)
1288 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
1292 // TODO: This function is only called when doing non-spooled printing.
1293 // 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).
1295 // Pass the parameters to WriteFile.
1296 if (!WriteFile(SOME_SPOOL_FILE_HANDLE
, pBuf
, cbBuf
, pcWritten
, NULL
))
1298 dwErrorCode
= GetLastError();
1299 ERR("WriteFile failed with error %lu!\n", GetLastError());
1304 dwErrorCode
= ERROR_SUCCESS
;
1307 SetLastError(dwErrorCode
);
1308 return (dwErrorCode
== ERROR_SUCCESS
);
1312 LocalEndPagePrinter(HANDLE hPrinter
)
1315 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1318 if (!pHandle
|| pHandle
->HandleType
!= HandleType_Printer
)
1320 dwErrorCode
= ERROR_INVALID_HANDLE
;
1324 // This function doesn't do anything else for now.
1325 dwErrorCode
= ERROR_SUCCESS
;
1328 SetLastError(dwErrorCode
);
1329 return (dwErrorCode
== ERROR_SUCCESS
);
1333 LocalEndDocPrinter(HANDLE hPrinter
)
1337 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1338 PLOCAL_PORT_HANDLE pPortHandle
;
1339 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1344 dwErrorCode
= ERROR_INVALID_HANDLE
;
1348 // Port handles are an entirely different thing.
1349 if (pHandle
->HandleType
== HandleType_Port
)
1351 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1353 // Call the monitor's EndDocPort function.
1354 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1355 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnEndDocPort(pPortHandle
->hPort
);
1357 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnEndDocPort(pPortHandle
->hPort
);
1361 // The EndDocPort function failed. Return its last error.
1362 dwErrorCode
= GetLastError();
1366 // We were successful!
1367 dwErrorCode
= ERROR_SUCCESS
;
1371 // The remaining function deals with Printer handles only.
1372 if (pHandle
->HandleType
!= HandleType_Printer
)
1374 dwErrorCode
= ERROR_INVALID_HANDLE
;
1378 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1380 // We require StartDocPrinter or AddJob to be called first.
1381 if (!pPrinterHandle
->bStartedDoc
)
1383 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
1387 // TODO: Something like ScheduleJob
1390 pPrinterHandle
->bStartedDoc
= FALSE
;
1391 pPrinterHandle
->pJob
= NULL
;
1392 dwErrorCode
= ERROR_SUCCESS
;
1395 SetLastError(dwErrorCode
);
1396 return (dwErrorCode
== ERROR_SUCCESS
);
1400 LocalClosePrinter(HANDLE hPrinter
)
1402 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1403 PLOCAL_PORT_HANDLE pPortHandle
;
1404 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1405 PLOCAL_XCV_HANDLE pXcvHandle
;
1409 SetLastError(ERROR_INVALID_HANDLE
);
1413 if (pHandle
->HandleType
== HandleType_Port
)
1415 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1417 // Call the monitor's ClosePort function.
1418 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1419 ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnClosePort(pPortHandle
->hPort
);
1421 ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnClosePort(pPortHandle
->hPort
);
1423 else if (pHandle
->HandleType
== HandleType_Printer
)
1425 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1427 // Terminate any started job.
1428 if (pPrinterHandle
->pJob
)
1429 FreeJob(pPrinterHandle
->pJob
);
1431 // Free memory for the fields.
1432 DllFreeSplMem(pPrinterHandle
->pDevMode
);
1433 DllFreeSplStr(pPrinterHandle
->pwszDatatype
);
1435 else if (pHandle
->HandleType
== HandleType_Xcv
)
1437 pXcvHandle
= (PLOCAL_XCV_HANDLE
)pHandle
->pSpecificHandle
;
1439 // Call the monitor's XcvClosePort function.
1440 if (pXcvHandle
->pPrintMonitor
->bIsLevel2
)
1441 ((PMONITOR2
)pXcvHandle
->pPrintMonitor
->pMonitor
)->pfnXcvClosePort(pXcvHandle
->hXcv
);
1443 ((LPMONITOREX
)pXcvHandle
->pPrintMonitor
->pMonitor
)->Monitor
.pfnXcvClosePort(pXcvHandle
->hXcv
);
1446 // Free memory for the handle and the specific handle.
1447 DllFreeSplMem(pHandle
->pSpecificHandle
);
1448 DllFreeSplMem(pHandle
);