2 * PROJECT: ReactOS Local Spooler
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
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()
110 DWORD cchPrinterName
;
116 PLOCAL_PRINTER pPrinter
= NULL
;
117 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
118 PWSTR pwszPort
= NULL
;
119 PWSTR pwszPrintProcessor
= NULL
;
120 WCHAR wszPrinterName
[MAX_PRINTER_NAME
+ 1];
122 TRACE("InitializePrinterList()\n");
124 // Initialize an empty list for our printers.
125 InitializeSkiplist(&PrinterList
, DllAllocSplMem
, _PrinterListCompareRoutine
, (PSKIPLIST_FREE_ROUTINE
)DllFreeSplMem
);
127 // Get the number of subkeys of the printers registry key. Each subkey is a local printer there.
128 dwErrorCode
= (DWORD
)RegQueryInfoKeyW(hPrintersKey
, NULL
, NULL
, NULL
, &dwSubKeys
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
129 if (dwErrorCode
!= ERROR_SUCCESS
)
131 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode
);
135 // Loop through all available local printers.
136 for (i
= 0; i
< dwSubKeys
; i
++)
138 // Cleanup tasks from the previous run
141 RegCloseKey(hSubKey
);
147 if (pPrinter
->pDefaultDevMode
)
148 DllFreeSplMem(pPrinter
->pDefaultDevMode
);
150 if (pPrinter
->pwszDefaultDatatype
)
151 DllFreeSplStr(pPrinter
->pwszDefaultDatatype
);
153 if (pPrinter
->pwszDescription
)
154 DllFreeSplStr(pPrinter
->pwszDescription
);
156 if (pPrinter
->pwszPrinterDriver
)
157 DllFreeSplStr(pPrinter
->pwszPrinterDriver
);
159 if (pPrinter
->pwszPrinterName
)
160 DllFreeSplStr(pPrinter
->pwszPrinterName
);
162 DllFreeSplMem(pPrinter
);
166 if (pwszPrintProcessor
)
168 DllFreeSplStr(pwszPrintProcessor
);
169 pwszPrintProcessor
= NULL
;
172 // Get the name of this printer.
173 cchPrinterName
= _countof(wszPrinterName
);
174 dwErrorCode
= (DWORD
)RegEnumKeyExW(hPrintersKey
, i
, wszPrinterName
, &cchPrinterName
, NULL
, NULL
, NULL
, NULL
);
175 if (dwErrorCode
== ERROR_MORE_DATA
)
177 // This printer name exceeds the maximum length and is invalid.
180 else if (dwErrorCode
!= ERROR_SUCCESS
)
182 ERR("RegEnumKeyExW failed for iteration %lu with status %lu!\n", i
, dwErrorCode
);
186 // Open this Printer's registry key.
187 dwErrorCode
= (DWORD
)RegOpenKeyExW(hPrintersKey
, wszPrinterName
, 0, KEY_READ
, &hSubKey
);
188 if (dwErrorCode
!= ERROR_SUCCESS
)
190 ERR("RegOpenKeyExW failed for Printer \"%S\" with status %lu!\n", wszPrinterName
, dwErrorCode
);
194 // Get the Print Processor.
195 pwszPrintProcessor
= AllocAndRegQueryWSZ(hSubKey
, L
"Print Processor");
196 if (!pwszPrintProcessor
)
199 // Try to find it in the Print Processor List.
200 pPrintProcessor
= FindPrintProcessor(pwszPrintProcessor
);
201 if (!pPrintProcessor
)
203 ERR("Invalid Print Processor \"%S\" for Printer \"%S\"!\n", pwszPrintProcessor
, wszPrinterName
);
208 pwszPort
= AllocAndRegQueryWSZ(hSubKey
, L
"Port");
212 // Try to find it in the Port List.
213 pPort
= FindPort(pwszPort
);
216 ERR("Invalid Port \"%S\" for Printer \"%S\"!\n", pwszPort
, wszPrinterName
);
220 // Create a new LOCAL_PRINTER structure for it.
221 pPrinter
= DllAllocSplMem(sizeof(LOCAL_PRINTER
));
224 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
225 ERR("DllAllocSplMem failed!\n");
229 pPrinter
->pwszPrinterName
= AllocSplStr(wszPrinterName
);
230 pPrinter
->pPrintProcessor
= pPrintProcessor
;
231 pPrinter
->pPort
= pPort
;
232 InitializePrinterJobList(pPrinter
);
235 pPrinter
->pwszLocation
= AllocAndRegQueryWSZ(hSubKey
, L
"Location");
236 if (!pPrinter
->pwszLocation
)
239 // Get the printer driver.
240 pPrinter
->pwszPrinterDriver
= AllocAndRegQueryWSZ(hSubKey
, L
"Printer Driver");
241 if (!pPrinter
->pwszPrinterDriver
)
244 // Get the description.
245 pPrinter
->pwszDescription
= AllocAndRegQueryWSZ(hSubKey
, L
"Description");
246 if (!pPrinter
->pwszDescription
)
249 // Get the default datatype.
250 pPrinter
->pwszDefaultDatatype
= AllocAndRegQueryWSZ(hSubKey
, L
"Datatype");
251 if (!pPrinter
->pwszDefaultDatatype
)
254 // Verify that it's valid.
255 if (!FindDatatype(pPrintProcessor
, pPrinter
->pwszDefaultDatatype
))
257 ERR("Invalid default datatype \"%S\" for Printer \"%S\"!\n", pPrinter
->pwszDefaultDatatype
, wszPrinterName
);
261 // Determine the size of the DevMode.
262 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Default DevMode", NULL
, NULL
, NULL
, &cbData
);
263 if (dwErrorCode
!= ERROR_SUCCESS
)
265 ERR("Couldn't query the size of the DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName
, dwErrorCode
, cbData
);
269 // Allocate enough memory for the DevMode.
270 pPrinter
->pDefaultDevMode
= DllAllocSplMem(cbData
);
271 if (!pPrinter
->pDefaultDevMode
)
273 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
274 ERR("DllAllocSplMem failed!\n");
278 // Get the default DevMode.
279 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Default DevMode", NULL
, NULL
, (PBYTE
)pPrinter
->pDefaultDevMode
, &cbData
);
280 if (dwErrorCode
!= ERROR_SUCCESS
)
282 ERR("Couldn't query a DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName
, dwErrorCode
, cbData
);
286 // Get the Attributes.
287 cbData
= sizeof(DWORD
);
288 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Attributes", NULL
, NULL
, (PBYTE
)&pPrinter
->dwAttributes
, &cbData
);
289 if (dwErrorCode
!= ERROR_SUCCESS
)
291 ERR("Couldn't query Attributes for Printer \"%S\", status is %lu!\n", wszPrinterName
, dwErrorCode
);
296 cbData
= sizeof(DWORD
);
297 dwErrorCode
= (DWORD
)RegQueryValueExW(hSubKey
, L
"Status", NULL
, NULL
, (PBYTE
)&pPrinter
->dwStatus
, &cbData
);
298 if (dwErrorCode
!= ERROR_SUCCESS
)
300 ERR("Couldn't query Status for Printer \"%S\", status is %lu!\n", wszPrinterName
, dwErrorCode
);
304 // Add this printer to the printer list.
305 if (!InsertElementSkiplist(&PrinterList
, pPrinter
))
307 ERR("InsertElementSkiplist failed for Printer \"%S\"!\n", pPrinter
->pwszPrinterName
);
311 // Don't let the cleanup routines free this.
315 dwErrorCode
= ERROR_SUCCESS
;
320 RegCloseKey(hSubKey
);
324 if (pPrinter
->pDefaultDevMode
)
325 DllFreeSplMem(pPrinter
->pDefaultDevMode
);
327 if (pPrinter
->pwszDefaultDatatype
)
328 DllFreeSplStr(pPrinter
->pwszDefaultDatatype
);
330 if (pPrinter
->pwszDescription
)
331 DllFreeSplStr(pPrinter
->pwszDescription
);
333 if (pPrinter
->pwszPrinterDriver
)
334 DllFreeSplStr(pPrinter
->pwszPrinterDriver
);
336 if (pPrinter
->pwszPrinterName
)
337 DllFreeSplStr(pPrinter
->pwszPrinterName
);
339 DllFreeSplMem(pPrinter
);
342 if (pwszPrintProcessor
)
343 DllFreeSplStr(pwszPrintProcessor
);
345 SetLastError(dwErrorCode
);
346 return (dwErrorCode
== ERROR_SUCCESS
);
350 * @name _LocalEnumPrintersCheckName
352 * Checks the Name parameter supplied to a call to EnumPrinters.
355 * Flags parameter of EnumPrinters.
358 * Name parameter of EnumPrinters to check.
360 * @param pwszComputerName
361 * Pointer to a string able to hold 2 + MAX_COMPUTERNAME_LENGTH + 1 + 1 characters.
362 * On return, it may contain a computer name to prepend in EnumPrinters depending on the case.
364 * @param pcchComputerName
365 * If a string to prepend is returned, this pointer receives its length in characters.
368 * ERROR_SUCCESS if processing in EnumPrinters can be continued.
369 * ERROR_INVALID_NAME if the Name parameter is invalid for the given flags and this Print Provider.
370 * Any other error code if GetComputerNameW fails. Error codes indicating failure should then be returned by EnumPrinters.
373 _LocalEnumPrintersCheckName(DWORD Flags
, PCWSTR Name
, PWSTR pwszComputerName
, PDWORD pcchComputerName
)
376 PCWSTR pComputerName
;
378 // If there is no Name parameter to check, we can just continue in EnumPrinters.
380 return ERROR_SUCCESS
;
382 // Check if Name does not begin with two backslashes (required for specifying Computer Names).
383 if (Name
[0] != L
'\\' || Name
[1] != L
'\\')
385 if (Flags
& PRINTER_ENUM_NAME
)
387 // If PRINTER_ENUM_NAME is specified, any given Name parameter may only contain the
388 // Print Provider Name or the local Computer Name.
390 // Compare with the Print Provider Name.
391 if (wcsicmp(Name
, wszPrintProviderInfo
[0]) == 0)
392 return ERROR_SUCCESS
;
394 // Dismiss anything else.
395 return ERROR_INVALID_NAME
;
399 // If PRINTER_ENUM_NAME is not specified, we just ignore anything that is not a Computer Name.
400 return ERROR_SUCCESS
;
404 // Prepend the backslashes to the output computer name.
405 pwszComputerName
[0] = L
'\\';
406 pwszComputerName
[1] = L
'\\';
408 // Get the local computer name for comparison.
409 *pcchComputerName
= MAX_COMPUTERNAME_LENGTH
+ 1;
410 if (!GetComputerNameW(&pwszComputerName
[2], pcchComputerName
))
412 ERR("GetComputerNameW failed with error %lu!\n", GetLastError());
413 return GetLastError();
416 // Add the leading slashes to the total length.
417 *pcchComputerName
+= 2;
419 // Compare both names.
420 pComputerName
= &pwszComputerName
[2];
424 // Are we at the end of the local Computer Name string?
427 // Are we also at the end of the supplied Name parameter?
428 // A terminating NUL character and a backslash are both treated as the end, but they are treated differently.
431 // If both names match and Name ends with a NUL character, the computer name will be prepended in EnumPrinters.
432 // Add a trailing backslash for that.
433 pwszComputerName
[(*pcchComputerName
)++] = L
'\\';
434 pwszComputerName
[*pcchComputerName
] = 0;
435 return ERROR_SUCCESS
;
437 else if (*pName
== L
'\\')
439 if (Flags
& PRINTER_ENUM_NAME
)
441 // If PRINTER_ENUM_NAME is specified and a Name parameter is given, it must be exactly the local
442 // Computer Name with two backslashes prepended. Anything else (like "\\COMPUTERNAME\") is dismissed.
443 return ERROR_INVALID_NAME
;
447 // If PRINTER_ENUM_NAME is not specified and a Name parameter is given, it may also end with a backslash.
448 // Only the Computer Name between the backslashes is checked then.
449 // This is largely undocumented, but verified by tests (see winspool_apitest).
450 // In this case, no computer name is prepended in EnumPrinters though.
451 *pwszComputerName
= 0;
452 *pcchComputerName
= 0;
453 return ERROR_SUCCESS
;
458 // Compare both Computer Names case-insensitively and reject with ERROR_INVALID_NAME if they don't match.
459 if (towlower(*pName
) != towlower(*pComputerName
))
460 return ERROR_INVALID_NAME
;
468 _DumpLevel1PrintProviderInformation(PBYTE pPrinterEnum
, DWORD cbBuf
, PDWORD pcbNeeded
, PDWORD pcReturned
)
472 // Count the needed bytes for Print Provider information.
473 *pcbNeeded
= sizeof(PRINTER_INFO_1W
);
475 for (i
= 0; i
< 3; i
++)
476 *pcbNeeded
+= (wcslen(wszPrintProviderInfo
[i
]) + 1) * sizeof(WCHAR
);
478 // Check if the supplied buffer is large enough.
479 if (cbBuf
< *pcbNeeded
)
480 return ERROR_INSUFFICIENT_BUFFER
;
482 // Copy over the Print Provider information.
483 ((PPRINTER_INFO_1W
)pPrinterEnum
)->Flags
= 0;
484 PackStrings(wszPrintProviderInfo
, pPrinterEnum
, dwPrinterInfo1Offsets
, &pPrinterEnum
[*pcbNeeded
]);
487 return ERROR_SUCCESS
;
491 _LocalGetPrinterLevel0(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_STRESS
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
495 PWSTR pwszStrings
[1];
496 SYSTEM_INFO SystemInfo
;
498 // Calculate the string lengths.
499 cbName
= (cchComputerName
+ wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
503 *pcbNeeded
+= sizeof(PRINTER_INFO_STRESS
) + cbName
;
507 // Set the general fields.
508 ZeroMemory(*ppPrinterInfo
, sizeof(PRINTER_INFO_STRESS
));
509 (*ppPrinterInfo
)->cJobs
= pPrinter
->JobList
.NodeCount
;
510 (*ppPrinterInfo
)->dwGetVersion
= GetVersion();
511 (*ppPrinterInfo
)->Status
= pPrinter
->dwStatus
;
514 (*ppPrinterInfo
)->fFreeBuild
= 1;
517 GetSystemInfo(&SystemInfo
);
518 (*ppPrinterInfo
)->dwNumberOfProcessors
= SystemInfo
.dwNumberOfProcessors
;
519 (*ppPrinterInfo
)->dwProcessorType
= SystemInfo
.dwProcessorType
;
520 (*ppPrinterInfo
)->wProcessorArchitecture
= SystemInfo
.wProcessorArchitecture
;
521 (*ppPrinterInfo
)->wProcessorLevel
= SystemInfo
.wProcessorLevel
;
523 // Copy the Printer Name.
524 pwszStrings
[0] = DllAllocSplMem(cbName
);
526 StringCbCopyExW(p
, cbName
, wszComputerName
, &p
, &cbName
, 0);
527 StringCbCopyExW(p
, cbName
, pPrinter
->pwszPrinterName
, &p
, &cbName
, 0);
529 // Finally copy the structure and advance to the next one in the output buffer.
530 *ppPrinterInfoEnd
= PackStrings(pwszStrings
, (PBYTE
)(*ppPrinterInfo
), dwPrinterInfo0Offsets
, *ppPrinterInfoEnd
);
533 // Free the memory for temporary strings.
534 DllFreeSplMem(pwszStrings
[0]);
538 _LocalGetPrinterLevel1(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_1W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
540 const WCHAR wszComma
[] = L
",";
544 size_t cbDescription
;
546 PWSTR pwszStrings
[3];
548 // Calculate the string lengths.
549 // Attention: pComment equals the "Description" registry value while pDescription is concatenated out of several strings.
550 // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
551 cbName
= (cchComputerName
+ wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
552 cbComment
= (wcslen(pPrinter
->pwszDescription
) + 1) * sizeof(WCHAR
);
553 cbDescription
= cbName
+ (wcslen(pPrinter
->pwszPrinterDriver
) + 1 + wcslen(pPrinter
->pwszLocation
) + 1) * sizeof(WCHAR
);
557 *pcbNeeded
+= sizeof(PRINTER_INFO_1W
) + cbName
+ cbComment
+ cbDescription
;
561 // Indicate that this is a Printer.
562 (*ppPrinterInfo
)->Flags
= PRINTER_ENUM_ICON8
;
564 // Copy the Printer Name.
565 pwszStrings
[0] = DllAllocSplMem(cbName
);
567 StringCbCopyExW(p
, cbName
, wszComputerName
, &p
, &cbName
, 0);
568 StringCbCopyExW(p
, cbName
, pPrinter
->pwszPrinterName
, &p
, &cbName
, 0);
570 // Copy the Printer comment (equals the "Description" registry value).
571 pwszStrings
[1] = pPrinter
->pwszDescription
;
573 // Copy the description, which for PRINTER_INFO_1W has the form "Name,Printer Driver,Location"
574 pwszStrings
[2] = DllAllocSplMem(cbDescription
);
576 StringCbCopyExW(p
, cbDescription
, wszComputerName
, &p
, &cbDescription
, 0);
577 StringCbCopyExW(p
, cbDescription
, pPrinter
->pwszPrinterName
, &p
, &cbDescription
, 0);
578 StringCbCopyExW(p
, cbDescription
, wszComma
, &p
, &cbDescription
, 0);
579 StringCbCopyExW(p
, cbDescription
, pPrinter
->pwszPrinterDriver
, &p
, &cbDescription
, 0);
580 StringCbCopyExW(p
, cbDescription
, wszComma
, &p
, &cbDescription
, 0);
581 StringCbCopyExW(p
, cbDescription
, pPrinter
->pwszLocation
, &p
, &cbDescription
, 0);
583 // Finally copy the structure and advance to the next one in the output buffer.
584 *ppPrinterInfoEnd
= PackStrings(pwszStrings
, (PBYTE
)(*ppPrinterInfo
), dwPrinterInfo1Offsets
, *ppPrinterInfoEnd
);
587 // Free the memory for temporary strings.
588 DllFreeSplMem(pwszStrings
[0]);
589 DllFreeSplMem(pwszStrings
[2]);
593 _LocalGetPrinterLevel2(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_2W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
595 WCHAR wszEmpty
[] = L
"";
598 size_t cbPrinterName
;
605 size_t cbPrintProcessor
;
609 PWSTR pwszStrings
[10];
611 // Calculate the string lengths.
612 cbDevMode
= pPrinter
->pDefaultDevMode
->dmSize
+ pPrinter
->pDefaultDevMode
->dmDriverExtra
;
613 cbPrinterName
= (cchComputerName
+ wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
617 // Attention: pComment equals the "Description" registry value.
618 cbShareName
= sizeof(wszEmpty
);
619 cbPortName
= (wcslen(pPrinter
->pPort
->pwszName
) + 1) * sizeof(WCHAR
);
620 cbDriverName
= (wcslen(pPrinter
->pwszPrinterDriver
) + 1) * sizeof(WCHAR
);
621 cbComment
= (wcslen(pPrinter
->pwszDescription
) + 1) * sizeof(WCHAR
);
622 cbLocation
= (wcslen(pPrinter
->pwszLocation
) + 1) * sizeof(WCHAR
);
623 cbSepFile
= sizeof(wszEmpty
);
624 cbPrintProcessor
= (wcslen(pPrinter
->pPrintProcessor
->pwszName
) + 1) * sizeof(WCHAR
);
625 cbDatatype
= (wcslen(pPrinter
->pwszDefaultDatatype
) + 1) * sizeof(WCHAR
);
626 cbParameters
= sizeof(wszEmpty
);
628 *pcbNeeded
+= sizeof(PRINTER_INFO_2W
) + cbDevMode
+ cbPrinterName
+ cbShareName
+ cbPortName
+ cbDriverName
+ cbComment
+ cbLocation
+ cbSepFile
+ cbPrintProcessor
+ cbDatatype
+ cbParameters
;
632 // Set the general fields.
633 ZeroMemory(*ppPrinterInfo
, sizeof(PRINTER_INFO_2W
));
634 (*ppPrinterInfo
)->Attributes
= pPrinter
->dwAttributes
;
635 (*ppPrinterInfo
)->cJobs
= pPrinter
->JobList
.NodeCount
;
636 (*ppPrinterInfo
)->Status
= pPrinter
->dwStatus
;
638 // Set the pDevMode field (and copy the DevMode).
639 *ppPrinterInfoEnd
-= cbDevMode
;
640 CopyMemory(*ppPrinterInfoEnd
, pPrinter
->pDefaultDevMode
, cbDevMode
);
641 (*ppPrinterInfo
)->pDevMode
= (PDEVMODEW
)(*ppPrinterInfoEnd
);
643 // Set the pPrinterName field.
644 pwszStrings
[0] = DllAllocSplMem(cbPrinterName
);
646 StringCbCopyExW(p
, cbPrinterName
, wszComputerName
, &p
, &cbPrinterName
, 0);
647 StringCbCopyExW(p
, cbPrinterName
, pPrinter
->pwszPrinterName
, &p
, &cbPrinterName
, 0);
649 // Set the pShareName field.
650 pwszStrings
[1] = wszEmpty
;
652 // Set the pPortName field.
653 pwszStrings
[2] = pPrinter
->pPort
->pwszName
;
655 // Set the pDriverName field.
656 pwszStrings
[3] = pPrinter
->pwszPrinterDriver
;
658 // Set the pComment field ((equals the "Description" registry value).
659 pwszStrings
[4] = pPrinter
->pwszDescription
;
661 // Set the pLocation field.
662 pwszStrings
[5] = pPrinter
->pwszLocation
;
664 // Set the pSepFile field.
665 pwszStrings
[6] = wszEmpty
;
667 // Set the pPrintProcessor field.
668 pwszStrings
[7] = pPrinter
->pPrintProcessor
->pwszName
;
670 // Set the pDatatype field.
671 pwszStrings
[8] = pPrinter
->pwszDefaultDatatype
;
673 // Set the pParameters field.
674 pwszStrings
[9] = wszEmpty
;
676 // Finally copy the structure and advance to the next one in the output buffer.
677 *ppPrinterInfoEnd
= PackStrings(pwszStrings
, (PBYTE
)(*ppPrinterInfo
), dwPrinterInfo2Offsets
, *ppPrinterInfoEnd
);
680 // Free the memory for temporary strings.
681 DllFreeSplMem(pwszStrings
[0]);
685 _LocalGetPrinterLevel3(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_3
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
687 SECURITY_DESCRIPTOR SecurityDescriptor
= { 0 };
691 *pcbNeeded
+= sizeof(PRINTER_INFO_3
) + sizeof(SECURITY_DESCRIPTOR
);
695 FIXME("Return a valid security descriptor for PRINTER_INFO_3\n");
697 // Set the pSecurityDescriptor field (and copy the Security Descriptor).
698 *ppPrinterInfoEnd
-= sizeof(SECURITY_DESCRIPTOR
);
699 CopyMemory(*ppPrinterInfoEnd
, &SecurityDescriptor
, sizeof(SECURITY_DESCRIPTOR
));
700 (*ppPrinterInfo
)->pSecurityDescriptor
= (PSECURITY_DESCRIPTOR
)(*ppPrinterInfoEnd
);
702 // Advance to the next structure.
707 _LocalGetPrinterLevel4(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_4W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
709 size_t cbPrinterName
;
711 PWSTR pwszStrings
[1];
713 // Calculate the string lengths.
714 cbPrinterName
= (cchComputerName
+ wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
718 *pcbNeeded
+= sizeof(PRINTER_INFO_4W
) + cbPrinterName
;
722 // Set the general fields.
723 (*ppPrinterInfo
)->pServerName
= NULL
;
724 (*ppPrinterInfo
)->Attributes
= pPrinter
->dwAttributes
;
726 // Set the pPrinterName field.
727 pwszStrings
[0] = DllAllocSplMem(cbPrinterName
);
729 StringCbCopyExW(p
, cbPrinterName
, wszComputerName
, &p
, &cbPrinterName
, 0);
730 StringCbCopyExW(p
, cbPrinterName
, pPrinter
->pwszPrinterName
, &p
, &cbPrinterName
, 0);
732 // Finally copy the structure and advance to the next one in the output buffer.
733 *ppPrinterInfoEnd
= PackStrings(pwszStrings
, (PBYTE
)(*ppPrinterInfo
), dwPrinterInfo4Offsets
, *ppPrinterInfoEnd
);
736 // Free the memory for temporary strings.
737 DllFreeSplMem(pwszStrings
[0]);
741 _LocalGetPrinterLevel5(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_5W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
743 size_t cbPrinterName
;
746 PWSTR pwszStrings
[2];
748 // Calculate the string lengths.
749 cbPrinterName
= (cchComputerName
+ wcslen(pPrinter
->pwszPrinterName
) + 1) * sizeof(WCHAR
);
753 cbPortName
= (wcslen(pPrinter
->pPort
->pwszName
) + 1) * sizeof(WCHAR
);
755 *pcbNeeded
+= sizeof(PRINTER_INFO_5W
) + cbPrinterName
+ cbPortName
;
759 // Set the general fields.
760 (*ppPrinterInfo
)->Attributes
= pPrinter
->dwAttributes
;
761 (*ppPrinterInfo
)->DeviceNotSelectedTimeout
= dwDeviceNotSelectedTimeout
;
762 (*ppPrinterInfo
)->TransmissionRetryTimeout
= dwTransmissionRetryTimeout
;
764 // Set the pPrinterName field.
765 pwszStrings
[0] = DllAllocSplMem(cbPrinterName
);
767 StringCbCopyExW(p
, cbPrinterName
, wszComputerName
, &p
, &cbPrinterName
, 0);
768 StringCbCopyExW(p
, cbPrinterName
, pPrinter
->pwszPrinterName
, &p
, &cbPrinterName
, 0);
770 // Set the pPortName field.
771 pwszStrings
[1] = pPrinter
->pPort
->pwszName
;
773 // Finally copy the structure and advance to the next one in the output buffer.
774 *ppPrinterInfoEnd
= PackStrings(pwszStrings
, (PBYTE
)(*ppPrinterInfo
), dwPrinterInfo5Offsets
, *ppPrinterInfoEnd
);
777 // Free the memory for temporary strings.
778 DllFreeSplMem(pwszStrings
[0]);
782 _LocalGetPrinterLevel6(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_6
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
786 *pcbNeeded
+= sizeof(PRINTER_INFO_6
);
790 // Set the general fields.
791 (*ppPrinterInfo
)->dwStatus
= pPrinter
->dwStatus
;
793 // Advance to the next structure.
798 _LocalGetPrinterLevel7(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_7W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
802 *pcbNeeded
+= sizeof(PRINTER_INFO_7W
);
806 FIXME("No Directory Support, returning DSPRINT_UNPUBLISH for PRINTER_INFO_7 all the time!\n");
808 // Set the general fields.
809 (*ppPrinterInfo
)->dwAction
= DSPRINT_UNPUBLISH
;
810 (*ppPrinterInfo
)->pszObjectGUID
= NULL
;
812 // Advance to the next structure.
817 _LocalGetPrinterLevel8(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_8W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
821 // Calculate the string lengths.
822 cbDevMode
= pPrinter
->pDefaultDevMode
->dmSize
+ pPrinter
->pDefaultDevMode
->dmDriverExtra
;
826 *pcbNeeded
+= sizeof(PRINTER_INFO_8W
) + cbDevMode
;
830 // Set the pDevMode field (and copy the DevMode).
831 *ppPrinterInfoEnd
-= cbDevMode
;
832 CopyMemory(*ppPrinterInfoEnd
, pPrinter
->pDefaultDevMode
, cbDevMode
);
833 (*ppPrinterInfo
)->pDevMode
= (PDEVMODEW
)(*ppPrinterInfoEnd
);
835 // Advance to the next structure.
840 _LocalGetPrinterLevel9(PLOCAL_PRINTER pPrinter
, PPRINTER_INFO_9W
* ppPrinterInfo
, PBYTE
* ppPrinterInfoEnd
, PDWORD pcbNeeded
, DWORD cchComputerName
, PWSTR wszComputerName
)
844 // Calculate the string lengths.
845 cbDevMode
= pPrinter
->pDefaultDevMode
->dmSize
+ pPrinter
->pDefaultDevMode
->dmDriverExtra
;
849 *pcbNeeded
+= sizeof(PRINTER_INFO_9W
) + cbDevMode
;
853 FIXME("Per-user settings are not yet implemented, returning the global DevMode for PRINTER_INFO_9!\n");
855 // Set the pDevMode field (and copy the DevMode).
856 *ppPrinterInfoEnd
-= cbDevMode
;
857 CopyMemory(*ppPrinterInfoEnd
, pPrinter
->pDefaultDevMode
, cbDevMode
);
858 (*ppPrinterInfo
)->pDevMode
= (PDEVMODEW
)(*ppPrinterInfoEnd
);
860 // Advance to the next structure.
865 LocalEnumPrinters(DWORD Flags
, LPWSTR Name
, DWORD Level
, LPBYTE pPrinterEnum
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
867 DWORD cchComputerName
= 0;
869 PBYTE pPrinterInfoEnd
;
870 PSKIPLIST_NODE pNode
;
871 WCHAR wszComputerName
[2 + MAX_COMPUTERNAME_LENGTH
+ 1 + 1] = { 0 };
872 PLOCAL_PRINTER pPrinter
;
874 TRACE("LocalEnumPrinters(%lu, %S, %lu, %p, %lu, %p, %p)\n", Flags
, Name
, Level
, pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
876 // Do no sanity checks or assertions for pcbNeeded and pcReturned here.
877 // This is verified and required by localspl_apitest!
883 if (Flags
& PRINTER_ENUM_CONNECTIONS
|| Flags
& PRINTER_ENUM_REMOTE
|| Flags
& PRINTER_ENUM_NETWORK
)
885 // If the flags for the Network Print Provider are given, bail out with ERROR_INVALID_NAME.
886 // This is the internal way for a Print Provider to signal that it doesn't handle this request.
887 dwErrorCode
= ERROR_INVALID_NAME
;
891 if (!(Flags
& PRINTER_ENUM_LOCAL
|| Flags
& PRINTER_ENUM_NAME
))
893 // The Local Print Provider is the right destination for the request, but without any of these flags,
894 // there is no information that can be returned.
895 // So just signal a successful request.
896 dwErrorCode
= ERROR_SUCCESS
;
900 if (Level
== 3 || Level
> 5)
902 // The caller supplied an invalid level for EnumPrinters.
903 dwErrorCode
= ERROR_INVALID_LEVEL
;
907 if (Level
== 1 && Flags
& PRINTER_ENUM_NAME
&& !Name
)
909 // The caller wants information about this Print Provider.
910 // spoolss packs this into an array of information about all Print Providers.
911 dwErrorCode
= _DumpLevel1PrintProviderInformation(pPrinterEnum
, cbBuf
, pcbNeeded
, pcReturned
);
915 // Check the supplied Name parameter (if any).
916 // This may return a Computer Name string we later prepend to the output.
917 dwErrorCode
= _LocalEnumPrintersCheckName(Flags
, Name
, wszComputerName
, &cchComputerName
);
918 if (dwErrorCode
!= ERROR_SUCCESS
)
921 // Count the required buffer size and the number of printers.
922 for (pNode
= PrinterList
.Head
.Next
[0]; pNode
; pNode
= pNode
->Next
[0])
924 pPrinter
= (PLOCAL_PRINTER
)pNode
->Element
;
926 // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
927 if (Flags
& PRINTER_ENUM_SHARED
)
929 FIXME("Printer Sharing is not supported yet, returning no printers!\n");
933 pfnGetPrinterLevels
[Level
](pPrinter
, NULL
, NULL
, pcbNeeded
, cchComputerName
, wszComputerName
);
936 // Check if the supplied buffer is large enough.
937 if (cbBuf
< *pcbNeeded
)
939 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
943 // Copy over the Printer information.
944 pPrinterInfoEnd
= &pPrinterEnum
[*pcbNeeded
];
946 for (pNode
= PrinterList
.Head
.Next
[0]; pNode
; pNode
= pNode
->Next
[0])
948 pPrinter
= (PLOCAL_PRINTER
)pNode
->Element
;
950 // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
951 if (Flags
& PRINTER_ENUM_SHARED
)
954 pfnGetPrinterLevels
[Level
](pPrinter
, &pPrinterEnum
, &pPrinterInfoEnd
, NULL
, cchComputerName
, wszComputerName
);
958 dwErrorCode
= ERROR_SUCCESS
;
961 SetLastError(dwErrorCode
);
962 return (dwErrorCode
== ERROR_SUCCESS
);
966 LocalGetPrinter(HANDLE hPrinter
, DWORD Level
, LPBYTE pPrinter
, DWORD cbBuf
, LPDWORD pcbNeeded
)
970 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
971 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
973 TRACE("LocalGetPrinter(%p, %lu, %p, %lu, %p)\n", hPrinter
, Level
, pPrinter
, cbBuf
, pcbNeeded
);
978 dwErrorCode
= ERROR_INVALID_HANDLE
;
982 // Check if this is a printer handle.
983 if (pHandle
->HandleType
!= HandleType_Printer
)
985 dwErrorCode
= ERROR_INVALID_HANDLE
;
989 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
993 // The caller supplied an invalid level for GetPrinter.
994 dwErrorCode
= ERROR_INVALID_LEVEL
;
998 // Count the required buffer size.
1000 pfnGetPrinterLevels
[Level
](pPrinterHandle
->pPrinter
, NULL
, NULL
, pcbNeeded
, 0, NULL
);
1002 // Check if the supplied buffer is large enough.
1003 if (cbBuf
< *pcbNeeded
)
1005 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
1009 // Copy over the Printer information.
1010 pPrinterEnd
= &pPrinter
[*pcbNeeded
];
1011 pfnGetPrinterLevels
[Level
](pPrinterHandle
->pPrinter
, &pPrinter
, &pPrinterEnd
, NULL
, 0, NULL
);
1012 dwErrorCode
= ERROR_SUCCESS
;
1015 SetLastError(dwErrorCode
);
1016 return (dwErrorCode
== ERROR_SUCCESS
);
1020 _LocalOpenPortHandle(PWSTR pwszPortName
, PHANDLE phPrinter
)
1025 PLOCAL_HANDLE pHandle
= NULL
;
1027 PLOCAL_PORT_HANDLE pPortHandle
= NULL
;
1028 PLOCAL_PRINT_MONITOR pPrintMonitor
;
1030 // Look for this port in our Print Monitor Port list.
1031 pPort
= FindPort(pwszPortName
);
1034 // The supplied port is unknown to all our Print Monitors.
1035 dwErrorCode
= ERROR_INVALID_NAME
;
1039 pPrintMonitor
= pPort
->pPrintMonitor
;
1041 // Call the monitor's OpenPort function.
1042 if (pPrintMonitor
->bIsLevel2
)
1043 bReturnValue
= ((PMONITOR2
)pPrintMonitor
->pMonitor
)->pfnOpenPort(pPrintMonitor
->hMonitor
, pwszPortName
, &hPort
);
1045 bReturnValue
= ((LPMONITOREX
)pPrintMonitor
->pMonitor
)->Monitor
.pfnOpenPort(pwszPortName
, &hPort
);
1049 // The OpenPort function failed. Return its last error.
1050 dwErrorCode
= GetLastError();
1054 // Create a new generic handle.
1055 pHandle
= DllAllocSplMem(sizeof(LOCAL_HANDLE
));
1058 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1059 ERR("DllAllocSplMem failed!\n");
1063 // Create a new LOCAL_PORT_HANDLE.
1064 pPortHandle
= DllAllocSplMem(sizeof(LOCAL_PORT_HANDLE
));
1067 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1068 ERR("DllAllocSplMem failed!\n");
1072 pPortHandle
->hPort
= hPort
;
1073 pPortHandle
->pPort
= pPort
;
1075 // Make the generic handle a Port handle.
1076 pHandle
->HandleType
= HandleType_Port
;
1077 pHandle
->pSpecificHandle
= pPortHandle
;
1080 *phPrinter
= (HANDLE
)pHandle
;
1081 return ERROR_SUCCESS
;
1085 DllFreeSplMem(pHandle
);
1088 DllFreeSplMem(pPortHandle
);
1094 _LocalOpenPrinterHandle(PWSTR pwszPrinterName
, PWSTR pwszJobParameter
, PHANDLE phPrinter
, PPRINTER_DEFAULTSW pDefault
)
1098 PLOCAL_HANDLE pHandle
= NULL
;
1100 PLOCAL_PRINTER pPrinter
;
1101 PLOCAL_PRINTER_HANDLE pPrinterHandle
= NULL
;
1102 WCHAR wszFullPath
[MAX_PATH
];
1104 // Retrieve the printer from the list.
1105 pPrinter
= LookupElementSkiplist(&PrinterList
, &pwszPrinterName
, NULL
);
1108 // The printer does not exist.
1109 dwErrorCode
= ERROR_INVALID_NAME
;
1113 // Create a new generic handle.
1114 pHandle
= DllAllocSplMem(sizeof(LOCAL_HANDLE
));
1117 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1118 ERR("DllAllocSplMem failed!\n");
1122 // Create a new LOCAL_PRINTER_HANDLE.
1123 pPrinterHandle
= DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE
));
1124 if (!pPrinterHandle
)
1126 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1127 ERR("DllAllocSplMem failed!\n");
1131 pPrinterHandle
->hSPLFile
= INVALID_HANDLE_VALUE
;
1132 pPrinterHandle
->pPrinter
= pPrinter
;
1134 // Check if a datatype was given.
1135 if (pDefault
&& pDefault
->pDatatype
)
1137 // Use the datatype if it's valid.
1138 if (!FindDatatype(pPrinter
->pPrintProcessor
, pDefault
->pDatatype
))
1140 dwErrorCode
= ERROR_INVALID_DATATYPE
;
1144 pPrinterHandle
->pwszDatatype
= AllocSplStr(pDefault
->pDatatype
);
1148 // Use the default datatype.
1149 pPrinterHandle
->pwszDatatype
= AllocSplStr(pPrinter
->pwszDefaultDatatype
);
1152 // Check if a DevMode was given, otherwise use the default.
1153 if (pDefault
&& pDefault
->pDevMode
)
1154 pPrinterHandle
->pDevMode
= DuplicateDevMode(pDefault
->pDevMode
);
1156 pPrinterHandle
->pDevMode
= DuplicateDevMode(pPrinter
->pDefaultDevMode
);
1158 // Check if the caller wants a handle to an existing Print Job.
1159 if (pwszJobParameter
)
1161 // The "Job " string has to follow now.
1162 if (wcsncmp(pwszJobParameter
, L
"Job ", 4) != 0)
1164 dwErrorCode
= ERROR_INVALID_NAME
;
1168 // Skip the "Job " string.
1169 pwszJobParameter
+= 4;
1171 // Skip even more whitespace.
1172 while (*pwszJobParameter
== ' ')
1175 // Finally extract the desired Job ID.
1176 dwJobID
= wcstoul(pwszJobParameter
, NULL
, 10);
1177 if (!IS_VALID_JOB_ID(dwJobID
))
1179 // The user supplied an invalid Job ID.
1180 dwErrorCode
= ERROR_INVALID_NAME
;
1184 // Look for this job in the Global Job List.
1185 pJob
= LookupElementSkiplist(&GlobalJobList
, &dwJobID
, NULL
);
1186 if (!pJob
|| pJob
->pPrinter
!= pPrinter
)
1188 // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
1189 dwErrorCode
= ERROR_INVALID_PRINTER_NAME
;
1193 // Try to open its SPL file.
1194 GetJobFilePath(L
"SPL", dwJobID
, wszFullPath
);
1195 pPrinterHandle
->hSPLFile
= CreateFileW(wszFullPath
, GENERIC_READ
| GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
1196 if (pPrinterHandle
->hSPLFile
== INVALID_HANDLE_VALUE
)
1198 dwErrorCode
= GetLastError();
1199 ERR("CreateFileW failed with error %lu for \"%S\"!", dwErrorCode
, wszFullPath
);
1203 // Associate the job to our Printer Handle, but don't set bStartedDoc.
1204 // This prevents the caller from doing further StartDocPrinter, WritePrinter, etc. calls on it.
1205 pPrinterHandle
->pJob
= pJob
;
1208 // Make the generic handle a Printer handle.
1209 pHandle
->HandleType
= HandleType_Printer
;
1210 pHandle
->pSpecificHandle
= pPrinterHandle
;
1213 *phPrinter
= (HANDLE
)pHandle
;
1214 return ERROR_SUCCESS
;
1218 DllFreeSplMem(pHandle
);
1222 if (pPrinterHandle
->pwszDatatype
)
1223 DllFreeSplStr(pPrinterHandle
->pwszDatatype
);
1225 if (pPrinterHandle
->pDevMode
)
1226 DllFreeSplMem(pPrinterHandle
->pDevMode
);
1228 DllFreeSplMem(pPrinterHandle
);
1235 _LocalOpenPrintServerHandle(PHANDLE phPrinter
)
1237 PLOCAL_HANDLE pHandle
;
1239 // Create a new generic handle.
1240 pHandle
= DllAllocSplMem(sizeof(LOCAL_HANDLE
));
1243 ERR("DllAllocSplMem failed!\n");
1244 return ERROR_NOT_ENOUGH_MEMORY
;
1247 // Make the generic handle a Print Server handle.
1248 pHandle
->HandleType
= HandleType_PrintServer
;
1249 pHandle
->pSpecificHandle
= NULL
;
1252 *phPrinter
= (HANDLE
)pHandle
;
1253 return ERROR_SUCCESS
;
1257 _LocalOpenXcvHandle(PWSTR pwszParameter
, PHANDLE phPrinter
)
1262 PLOCAL_HANDLE pHandle
= NULL
;
1264 PLOCAL_PRINT_MONITOR pPrintMonitor
;
1265 PLOCAL_XCV_HANDLE pXcvHandle
= NULL
;
1267 // Skip the "Xcv" string.
1270 // Is XcvMonitor or XcvPort requested?
1271 if (wcsncmp(pwszParameter
, L
"Monitor ", 8) == 0)
1273 // Skip the "Monitor " string.
1276 // Look for this monitor in our Print Monitor list.
1277 pPrintMonitor
= FindPrintMonitor(pwszParameter
);
1280 // The caller supplied a non-existing Monitor name.
1281 dwErrorCode
= ERROR_INVALID_NAME
;
1285 else if (wcsncmp(pwszParameter
, L
"Port ", 5) == 0)
1287 // Skip the "Port " string.
1290 // Look for this port in our Print Monitor Port list.
1291 pPort
= FindPort(pwszParameter
);
1294 // The supplied port is unknown to all our Print Monitors.
1295 dwErrorCode
= ERROR_INVALID_NAME
;
1299 pPrintMonitor
= pPort
->pPrintMonitor
;
1303 dwErrorCode
= ERROR_INVALID_NAME
;
1307 // Call the monitor's XcvOpenPort function.
1308 if (pPrintMonitor
->bIsLevel2
)
1309 bReturnValue
= ((PMONITOR2
)pPrintMonitor
->pMonitor
)->pfnXcvOpenPort(pPrintMonitor
->hMonitor
, pwszParameter
, SERVER_EXECUTE
, &hXcv
);
1311 bReturnValue
= ((LPMONITOREX
)pPrintMonitor
->pMonitor
)->Monitor
.pfnXcvOpenPort(pwszParameter
, SERVER_EXECUTE
, &hXcv
);
1315 // The XcvOpenPort function failed. Return its last error.
1316 dwErrorCode
= GetLastError();
1320 // Create a new generic handle.
1321 pHandle
= DllAllocSplMem(sizeof(LOCAL_HANDLE
));
1324 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1325 ERR("DllAllocSplMem failed!\n");
1329 // Create a new LOCAL_XCV_HANDLE.
1330 pXcvHandle
= DllAllocSplMem(sizeof(LOCAL_XCV_HANDLE
));
1333 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1334 ERR("DllAllocSplMem failed!\n");
1338 pXcvHandle
->hXcv
= hXcv
;
1339 pXcvHandle
->pPrintMonitor
= pPrintMonitor
;
1341 // Make the generic handle a Xcv handle.
1342 pHandle
->HandleType
= HandleType_Xcv
;
1343 pHandle
->pSpecificHandle
= pXcvHandle
;
1346 *phPrinter
= (HANDLE
)pHandle
;
1347 return ERROR_SUCCESS
;
1351 DllFreeSplMem(pHandle
);
1354 DllFreeSplMem(pXcvHandle
);
1360 LocalOpenPrinter(PWSTR lpPrinterName
, HANDLE
* phPrinter
, PPRINTER_DEFAULTSW pDefault
)
1362 DWORD cchComputerName
;
1363 DWORD cchFirstParameter
;
1365 PWSTR p
= lpPrinterName
;
1366 PWSTR pwszFirstParameter
= NULL
;
1367 PWSTR pwszSecondParameter
;
1368 WCHAR wszComputerName
[MAX_COMPUTERNAME_LENGTH
+ 1];
1370 TRACE("LocalOpenPrinter(%S, %p, %p)\n", lpPrinterName
, phPrinter
, pDefault
);
1377 // The caller wants a Print Server handle and provided a NULL string.
1378 dwErrorCode
= _LocalOpenPrintServerHandle(phPrinter
);
1382 // Skip any server name in the first parameter.
1383 // Does lpPrinterName begin with two backslashes to indicate a server name?
1384 if (lpPrinterName
[0] == L
'\\' && lpPrinterName
[1] == L
'\\')
1386 // Skip these two backslashes.
1389 // Look for the terminating null character or closing backslash.
1391 while (*p
!= L
'\0' && *p
!= L
'\\')
1394 // Get the local computer name for comparison.
1395 cchComputerName
= _countof(wszComputerName
);
1396 if (!GetComputerNameW(wszComputerName
, &cchComputerName
))
1398 dwErrorCode
= GetLastError();
1399 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode
);
1403 // Now compare this string excerpt with the local computer name.
1404 // The input parameter may not be writable, so we can't null-terminate the input string at this point.
1405 // This print provider only supports local printers, so both strings have to match.
1406 if (p
- lpPrinterName
!= cchComputerName
|| _wcsnicmp(lpPrinterName
, wszComputerName
, cchComputerName
) != 0)
1408 dwErrorCode
= ERROR_INVALID_NAME
;
1412 // If lpPrinterName is only "\\COMPUTERNAME" with nothing more, the caller wants a handle to the local Print Server.
1415 // The caller wants a Print Server handle and provided a string like:
1417 dwErrorCode
= _LocalOpenPrintServerHandle(phPrinter
);
1421 // We have checked the server name and don't need it anymore.
1422 lpPrinterName
= p
+ 1;
1425 // Look for a comma. If it exists, it indicates the end of the first parameter.
1426 pwszSecondParameter
= wcschr(lpPrinterName
, L
',');
1427 if (pwszSecondParameter
)
1428 cchFirstParameter
= pwszSecondParameter
- p
;
1430 cchFirstParameter
= wcslen(lpPrinterName
);
1432 // We must have at least one parameter.
1433 if (!cchFirstParameter
&& !pwszSecondParameter
)
1435 dwErrorCode
= ERROR_INVALID_NAME
;
1439 // Do we have a first parameter?
1440 if (cchFirstParameter
)
1443 // No null-termination is necessary here, because DllAllocSplMem returns a zero-initialized buffer.
1444 pwszFirstParameter
= DllAllocSplMem((cchFirstParameter
+ 1) * sizeof(WCHAR
));
1445 CopyMemory(pwszFirstParameter
, lpPrinterName
, cchFirstParameter
* sizeof(WCHAR
));
1448 // Do we have a second parameter?
1449 if (pwszSecondParameter
)
1451 // Yes, skip the comma at the beginning.
1452 ++pwszSecondParameter
;
1454 // Skip whitespace as well.
1455 while (*pwszSecondParameter
== L
' ')
1456 ++pwszSecondParameter
;
1459 // Now we can finally check the type of handle actually requested.
1460 if (pwszFirstParameter
&& pwszSecondParameter
&& wcsncmp(pwszSecondParameter
, L
"Port", 4) == 0)
1462 // The caller wants a port handle and provided a string like:
1464 // "\\COMPUTERNAME\LPT1:, Port"
1465 dwErrorCode
= _LocalOpenPortHandle(pwszFirstParameter
, phPrinter
);
1467 else if (!pwszFirstParameter
&& pwszSecondParameter
&& wcsncmp(pwszSecondParameter
, L
"Xcv", 3) == 0)
1469 // The caller wants an Xcv handle and provided a string like:
1470 // ", XcvMonitor Local Port"
1471 // "\\COMPUTERNAME\, XcvMonitor Local Port"
1472 // ", XcvPort LPT1:"
1473 // "\\COMPUTERNAME\, XcvPort LPT1:"
1474 dwErrorCode
= _LocalOpenXcvHandle(pwszSecondParameter
, phPrinter
);
1478 // The caller wants a Printer or Printer Job handle and provided a string like:
1480 // "\\COMPUTERNAME\HP DeskJet"
1481 // "HP DeskJet, Job 5"
1482 // "\\COMPUTERNAME\HP DeskJet, Job 5"
1483 dwErrorCode
= _LocalOpenPrinterHandle(pwszFirstParameter
, pwszSecondParameter
, phPrinter
, pDefault
);
1487 if (pwszFirstParameter
)
1488 DllFreeSplMem(pwszFirstParameter
);
1490 SetLastError(dwErrorCode
);
1491 return (dwErrorCode
== ERROR_SUCCESS
);
1495 LocalReadPrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pNoBytesRead
)
1499 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1500 PLOCAL_PORT_HANDLE pPortHandle
;
1501 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1503 TRACE("LocalReadPrinter(%p, %p, %lu, %p)\n", hPrinter
, pBuf
, cbBuf
, pNoBytesRead
);
1508 dwErrorCode
= ERROR_INVALID_HANDLE
;
1512 // Port handles are an entirely different thing.
1513 if (pHandle
->HandleType
== HandleType_Port
)
1515 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1517 // Call the monitor's ReadPort function.
1518 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1519 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnReadPort(pPortHandle
->hPort
, pBuf
, cbBuf
, pNoBytesRead
);
1521 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnReadPort(pPortHandle
->hPort
, pBuf
, cbBuf
, pNoBytesRead
);
1525 // The ReadPort function failed. Return its last error.
1526 dwErrorCode
= GetLastError();
1530 // We were successful!
1531 dwErrorCode
= ERROR_SUCCESS
;
1535 // The remaining function deals with Printer handles only.
1536 if (pHandle
->HandleType
!= HandleType_Printer
)
1538 dwErrorCode
= ERROR_INVALID_HANDLE
;
1542 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1544 // ReadPrinter needs an opened SPL file to work.
1545 // This only works if a Printer Job Handle was requested in OpenPrinter.
1546 if (pPrinterHandle
->hSPLFile
== INVALID_HANDLE_VALUE
)
1548 dwErrorCode
= ERROR_INVALID_HANDLE
;
1552 // Pass the parameters to ReadFile.
1553 if (!ReadFile(pPrinterHandle
->hSPLFile
, pBuf
, cbBuf
, pNoBytesRead
, NULL
))
1555 dwErrorCode
= GetLastError();
1556 ERR("ReadFile failed with error %lu!\n", dwErrorCode
);
1560 dwErrorCode
= ERROR_SUCCESS
;
1563 SetLastError(dwErrorCode
);
1564 return (dwErrorCode
== ERROR_SUCCESS
);
1568 LocalStartDocPrinter(HANDLE hPrinter
, DWORD Level
, PBYTE pDocInfo
)
1572 DWORD dwReturnValue
= 0;
1573 PDOC_INFO_1W pDocInfo1
= (PDOC_INFO_1W
)pDocInfo
;
1575 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1576 PLOCAL_PORT_HANDLE pPortHandle
;
1577 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1579 TRACE("LocalStartDocPrinter(%p, %lu, %p)\n", hPrinter
, Level
, pDocInfo
);
1584 dwErrorCode
= ERROR_INVALID_HANDLE
;
1588 // Port handles are an entirely different thing.
1589 if (pHandle
->HandleType
== HandleType_Port
)
1591 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1593 // 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.
1594 // Claim it exclusively for this port handle.
1595 pJob
= pPortHandle
->pPort
->pNextJobToProcess
;
1596 pPortHandle
->pPort
->pNextJobToProcess
= NULL
;
1599 // Call the monitor's StartDocPort function.
1600 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1601 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnStartDocPort(pPortHandle
->hPort
, pJob
->pPrinter
->pwszPrinterName
, pJob
->dwJobID
, Level
, pDocInfo
);
1603 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnStartDocPort(pPortHandle
->hPort
, pJob
->pPrinter
->pwszPrinterName
, pJob
->dwJobID
, Level
, pDocInfo
);
1607 // The StartDocPort function failed. Return its last error.
1608 dwErrorCode
= GetLastError();
1612 // We were successful!
1613 dwErrorCode
= ERROR_SUCCESS
;
1614 dwReturnValue
= pJob
->dwJobID
;
1618 // The remaining function deals with Printer handles only.
1619 if (pHandle
->HandleType
!= HandleType_Printer
)
1621 dwErrorCode
= ERROR_INVALID_HANDLE
;
1627 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1631 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1633 // pJob may already be occupied if this is a Print Job handle. In this case, StartDocPrinter has to fail.
1634 if (pPrinterHandle
->pJob
)
1636 dwErrorCode
= ERROR_INVALID_PARAMETER
;
1640 // Check the validity of the datatype if we got one.
1641 if (pDocInfo1
->pDatatype
&& !FindDatatype(pPrinterHandle
->pJob
->pPrintProcessor
, pDocInfo1
->pDatatype
))
1643 dwErrorCode
= ERROR_INVALID_DATATYPE
;
1647 // Check if this is the right document information level.
1650 dwErrorCode
= ERROR_INVALID_LEVEL
;
1654 // All requirements are met - create a new job.
1655 dwErrorCode
= CreateJob(pPrinterHandle
);
1656 if (dwErrorCode
!= ERROR_SUCCESS
)
1659 // Use any given datatype.
1660 if (pDocInfo1
->pDatatype
&& !ReallocSplStr(&pPrinterHandle
->pJob
->pwszDatatype
, pDocInfo1
->pDatatype
))
1662 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1663 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1667 // Use any given document name.
1668 if (pDocInfo1
->pDocName
&& !ReallocSplStr(&pPrinterHandle
->pJob
->pwszDocumentName
, pDocInfo1
->pDocName
))
1670 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
1671 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1675 // We were successful!
1676 dwErrorCode
= ERROR_SUCCESS
;
1677 dwReturnValue
= pPrinterHandle
->pJob
->dwJobID
;
1680 SetLastError(dwErrorCode
);
1681 return dwReturnValue
;
1685 LocalStartPagePrinter(HANDLE hPrinter
)
1688 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1689 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1691 TRACE("LocalStartPagePrinter(%p)\n", hPrinter
);
1694 if (!pHandle
|| pHandle
->HandleType
!= HandleType_Printer
)
1696 dwErrorCode
= ERROR_INVALID_HANDLE
;
1700 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1702 // We require StartDocPrinter or AddJob to be called first.
1703 if (!pPrinterHandle
->bStartedDoc
)
1705 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
1709 // Increase the page count.
1710 ++pPrinterHandle
->pJob
->dwTotalPages
;
1711 dwErrorCode
= ERROR_SUCCESS
;
1714 SetLastError(dwErrorCode
);
1715 return (dwErrorCode
== ERROR_SUCCESS
);
1719 LocalWritePrinter(HANDLE hPrinter
, PVOID pBuf
, DWORD cbBuf
, PDWORD pcWritten
)
1723 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1724 PLOCAL_PORT_HANDLE pPortHandle
;
1725 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1727 TRACE("LocalWritePrinter(%p, %p, %lu, %p)\n", hPrinter
, pBuf
, cbBuf
, pcWritten
);
1732 dwErrorCode
= ERROR_INVALID_HANDLE
;
1736 // Port handles are an entirely different thing.
1737 if (pHandle
->HandleType
== HandleType_Port
)
1739 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1741 // Call the monitor's WritePort function.
1742 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1743 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnWritePort(pPortHandle
->hPort
, pBuf
, cbBuf
, pcWritten
);
1745 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnWritePort(pPortHandle
->hPort
, pBuf
, cbBuf
, pcWritten
);
1749 // The WritePort function failed. Return its last error.
1750 dwErrorCode
= GetLastError();
1754 // We were successful!
1755 dwErrorCode
= ERROR_SUCCESS
;
1759 // The remaining function deals with Printer handles only.
1760 if (pHandle
->HandleType
!= HandleType_Printer
)
1762 dwErrorCode
= ERROR_INVALID_HANDLE
;
1766 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1768 // We require StartDocPrinter or AddJob to be called first.
1769 if (!pPrinterHandle
->bStartedDoc
)
1771 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
1775 // TODO: This function is only called when doing non-spooled printing.
1776 // 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).
1778 // Pass the parameters to WriteFile.
1779 if (!WriteFile(SOME_SPOOL_FILE_HANDLE
, pBuf
, cbBuf
, pcWritten
, NULL
))
1781 dwErrorCode
= GetLastError();
1782 ERR("WriteFile failed with error %lu!\n", GetLastError());
1787 dwErrorCode
= ERROR_SUCCESS
;
1790 SetLastError(dwErrorCode
);
1791 return (dwErrorCode
== ERROR_SUCCESS
);
1795 LocalEndPagePrinter(HANDLE hPrinter
)
1798 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1800 TRACE("LocalEndPagePrinter(%p)\n", hPrinter
);
1803 if (!pHandle
|| pHandle
->HandleType
!= HandleType_Printer
)
1805 dwErrorCode
= ERROR_INVALID_HANDLE
;
1809 // This function doesn't do anything else for now.
1810 dwErrorCode
= ERROR_SUCCESS
;
1813 SetLastError(dwErrorCode
);
1814 return (dwErrorCode
== ERROR_SUCCESS
);
1818 LocalEndDocPrinter(HANDLE hPrinter
)
1822 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1823 PLOCAL_PORT_HANDLE pPortHandle
;
1824 PLOCAL_PRINTER_HANDLE pPrinterHandle
;
1826 TRACE("LocalEndDocPrinter(%p)\n", hPrinter
);
1831 dwErrorCode
= ERROR_INVALID_HANDLE
;
1835 // Port handles are an entirely different thing.
1836 if (pHandle
->HandleType
== HandleType_Port
)
1838 pPortHandle
= (PLOCAL_PORT_HANDLE
)pHandle
->pSpecificHandle
;
1840 // Call the monitor's EndDocPort function.
1841 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1842 bReturnValue
= ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnEndDocPort(pPortHandle
->hPort
);
1844 bReturnValue
= ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnEndDocPort(pPortHandle
->hPort
);
1848 // The EndDocPort function failed. Return its last error.
1849 dwErrorCode
= GetLastError();
1853 // We were successful!
1854 dwErrorCode
= ERROR_SUCCESS
;
1858 // The remaining function deals with Printer handles only.
1859 if (pHandle
->HandleType
!= HandleType_Printer
)
1861 dwErrorCode
= ERROR_INVALID_HANDLE
;
1865 pPrinterHandle
= (PLOCAL_PRINTER_HANDLE
)pHandle
->pSpecificHandle
;
1867 // We require StartDocPrinter or AddJob to be called first.
1868 if (!pPrinterHandle
->bStartedDoc
)
1870 dwErrorCode
= ERROR_SPL_NO_STARTDOC
;
1874 // TODO: Something like ScheduleJob
1877 pPrinterHandle
->bStartedDoc
= FALSE
;
1878 pPrinterHandle
->pJob
= NULL
;
1879 dwErrorCode
= ERROR_SUCCESS
;
1882 SetLastError(dwErrorCode
);
1883 return (dwErrorCode
== ERROR_SUCCESS
);
1887 _LocalClosePortHandle(PLOCAL_PORT_HANDLE pPortHandle
)
1889 // Call the monitor's ClosePort function.
1890 if (pPortHandle
->pPort
->pPrintMonitor
->bIsLevel2
)
1891 ((PMONITOR2
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->pfnClosePort(pPortHandle
->hPort
);
1893 ((LPMONITOREX
)pPortHandle
->pPort
->pPrintMonitor
->pMonitor
)->Monitor
.pfnClosePort(pPortHandle
->hPort
);
1897 _LocalClosePrinterHandle(PLOCAL_PRINTER_HANDLE pPrinterHandle
)
1899 // Terminate any started job.
1900 if (pPrinterHandle
->pJob
)
1901 FreeJob(pPrinterHandle
->pJob
);
1903 // Free memory for the fields.
1904 DllFreeSplMem(pPrinterHandle
->pDevMode
);
1905 DllFreeSplStr(pPrinterHandle
->pwszDatatype
);
1909 _LocalCloseXcvHandle(PLOCAL_XCV_HANDLE pXcvHandle
)
1911 // Call the monitor's XcvClosePort function.
1912 if (pXcvHandle
->pPrintMonitor
->bIsLevel2
)
1913 ((PMONITOR2
)pXcvHandle
->pPrintMonitor
->pMonitor
)->pfnXcvClosePort(pXcvHandle
->hXcv
);
1915 ((LPMONITOREX
)pXcvHandle
->pPrintMonitor
->pMonitor
)->Monitor
.pfnXcvClosePort(pXcvHandle
->hXcv
);
1919 LocalClosePrinter(HANDLE hPrinter
)
1921 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
1923 TRACE("LocalClosePrinter(%p)\n", hPrinter
);
1927 SetLastError(ERROR_INVALID_HANDLE
);
1931 if (pHandle
->HandleType
== HandleType_Port
)
1933 _LocalClosePortHandle(pHandle
->pSpecificHandle
);
1935 else if (pHandle
->HandleType
== HandleType_Printer
)
1937 _LocalClosePrinterHandle(pHandle
->pSpecificHandle
);
1939 else if (pHandle
->HandleType
== HandleType_PrintServer
)
1943 else if (pHandle
->HandleType
== HandleType_Xcv
)
1945 _LocalCloseXcvHandle(pHandle
->pSpecificHandle
);
1948 // Free memory for the handle and the specific handle (if any).
1949 if (pHandle
->pSpecificHandle
)
1950 DllFreeSplMem(pHandle
->pSpecificHandle
);
1952 DllFreeSplMem(pHandle
);