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>
13 // Forward Declarations
14 static void _LocalGetPrinterLevel0(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_STRESS
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
);
15 static void _LocalGetPrinterLevel1(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_1W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
);
16 static void _LocalGetPrinterLevel2(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_2W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
);
17 static void _LocalGetPrinterLevel3(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_3
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
);
18 static void _LocalGetPrinterLevel4(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_4W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
);
19 static void _LocalGetPrinterLevel5(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_5W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
);
20 static void _LocalGetPrinterLevel6(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_6
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
);
21 static void _LocalGetPrinterLevel7(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_7W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
);
22 static void _LocalGetPrinterLevel8(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_8W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
);
23 static void _LocalGetPrinterLevel9(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_9W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
);
26 typedef void (*PLocalGetPrinterLevelFunc
)(PLOCAL_PRINTER
, PVOID
, PBYTE
*, PDWORD
, DWORD
, PWSTR
);
28 static const PLocalGetPrinterLevelFunc pfnGetPrinterLevels
[] = {
29 (PLocalGetPrinterLevelFunc
)&_LocalGetPrinterLevel0
,
30 (PLocalGetPrinterLevelFunc
)&_LocalGetPrinterLevel1
,
31 (PLocalGetPrinterLevelFunc
)&_LocalGetPrinterLevel2
,
32 (PLocalGetPrinterLevelFunc
)&_LocalGetPrinterLevel3
,
33 (PLocalGetPrinterLevelFunc
)&_LocalGetPrinterLevel4
,
34 (PLocalGetPrinterLevelFunc
)&_LocalGetPrinterLevel5
,
35 (PLocalGetPrinterLevelFunc
)&_LocalGetPrinterLevel6
,
36 (PLocalGetPrinterLevelFunc
)&_LocalGetPrinterLevel7
,
37 (PLocalGetPrinterLevelFunc
)&_LocalGetPrinterLevel8
,
38 (PLocalGetPrinterLevelFunc
)&_LocalGetPrinterLevel9
41 static DWORD dwPrinterInfo0Offsets
[] = {
42 FIELD_OFFSET(PRINTER_INFO_STRESS
, pPrinterName
),
46 static DWORD dwPrinterInfo1Offsets
[] = {
47 FIELD_OFFSET(PRINTER_INFO_1W
, pName
),
48 FIELD_OFFSET(PRINTER_INFO_1W
, pComment
),
49 FIELD_OFFSET(PRINTER_INFO_1W
, pDescription
),
53 static DWORD dwPrinterInfo2Offsets
[] = {
54 FIELD_OFFSET(PRINTER_INFO_2W
, pPrinterName
),
55 FIELD_OFFSET(PRINTER_INFO_2W
, pShareName
),
56 FIELD_OFFSET(PRINTER_INFO_2W
, pPortName
),
57 FIELD_OFFSET(PRINTER_INFO_2W
, pDriverName
),
58 FIELD_OFFSET(PRINTER_INFO_2W
, pComment
),
59 FIELD_OFFSET(PRINTER_INFO_2W
, pLocation
),
60 FIELD_OFFSET(PRINTER_INFO_2W
, pSepFile
),
61 FIELD_OFFSET(PRINTER_INFO_2W
, pPrintProcessor
),
62 FIELD_OFFSET(PRINTER_INFO_2W
, pDatatype
),
63 FIELD_OFFSET(PRINTER_INFO_2W
, pParameters
),
67 static DWORD dwPrinterInfo4Offsets
[] = {
68 FIELD_OFFSET(PRINTER_INFO_4W
, pPrinterName
),
72 static DWORD dwPrinterInfo5Offsets
[] = {
73 FIELD_OFFSET(PRINTER_INFO_5W
, pPrinterName
),
74 FIELD_OFFSET(PRINTER_INFO_5W
, pPortName
),
78 /** These values serve no purpose anymore, but are still used in PRINTER_INFO_5 and
79 HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\PrinterPorts */
80 static const DWORD dwDeviceNotSelectedTimeout
= 15000;
81 static const DWORD dwTransmissionRetryTimeout
= 45000;
85 * @name _PrinterListCompareRoutine
87 * SKIPLIST_COMPARE_ROUTINE for the Printer List.
88 * Does a case-insensitive comparison, because e.g. LocalOpenPrinter doesn't match the case when looking for Printers.
91 _PrinterListCompareRoutine(PVOID FirstStruct
, PVOID SecondStruct
)
93 PLOCAL_PRINTER A
= (PLOCAL_PRINTER
)FirstStruct
;
94 PLOCAL_PRINTER B
= (PLOCAL_PRINTER
)SecondStruct
;
96 return wcsicmp(A
->pwszPrinterName
, B
->pwszPrinterName
);
100 * @name InitializePrinterList
102 * Initializes a list of locally available Printers.
103 * The list is searchable by name and returns information about the printers, including their job queues.
104 * During this process, the job queues are also initialized.
107 InitializePrinterList()
109 const WCHAR wszPrintersKey
[] = L
"SYSTEM\\CurrentControlSet\\Control\\Print\\Printers";
112 DWORD cchPrinterName
;
119 PLOCAL_PRINTER pPrinter
= NULL
;
120 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
121 PWSTR pwszPort
= NULL
;
122 PWSTR pwszPrintProcessor
= NULL
;
123 WCHAR wszPrinterName
[MAX_PRINTER_NAME
+ 1];
125 // Initialize an empty list for our printers.
126 InitializeSkiplist(&PrinterList
, DllAllocSplMem
, _PrinterListCompareRoutine
, (PSKIPLIST_FREE_ROUTINE
)DllFreeSplMem
);
128 // Open our printers registry key. Each subkey is a local printer there.
129 dwErrorCode
= (DWORD
)RegOpenKeyExW(HKEY_LOCAL_MACHINE
, wszPrintersKey
, 0, KEY_READ
, &hKey
);
130 if (dwErrorCode
!= ERROR_SUCCESS
)
132 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode
);
136 // Get the number of subkeys.
137 dwErrorCode
= (DWORD
)RegQueryInfoKeyW(hKey
, NULL
, NULL
, NULL
, &dwSubKeys
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
138 if (dwErrorCode
!= ERROR_SUCCESS
)
140 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode
);
144 // Loop through all available local printers.
145 for (i
= 0; i
< dwSubKeys
; i
++)
147 // Cleanup tasks from the previous run
150 RegCloseKey(hSubKey
);
156 if (pPrinter
->pDefaultDevMode
)
157 DllFreeSplMem(pPrinter
->pDefaultDevMode
);
159 if (pPrinter
->pwszDefaultDatatype
)
160 DllFreeSplStr(pPrinter
->pwszDefaultDatatype
);
162 if (pPrinter
->pwszDescription
)
163 DllFreeSplStr(pPrinter
->pwszDescription
);
165 if (pPrinter
->pwszPrinterDriver
)
166 DllFreeSplStr(pPrinter
->pwszPrinterDriver
);
168 if (pPrinter
->pwszPrinterName
)
169 DllFreeSplStr(pPrinter
->pwszPrinterName
);
171 DllFreeSplMem(pPrinter
);
175 if (pwszPrintProcessor
)
177 DllFreeSplStr(pwszPrintProcessor
);
178 pwszPrintProcessor
= NULL
;
181 // Get the name of this printer.
182 cchPrinterName
= _countof(wszPrinterName
);
183 dwErrorCode
= (DWORD
)RegEnumKeyExW(hKey
, i
, wszPrinterName
, &cchPrinterName
, NULL
, NULL
, NULL
, NULL
);
184 if (dwErrorCode
== ERROR_MORE_DATA
)
186 // This printer name exceeds the maximum length and is invalid.
189 else if (dwErrorCode
!= ERROR_SUCCESS
)
191 ERR("RegEnumKeyExW failed for iteration %lu with status %lu!\n", i
, dwErrorCode
);
195 // Open this Printer's registry key.
196 dwErrorCode
= (DWORD
)RegOpenKeyExW(hKey
, wszPrinterName
, 0, KEY_READ
, &hSubKey
);
197 if (dwErrorCode
!= ERROR_SUCCESS
)
199 ERR("RegOpenKeyExW failed for Printer \"%S\" with status %lu!\n", wszPrinterName
, dwErrorCode
);
203 // Get the Print Processor.
204 pwszPrintProcessor
= AllocAndRegQueryWSZ(hSubKey
, L
"Print Processor");
205 if (!pwszPrintProcessor
)
208 // Try to find it in the Print Processor List.
209 pPrintProcessor
= FindPrintProcessor(pwszPrintProcessor
);
210 if (!pPrintProcessor
)
212 ERR("Invalid Print Processor \"%S\" for Printer \"%S\"!\n", pwszPrintProcessor
, wszPrinterName
);
217 pwszPort
= AllocAndRegQueryWSZ(hSubKey
, L
"Port");
221 // Try to find it in the Port List.
222 pPort
= FindPort(pwszPort
);
225 ERR("Invalid Port \"%S\" for Printer \"%S\"!\n", pwszPort
, wszPrinterName
);
229 // Create a new LOCAL_PRINTER structure for it.
230 pPrinter
= DllAllocSplMem(sizeof(LOCAL_PRINTER
));
233 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
234 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
238 pPrinter
->pwszPrinterName
= AllocSplStr(wszPrinterName
);
239 pPrinter
->pPrintProcessor
= pPrintProcessor
;
240 pPrinter
->pPort
= pPort
;
241 InitializePrinterJobList(pPrinter
);
244 pPrinter
->pwszLocation
= AllocAndRegQueryWSZ(hSubKey
, L
"Location");
245 if (!pPrinter
->pwszLocation
)
248 // Get the printer driver.
249 pPrinter
->pwszPrinterDriver
= AllocAndRegQueryWSZ(hSubKey
, L
"Printer Driver");
250 if (!pPrinter
->pwszPrinterDriver
)
253 // Get the description.
254 pPrinter
->pwszDescription
= AllocAndRegQueryWSZ(hSubKey
, L
"Description");
255 if (!pPrinter
->pwszDescription
)
258 // Get the default datatype.
259 pPrinter
->pwszDefaultDatatype
= AllocAndRegQueryWSZ(hSubKey
, L
"Datatype");
260 if (!pPrinter
->pwszDefaultDatatype
)
263 // Verify that it's valid.
264 if (!FindDatatype(pPrintProcessor
, pPrinter
->pwszDefaultDatatype
))
266 ERR("Invalid default datatype \"%S\" for Printer \"%S\"!\n", pPrinter
->pwszDefaultDatatype
, wszPrinterName
);
270 // Determine the size of the DevMode.
271 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Default DevMode", NULL
, NULL
, NULL
, &cbData
);
272 if (dwErrorCode
!= ERROR_SUCCESS
)
274 ERR("Couldn't query the size of the DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName
, dwErrorCode
, cbData
);
278 // Allocate enough memory for the DevMode.
279 pPrinter
->pDefaultDevMode
= DllAllocSplMem(cbData
);
280 if (!pPrinter
->pDefaultDevMode
)
282 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
283 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
287 // Get the default DevMode.
288 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Default DevMode", NULL
, NULL
, (PBYTE
)pPrinter
->pDefaultDevMode
, &cbData
);
289 if (dwErrorCode
!= ERROR_SUCCESS
)
291 ERR("Couldn't query a DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName
, dwErrorCode
, cbData
);
295 // Get the Attributes.
296 cbData
= sizeof(DWORD
);
297 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Attributes", NULL
, NULL
, (PBYTE
)&pPrinter
->dwAttributes
, &cbData
);
298 if (dwErrorCode
!= ERROR_SUCCESS
)
300 ERR("Couldn't query Attributes for Printer \"%S\", status is %lu!\n", wszPrinterName
, dwErrorCode
);
305 cbData
= sizeof(DWORD
);
306 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Status", NULL
, NULL
, (PBYTE
)&pPrinter
->dwStatus
, &cbData
);
307 if (dwErrorCode
!= ERROR_SUCCESS
)
309 ERR("Couldn't query Status for Printer \"%S\", status is %lu!\n", wszPrinterName
, dwErrorCode
);
313 // Add this printer to the printer list.
314 if (!InsertElementSkiplist(&PrinterList
, pPrinter
))
316 ERR("InsertElementSkiplist failed for Printer \"%S\"!\n", pPrinter
->pwszPrinterName
);
320 // Don't let the cleanup routines free this.
324 dwErrorCode
= ERROR_SUCCESS
;
329 RegCloseKey(hSubKey
);
333 if (pPrinter
->pDefaultDevMode
)
334 DllFreeSplMem(pPrinter
->pDefaultDevMode
);
336 if (pPrinter
->pwszDefaultDatatype
)
337 DllFreeSplStr(pPrinter
->pwszDefaultDatatype
);
339 if (pPrinter
->pwszDescription
)
340 DllFreeSplStr(pPrinter
->pwszDescription
);
342 if (pPrinter
->pwszPrinterDriver
)
343 DllFreeSplStr(pPrinter
->pwszPrinterDriver
);
345 if (pPrinter
->pwszPrinterName
)
346 DllFreeSplStr(pPrinter
->pwszPrinterName
);
348 DllFreeSplMem(pPrinter
);
351 if (pwszPrintProcessor
)
352 DllFreeSplStr(pwszPrintProcessor
);
358 SetLastError(dwErrorCode
);
359 return (dwErrorCode
== ERROR_SUCCESS
);
363 * @name _LocalEnumPrintersCheckName
365 * Checks the Name parameter supplied to a call to EnumPrinters.
368 * Flags parameter of EnumPrinters.
371 * Name parameter of EnumPrinters to check.
373 * @param pwszComputerName
374 * Pointer to a string able to hold 2 + MAX_COMPUTERNAME_LENGTH + 1 + 1 characters.
375 * On return, it may contain a computer name to prepend in EnumPrinters depending on the case.
377 * @param pcchComputerName
378 * If a string to prepend is returned, this pointer receives its length in characters.
381 * ERROR_SUCCESS if processing in EnumPrinters can be continued.
382 * ERROR_INVALID_NAME if the Name parameter is invalid for the given flags and this Print Provider.
383 * Any other error code if GetComputerNameW fails. Error codes indicating failure should then be returned by EnumPrinters.
386 _LocalEnumPrintersCheckName(DWORD Flags
, PCWSTR Name
, PWSTR pwszComputerName
, PDWORD pcchComputerName
)
389 PCWSTR pComputerName
;
391 // If there is no Name parameter to check, we can just continue in EnumPrinters.
393 return ERROR_SUCCESS
;
395 // Check if Name does not begin with two backslashes (required for specifying Computer Names).
396 if (Name
[0] != L
'\\' || Name
[1] != L
'\\')
398 if (Flags
& PRINTER_ENUM_NAME
)
400 // If PRINTER_ENUM_NAME is specified, any given Name parameter may only contain the
401 // Print Provider Name or the local Computer Name.
403 // Compare with the Print Provider Name.
404 if (wcsicmp(Name
, wszPrintProviderInfo
[0]) == 0)
405 return ERROR_SUCCESS
;
407 // Dismiss anything else.
408 return ERROR_INVALID_NAME
;
412 // If PRINTER_ENUM_NAME is not specified, we just ignore anything that is not a Computer Name.
413 return ERROR_SUCCESS
;
417 // Prepend the backslashes to the output computer name.
418 pwszComputerName
[0] = L
'\\';
419 pwszComputerName
[1] = L
'\\';
421 // Get the local computer name for comparison.
422 *pcchComputerName
= MAX_COMPUTERNAME_LENGTH
+ 1;
423 if (!GetComputerNameW(&pwszComputerName
[2], pcchComputerName
))
425 ERR("GetComputerNameW failed with error %lu!\n", GetLastError());
426 return GetLastError();
429 // Add the leading slashes to the total length.
430 *pcchComputerName
+= 2;
432 // Compare both names.
433 pComputerName
= &pwszComputerName
[2];
437 // Are we at the end of the local Computer Name string?
440 // Are we also at the end of the supplied Name parameter?
441 // A terminating NUL character and a backslash are both treated as the end, but they are treated differently.
444 // If both names match and Name ends with a NUL character, the computer name will be prepended in EnumPrinters.
445 // Add a trailing backslash for that.
446 pwszComputerName
[(*pcchComputerName
)++] = L
'\\';
447 pwszComputerName
[*pcchComputerName
] = 0;
448 return ERROR_SUCCESS
;
450 else if (*pName
== L
'\\')
452 if (Flags
& PRINTER_ENUM_NAME
)
454 // If PRINTER_ENUM_NAME is specified and a Name parameter is given, it must be exactly the local
455 // Computer Name with two backslashes prepended. Anything else (like "\\COMPUTERNAME\") is dismissed.
456 return ERROR_INVALID_NAME
;
460 // If PRINTER_ENUM_NAME is not specified and a Name parameter is given, it may also end with a backslash.
461 // Only the Computer Name between the backslashes is checked then.
462 // This is largely undocumented, but verified by tests (see winspool_apitest).
463 // In this case, no computer name is prepended in EnumPrinters though.
464 *pwszComputerName
= 0;
465 *pcchComputerName
= 0;
466 return ERROR_SUCCESS
;
471 // Compare both Computer Names case-insensitively and reject with ERROR_INVALID_NAME if they don't match.
472 if (towlower(*pName
) != towlower(*pComputerName
))
473 return ERROR_INVALID_NAME
;
481 _DumpLevel1PrintProviderInformation(PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
)
485 // Count the needed bytes for Print Provider information.
486 *pcbNeeded
= sizeof(PRINTER_INFO_1W
);
488 for (i
= 0; i
< 3; i
++)
489 *pcbNeeded
+= (wcslen(wszPrintProviderInfo
[i
]) + 1) * sizeof(WCHAR
);
491 // Check if the supplied buffer is large enough.
492 if (cbBuf
< *pcbNeeded
)
493 return ERROR_INSUFFICIENT_BUFFER
;
495 // Copy over the Print Provider information.
496 ((PPRINTER_INFO_1W
)pPrinterEnum
)->Flags
= 0;
497 PackStrings(wszPrintProviderInfo
, pPrinterEnum
, dwPrinterInfo1Offsets
, &pPrinterEnum
[*pcbNeeded
]);
500 return ERROR_SUCCESS
;
504 _LocalGetPrinterLevel0(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_STRESS
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
508 PWSTR pwszStrings
[1];
509 SYSTEM_INFO SystemInfo
;
511 // Calculate the string lengths.
512 cbName
= (cchComputerName
+ wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
516 *pcbNeeded
+= sizeof(PRINTER_INFO_STRESS
) + cbName
;
520 // Set the general fields.
521 ZeroMemory(*ppPrinterInfo
, sizeof(PRINTER_INFO_STRESS
));
522 (*ppPrinterInfo
)->cJobs
= pPrinter
->JobList
.NodeCount
;
523 (*ppPrinterInfo
)->dwGetVersion
= GetVersion();
524 (*ppPrinterInfo
)->Status
= pPrinter
->dwStatus
;
527 (*ppPrinterInfo
)->fFreeBuild
= 1;
530 GetSystemInfo(&SystemInfo
);
531 (*ppPrinterInfo
)->dwNumberOfProcessors
= SystemInfo
.dwNumberOfProcessors
;
532 (*ppPrinterInfo
)->dwProcessorType
= SystemInfo
.dwProcessorType
;
533 (*ppPrinterInfo
)->wProcessorArchitecture
= SystemInfo
.wProcessorArchitecture
;
534 (*ppPrinterInfo
)->wProcessorLevel
= SystemInfo
.wProcessorLevel
;
536 // Copy the Printer Name.
537 pwszStrings
[0] = DllAllocSplMem(cbName
);
539 StringCbCopyExW(p
, cbName
, wszComputerName
, &p
, &cbName
, 0);
540 StringCbCopyExW(p
, cbName
, pPrinter
->pwszPrinterName
, &p
, &cbName
, 0);
542 // Finally copy the structure and advance to the next one in the output buffer.
543 *ppPrinterInfoEnd
= PackStrings(pwszStrings
, (PBYTE
)(*ppPrinterInfo
), dwPrinterInfo0Offsets
, *ppPrinterInfoEnd
);
546 // Free the memory for temporary strings.
547 DllFreeSplMem(pwszStrings
[0]);
551 _LocalGetPrinterLevel1(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_1W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
553 const WCHAR wszComma
[] = L
",";
557 size_t cbDescription
;
559 PWSTR pwszStrings
[3];
561 // Calculate the string lengths.
562 // Attention: pComment equals the "Description" registry value while pDescription is concatenated out of several strings.
563 // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
564 cbName
= (cchComputerName
+ wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
565 cbComment
= (wcslen(pPrinter
->pwszDescription
) + 1) * sizeof(WCHAR
);
566 cbDescription
= cbName
+ (wcslen(pPrinter
->pwszPrinterDriver
) + 1 + wcslen(pPrinter
->pwszLocation
) + 1) * sizeof(WCHAR
);
570 *pcbNeeded
+= sizeof(PRINTER_INFO_1W
) + cbName
+ cbComment
+ cbDescription
;
574 // Indicate that this is a Printer.
575 (*ppPrinterInfo
)->Flags
= PRINTER_ENUM_ICON8
;
577 // Copy the Printer Name.
578 pwszStrings
[0] = DllAllocSplMem(cbName
);
580 StringCbCopyExW(p
, cbName
, wszComputerName
, &p
, &cbName
, 0);
581 StringCbCopyExW(p
, cbName
, pPrinter
->pwszPrinterName
, &p
, &cbName
, 0);
583 // Copy the Printer comment (equals the "Description" registry value).
584 pwszStrings
[1] = pPrinter
->pwszDescription
;
586 // Copy the description, which for PRINTER_INFO_1W has the form "Name,Printer Driver,Location"
587 pwszStrings
[2] = DllAllocSplMem(cbDescription
);
589 StringCbCopyExW(p
, cbDescription
, wszComputerName
, &p
, &cbDescription
, 0);
590 StringCbCopyExW(p
, cbDescription
, pPrinter
->pwszPrinterName
, &p
, &cbDescription
, 0);
591 StringCbCopyExW(p
, cbDescription
, wszComma
, &p
, &cbDescription
, 0);
592 StringCbCopyExW(p
, cbDescription
, pPrinter
->pwszPrinterDriver
, &p
, &cbDescription
, 0);
593 StringCbCopyExW(p
, cbDescription
, wszComma
, &p
, &cbDescription
, 0);
594 StringCbCopyExW(p
, cbDescription
, pPrinter
->pwszLocation
, &p
, &cbDescription
, 0);
596 // Finally copy the structure and advance to the next one in the output buffer.
597 *ppPrinterInfoEnd
= PackStrings(pwszStrings
, (PBYTE
)(*ppPrinterInfo
), dwPrinterInfo1Offsets
, *ppPrinterInfoEnd
);
600 // Free the memory for temporary strings.
601 DllFreeSplMem(pwszStrings
[0]);
602 DllFreeSplMem(pwszStrings
[2]);
606 _LocalGetPrinterLevel2(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_2W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
608 WCHAR wszEmpty
[] = L
"";
611 size_t cbPrinterName
;
618 size_t cbPrintProcessor
;
622 PWSTR pwszStrings
[10];
624 // Calculate the string lengths.
625 cbDevMode
= pPrinter
->pDefaultDevMode
->dmSize
+ pPrinter
->pDefaultDevMode
->dmDriverExtra
;
626 cbPrinterName
= (cchComputerName
+ wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
630 // Attention: pComment equals the "Description" registry value.
631 cbShareName
= sizeof(wszEmpty
);
632 cbPortName
= (wcslen(pPrinter
->pPort
->pwszName
) + 1) * sizeof(WCHAR
);
633 cbDriverName
= (wcslen(pPrinter
->pwszPrinterDriver
) + 1) * sizeof(WCHAR
);
634 cbComment
= (wcslen(pPrinter
->pwszDescription
) + 1) * sizeof(WCHAR
);
635 cbLocation
= (wcslen(pPrinter
->pwszLocation
) + 1) * sizeof(WCHAR
);
636 cbSepFile
= sizeof(wszEmpty
);
637 cbPrintProcessor
= (wcslen(pPrinter
->pPrintProcessor
->pwszName
) + 1) * sizeof(WCHAR
);
638 cbDatatype
= (wcslen(pPrinter
->pwszDefaultDatatype
) + 1) * sizeof(WCHAR
);
639 cbParameters
= sizeof(wszEmpty
);
641 *pcbNeeded
+= sizeof(PRINTER_INFO_2W
) + cbDevMode
+ cbPrinterName
+ cbShareName
+ cbPortName
+ cbDriverName
+ cbComment
+ cbLocation
+ cbSepFile
+ cbPrintProcessor
+ cbDatatype
+ cbParameters
;
645 // Set the general fields.
646 ZeroMemory(*ppPrinterInfo
, sizeof(PRINTER_INFO_2W
));
647 (*ppPrinterInfo
)->Attributes
= pPrinter
->dwAttributes
;
648 (*ppPrinterInfo
)->cJobs
= pPrinter
->JobList
.NodeCount
;
649 (*ppPrinterInfo
)->Status
= pPrinter
->dwStatus
;
651 // Set the pDevMode field (and copy the DevMode).
652 *ppPrinterInfoEnd
-= cbDevMode
;
653 CopyMemory(*ppPrinterInfoEnd
, pPrinter
->pDefaultDevMode
, cbDevMode
);
654 (*ppPrinterInfo
)->pDevMode
= (PDEVMODEW
)(*ppPrinterInfoEnd
);
656 // Set the pPrinterName field.
657 pwszStrings
[0] = DllAllocSplMem(cbPrinterName
);
659 StringCbCopyExW(p
, cbPrinterName
, wszComputerName
, &p
, &cbPrinterName
, 0);
660 StringCbCopyExW(p
, cbPrinterName
, pPrinter
->pwszPrinterName
, &p
, &cbPrinterName
, 0);
662 // Set the pShareName field.
663 pwszStrings
[1] = wszEmpty
;
665 // Set the pPortName field.
666 pwszStrings
[2] = pPrinter
->pPort
->pwszName
;
668 // Set the pDriverName field.
669 pwszStrings
[3] = pPrinter
->pwszPrinterDriver
;
671 // Set the pComment field ((equals the "Description" registry value).
672 pwszStrings
[4] = pPrinter
->pwszDescription
;
674 // Set the pLocation field.
675 pwszStrings
[5] = pPrinter
->pwszLocation
;
677 // Set the pSepFile field.
678 pwszStrings
[6] = wszEmpty
;
680 // Set the pPrintProcessor field.
681 pwszStrings
[7] = pPrinter
->pPrintProcessor
->pwszName
;
683 // Set the pDatatype field.
684 pwszStrings
[8] = pPrinter
->pwszDefaultDatatype
;
686 // Set the pParameters field.
687 pwszStrings
[9] = wszEmpty
;
689 // Finally copy the structure and advance to the next one in the output buffer.
690 *ppPrinterInfoEnd
= PackStrings(pwszStrings
, (PBYTE
)(*ppPrinterInfo
), dwPrinterInfo2Offsets
, *ppPrinterInfoEnd
);
693 // Free the memory for temporary strings.
694 DllFreeSplMem(pwszStrings
[0]);
698 _LocalGetPrinterLevel3(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_3
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
700 SECURITY_DESCRIPTOR SecurityDescriptor
= { 0 };
704 *pcbNeeded
+= sizeof(PRINTER_INFO_3
) + sizeof(SECURITY_DESCRIPTOR
);
708 FIXME("Return a valid security descriptor for PRINTER_INFO_3\n");
710 // Set the pSecurityDescriptor field (and copy the Security Descriptor).
711 *ppPrinterInfoEnd
-= sizeof(SECURITY_DESCRIPTOR
);
712 CopyMemory(*ppPrinterInfoEnd
, &SecurityDescriptor
, sizeof(SECURITY_DESCRIPTOR
));
713 (*ppPrinterInfo
)->pSecurityDescriptor
= (PSECURITY_DESCRIPTOR
)(*ppPrinterInfoEnd
);
715 // Advance to the next structure.
720 _LocalGetPrinterLevel4(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_4W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
722 size_t cbPrinterName
;
724 PWSTR pwszStrings
[1];
726 // Calculate the string lengths.
727 cbPrinterName
= (cchComputerName
+ wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
731 *pcbNeeded
+= sizeof(PRINTER_INFO_4W
) + cbPrinterName
;
735 // Set the general fields.
736 (*ppPrinterInfo
)->pServerName
= NULL
;
737 (*ppPrinterInfo
)->Attributes
= pPrinter
->dwAttributes
;
739 // Set the pPrinterName field.
740 pwszStrings
[0] = DllAllocSplMem(cbPrinterName
);
742 StringCbCopyExW(p
, cbPrinterName
, wszComputerName
, &p
, &cbPrinterName
, 0);
743 StringCbCopyExW(p
, cbPrinterName
, pPrinter
->pwszPrinterName
, &p
, &cbPrinterName
, 0);
745 // Finally copy the structure and advance to the next one in the output buffer.
746 *ppPrinterInfoEnd
= PackStrings(pwszStrings
, (PBYTE
)(*ppPrinterInfo
), dwPrinterInfo4Offsets
, *ppPrinterInfoEnd
);
749 // Free the memory for temporary strings.
750 DllFreeSplMem(pwszStrings
[0]);
754 _LocalGetPrinterLevel5(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_5W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
756 size_t cbPrinterName
;
759 PWSTR pwszStrings
[1];
761 // Calculate the string lengths.
762 cbPrinterName
= (cchComputerName
+ wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
766 cbPortName
= (wcslen(pPrinter
->pPort
->pwszName
) + 1) * sizeof(WCHAR
);
768 *pcbNeeded
+= sizeof(PRINTER_INFO_5W
) + cbPrinterName
+ cbPortName
;
772 // Set the general fields.
773 (*ppPrinterInfo
)->Attributes
= pPrinter
->dwAttributes
;
774 (*ppPrinterInfo
)->DeviceNotSelectedTimeout
= dwDeviceNotSelectedTimeout
;
775 (*ppPrinterInfo
)->TransmissionRetryTimeout
= dwTransmissionRetryTimeout
;
777 // Set the pPrinterName field.
778 pwszStrings
[0] = DllAllocSplMem(cbPrinterName
);
780 StringCbCopyExW(p
, cbPrinterName
, wszComputerName
, &p
, &cbPrinterName
, 0);
781 StringCbCopyExW(p
, cbPrinterName
, pPrinter
->pwszPrinterName
, &p
, &cbPrinterName
, 0);
783 // Set the pPortName field.
784 pwszStrings
[1] = pPrinter
->pPort
->pwszName
;
786 // Finally copy the structure and advance to the next one in the output buffer.
787 *ppPrinterInfoEnd
= PackStrings(pwszStrings
, (PBYTE
)(*ppPrinterInfo
), dwPrinterInfo5Offsets
, *ppPrinterInfoEnd
);
790 // Free the memory for temporary strings.
791 DllFreeSplMem(pwszStrings
[0]);
795 _LocalGetPrinterLevel6(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_6
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
799 *pcbNeeded
+= sizeof(PRINTER_INFO_6
);
803 // Set the general fields.
804 (*ppPrinterInfo
)->dwStatus
= pPrinter
->dwStatus
;
806 // Advance to the next structure.
811 _LocalGetPrinterLevel7(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_7W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
815 *pcbNeeded
+= sizeof(PRINTER_INFO_7W
);
819 FIXME("No Directory Support, returning DSPRINT_UNPUBLISH for PRINTER_INFO_7 all the time!\n");
821 // Set the general fields.
822 (*ppPrinterInfo
)->dwAction
= DSPRINT_UNPUBLISH
;
823 (*ppPrinterInfo
)->pszObjectGUID
= NULL
;
825 // Advance to the next structure.
830 _LocalGetPrinterLevel8(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_8W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
834 // Calculate the string lengths.
835 cbDevMode
= pPrinter
->pDefaultDevMode
->dmSize
+ pPrinter
->pDefaultDevMode
->dmDriverExtra
;
839 *pcbNeeded
+= sizeof(PRINTER_INFO_8W
) + cbDevMode
;
843 // Set the pDevMode field (and copy the DevMode).
844 *ppPrinterInfoEnd
-= cbDevMode
;
845 CopyMemory(*ppPrinterInfoEnd
, pPrinter
->pDefaultDevMode
, cbDevMode
);
846 (*ppPrinterInfo
)->pDevMode
= (PDEVMODEW
)(*ppPrinterInfoEnd
);
848 // Advance to the next structure.
853 _LocalGetPrinterLevel9(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_9W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
857 // Calculate the string lengths.
858 cbDevMode
= pPrinter
->pDefaultDevMode
->dmSize
+ pPrinter
->pDefaultDevMode
->dmDriverExtra
;
862 *pcbNeeded
+= sizeof(PRINTER_INFO_9W
) + cbDevMode
;
866 FIXME("Per-user settings are not yet implemented, returning the global DevMode for PRINTER_INFO_9!\n");
868 // Set the pDevMode field (and copy the DevMode).
869 *ppPrinterInfoEnd
-= cbDevMode
;
870 CopyMemory(*ppPrinterInfoEnd
, pPrinter
->pDefaultDevMode
, cbDevMode
);
871 (*ppPrinterInfo
)->pDevMode
= (PDEVMODEW
)(*ppPrinterInfoEnd
);
873 // Advance to the next structure.
878 LocalEnumPrinters(DWORD Flags
, LPWSTR Name
, DWORD Level
, LPBYTE pPrinterEnum
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
880 DWORD cchComputerName
= 0;
883 PBYTE pPrinterInfoEnd
;
884 PSKIPLIST_NODE pNode
;
885 WCHAR wszComputerName
[2 + MAX_COMPUTERNAME_LENGTH
+ 1 + 1] = { 0 };
886 PLOCAL_PRINTER pPrinter
;
895 if (Flags
& PRINTER_ENUM_CONNECTIONS
|| Flags
& PRINTER_ENUM_REMOTE
|| Flags
& PRINTER_ENUM_NETWORK
)
897 // If the flags for the Network Print Provider are given, bail out with ERROR_INVALID_NAME.
898 // This is the internal way for a Print Provider to signal that it doesn't handle this request.
899 dwErrorCode
= ERROR_INVALID_NAME
;
903 if (!(Flags
& PRINTER_ENUM_LOCAL
|| Flags
& PRINTER_ENUM_NAME
))
905 // The Local Print Provider is the right destination for the request, but without any of these flags,
906 // there is no information that can be returned.
907 // So just signal a successful request.
908 dwErrorCode
= ERROR_SUCCESS
;
912 if (Level
== 3 || Level
> 5)
914 // The caller supplied an invalid level for EnumPrinters.
915 dwErrorCode
= ERROR_INVALID_LEVEL
;
919 if (Level
== 1 && Flags
& PRINTER_ENUM_NAME
&& !Name
)
921 // The caller wants information about this Print Provider.
922 // spoolss packs this into an array of information about all Print Providers.
923 dwErrorCode
= _DumpLevel1PrintProviderInformation(pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
927 // Check the supplied Name parameter (if any).
928 // This may return a Computer Name string we later prepend to the output.
929 dwErrorCode
= _LocalEnumPrintersCheckName(Flags
, Name
, wszComputerName
, &cchComputerName
);
930 if (dwErrorCode
!= ERROR_SUCCESS
)
933 // Count the required buffer size and the number of printers.
935 for (pNode
= PrinterList
.Head
.Next
[0]; pNode
; pNode
= pNode
->Next
[0])
937 pPrinter
= (PLOCAL_PRINTER
)pNode
->Element
;
939 // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
940 if (Flags
& PRINTER_ENUM_SHARED
)
942 FIXME("Printer Sharing is not supported yet, returning no printers!\n");
946 pfnGetPrinterLevels
[Level
](pPrinter
, NULL
, NULL
, pcbNeeded
, cchComputerName
, wszComputerName
);
950 // Check if the supplied buffer is large enough.
951 if (cbBuf
< *pcbNeeded
)
953 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
957 // Copy over the Printer information.
958 pPrinterInfoEnd
= &pPrinterEnum
[*pcbNeeded
];
960 for (pNode
= PrinterList
.Head
.Next
[0]; pNode
; pNode
= pNode
->Next
[0])
962 pPrinter
= (PLOCAL_PRINTER
)pNode
->Element
;
964 // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
965 if (Flags
& PRINTER_ENUM_SHARED
)
968 pfnGetPrinterLevels
[Level
](pPrinter
, &pPrinterEnum
, &pPrinterInfoEnd
, NULL
, cchComputerName
, wszComputerName
);
972 dwErrorCode
= ERROR_SUCCESS
;
975 SetLastError(dwErrorCode
);
976 return (dwErrorCode
== ERROR_SUCCESS
);
980 LocalGetPrinter(HANDLE hPrinter
, DWORD Level
, LPBYTE pPrinter
, DWORD cbBuf
, LPDWORD pcbNeeded
)
984 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
985 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
987 // Check if this is a printer handle.
988 if (pHandle
->HandleType
!= HandleType_Printer
)
990 dwErrorCode
= ERROR_INVALID_HANDLE
;
994 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
998 // The caller supplied an invalid level for GetPrinter.
999 dwErrorCode
= ERROR_INVALID_LEVEL
;
1003 // Count the required buffer size.
1004 pfnGetPrinterLevels
[Level
](pPrinterHandle
->pPrinter
, NULL
, NULL
, pcbNeeded
, 0, NULL
);
1006 // Check if the supplied buffer is large enough.
1007 if (cbBuf
< *pcbNeeded
)
1009 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
1013 // Copy over the Printer information.
1014 pPrinterEnd
= &pPrinter
[*pcbNeeded
];
1015 pfnGetPrinterLevels
[Level
](pPrinterHandle
->pPrinter
, &pPrinter
, &pPrinterEnd
, NULL
, 0, NULL
);
1016 dwErrorCode
= ERROR_SUCCESS
;
1019 SetLastError(dwErrorCode
);
1020 return (dwErrorCode
== ERROR_SUCCESS
);
1024 LocalOpenPrinter(PWSTR lpPrinterName
, HANDLE
* phPrinter
, PPRINTER_DEFAULTSW pDefault
)
1027 DWORD cchComputerName
;
1028 DWORD cchFirstParameter
;
1031 HANDLE hExternalHandle
;
1032 PWSTR p
= lpPrinterName
;
1033 PWSTR pwszFirstParameter
= NULL
;
1034 PWSTR pwszSecondParameter
= NULL
;
1036 PLOCAL_HANDLE pHandle
= NULL
;
1038 PLOCAL_PORT_HANDLE pPortHandle
= NULL
;
1039 PLOCAL_PRINT_MONITOR pPrintMonitor
;
1040 PLOCAL_PRINTER pPrinter
;
1041 PLOCAL_PRINTER_HANDLE pPrinterHandle
= NULL
;
1042 PLOCAL_XCV_HANDLE pXcvHandle
= NULL
;
1043 WCHAR wszComputerName
[MAX_COMPUTERNAME_LENGTH
+ 1];
1044 WCHAR wszFullPath
[MAX_PATH
];
1046 // TODO: lpPrinterName == NULL is supported and means access to the local printer server.
1047 // Not sure yet if that is passed down to localspl.dll or processed in advance.
1050 if (!lpPrinterName
|| !phPrinter
)
1052 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1058 // Skip any server name in the first parameter.
1059 // Does lpPrinterName begin with two backslashes to indicate a server name?
1060 if (lpPrinterName
[0] == L
'\\' && lpPrinterName
[1] == L
'\\')
1062 // Skip these two backslashes.
1065 // Look for the closing backslash.
1066 p
= wcschr(lpPrinterName
, L
'\\');
1069 // We didn't get a proper server name.
1070 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
1074 // Get the local computer name for comparison.
1075 cchComputerName
= _countof(wszComputerName
);
1076 if (!GetComputerNameW(wszComputerName
, &cchComputerName
))
1078 dwErrorCode
= GetLastError();
1079 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode
);
1083 // Now compare this string excerpt with the local computer name.
1084 // The input parameter may not be writable, so we can't null-terminate the input string at this point.
1085 // This print provider only supports local printers, so both strings have to match.
1086 if (p
- lpPrinterName
!= cchComputerName
|| _wcsnicmp(lpPrinterName
, wszComputerName
, cchComputerName
) != 0)
1088 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
1092 // We have checked the server name and don't need it anymore.
1093 lpPrinterName
= p
+ 1;
1096 // Look for a comma. If it exists, it indicates the end of the first parameter.
1097 pwszSecondParameter
= wcschr(lpPrinterName
, L
',');
1098 if (pwszSecondParameter
)
1099 cchFirstParameter
= pwszSecondParameter
- p
;
1101 cchFirstParameter
= wcslen(lpPrinterName
);
1103 // We must have at least one parameter.
1104 if (!cchFirstParameter
&& !pwszSecondParameter
)
1106 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
1110 // Do we have a first parameter?
1111 if (cchFirstParameter
)
1114 // No null-termination is necessary here, because DllAllocSplMem returns a zero-initialized buffer.
1115 pwszFirstParameter
= DllAllocSplMem((cchFirstParameter
+ 1) * sizeof(WCHAR
));
1116 CopyMemory(pwszFirstParameter
, lpPrinterName
, cchFirstParameter
* sizeof(WCHAR
));
1119 // Do we have a second parameter?
1120 if (pwszSecondParameter
)
1122 // Yes, skip the comma at the beginning.
1123 ++pwszSecondParameter
;
1125 // Skip whitespace as well.
1126 while (*pwszSecondParameter
== L
' ')
1127 ++pwszSecondParameter
;
1130 // Create a new handle.
1131 pHandle
= DllAllocSplMem(sizeof(LOCAL_HANDLE
));
1134 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1135 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
1139 // Now we can finally check the type of handle actually requested.
1140 if (pwszFirstParameter
&& pwszSecondParameter
&& wcsncmp(pwszSecondParameter
, L
"Port", 4) == 0)
1142 // The caller wants a port handle and provided a string like:
1144 // "\\COMPUTERNAME\LPT1:, Port"
1146 // Look for this port in our Print Monitor Port list.
1147 pPort
= FindPort(pwszFirstParameter
);
1150 // The supplied port is unknown to all our Print Monitors.
1151 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
1155 pPrintMonitor
= pPort
->pPrintMonitor
;
1157 // Call the monitor's OpenPort function.
1158 if (pPrintMonitor
->bIsLevel2
)
1159 bReturnValue
= ((PMONITOR2
)pPrintMonitor
->pMonitor
)->pfnOpenPort(pPrintMonitor
->hMonitor
, pwszFirstParameter
, &hExternalHandle
);
1161 bReturnValue
= ((LPMONITOREX
)pPrintMonitor
->pMonitor
)->Monitor
.pfnOpenPort(pwszFirstParameter
, &hExternalHandle
);
1165 // The OpenPort function failed. Return its last error.
1166 dwErrorCode
= GetLastError();
1170 // Create a new LOCAL_PORT_HANDLE.
1171 pPortHandle
= DllAllocSplMem(sizeof(LOCAL_PORT_HANDLE
));
1174 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1175 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
1179 pPortHandle
->hPort
= hExternalHandle
;
1180 pPortHandle
->pPort
= pPort
;
1182 // Return the Port handle through our general handle.
1183 pHandle
->HandleType
= HandleType_Port
;
1184 pHandle
->pSpecificHandle
= pPortHandle
;
1186 else if (!pwszFirstParameter
&& pwszSecondParameter
&& wcsncmp(pwszSecondParameter
, L
"Xcv", 3) == 0)
1188 // The caller wants an Xcv handle and provided a string like:
1189 // ", XcvMonitor Local Port"
1190 // "\\COMPUTERNAME\, XcvMonitor Local Port"
1191 // ", XcvPort LPT1:"
1192 // "\\COMPUTERNAME\, XcvPort LPT1:"
1194 // Skip the "Xcv" string.
1195 pwszSecondParameter
+= 3;
1197 // Is XcvMonitor or XcvPort requested?
1198 if (wcsncmp(pwszSecondParameter
, L
"Monitor ", 8) == 0)
1200 // Skip the "Monitor " string.
1201 pwszSecondParameter
+= 8;
1203 // Look for this monitor in our Print Monitor list.
1204 pPrintMonitor
= FindPrintMonitor(pwszSecondParameter
);
1207 // The caller supplied a non-existing Monitor name.
1208 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
1212 else if (wcsncmp(pwszSecondParameter
, L
"Port ", 5) == 0)
1214 // Skip the "Port " string.
1215 pwszSecondParameter
+= 5;
1217 // Look for this port in our Print Monitor Port list.
1218 pPort
= FindPort(pwszFirstParameter
);
1221 // The supplied port is unknown to all our Print Monitors.
1222 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
1226 pPrintMonitor
= pPort
->pPrintMonitor
;
1230 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
1234 // Call the monitor's XcvOpenPort function.
1235 if (pPrintMonitor
->bIsLevel2
)
1236 bReturnValue
= ((PMONITOR2
)pPrintMonitor
->pMonitor
)->pfnXcvOpenPort(pPrintMonitor
->hMonitor
, pwszSecondParameter
, SERVER_EXECUTE
, &hExternalHandle
);
1238 bReturnValue
= ((LPMONITOREX
)pPrintMonitor
->pMonitor
)->Monitor
.pfnXcvOpenPort(pwszSecondParameter
, SERVER_EXECUTE
, &hExternalHandle
);
1242 // The XcvOpenPort function failed. Return its last error.
1243 dwErrorCode
= GetLastError();
1247 // Create a new LOCAL_XCV_HANDLE.
1248 pXcvHandle
= DllAllocSplMem(sizeof(LOCAL_XCV_HANDLE
));
1251 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1252 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
1256 pXcvHandle
->hXcv
= hExternalHandle
;
1257 pXcvHandle
->pPrintMonitor
= pPrintMonitor
;
1259 // Return the Xcv handle through our general handle.
1260 pHandle
->HandleType
= HandleType_Xcv
;
1261 pHandle
->pSpecificHandle
= pXcvHandle
;
1265 // The caller wants a Printer or Printer Job handle and provided a string like:
1267 // "\\COMPUTERNAME\HP DeskJet"
1268 // "HP DeskJet, Job 5"
1269 // "\\COMPUTERNAME\HP DeskJet, Job 5"
1271 // Retrieve the printer from the list.
1272 pPrinter
= LookupElementSkiplist(&PrinterList
, &pwszFirstParameter
, NULL
);
1275 // The printer does not exist.
1276 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
1280 // Create a new LOCAL_PRINTER_HANDLE.
1281 pPrinterHandle
= DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE
));
1282 if (!pPrinterHandle
)
1284 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1285 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
1289 pPrinterHandle
->hSPLFile
= INVALID_HANDLE_VALUE
;
1290 pPrinterHandle
->pPrinter
= pPrinter
;
1292 // Check if a datatype was given.
1293 if (pDefault
&& pDefault
->pDatatype
)
1295 // Use the datatype if it's valid.
1296 if (!FindDatatype(pPrinter
->pPrintProcessor
, pDefault
->pDatatype
))
1298 dwErrorCode
= ERROR_INVALID_DATATYPE
;
1302 pPrinterHandle
->pwszDatatype
= AllocSplStr(pDefault
->pDatatype
);
1306 // Use the default datatype.
1307 pPrinterHandle
->pwszDatatype
= AllocSplStr(pPrinter
->pwszDefaultDatatype
);
1310 // Check if a DevMode was given, otherwise use the default.
1311 if (pDefault
&& pDefault
->pDevMode
)
1312 pPrinterHandle
->pDevMode
= DuplicateDevMode(pDefault
->pDevMode
);
1314 pPrinterHandle
->pDevMode
= DuplicateDevMode(pPrinter
->pDefaultDevMode
);
1316 // Check if the caller wants a handle to an existing Print Job.
1317 if (pwszSecondParameter
)
1319 // The "Job " string has to follow now.
1320 if (wcsncmp(pwszSecondParameter
, L
"Job ", 4) != 0)
1322 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
1326 // Skip the "Job " string.
1327 pwszSecondParameter
+= 4;
1329 // Skip even more whitespace.
1330 while (*pwszSecondParameter
== ' ')
1331 ++pwszSecondParameter
;
1333 // Finally extract the desired Job ID.
1334 dwJobID
= wcstoul(pwszSecondParameter
, NULL
, 10);
1335 if (!IS_VALID_JOB_ID(dwJobID
))
1337 // The user supplied an invalid Job ID.
1338 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
1342 // Look for this job in the Global Job List.
1343 pJob
= LookupElementSkiplist(&GlobalJobList
, &dwJobID
, NULL
);
1344 if (!pJob
|| pJob
->pPrinter
!= pPrinter
)
1346 // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
1347 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
1351 // Try to open its SPL file.
1352 GetJobFilePath(L
"SPL", dwJobID
, wszFullPath
);
1353 pPrinterHandle
->hSPLFile
= CreateFileW(wszFullPath
, GENERIC_READ
| GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
1354 if (pPrinterHandle
->hSPLFile
== INVALID_HANDLE_VALUE
)
1356 dwErrorCode
= GetLastError();
1357 ERR("CreateFileW failed with error %lu for \"%S\"!", dwErrorCode
, wszFullPath
);
1361 // Associate the job to our Printer Handle, but don't set bStartedDoc.
1362 // This prevents the caller from doing further StartDocPrinter, WritePrinter, etc. calls on it.
1363 pPrinterHandle
->pJob
= pJob
;
1366 // Return the Printer handle through our general handle.
1367 pHandle
->HandleType
= HandleType_Printer
;
1368 pHandle
->pSpecificHandle
= pPrinterHandle
;
1371 // We were successful! Return the handle.
1372 *phPrinter
= (HANDLE
)pHandle
;
1373 dwErrorCode
= ERROR_SUCCESS
;
1375 // Don't let the cleanup routines free this.
1377 pPrinterHandle
= NULL
;
1381 DllFreeSplMem(pHandle
);
1385 if (pPrinterHandle
->pwszDatatype
)
1386 DllFreeSplStr(pPrinterHandle
->pwszDatatype
);
1388 if (pPrinterHandle
->pDevMode
)
1389 DllFreeSplMem(pPrinterHandle
->pDevMode
);
1391 DllFreeSplMem(pPrinterHandle
);
1394 if (pwszFirstParameter
)
1395 DllFreeSplMem(pwszFirstParameter
);
1397 SetLastError(dwErrorCode
);
1398 return (dwErrorCode
== ERROR_SUCCESS
);
1402 LocalReadPrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pNoBytesRead
)
1406 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1407 PLOCAL_PORT_HANDLE pPortHandle
;
1408 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1413 dwErrorCode
= ERROR_INVALID_HANDLE
;
1417 // Port handles are an entirely different thing.
1418 if (pHandle
->HandleType
== HandleType_Port
)
1420 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1422 // Call the monitor's ReadPort function.
1423 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1424 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnReadPort(pPortHandle
->hPort
, pBuf
, cbBuf
, pNoBytesRead
);
1426 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnReadPort(pPortHandle
->hPort
, pBuf
, cbBuf
, pNoBytesRead
);
1430 // The ReadPort function failed. Return its last error.
1431 dwErrorCode
= GetLastError();
1435 // We were successful!
1436 dwErrorCode
= ERROR_SUCCESS
;
1440 // The remaining function deals with Printer handles only.
1441 if (pHandle
->HandleType
!= HandleType_Printer
)
1443 dwErrorCode
= ERROR_INVALID_HANDLE
;
1447 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1449 // ReadPrinter needs an opened SPL file to work.
1450 // This only works if a Printer Job Handle was requested in OpenPrinter.
1451 if (pPrinterHandle
->hSPLFile
== INVALID_HANDLE_VALUE
)
1453 dwErrorCode
= ERROR_INVALID_HANDLE
;
1457 // Pass the parameters to ReadFile.
1458 if (!ReadFile(pPrinterHandle
->hSPLFile
, pBuf
, cbBuf
, pNoBytesRead
, NULL
))
1460 dwErrorCode
= GetLastError();
1461 ERR("ReadFile failed with error %lu!\n", dwErrorCode
);
1465 dwErrorCode
= ERROR_SUCCESS
;
1468 SetLastError(dwErrorCode
);
1469 return (dwErrorCode
== ERROR_SUCCESS
);
1473 LocalStartDocPrinter(HANDLE hPrinter
, DWORD Level
, PBYTE pDocInfo
)
1477 DWORD dwReturnValue
= 0;
1478 PDOC_INFO_1W pDocInfo1
= (PDOC_INFO_1W
)pDocInfo
;
1480 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1481 PLOCAL_PORT_HANDLE pPortHandle
;
1482 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1487 dwErrorCode
= ERROR_INVALID_HANDLE
;
1491 // Port handles are an entirely different thing.
1492 if (pHandle
->HandleType
== HandleType_Port
)
1494 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1496 // 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.
1497 // Claim it exclusively for this port handle.
1498 pJob
= pPortHandle
->pPort
->pNextJobToProcess
;
1499 pPortHandle
->pPort
->pNextJobToProcess
= NULL
;
1502 // Call the monitor's StartDocPort function.
1503 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1504 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnStartDocPort(pPortHandle
->hPort
, pJob
->pPrinter
->pwszPrinterName
, pJob
->dwJobID
, Level
, pDocInfo
);
1506 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnStartDocPort(pPortHandle
->hPort
, pJob
->pPrinter
->pwszPrinterName
, pJob
->dwJobID
, Level
, pDocInfo
);
1510 // The StartDocPort function failed. Return its last error.
1511 dwErrorCode
= GetLastError();
1515 // We were successful!
1516 dwErrorCode
= ERROR_SUCCESS
;
1517 dwReturnValue
= pJob
->dwJobID
;
1521 // The remaining function deals with Printer handles only.
1522 if (pHandle
->HandleType
!= HandleType_Printer
)
1524 dwErrorCode
= ERROR_INVALID_HANDLE
;
1530 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1534 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1536 // pJob may already be occupied if this is a Print Job handle. In this case, StartDocPrinter has to fail.
1537 if (pPrinterHandle
->pJob
)
1539 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1543 // Check the validity of the datatype if we got one.
1544 if (pDocInfo1
->pDatatype
&& !FindDatatype(pPrinterHandle
->pJob
->pPrintProcessor
, pDocInfo1
->pDatatype
))
1546 dwErrorCode
= ERROR_INVALID_DATATYPE
;
1550 // Check if this is the right document information level.
1553 dwErrorCode
= ERROR_INVALID_LEVEL
;
1557 // All requirements are met - create a new job.
1558 dwErrorCode
= CreateJob(pPrinterHandle
);
1559 if (dwErrorCode
!= ERROR_SUCCESS
)
1562 // Use any given datatype.
1563 if (pDocInfo1
->pDatatype
&& !ReallocSplStr(&pPrinterHandle
->pJob
->pwszDatatype
, pDocInfo1
->pDatatype
))
1565 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1566 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1570 // Use any given document name.
1571 if (pDocInfo1
->pDocName
&& !ReallocSplStr(&pPrinterHandle
->pJob
->pwszDocumentName
, pDocInfo1
->pDocName
))
1573 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1574 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1578 // We were successful!
1579 dwErrorCode
= ERROR_SUCCESS
;
1580 dwReturnValue
= pPrinterHandle
->pJob
->dwJobID
;
1583 SetLastError(dwErrorCode
);
1584 return dwReturnValue
;
1588 LocalStartPagePrinter(HANDLE hPrinter
)
1591 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1592 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1595 if (!pHandle
|| pHandle
->HandleType
!= HandleType_Printer
)
1597 dwErrorCode
= ERROR_INVALID_HANDLE
;
1601 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1603 // We require StartDocPrinter or AddJob to be called first.
1604 if (!pPrinterHandle
->bStartedDoc
)
1606 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
1610 // Increase the page count.
1611 ++pPrinterHandle
->pJob
->dwTotalPages
;
1612 dwErrorCode
= ERROR_SUCCESS
;
1615 SetLastError(dwErrorCode
);
1616 return (dwErrorCode
== ERROR_SUCCESS
);
1620 LocalWritePrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pcWritten
)
1624 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1625 PLOCAL_PORT_HANDLE pPortHandle
;
1626 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1631 dwErrorCode
= ERROR_INVALID_HANDLE
;
1635 // Port handles are an entirely different thing.
1636 if (pHandle
->HandleType
== HandleType_Port
)
1638 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1640 // Call the monitor's WritePort function.
1641 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1642 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnWritePort(pPortHandle
->hPort
, pBuf
, cbBuf
, pcWritten
);
1644 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnWritePort(pPortHandle
->hPort
, pBuf
, cbBuf
, pcWritten
);
1648 // The WritePort function failed. Return its last error.
1649 dwErrorCode
= GetLastError();
1653 // We were successful!
1654 dwErrorCode
= ERROR_SUCCESS
;
1658 // The remaining function deals with Printer handles only.
1659 if (pHandle
->HandleType
!= HandleType_Printer
)
1661 dwErrorCode
= ERROR_INVALID_HANDLE
;
1665 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1667 // We require StartDocPrinter or AddJob to be called first.
1668 if (!pPrinterHandle
->bStartedDoc
)
1670 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
1674 // TODO: This function is only called when doing non-spooled printing.
1675 // 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).
1677 // Pass the parameters to WriteFile.
1678 if (!WriteFile(SOME_SPOOL_FILE_HANDLE
, pBuf
, cbBuf
, pcWritten
, NULL
))
1680 dwErrorCode
= GetLastError();
1681 ERR("WriteFile failed with error %lu!\n", GetLastError());
1686 dwErrorCode
= ERROR_SUCCESS
;
1689 SetLastError(dwErrorCode
);
1690 return (dwErrorCode
== ERROR_SUCCESS
);
1694 LocalEndPagePrinter(HANDLE hPrinter
)
1697 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1700 if (!pHandle
|| pHandle
->HandleType
!= HandleType_Printer
)
1702 dwErrorCode
= ERROR_INVALID_HANDLE
;
1706 // This function doesn't do anything else for now.
1707 dwErrorCode
= ERROR_SUCCESS
;
1710 SetLastError(dwErrorCode
);
1711 return (dwErrorCode
== ERROR_SUCCESS
);
1715 LocalEndDocPrinter(HANDLE hPrinter
)
1719 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1720 PLOCAL_PORT_HANDLE pPortHandle
;
1721 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1726 dwErrorCode
= ERROR_INVALID_HANDLE
;
1730 // Port handles are an entirely different thing.
1731 if (pHandle
->HandleType
== HandleType_Port
)
1733 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1735 // Call the monitor's EndDocPort function.
1736 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1737 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnEndDocPort(pPortHandle
->hPort
);
1739 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnEndDocPort(pPortHandle
->hPort
);
1743 // The EndDocPort function failed. Return its last error.
1744 dwErrorCode
= GetLastError();
1748 // We were successful!
1749 dwErrorCode
= ERROR_SUCCESS
;
1753 // The remaining function deals with Printer handles only.
1754 if (pHandle
->HandleType
!= HandleType_Printer
)
1756 dwErrorCode
= ERROR_INVALID_HANDLE
;
1760 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1762 // We require StartDocPrinter or AddJob to be called first.
1763 if (!pPrinterHandle
->bStartedDoc
)
1765 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
1769 // TODO: Something like ScheduleJob
1772 pPrinterHandle
->bStartedDoc
= FALSE
;
1773 pPrinterHandle
->pJob
= NULL
;
1774 dwErrorCode
= ERROR_SUCCESS
;
1777 SetLastError(dwErrorCode
);
1778 return (dwErrorCode
== ERROR_SUCCESS
);
1782 LocalClosePrinter(HANDLE hPrinter
)
1784 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1785 PLOCAL_PORT_HANDLE pPortHandle
;
1786 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1787 PLOCAL_XCV_HANDLE pXcvHandle
;
1791 SetLastError(ERROR_INVALID_HANDLE
);
1795 if (pHandle
->HandleType
== HandleType_Port
)
1797 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1799 // Call the monitor's ClosePort function.
1800 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1801 ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnClosePort(pPortHandle
->hPort
);
1803 ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnClosePort(pPortHandle
->hPort
);
1805 else if (pHandle
->HandleType
== HandleType_Printer
)
1807 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1809 // Terminate any started job.
1810 if (pPrinterHandle
->pJob
)
1811 FreeJob(pPrinterHandle
->pJob
);
1813 // Free memory for the fields.
1814 DllFreeSplMem(pPrinterHandle
->pDevMode
);
1815 DllFreeSplStr(pPrinterHandle
->pwszDatatype
);
1817 else if (pHandle
->HandleType
== HandleType_Xcv
)
1819 pXcvHandle
= (PLOCAL_XCV_HANDLE
)pHandle
->pSpecificHandle
;
1821 // Call the monitor's XcvClosePort function.
1822 if (pXcvHandle
->pPrintMonitor
->bIsLevel2
)
1823 ((PMONITOR2
)pXcvHandle
->pPrintMonitor
->pMonitor
)->pfnXcvClosePort(pXcvHandle
->hXcv
);
1825 ((LPMONITOREX
)pXcvHandle
->pPrintMonitor
->pMonitor
)->Monitor
.pfnXcvClosePort(pXcvHandle
->hXcv
);
1828 // Free memory for the handle and the specific handle.
1829 DllFreeSplMem(pHandle
->pSpecificHandle
);
1830 DllFreeSplMem(pHandle
);