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 _IsLocalComputerName
302 * Checks if the given Computer Name matches the local Computer Name.
305 * Computer Name prepended with two backslashes to check.
307 * @param pwszComputerName
308 * Pointer to a string able to hold 2 + MAX_COMPUTERNAME_LENGTH + 1 + 1 characters.
309 * Will contain a string "\\COMPUTERNAME\" on success that can be prepended in EnumPrinters.
311 * @param pcchComputerName
312 * On success, this pointer receives the length in characters of pwszComputerName.
315 * ERROR_SUCCESS on success or an error code on failure.
318 _IsLocalComputerName(PCWSTR Name
, PWSTR pwszComputerName
, PDWORD pcchComputerName
)
322 // Prepend slashes to the computer name.
323 pwszComputerName
[0] = L
'\\';
324 pwszComputerName
[1] = L
'\\';
326 // Get the local computer name for comparison.
327 *pcchComputerName
= MAX_COMPUTERNAME_LENGTH
+ 1;
328 if (!GetComputerNameW(&pwszComputerName
[2], pcchComputerName
))
330 dwErrorCode
= GetLastError();
331 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode
);
335 // Add the leading slashes to the total length.
336 *pcchComputerName
+= 2;
338 // Now compare this with the local computer name and reject it with ERROR_INVALID_NAME if it doesn't match.
339 if (wcsicmp(&Name
[2], &pwszComputerName
[2]) != 0)
341 dwErrorCode
= ERROR_INVALID_NAME
;
345 // Add a trailing backslash to pwszComputerName, which will later be prepended in front of the printer names.
346 pwszComputerName
[(*pcchComputerName
)++] = L
'\\';
347 pwszComputerName
[*pcchComputerName
] = 0;
349 dwErrorCode
= ERROR_SUCCESS
;
356 _DumpLevel1PrintProviderInformation(PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
)
360 // Count the needed bytes for Print Provider information.
361 *pcbNeeded
= sizeof(PRINTER_INFO_1W
);
363 for (i
= 0; i
< 3; i
++)
364 *pcbNeeded
+= (wcslen(wszPrintProviderInfo
[i
]) + 1) * sizeof(WCHAR
);
366 // Check if the supplied buffer is large enough.
367 if (cbBuf
< *pcbNeeded
)
368 return ERROR_INSUFFICIENT_BUFFER
;
370 // Copy over the Print Provider information.
371 ((PPRINTER_INFO_1W
)pPrinterEnum
)->Flags
= 0;
372 PackStrings(wszPrintProviderInfo
, pPrinterEnum
, dwPrinterInfo1Offsets
, &pPrinterEnum
[*pcbNeeded
]);
375 return ERROR_SUCCESS
;
379 _LocalEnumPrintersLevel0(DWORD Flags
, PCWSTR Name
, PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
)
381 return ERROR_INVALID_LEVEL
;
385 _LocalEnumPrintersLevel1(DWORD Flags
, PCWSTR Name
, PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
)
387 const WCHAR wszComma
[] = L
",";
392 DWORD cchComputerName
= 0;
396 PBYTE pPrinterStrings
;
397 PSKIPLIST_NODE pNode
;
398 PLOCAL_PRINTER pPrinter
;
400 PWSTR pwszStrings
[3];
401 WCHAR wszComputerName
[2 + MAX_COMPUTERNAME_LENGTH
+ 1 + 1] = { 0 };
403 if (Flags
& PRINTER_ENUM_NAME
)
407 // The user supplied a Computer Name (with leading double backslashes) or Print Provider Name.
408 // Only process what's directed at us.
409 if (Name
[0] == L
'\\' && Name
[1] == L
'\\')
411 dwErrorCode
= _IsLocalComputerName(Name
, wszComputerName
, &cchComputerName
);
412 if (dwErrorCode
!= ERROR_SUCCESS
)
415 else if (wcsicmp(Name
, wszPrintProviderInfo
[0]) != 0)
417 // The user supplied a name that cannot be processed by the Local Print Provider.
418 dwErrorCode
= ERROR_INVALID_NAME
;
424 // The caller wants information about this Print Provider.
425 // spoolss packs this into an array of information about all Print Providers.
426 dwErrorCode
= _DumpLevel1PrintProviderInformation(pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
431 // Count the required buffer size and the number of printers.
434 for (pNode
= PrinterList
.Head
.Next
[0]; pNode
; pNode
= pNode
->Next
[0])
436 pPrinter
= (PLOCAL_PRINTER
)pNode
->Element
;
438 // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
439 if (Flags
& PRINTER_ENUM_SHARED
)
442 // Attention: pComment equals the "Description" registry value while pDescription is concatenated out of several strings.
443 // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
444 cbName
= (cchComputerName
+ wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
445 cbComment
= (wcslen(pPrinter
->pwszDescription
) + 1) * sizeof(WCHAR
);
446 cbDescription
= cbName
+ (wcslen(pPrinter
->pwszPrinterDriver
) + 1 + wcslen(pPrinter
->pwszLocation
) + 1) * sizeof(WCHAR
);
448 *pcbNeeded
+= sizeof(PRINTER_INFO_1W
) + cbName
+ cbComment
+ cbDescription
;
452 // Check if the supplied buffer is large enough.
453 if (cbBuf
< *pcbNeeded
)
455 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
459 // Initialize the variables for filling the output buffer using PackStrings.
460 pPrinterInfo
= pPrinterEnum
;
461 pPrinterStrings
= &pPrinterEnum
[*pcbNeeded
];
463 // Copy over the Printer information.
464 for (pNode
= PrinterList
.Head
.Next
[0]; pNode
; pNode
= pNode
->Next
[0])
466 pPrinter
= (PLOCAL_PRINTER
)pNode
->Element
;
468 // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
469 if (Flags
& PRINTER_ENUM_SHARED
)
472 // Indicate that this is a Printer.
473 ((PPRINTER_INFO_1W
)pPrinterInfo
)->Flags
= PRINTER_ENUM_ICON8
;
475 // Calculate the string lengths.
476 cbName
= (cchComputerName
+ wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
477 cbComment
= (wcslen(pPrinter
->pwszDescription
) + 1) * sizeof(WCHAR
);
478 cbDescription
= cbName
+ (wcslen(pPrinter
->pwszPrinterDriver
) + 1 + wcslen(pPrinter
->pwszLocation
) + 1) * sizeof(WCHAR
);
480 // Copy the Printer Name.
481 pwszStrings
[0] = DllAllocSplMem(cbName
);
483 StringCbCopyExW(p
, cbName
, wszComputerName
, &p
, &cbName
, 0);
484 StringCbCopyExW(p
, cbName
, pPrinter
->pwszPrinterName
, &p
, &cbName
, 0);
486 // Copy the Printer comment (equals the "Description" registry value).
487 pwszStrings
[1] = pPrinter
->pwszDescription
;
489 // Copy the description, which for PRINTER_INFO_1W has the form "Name,Printer Driver,Location"
490 pwszStrings
[2] = DllAllocSplMem(cbDescription
);
492 StringCbCopyExW(p
, cbDescription
, wszComputerName
, &p
, &cbDescription
, 0);
493 StringCbCopyExW(p
, cbDescription
, pPrinter
->pwszPrinterName
, &p
, &cbDescription
, 0);
494 StringCbCopyExW(p
, cbDescription
, wszComma
, &p
, &cbDescription
, 0);
495 StringCbCopyExW(p
, cbDescription
, pPrinter
->pwszPrinterDriver
, &p
, &cbDescription
, 0);
496 StringCbCopyExW(p
, cbDescription
, wszComma
, &p
, &cbDescription
, 0);
497 StringCbCopyExW(p
, cbDescription
, pPrinter
->pwszLocation
, &p
, &cbDescription
, 0);
499 // Finally copy the structure and advance to the next one in the output buffer.
500 pPrinterStrings
= PackStrings(pwszStrings
, pPrinterInfo
, dwPrinterInfo1Offsets
, pPrinterStrings
);
501 pPrinterInfo
+= sizeof(PRINTER_INFO_1W
);
503 // Free the memory for temporary strings.
504 DllFreeSplMem(pwszStrings
[0]);
505 DllFreeSplMem(pwszStrings
[2]);
509 dwErrorCode
= ERROR_SUCCESS
;
516 _LocalEnumPrintersLevel2(DWORD Flags
, PCWSTR Name
, PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
)
518 return ERROR_INVALID_LEVEL
;
522 _LocalEnumPrintersLevel4(DWORD Flags
, PCWSTR Name
, PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
)
524 return ERROR_INVALID_LEVEL
;
528 _LocalEnumPrintersLevel5(DWORD Flags
, PCWSTR Name
, PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
)
530 return ERROR_INVALID_LEVEL
;
534 LocalEnumPrinters(DWORD Flags
, LPWSTR Name
, DWORD Level
, LPBYTE pPrinterEnum
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
538 // Do no sanity checks here. This is verified by localspl_apitest!
544 if (Flags
& PRINTER_ENUM_CONNECTIONS
|| Flags
& PRINTER_ENUM_REMOTE
|| Flags
& PRINTER_ENUM_NETWORK
)
546 // If the flags for the Network Print Provider are given, bail out with ERROR_INVALID_NAME.
547 // This is the internal way for a Print Provider to signal that it doesn't handle this request.
548 dwErrorCode
= ERROR_INVALID_NAME
;
552 if (!(Flags
& PRINTER_ENUM_LOCAL
|| Flags
& PRINTER_ENUM_NAME
))
554 // The Local Print Provider is the right destination for the request, but without any of these flags,
555 // there is no information that can be returned.
556 // So just signal a successful request.
557 dwErrorCode
= ERROR_SUCCESS
;
563 dwErrorCode
= _LocalEnumPrintersLevel0(Flags
, Name
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
567 dwErrorCode
= _LocalEnumPrintersLevel1(Flags
, Name
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
571 dwErrorCode
= _LocalEnumPrintersLevel2(Flags
, Name
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
575 dwErrorCode
= _LocalEnumPrintersLevel4(Flags
, Name
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
579 dwErrorCode
= _LocalEnumPrintersLevel5(Flags
, Name
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
583 // The caller supplied an invalid level.
584 dwErrorCode
= ERROR_INVALID_LEVEL
;
588 SetLastError(dwErrorCode
);
589 return (dwErrorCode
== ERROR_SUCCESS
);
593 LocalOpenPrinter(PWSTR lpPrinterName
, HANDLE
* phPrinter
, PPRINTER_DEFAULTSW pDefault
)
596 DWORD cchComputerName
;
597 DWORD cchFirstParameter
;
600 HANDLE hExternalHandle
;
601 PWSTR p
= lpPrinterName
;
602 PWSTR pwszFirstParameter
= NULL
;
603 PWSTR pwszSecondParameter
= NULL
;
605 PLOCAL_HANDLE pHandle
= NULL
;
607 PLOCAL_PORT_HANDLE pPortHandle
= NULL
;
608 PLOCAL_PRINT_MONITOR pPrintMonitor
;
609 PLOCAL_PRINTER pPrinter
;
610 PLOCAL_PRINTER_HANDLE pPrinterHandle
= NULL
;
611 PLOCAL_XCV_HANDLE pXcvHandle
= NULL
;
612 WCHAR wszComputerName
[MAX_COMPUTERNAME_LENGTH
+ 1];
613 WCHAR wszFullPath
[MAX_PATH
];
615 // TODO: lpPrinterName == NULL is supported and means access to the local printer server.
616 // Not sure yet if that is passed down to localspl.dll or processed in advance.
619 if (!lpPrinterName
|| !phPrinter
)
621 dwErrorCode
= ERROR_INVALID_PARAMETER
;
627 // Skip any server name in the first parameter.
628 // Does lpPrinterName begin with two backslashes to indicate a server name?
629 if (lpPrinterName
[0] == L
'\\' && lpPrinterName
[1] == L
'\\')
631 // Skip these two backslashes.
634 // Look for the closing backslash.
635 p
= wcschr(lpPrinterName
, L
'\\');
638 // We didn't get a proper server name.
639 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
643 // Get the local computer name for comparison.
644 cchComputerName
= _countof(wszComputerName
);
645 if (!GetComputerNameW(wszComputerName
, &cchComputerName
))
647 dwErrorCode
= GetLastError();
648 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode
);
652 // Now compare this string excerpt with the local computer name.
653 // The input parameter may not be writable, so we can't null-terminate the input string at this point.
654 // This print provider only supports local printers, so both strings have to match.
655 if (p
- lpPrinterName
!= cchComputerName
|| _wcsnicmp(lpPrinterName
, wszComputerName
, cchComputerName
) != 0)
657 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
661 // We have checked the server name and don't need it anymore.
662 lpPrinterName
= p
+ 1;
665 // Look for a comma. If it exists, it indicates the end of the first parameter.
666 pwszSecondParameter
= wcschr(lpPrinterName
, L
',');
667 if (pwszSecondParameter
)
668 cchFirstParameter
= pwszSecondParameter
- p
;
670 cchFirstParameter
= wcslen(lpPrinterName
);
672 // We must have at least one parameter.
673 if (!cchFirstParameter
&& !pwszSecondParameter
)
675 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
679 // Do we have a first parameter?
680 if (cchFirstParameter
)
683 // No null-termination is necessary here, because DllAllocSplMem returns a zero-initialized buffer.
684 pwszFirstParameter
= DllAllocSplMem((cchFirstParameter
+ 1) * sizeof(WCHAR
));
685 CopyMemory(pwszFirstParameter
, lpPrinterName
, cchFirstParameter
* sizeof(WCHAR
));
688 // Do we have a second parameter?
689 if (pwszSecondParameter
)
691 // Yes, skip the comma at the beginning.
692 ++pwszSecondParameter
;
694 // Skip whitespace as well.
695 while (*pwszSecondParameter
== L
' ')
696 ++pwszSecondParameter
;
699 // Create a new handle.
700 pHandle
= DllAllocSplMem(sizeof(LOCAL_HANDLE
));
703 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
704 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
708 // Now we can finally check the type of handle actually requested.
709 if (pwszFirstParameter
&& pwszSecondParameter
&& wcsncmp(pwszSecondParameter
, L
"Port", 4) == 0)
711 // The caller wants a port handle and provided a string like:
713 // "\\COMPUTERNAME\LPT1:, Port"
715 // Look for this port in our Print Monitor Port list.
716 pPort
= FindPort(pwszFirstParameter
);
719 // The supplied port is unknown to all our Print Monitors.
720 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
724 pPrintMonitor
= pPort
->pPrintMonitor
;
726 // Call the monitor's OpenPort function.
727 if (pPrintMonitor
->bIsLevel2
)
728 bReturnValue
= ((PMONITOR2
)pPrintMonitor
->pMonitor
)->pfnOpenPort(pPrintMonitor
->hMonitor
, pwszFirstParameter
, &hExternalHandle
);
730 bReturnValue
= ((LPMONITOREX
)pPrintMonitor
->pMonitor
)->Monitor
.pfnOpenPort(pwszFirstParameter
, &hExternalHandle
);
734 // The OpenPort function failed. Return its last error.
735 dwErrorCode
= GetLastError();
739 // Create a new LOCAL_PORT_HANDLE.
740 pPortHandle
= DllAllocSplMem(sizeof(LOCAL_PORT_HANDLE
));
743 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
744 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
748 pPortHandle
->hPort
= hExternalHandle
;
749 pPortHandle
->pPort
= pPort
;
751 // Return the Port handle through our general handle.
752 pHandle
->HandleType
= HandleType_Port
;
753 pHandle
->pSpecificHandle
= pPortHandle
;
755 else if (!pwszFirstParameter
&& pwszSecondParameter
&& wcsncmp(pwszSecondParameter
, L
"Xcv", 3) == 0)
757 // The caller wants an Xcv handle and provided a string like:
758 // ", XcvMonitor Local Port"
759 // "\\COMPUTERNAME\, XcvMonitor Local Port"
761 // "\\COMPUTERNAME\, XcvPort LPT1:"
763 // Skip the "Xcv" string.
764 pwszSecondParameter
+= 3;
766 // Is XcvMonitor or XcvPort requested?
767 if (wcsncmp(pwszSecondParameter
, L
"Monitor ", 8) == 0)
769 // Skip the "Monitor " string.
770 pwszSecondParameter
+= 8;
772 // Look for this monitor in our Print Monitor list.
773 pPrintMonitor
= FindPrintMonitor(pwszSecondParameter
);
776 // The caller supplied a non-existing Monitor name.
777 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
781 else if (wcsncmp(pwszSecondParameter
, L
"Port ", 5) == 0)
783 // Skip the "Port " string.
784 pwszSecondParameter
+= 5;
786 // Look for this port in our Print Monitor Port list.
787 pPort
= FindPort(pwszFirstParameter
);
790 // The supplied port is unknown to all our Print Monitors.
791 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
795 pPrintMonitor
= pPort
->pPrintMonitor
;
799 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
803 // Call the monitor's XcvOpenPort function.
804 if (pPrintMonitor
->bIsLevel2
)
805 bReturnValue
= ((PMONITOR2
)pPrintMonitor
->pMonitor
)->pfnXcvOpenPort(pPrintMonitor
->hMonitor
, pwszSecondParameter
, SERVER_EXECUTE
, &hExternalHandle
);
807 bReturnValue
= ((LPMONITOREX
)pPrintMonitor
->pMonitor
)->Monitor
.pfnXcvOpenPort(pwszSecondParameter
, SERVER_EXECUTE
, &hExternalHandle
);
811 // The XcvOpenPort function failed. Return its last error.
812 dwErrorCode
= GetLastError();
816 // Create a new LOCAL_XCV_HANDLE.
817 pXcvHandle
= DllAllocSplMem(sizeof(LOCAL_XCV_HANDLE
));
820 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
821 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
825 pXcvHandle
->hXcv
= hExternalHandle
;
826 pXcvHandle
->pPrintMonitor
= pPrintMonitor
;
828 // Return the Xcv handle through our general handle.
829 pHandle
->HandleType
= HandleType_Xcv
;
830 pHandle
->pSpecificHandle
= pXcvHandle
;
834 // The caller wants a Printer or Printer Job handle and provided a string like:
836 // "\\COMPUTERNAME\HP DeskJet"
837 // "HP DeskJet, Job 5"
838 // "\\COMPUTERNAME\HP DeskJet, Job 5"
840 // Retrieve the printer from the list.
841 pPrinter
= LookupElementSkiplist(&PrinterList
, &pwszFirstParameter
, NULL
);
844 // The printer does not exist.
845 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
849 // Create a new LOCAL_PRINTER_HANDLE.
850 pPrinterHandle
= DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE
));
853 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
854 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
858 pPrinterHandle
->hSPLFile
= INVALID_HANDLE_VALUE
;
859 pPrinterHandle
->pPrinter
= pPrinter
;
861 // Check if a datatype was given.
862 if (pDefault
&& pDefault
->pDatatype
)
864 // Use the datatype if it's valid.
865 if (!FindDatatype(pPrinter
->pPrintProcessor
, pDefault
->pDatatype
))
867 dwErrorCode
= ERROR_INVALID_DATATYPE
;
871 pPrinterHandle
->pwszDatatype
= AllocSplStr(pDefault
->pDatatype
);
875 // Use the default datatype.
876 pPrinterHandle
->pwszDatatype
= AllocSplStr(pPrinter
->pwszDefaultDatatype
);
879 // Check if a DevMode was given, otherwise use the default.
880 if (pDefault
&& pDefault
->pDevMode
)
881 pPrinterHandle
->pDevMode
= DuplicateDevMode(pDefault
->pDevMode
);
883 pPrinterHandle
->pDevMode
= DuplicateDevMode(pPrinter
->pDefaultDevMode
);
885 // Check if the caller wants a handle to an existing Print Job.
886 if (pwszSecondParameter
)
888 // The "Job " string has to follow now.
889 if (wcsncmp(pwszSecondParameter
, L
"Job ", 4) != 0)
891 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
895 // Skip the "Job " string.
896 pwszSecondParameter
+= 4;
898 // Skip even more whitespace.
899 while (*pwszSecondParameter
== ' ')
900 ++pwszSecondParameter
;
902 // Finally extract the desired Job ID.
903 dwJobID
= wcstoul(pwszSecondParameter
, NULL
, 10);
904 if (!IS_VALID_JOB_ID(dwJobID
))
906 // The user supplied an invalid Job ID.
907 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
911 // Look for this job in the Global Job List.
912 pJob
= LookupElementSkiplist(&GlobalJobList
, &dwJobID
, NULL
);
913 if (!pJob
|| pJob
->pPrinter
!= pPrinter
)
915 // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
916 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
920 // Try to open its SPL file.
921 GetJobFilePath(L
"SPL", dwJobID
, wszFullPath
);
922 pPrinterHandle
->hSPLFile
= CreateFileW(wszFullPath
, GENERIC_READ
| GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
923 if (pPrinterHandle
->hSPLFile
== INVALID_HANDLE_VALUE
)
925 dwErrorCode
= GetLastError();
926 ERR("CreateFileW failed with error %lu for \"%S\"!", dwErrorCode
, wszFullPath
);
930 // Associate the job to our Printer Handle, but don't set bStartedDoc.
931 // This prevents the caller from doing further StartDocPrinter, WritePrinter, etc. calls on it.
932 pPrinterHandle
->pJob
= pJob
;
935 // Return the Printer handle through our general handle.
936 pHandle
->HandleType
= HandleType_Printer
;
937 pHandle
->pSpecificHandle
= pPrinterHandle
;
940 // We were successful! Return the handle.
941 *phPrinter
= (HANDLE
)pHandle
;
942 dwErrorCode
= ERROR_SUCCESS
;
944 // Don't let the cleanup routines free this.
946 pPrinterHandle
= NULL
;
950 DllFreeSplMem(pHandle
);
954 if (pPrinterHandle
->pwszDatatype
)
955 DllFreeSplStr(pPrinterHandle
->pwszDatatype
);
957 if (pPrinterHandle
->pDevMode
)
958 DllFreeSplMem(pPrinterHandle
->pDevMode
);
960 DllFreeSplMem(pPrinterHandle
);
963 if (pwszFirstParameter
)
964 DllFreeSplMem(pwszFirstParameter
);
966 SetLastError(dwErrorCode
);
967 return (dwErrorCode
== ERROR_SUCCESS
);
971 LocalReadPrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pNoBytesRead
)
975 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
976 PLOCAL_PORT_HANDLE pPortHandle
;
977 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
982 dwErrorCode
= ERROR_INVALID_HANDLE
;
986 // Port handles are an entirely different thing.
987 if (pHandle
->HandleType
== HandleType_Port
)
989 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
991 // Call the monitor's ReadPort function.
992 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
993 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnReadPort(pPortHandle
->hPort
, pBuf
, cbBuf
, pNoBytesRead
);
995 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnReadPort(pPortHandle
->hPort
, pBuf
, cbBuf
, pNoBytesRead
);
999 // The ReadPort function failed. Return its last error.
1000 dwErrorCode
= GetLastError();
1004 // We were successful!
1005 dwErrorCode
= ERROR_SUCCESS
;
1009 // The remaining function deals with Printer handles only.
1010 if (pHandle
->HandleType
!= HandleType_Printer
)
1012 dwErrorCode
= ERROR_INVALID_HANDLE
;
1016 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1018 // ReadPrinter needs an opened SPL file to work.
1019 // This only works if a Printer Job Handle was requested in OpenPrinter.
1020 if (pPrinterHandle
->hSPLFile
== INVALID_HANDLE_VALUE
)
1022 dwErrorCode
= ERROR_INVALID_HANDLE
;
1026 // Pass the parameters to ReadFile.
1027 if (!ReadFile(pPrinterHandle
->hSPLFile
, pBuf
, cbBuf
, pNoBytesRead
, NULL
))
1029 dwErrorCode
= GetLastError();
1030 ERR("ReadFile failed with error %lu!\n", dwErrorCode
);
1034 dwErrorCode
= ERROR_SUCCESS
;
1037 SetLastError(dwErrorCode
);
1038 return (dwErrorCode
== ERROR_SUCCESS
);
1042 LocalStartDocPrinter(HANDLE hPrinter
, DWORD Level
, PBYTE pDocInfo
)
1046 DWORD dwReturnValue
= 0;
1047 PDOC_INFO_1W pDocInfo1
= (PDOC_INFO_1W
)pDocInfo
;
1049 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1050 PLOCAL_PORT_HANDLE pPortHandle
;
1051 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1056 dwErrorCode
= ERROR_INVALID_HANDLE
;
1060 // Port handles are an entirely different thing.
1061 if (pHandle
->HandleType
== HandleType_Port
)
1063 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1065 // 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.
1066 // Claim it exclusively for this port handle.
1067 pJob
= pPortHandle
->pPort
->pNextJobToProcess
;
1068 pPortHandle
->pPort
->pNextJobToProcess
= NULL
;
1071 // Call the monitor's StartDocPort function.
1072 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1073 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnStartDocPort(pPortHandle
->hPort
, pJob
->pPrinter
->pwszPrinterName
, pJob
->dwJobID
, Level
, pDocInfo
);
1075 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnStartDocPort(pPortHandle
->hPort
, pJob
->pPrinter
->pwszPrinterName
, pJob
->dwJobID
, Level
, pDocInfo
);
1079 // The StartDocPort function failed. Return its last error.
1080 dwErrorCode
= GetLastError();
1084 // We were successful!
1085 dwErrorCode
= ERROR_SUCCESS
;
1086 dwReturnValue
= pJob
->dwJobID
;
1090 // The remaining function deals with Printer handles only.
1091 if (pHandle
->HandleType
!= HandleType_Printer
)
1093 dwErrorCode
= ERROR_INVALID_HANDLE
;
1099 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1103 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1105 // pJob may already be occupied if this is a Print Job handle. In this case, StartDocPrinter has to fail.
1106 if (pPrinterHandle
->pJob
)
1108 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1112 // Check the validity of the datatype if we got one.
1113 if (pDocInfo1
->pDatatype
&& !FindDatatype(pPrinterHandle
->pJob
->pPrintProcessor
, pDocInfo1
->pDatatype
))
1115 dwErrorCode
= ERROR_INVALID_DATATYPE
;
1119 // Check if this is the right document information level.
1122 dwErrorCode
= ERROR_INVALID_LEVEL
;
1126 // All requirements are met - create a new job.
1127 dwErrorCode
= CreateJob(pPrinterHandle
);
1128 if (dwErrorCode
!= ERROR_SUCCESS
)
1131 // Use any given datatype.
1132 if (pDocInfo1
->pDatatype
&& !ReallocSplStr(&pPrinterHandle
->pJob
->pwszDatatype
, pDocInfo1
->pDatatype
))
1134 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1135 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1139 // Use any given document name.
1140 if (pDocInfo1
->pDocName
&& !ReallocSplStr(&pPrinterHandle
->pJob
->pwszDocumentName
, pDocInfo1
->pDocName
))
1142 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1143 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1147 // We were successful!
1148 dwErrorCode
= ERROR_SUCCESS
;
1149 dwReturnValue
= pPrinterHandle
->pJob
->dwJobID
;
1152 SetLastError(dwErrorCode
);
1153 return dwReturnValue
;
1157 LocalStartPagePrinter(HANDLE hPrinter
)
1160 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1161 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1164 if (!pHandle
|| pHandle
->HandleType
!= HandleType_Printer
)
1166 dwErrorCode
= ERROR_INVALID_HANDLE
;
1170 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1172 // We require StartDocPrinter or AddJob to be called first.
1173 if (!pPrinterHandle
->bStartedDoc
)
1175 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
1179 // Increase the page count.
1180 ++pPrinterHandle
->pJob
->dwTotalPages
;
1181 dwErrorCode
= ERROR_SUCCESS
;
1184 SetLastError(dwErrorCode
);
1185 return (dwErrorCode
== ERROR_SUCCESS
);
1189 LocalWritePrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pcWritten
)
1193 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1194 PLOCAL_PORT_HANDLE pPortHandle
;
1195 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1200 dwErrorCode
= ERROR_INVALID_HANDLE
;
1204 // Port handles are an entirely different thing.
1205 if (pHandle
->HandleType
== HandleType_Port
)
1207 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1209 // Call the monitor's WritePort function.
1210 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1211 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnWritePort(pPortHandle
->hPort
, pBuf
, cbBuf
, pcWritten
);
1213 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnWritePort(pPortHandle
->hPort
, pBuf
, cbBuf
, pcWritten
);
1217 // The WritePort function failed. Return its last error.
1218 dwErrorCode
= GetLastError();
1222 // We were successful!
1223 dwErrorCode
= ERROR_SUCCESS
;
1227 // The remaining function deals with Printer handles only.
1228 if (pHandle
->HandleType
!= HandleType_Printer
)
1230 dwErrorCode
= ERROR_INVALID_HANDLE
;
1234 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1236 // We require StartDocPrinter or AddJob to be called first.
1237 if (!pPrinterHandle
->bStartedDoc
)
1239 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
1243 // TODO: This function is only called when doing non-spooled printing.
1244 // 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).
1246 // Pass the parameters to WriteFile.
1247 if (!WriteFile(SOME_SPOOL_FILE_HANDLE
, pBuf
, cbBuf
, pcWritten
, NULL
))
1249 dwErrorCode
= GetLastError();
1250 ERR("WriteFile failed with error %lu!\n", GetLastError());
1255 dwErrorCode
= ERROR_SUCCESS
;
1258 SetLastError(dwErrorCode
);
1259 return (dwErrorCode
== ERROR_SUCCESS
);
1263 LocalEndPagePrinter(HANDLE hPrinter
)
1266 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1269 if (!pHandle
|| pHandle
->HandleType
!= HandleType_Printer
)
1271 dwErrorCode
= ERROR_INVALID_HANDLE
;
1275 // This function doesn't do anything else for now.
1276 dwErrorCode
= ERROR_SUCCESS
;
1279 SetLastError(dwErrorCode
);
1280 return (dwErrorCode
== ERROR_SUCCESS
);
1284 LocalEndDocPrinter(HANDLE hPrinter
)
1288 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1289 PLOCAL_PORT_HANDLE pPortHandle
;
1290 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1295 dwErrorCode
= ERROR_INVALID_HANDLE
;
1299 // Port handles are an entirely different thing.
1300 if (pHandle
->HandleType
== HandleType_Port
)
1302 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1304 // Call the monitor's EndDocPort function.
1305 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1306 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnEndDocPort(pPortHandle
->hPort
);
1308 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnEndDocPort(pPortHandle
->hPort
);
1312 // The EndDocPort function failed. Return its last error.
1313 dwErrorCode
= GetLastError();
1317 // We were successful!
1318 dwErrorCode
= ERROR_SUCCESS
;
1322 // The remaining function deals with Printer handles only.
1323 if (pHandle
->HandleType
!= HandleType_Printer
)
1325 dwErrorCode
= ERROR_INVALID_HANDLE
;
1329 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1331 // We require StartDocPrinter or AddJob to be called first.
1332 if (!pPrinterHandle
->bStartedDoc
)
1334 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
1338 // TODO: Something like ScheduleJob
1341 pPrinterHandle
->bStartedDoc
= FALSE
;
1342 pPrinterHandle
->pJob
= NULL
;
1343 dwErrorCode
= ERROR_SUCCESS
;
1346 SetLastError(dwErrorCode
);
1347 return (dwErrorCode
== ERROR_SUCCESS
);
1351 LocalClosePrinter(HANDLE hPrinter
)
1353 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1354 PLOCAL_PORT_HANDLE pPortHandle
;
1355 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1356 PLOCAL_XCV_HANDLE pXcvHandle
;
1360 SetLastError(ERROR_INVALID_HANDLE
);
1364 if (pHandle
->HandleType
== HandleType_Port
)
1366 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1368 // Call the monitor's ClosePort function.
1369 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1370 ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnClosePort(pPortHandle
->hPort
);
1372 ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnClosePort(pPortHandle
->hPort
);
1374 else if (pHandle
->HandleType
== HandleType_Printer
)
1376 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1378 // Terminate any started job.
1379 if (pPrinterHandle
->pJob
)
1380 FreeJob(pPrinterHandle
->pJob
);
1382 // Free memory for the fields.
1383 DllFreeSplMem(pPrinterHandle
->pDevMode
);
1384 DllFreeSplStr(pPrinterHandle
->pwszDatatype
);
1386 else if (pHandle
->HandleType
== HandleType_Xcv
)
1388 pXcvHandle
= (PLOCAL_XCV_HANDLE
)pHandle
->pSpecificHandle
;
1390 // Call the monitor's XcvClosePort function.
1391 if (pXcvHandle
->pPrintMonitor
->bIsLevel2
)
1392 ((PMONITOR2
)pXcvHandle
->pPrintMonitor
->pMonitor
)->pfnXcvClosePort(pXcvHandle
->hXcv
);
1394 ((LPMONITOREX
)pXcvHandle
->pPrintMonitor
->pMonitor
)->Monitor
.pfnXcvClosePort(pXcvHandle
->hXcv
);
1397 // Free memory for the handle and the specific handle.
1398 DllFreeSplMem(pHandle
->pSpecificHandle
);
1399 DllFreeSplMem(pHandle
);