4bbcf6ed3297bea21edf2f581f67fd998f42991c
[reactos.git] / reactos / win32ss / printing / providers / localspl / printers.c
1 /*
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>
6 */
7
8 #include "precomp.h"
9
10 // Global Variables
11 SKIPLIST PrinterList;
12
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);
24
25 // Local Constants
26 typedef void (*PLocalGetPrinterLevelFunc)(PLOCAL_PRINTER, PVOID, PBYTE*, PDWORD, DWORD, PWSTR);
27
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
39 };
40
41 static DWORD dwPrinterInfo0Offsets[] = {
42 FIELD_OFFSET(PRINTER_INFO_STRESS, pPrinterName),
43 MAXDWORD
44 };
45
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),
50 MAXDWORD
51 };
52
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),
64 MAXDWORD
65 };
66
67 static DWORD dwPrinterInfo4Offsets[] = {
68 FIELD_OFFSET(PRINTER_INFO_4W, pPrinterName),
69 MAXDWORD
70 };
71
72 static DWORD dwPrinterInfo5Offsets[] = {
73 FIELD_OFFSET(PRINTER_INFO_5W, pPrinterName),
74 FIELD_OFFSET(PRINTER_INFO_5W, pPortName),
75 MAXDWORD
76 };
77
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;
82
83
84 /**
85 * @name _PrinterListCompareRoutine
86 *
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.
89 */
90 static int WINAPI
91 _PrinterListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
92 {
93 PLOCAL_PRINTER A = (PLOCAL_PRINTER)FirstStruct;
94 PLOCAL_PRINTER B = (PLOCAL_PRINTER)SecondStruct;
95
96 return wcsicmp(A->pwszPrinterName, B->pwszPrinterName);
97 }
98
99 /**
100 * @name InitializePrinterList
101 *
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.
105 */
106 BOOL
107 InitializePrinterList()
108 {
109 const WCHAR wszPrintersKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Printers";
110
111 DWORD cbData;
112 DWORD cchPrinterName;
113 DWORD dwErrorCode;
114 DWORD dwSubKeys;
115 DWORD i;
116 HKEY hKey = NULL;
117 HKEY hSubKey = NULL;
118 PLOCAL_PORT pPort;
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];
124
125 // Initialize an empty list for our printers.
126 InitializeSkiplist(&PrinterList, DllAllocSplMem, _PrinterListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
127
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)
131 {
132 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
133 goto Cleanup;
134 }
135
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)
139 {
140 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
141 goto Cleanup;
142 }
143
144 // Loop through all available local printers.
145 for (i = 0; i < dwSubKeys; i++)
146 {
147 // Cleanup tasks from the previous run
148 if (hSubKey)
149 {
150 RegCloseKey(hSubKey);
151 hSubKey = NULL;
152 }
153
154 if (pPrinter)
155 {
156 if (pPrinter->pDefaultDevMode)
157 DllFreeSplMem(pPrinter->pDefaultDevMode);
158
159 if (pPrinter->pwszDefaultDatatype)
160 DllFreeSplStr(pPrinter->pwszDefaultDatatype);
161
162 if (pPrinter->pwszDescription)
163 DllFreeSplStr(pPrinter->pwszDescription);
164
165 if (pPrinter->pwszPrinterDriver)
166 DllFreeSplStr(pPrinter->pwszPrinterDriver);
167
168 if (pPrinter->pwszPrinterName)
169 DllFreeSplStr(pPrinter->pwszPrinterName);
170
171 DllFreeSplMem(pPrinter);
172 pPrinter = NULL;
173 }
174
175 if (pwszPrintProcessor)
176 {
177 DllFreeSplStr(pwszPrintProcessor);
178 pwszPrintProcessor = NULL;
179 }
180
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)
185 {
186 // This printer name exceeds the maximum length and is invalid.
187 continue;
188 }
189 else if (dwErrorCode != ERROR_SUCCESS)
190 {
191 ERR("RegEnumKeyExW failed for iteration %lu with status %lu!\n", i, dwErrorCode);
192 continue;
193 }
194
195 // Open this Printer's registry key.
196 dwErrorCode = (DWORD)RegOpenKeyExW(hKey, wszPrinterName, 0, KEY_READ, &hSubKey);
197 if (dwErrorCode != ERROR_SUCCESS)
198 {
199 ERR("RegOpenKeyExW failed for Printer \"%S\" with status %lu!\n", wszPrinterName, dwErrorCode);
200 continue;
201 }
202
203 // Get the Print Processor.
204 pwszPrintProcessor = AllocAndRegQueryWSZ(hSubKey, L"Print Processor");
205 if (!pwszPrintProcessor)
206 continue;
207
208 // Try to find it in the Print Processor List.
209 pPrintProcessor = FindPrintProcessor(pwszPrintProcessor);
210 if (!pPrintProcessor)
211 {
212 ERR("Invalid Print Processor \"%S\" for Printer \"%S\"!\n", pwszPrintProcessor, wszPrinterName);
213 continue;
214 }
215
216 // Get the Port.
217 pwszPort = AllocAndRegQueryWSZ(hSubKey, L"Port");
218 if (!pwszPort)
219 continue;
220
221 // Try to find it in the Port List.
222 pPort = FindPort(pwszPort);
223 if (!pPort)
224 {
225 ERR("Invalid Port \"%S\" for Printer \"%S\"!\n", pwszPort, wszPrinterName);
226 continue;
227 }
228
229 // Create a new LOCAL_PRINTER structure for it.
230 pPrinter = DllAllocSplMem(sizeof(LOCAL_PRINTER));
231 if (!pPrinter)
232 {
233 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
234 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
235 goto Cleanup;
236 }
237
238 pPrinter->pwszPrinterName = AllocSplStr(wszPrinterName);
239 pPrinter->pPrintProcessor = pPrintProcessor;
240 pPrinter->pPort = pPort;
241 InitializePrinterJobList(pPrinter);
242
243 // Get the location.
244 pPrinter->pwszLocation = AllocAndRegQueryWSZ(hSubKey, L"Location");
245 if (!pPrinter->pwszLocation)
246 continue;
247
248 // Get the printer driver.
249 pPrinter->pwszPrinterDriver = AllocAndRegQueryWSZ(hSubKey, L"Printer Driver");
250 if (!pPrinter->pwszPrinterDriver)
251 continue;
252
253 // Get the description.
254 pPrinter->pwszDescription = AllocAndRegQueryWSZ(hSubKey, L"Description");
255 if (!pPrinter->pwszDescription)
256 continue;
257
258 // Get the default datatype.
259 pPrinter->pwszDefaultDatatype = AllocAndRegQueryWSZ(hSubKey, L"Datatype");
260 if (!pPrinter->pwszDefaultDatatype)
261 continue;
262
263 // Verify that it's valid.
264 if (!FindDatatype(pPrintProcessor, pPrinter->pwszDefaultDatatype))
265 {
266 ERR("Invalid default datatype \"%S\" for Printer \"%S\"!\n", pPrinter->pwszDefaultDatatype, wszPrinterName);
267 continue;
268 }
269
270 // Determine the size of the DevMode.
271 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, NULL, &cbData);
272 if (dwErrorCode != ERROR_SUCCESS)
273 {
274 ERR("Couldn't query the size of the DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName, dwErrorCode, cbData);
275 continue;
276 }
277
278 // Allocate enough memory for the DevMode.
279 pPrinter->pDefaultDevMode = DllAllocSplMem(cbData);
280 if (!pPrinter->pDefaultDevMode)
281 {
282 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
283 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
284 goto Cleanup;
285 }
286
287 // Get the default DevMode.
288 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, (PBYTE)pPrinter->pDefaultDevMode, &cbData);
289 if (dwErrorCode != ERROR_SUCCESS)
290 {
291 ERR("Couldn't query a DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName, dwErrorCode, cbData);
292 continue;
293 }
294
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)
299 {
300 ERR("Couldn't query Attributes for Printer \"%S\", status is %lu!\n", wszPrinterName, dwErrorCode);
301 continue;
302 }
303
304 // Get the Status.
305 cbData = sizeof(DWORD);
306 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Status", NULL, NULL, (PBYTE)&pPrinter->dwStatus, &cbData);
307 if (dwErrorCode != ERROR_SUCCESS)
308 {
309 ERR("Couldn't query Status for Printer \"%S\", status is %lu!\n", wszPrinterName, dwErrorCode);
310 continue;
311 }
312
313 // Add this printer to the printer list.
314 if (!InsertElementSkiplist(&PrinterList, pPrinter))
315 {
316 ERR("InsertElementSkiplist failed for Printer \"%S\"!\n", pPrinter->pwszPrinterName);
317 goto Cleanup;
318 }
319
320 // Don't let the cleanup routines free this.
321 pPrinter = NULL;
322 }
323
324 dwErrorCode = ERROR_SUCCESS;
325
326 Cleanup:
327 // Inside the loop
328 if (hSubKey)
329 RegCloseKey(hSubKey);
330
331 if (pPrinter)
332 {
333 if (pPrinter->pDefaultDevMode)
334 DllFreeSplMem(pPrinter->pDefaultDevMode);
335
336 if (pPrinter->pwszDefaultDatatype)
337 DllFreeSplStr(pPrinter->pwszDefaultDatatype);
338
339 if (pPrinter->pwszDescription)
340 DllFreeSplStr(pPrinter->pwszDescription);
341
342 if (pPrinter->pwszPrinterDriver)
343 DllFreeSplStr(pPrinter->pwszPrinterDriver);
344
345 if (pPrinter->pwszPrinterName)
346 DllFreeSplStr(pPrinter->pwszPrinterName);
347
348 DllFreeSplMem(pPrinter);
349 }
350
351 if (pwszPrintProcessor)
352 DllFreeSplStr(pwszPrintProcessor);
353
354 // Outside the loop
355 if (hKey)
356 RegCloseKey(hKey);
357
358 SetLastError(dwErrorCode);
359 return (dwErrorCode == ERROR_SUCCESS);
360 }
361
362 /**
363 * @name _LocalEnumPrintersCheckName
364 *
365 * Checks the Name parameter supplied to a call to EnumPrinters.
366 *
367 * @param Flags
368 * Flags parameter of EnumPrinters.
369 *
370 * @param Name
371 * Name parameter of EnumPrinters to check.
372 *
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.
376 *
377 * @param pcchComputerName
378 * If a string to prepend is returned, this pointer receives its length in characters.
379 *
380 * @return
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.
384 */
385 static DWORD
386 _LocalEnumPrintersCheckName(DWORD Flags, PCWSTR Name, PWSTR pwszComputerName, PDWORD pcchComputerName)
387 {
388 PCWSTR pName;
389 PCWSTR pComputerName;
390
391 // If there is no Name parameter to check, we can just continue in EnumPrinters.
392 if (!Name)
393 return ERROR_SUCCESS;
394
395 // Check if Name does not begin with two backslashes (required for specifying Computer Names).
396 if (Name[0] != L'\\' || Name[1] != L'\\')
397 {
398 if (Flags & PRINTER_ENUM_NAME)
399 {
400 // If PRINTER_ENUM_NAME is specified, any given Name parameter may only contain the
401 // Print Provider Name or the local Computer Name.
402
403 // Compare with the Print Provider Name.
404 if (wcsicmp(Name, wszPrintProviderInfo[0]) == 0)
405 return ERROR_SUCCESS;
406
407 // Dismiss anything else.
408 return ERROR_INVALID_NAME;
409 }
410 else
411 {
412 // If PRINTER_ENUM_NAME is not specified, we just ignore anything that is not a Computer Name.
413 return ERROR_SUCCESS;
414 }
415 }
416
417 // Prepend the backslashes to the output computer name.
418 pwszComputerName[0] = L'\\';
419 pwszComputerName[1] = L'\\';
420
421 // Get the local computer name for comparison.
422 *pcchComputerName = MAX_COMPUTERNAME_LENGTH + 1;
423 if (!GetComputerNameW(&pwszComputerName[2], pcchComputerName))
424 {
425 ERR("GetComputerNameW failed with error %lu!\n", GetLastError());
426 return GetLastError();
427 }
428
429 // Add the leading slashes to the total length.
430 *pcchComputerName += 2;
431
432 // Compare both names.
433 pComputerName = &pwszComputerName[2];
434 pName = &Name[2];
435 for (;;)
436 {
437 // Are we at the end of the local Computer Name string?
438 if (!*pComputerName)
439 {
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.
442 if (!*pName)
443 {
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;
449 }
450 else if (*pName == L'\\')
451 {
452 if (Flags & PRINTER_ENUM_NAME)
453 {
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;
457 }
458 else
459 {
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;
467 }
468 }
469 }
470
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;
474
475 pName++;
476 pComputerName++;
477 }
478 }
479
480 static DWORD
481 _DumpLevel1PrintProviderInformation(PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
482 {
483 int i;
484
485 // Count the needed bytes for Print Provider information.
486 *pcbNeeded = sizeof(PRINTER_INFO_1W);
487
488 for (i = 0; i < 3; i++)
489 *pcbNeeded += (wcslen(wszPrintProviderInfo[i]) + 1) * sizeof(WCHAR);
490
491 // Check if the supplied buffer is large enough.
492 if (cbBuf < *pcbNeeded)
493 return ERROR_INSUFFICIENT_BUFFER;
494
495 // Copy over the Print Provider information.
496 ((PPRINTER_INFO_1W)pPrinterEnum)->Flags = 0;
497 PackStrings(wszPrintProviderInfo, pPrinterEnum, dwPrinterInfo1Offsets, &pPrinterEnum[*pcbNeeded]);
498 *pcReturned = 1;
499
500 return ERROR_SUCCESS;
501 }
502
503 static void
504 _LocalGetPrinterLevel0(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_STRESS* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
505 {
506 size_t cbName;
507 PWSTR p;
508 PWSTR pwszStrings[1];
509 SYSTEM_INFO SystemInfo;
510
511 // Calculate the string lengths.
512 cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
513
514 if (!ppPrinterInfo)
515 {
516 *pcbNeeded += sizeof(PRINTER_INFO_STRESS) + cbName;
517 return;
518 }
519
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;
525
526 #if !defined(DBG)
527 (*ppPrinterInfo)->fFreeBuild = 1;
528 #endif
529
530 GetSystemInfo(&SystemInfo);
531 (*ppPrinterInfo)->dwNumberOfProcessors = SystemInfo.dwNumberOfProcessors;
532 (*ppPrinterInfo)->dwProcessorType = SystemInfo.dwProcessorType;
533 (*ppPrinterInfo)->wProcessorArchitecture = SystemInfo.wProcessorArchitecture;
534 (*ppPrinterInfo)->wProcessorLevel = SystemInfo.wProcessorLevel;
535
536 // Copy the Printer Name.
537 pwszStrings[0] = DllAllocSplMem(cbName);
538 p = pwszStrings[0];
539 StringCbCopyExW(p, cbName, wszComputerName, &p, &cbName, 0);
540 StringCbCopyExW(p, cbName, pPrinter->pwszPrinterName, &p, &cbName, 0);
541
542 // Finally copy the structure and advance to the next one in the output buffer.
543 *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo0Offsets, *ppPrinterInfoEnd);
544 (*ppPrinterInfo)++;
545
546 // Free the memory for temporary strings.
547 DllFreeSplMem(pwszStrings[0]);
548 }
549
550 static void
551 _LocalGetPrinterLevel1(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_1W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
552 {
553 const WCHAR wszComma[] = L",";
554
555 size_t cbName;
556 size_t cbComment;
557 size_t cbDescription;
558 PWSTR p;
559 PWSTR pwszStrings[3];
560
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);
567
568 if (!ppPrinterInfo)
569 {
570 *pcbNeeded += sizeof(PRINTER_INFO_1W) + cbName + cbComment + cbDescription;
571 return;
572 }
573
574 // Indicate that this is a Printer.
575 (*ppPrinterInfo)->Flags = PRINTER_ENUM_ICON8;
576
577 // Copy the Printer Name.
578 pwszStrings[0] = DllAllocSplMem(cbName);
579 p = pwszStrings[0];
580 StringCbCopyExW(p, cbName, wszComputerName, &p, &cbName, 0);
581 StringCbCopyExW(p, cbName, pPrinter->pwszPrinterName, &p, &cbName, 0);
582
583 // Copy the Printer comment (equals the "Description" registry value).
584 pwszStrings[1] = pPrinter->pwszDescription;
585
586 // Copy the description, which for PRINTER_INFO_1W has the form "Name,Printer Driver,Location"
587 pwszStrings[2] = DllAllocSplMem(cbDescription);
588 p = pwszStrings[2];
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);
595
596 // Finally copy the structure and advance to the next one in the output buffer.
597 *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo1Offsets, *ppPrinterInfoEnd);
598 (*ppPrinterInfo)++;
599
600 // Free the memory for temporary strings.
601 DllFreeSplMem(pwszStrings[0]);
602 DllFreeSplMem(pwszStrings[2]);
603 }
604
605 static void
606 _LocalGetPrinterLevel2(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_2W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
607 {
608 WCHAR wszEmpty[] = L"";
609
610 size_t cbDevMode;
611 size_t cbPrinterName;
612 size_t cbShareName;
613 size_t cbPortName;
614 size_t cbDriverName;
615 size_t cbComment;
616 size_t cbLocation;
617 size_t cbSepFile;
618 size_t cbPrintProcessor;
619 size_t cbDatatype;
620 size_t cbParameters;
621 PWSTR p;
622 PWSTR pwszStrings[10];
623
624 // Calculate the string lengths.
625 cbDevMode = pPrinter->pDefaultDevMode->dmSize + pPrinter->pDefaultDevMode->dmDriverExtra;
626 cbPrinterName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
627
628 if (!ppPrinterInfo)
629 {
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);
640
641 *pcbNeeded += sizeof(PRINTER_INFO_2W) + cbDevMode + cbPrinterName + cbShareName + cbPortName + cbDriverName + cbComment + cbLocation + cbSepFile + cbPrintProcessor + cbDatatype + cbParameters;
642 return;
643 }
644
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;
650
651 // Set the pDevMode field (and copy the DevMode).
652 *ppPrinterInfoEnd -= cbDevMode;
653 CopyMemory(*ppPrinterInfoEnd, pPrinter->pDefaultDevMode, cbDevMode);
654 (*ppPrinterInfo)->pDevMode = (PDEVMODEW)(*ppPrinterInfoEnd);
655
656 // Set the pPrinterName field.
657 pwszStrings[0] = DllAllocSplMem(cbPrinterName);
658 p = pwszStrings[0];
659 StringCbCopyExW(p, cbPrinterName, wszComputerName, &p, &cbPrinterName, 0);
660 StringCbCopyExW(p, cbPrinterName, pPrinter->pwszPrinterName, &p, &cbPrinterName, 0);
661
662 // Set the pShareName field.
663 pwszStrings[1] = wszEmpty;
664
665 // Set the pPortName field.
666 pwszStrings[2] = pPrinter->pPort->pwszName;
667
668 // Set the pDriverName field.
669 pwszStrings[3] = pPrinter->pwszPrinterDriver;
670
671 // Set the pComment field ((equals the "Description" registry value).
672 pwszStrings[4] = pPrinter->pwszDescription;
673
674 // Set the pLocation field.
675 pwszStrings[5] = pPrinter->pwszLocation;
676
677 // Set the pSepFile field.
678 pwszStrings[6] = wszEmpty;
679
680 // Set the pPrintProcessor field.
681 pwszStrings[7] = pPrinter->pPrintProcessor->pwszName;
682
683 // Set the pDatatype field.
684 pwszStrings[8] = pPrinter->pwszDefaultDatatype;
685
686 // Set the pParameters field.
687 pwszStrings[9] = wszEmpty;
688
689 // Finally copy the structure and advance to the next one in the output buffer.
690 *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo2Offsets, *ppPrinterInfoEnd);
691 (*ppPrinterInfo)++;
692
693 // Free the memory for temporary strings.
694 DllFreeSplMem(pwszStrings[0]);
695 }
696
697 static void
698 _LocalGetPrinterLevel3(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_3* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
699 {
700 SECURITY_DESCRIPTOR SecurityDescriptor = { 0 };
701
702 if (!ppPrinterInfo)
703 {
704 *pcbNeeded += sizeof(PRINTER_INFO_3) + sizeof(SECURITY_DESCRIPTOR);
705 return;
706 }
707
708 FIXME("Return a valid security descriptor for PRINTER_INFO_3\n");
709
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);
714
715 // Advance to the next structure.
716 (*ppPrinterInfo)++;
717 }
718
719 static void
720 _LocalGetPrinterLevel4(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_4W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
721 {
722 size_t cbPrinterName;
723 PWSTR p;
724 PWSTR pwszStrings[1];
725
726 // Calculate the string lengths.
727 cbPrinterName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
728
729 if (!ppPrinterInfo)
730 {
731 *pcbNeeded += sizeof(PRINTER_INFO_4W) + cbPrinterName;
732 return;
733 }
734
735 // Set the general fields.
736 (*ppPrinterInfo)->pServerName = NULL;
737 (*ppPrinterInfo)->Attributes = pPrinter->dwAttributes;
738
739 // Set the pPrinterName field.
740 pwszStrings[0] = DllAllocSplMem(cbPrinterName);
741 p = pwszStrings[0];
742 StringCbCopyExW(p, cbPrinterName, wszComputerName, &p, &cbPrinterName, 0);
743 StringCbCopyExW(p, cbPrinterName, pPrinter->pwszPrinterName, &p, &cbPrinterName, 0);
744
745 // Finally copy the structure and advance to the next one in the output buffer.
746 *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo4Offsets, *ppPrinterInfoEnd);
747 (*ppPrinterInfo)++;
748
749 // Free the memory for temporary strings.
750 DllFreeSplMem(pwszStrings[0]);
751 }
752
753 static void
754 _LocalGetPrinterLevel5(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_5W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
755 {
756 size_t cbPrinterName;
757 size_t cbPortName;
758 PWSTR p;
759 PWSTR pwszStrings[1];
760
761 // Calculate the string lengths.
762 cbPrinterName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
763
764 if (!ppPrinterInfo)
765 {
766 cbPortName = (wcslen(pPrinter->pPort->pwszName) + 1) * sizeof(WCHAR);
767
768 *pcbNeeded += sizeof(PRINTER_INFO_5W) + cbPrinterName + cbPortName;
769 return;
770 }
771
772 // Set the general fields.
773 (*ppPrinterInfo)->Attributes = pPrinter->dwAttributes;
774 (*ppPrinterInfo)->DeviceNotSelectedTimeout = dwDeviceNotSelectedTimeout;
775 (*ppPrinterInfo)->TransmissionRetryTimeout = dwTransmissionRetryTimeout;
776
777 // Set the pPrinterName field.
778 pwszStrings[0] = DllAllocSplMem(cbPrinterName);
779 p = pwszStrings[0];
780 StringCbCopyExW(p, cbPrinterName, wszComputerName, &p, &cbPrinterName, 0);
781 StringCbCopyExW(p, cbPrinterName, pPrinter->pwszPrinterName, &p, &cbPrinterName, 0);
782
783 // Set the pPortName field.
784 pwszStrings[1] = pPrinter->pPort->pwszName;
785
786 // Finally copy the structure and advance to the next one in the output buffer.
787 *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo5Offsets, *ppPrinterInfoEnd);
788 (*ppPrinterInfo)++;
789
790 // Free the memory for temporary strings.
791 DllFreeSplMem(pwszStrings[0]);
792 }
793
794 static void
795 _LocalGetPrinterLevel6(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_6* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
796 {
797 if (!ppPrinterInfo)
798 {
799 *pcbNeeded += sizeof(PRINTER_INFO_6);
800 return;
801 }
802
803 // Set the general fields.
804 (*ppPrinterInfo)->dwStatus = pPrinter->dwStatus;
805
806 // Advance to the next structure.
807 (*ppPrinterInfo)++;
808 }
809
810 static void
811 _LocalGetPrinterLevel7(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_7W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
812 {
813 if (!ppPrinterInfo)
814 {
815 *pcbNeeded += sizeof(PRINTER_INFO_7W);
816 return;
817 }
818
819 FIXME("No Directory Support, returning DSPRINT_UNPUBLISH for PRINTER_INFO_7 all the time!\n");
820
821 // Set the general fields.
822 (*ppPrinterInfo)->dwAction = DSPRINT_UNPUBLISH;
823 (*ppPrinterInfo)->pszObjectGUID = NULL;
824
825 // Advance to the next structure.
826 (*ppPrinterInfo)++;
827 }
828
829 static void
830 _LocalGetPrinterLevel8(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_8W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
831 {
832 DWORD cbDevMode;
833
834 // Calculate the string lengths.
835 cbDevMode = pPrinter->pDefaultDevMode->dmSize + pPrinter->pDefaultDevMode->dmDriverExtra;
836
837 if (!ppPrinterInfo)
838 {
839 *pcbNeeded += sizeof(PRINTER_INFO_8W) + cbDevMode;
840 return;
841 }
842
843 // Set the pDevMode field (and copy the DevMode).
844 *ppPrinterInfoEnd -= cbDevMode;
845 CopyMemory(*ppPrinterInfoEnd, pPrinter->pDefaultDevMode, cbDevMode);
846 (*ppPrinterInfo)->pDevMode = (PDEVMODEW)(*ppPrinterInfoEnd);
847
848 // Advance to the next structure.
849 (*ppPrinterInfo)++;
850 }
851
852 static void
853 _LocalGetPrinterLevel9(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_9W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
854 {
855 DWORD cbDevMode;
856
857 // Calculate the string lengths.
858 cbDevMode = pPrinter->pDefaultDevMode->dmSize + pPrinter->pDefaultDevMode->dmDriverExtra;
859
860 if (!ppPrinterInfo)
861 {
862 *pcbNeeded += sizeof(PRINTER_INFO_9W) + cbDevMode;
863 return;
864 }
865
866 FIXME("Per-user settings are not yet implemented, returning the global DevMode for PRINTER_INFO_9!\n");
867
868 // Set the pDevMode field (and copy the DevMode).
869 *ppPrinterInfoEnd -= cbDevMode;
870 CopyMemory(*ppPrinterInfoEnd, pPrinter->pDefaultDevMode, cbDevMode);
871 (*ppPrinterInfo)->pDevMode = (PDEVMODEW)(*ppPrinterInfoEnd);
872
873 // Advance to the next structure.
874 (*ppPrinterInfo)++;
875 }
876
877 BOOL WINAPI
878 LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
879 {
880 DWORD cchComputerName = 0;
881 DWORD dwErrorCode;
882 DWORD i;
883 PBYTE pPrinterInfoEnd;
884 PSKIPLIST_NODE pNode;
885 WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1] = { 0 };
886 PLOCAL_PRINTER pPrinter;
887
888 ASSERT(pcbNeeded);
889 ASSERT(pcReturned);
890
891 // Begin counting.
892 *pcbNeeded = 0;
893 *pcReturned = 0;
894
895 if (Flags & PRINTER_ENUM_CONNECTIONS || Flags & PRINTER_ENUM_REMOTE || Flags & PRINTER_ENUM_NETWORK)
896 {
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;
900 goto Cleanup;
901 }
902
903 if (!(Flags & PRINTER_ENUM_LOCAL || Flags & PRINTER_ENUM_NAME))
904 {
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;
909 goto Cleanup;
910 }
911
912 if (Level == 3 || Level > 5)
913 {
914 // The caller supplied an invalid level for EnumPrinters.
915 dwErrorCode = ERROR_INVALID_LEVEL;
916 goto Cleanup;
917 }
918
919 if (Level == 1 && Flags & PRINTER_ENUM_NAME && !Name)
920 {
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);
924 goto Cleanup;
925 }
926
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)
931 goto Cleanup;
932
933 // Count the required buffer size and the number of printers.
934 i = 0;
935 for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
936 {
937 pPrinter = (PLOCAL_PRINTER)pNode->Element;
938
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)
941 {
942 FIXME("Printer Sharing is not supported yet, returning no printers!\n");
943 continue;
944 }
945
946 pfnGetPrinterLevels[Level](pPrinter, NULL, NULL, pcbNeeded, cchComputerName, wszComputerName);
947 i++;
948 }
949
950 // Check if the supplied buffer is large enough.
951 if (cbBuf < *pcbNeeded)
952 {
953 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
954 goto Cleanup;
955 }
956
957 // Copy over the Printer information.
958 pPrinterInfoEnd = &pPrinterEnum[*pcbNeeded];
959
960 for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
961 {
962 pPrinter = (PLOCAL_PRINTER)pNode->Element;
963
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)
966 continue;
967
968 pfnGetPrinterLevels[Level](pPrinter, &pPrinterEnum, &pPrinterInfoEnd, NULL, cchComputerName, wszComputerName);
969 }
970
971 *pcReturned = i;
972 dwErrorCode = ERROR_SUCCESS;
973
974 Cleanup:
975 SetLastError(dwErrorCode);
976 return (dwErrorCode == ERROR_SUCCESS);
977 }
978
979 BOOL WINAPI
980 LocalGetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
981 {
982 DWORD dwErrorCode;
983 PBYTE pPrinterEnd;
984 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
985 PLOCAL_PRINTER_HANDLE pPrinterHandle;
986
987 // Check if this is a printer handle.
988 if (pHandle->HandleType != HandleType_Printer)
989 {
990 dwErrorCode = ERROR_INVALID_HANDLE;
991 goto Cleanup;
992 }
993
994 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
995
996 if (Level > 9)
997 {
998 // The caller supplied an invalid level for GetPrinter.
999 dwErrorCode = ERROR_INVALID_LEVEL;
1000 goto Cleanup;
1001 }
1002
1003 // Count the required buffer size.
1004 pfnGetPrinterLevels[Level](pPrinterHandle->pPrinter, NULL, NULL, pcbNeeded, 0, NULL);
1005
1006 // Check if the supplied buffer is large enough.
1007 if (cbBuf < *pcbNeeded)
1008 {
1009 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
1010 goto Cleanup;
1011 }
1012
1013 // Copy over the Printer information.
1014 pPrinterEnd = &pPrinter[*pcbNeeded];
1015 pfnGetPrinterLevels[Level](pPrinterHandle->pPrinter, &pPrinter, &pPrinterEnd, NULL, 0, NULL);
1016 dwErrorCode = ERROR_SUCCESS;
1017
1018 Cleanup:
1019 SetLastError(dwErrorCode);
1020 return (dwErrorCode == ERROR_SUCCESS);
1021 }
1022
1023 BOOL WINAPI
1024 LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault)
1025 {
1026 BOOL bReturnValue;
1027 DWORD cchComputerName;
1028 DWORD cchFirstParameter;
1029 DWORD dwErrorCode;
1030 DWORD dwJobID;
1031 HANDLE hExternalHandle;
1032 PWSTR p = lpPrinterName;
1033 PWSTR pwszFirstParameter = NULL;
1034 PWSTR pwszSecondParameter = NULL;
1035 PLOCAL_JOB pJob;
1036 PLOCAL_HANDLE pHandle = NULL;
1037 PLOCAL_PORT pPort;
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];
1045
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.
1048
1049 // Sanity checks
1050 if (!lpPrinterName || !phPrinter)
1051 {
1052 dwErrorCode = ERROR_INVALID_PARAMETER;
1053 goto Cleanup;
1054 }
1055
1056 *phPrinter = NULL;
1057
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'\\')
1061 {
1062 // Skip these two backslashes.
1063 lpPrinterName += 2;
1064
1065 // Look for the closing backslash.
1066 p = wcschr(lpPrinterName, L'\\');
1067 if (!p)
1068 {
1069 // We didn't get a proper server name.
1070 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
1071 goto Cleanup;
1072 }
1073
1074 // Get the local computer name for comparison.
1075 cchComputerName = _countof(wszComputerName);
1076 if (!GetComputerNameW(wszComputerName, &cchComputerName))
1077 {
1078 dwErrorCode = GetLastError();
1079 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
1080 goto Cleanup;
1081 }
1082
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)
1087 {
1088 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
1089 goto Cleanup;
1090 }
1091
1092 // We have checked the server name and don't need it anymore.
1093 lpPrinterName = p + 1;
1094 }
1095
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;
1100 else
1101 cchFirstParameter = wcslen(lpPrinterName);
1102
1103 // We must have at least one parameter.
1104 if (!cchFirstParameter && !pwszSecondParameter)
1105 {
1106 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
1107 goto Cleanup;
1108 }
1109
1110 // Do we have a first parameter?
1111 if (cchFirstParameter)
1112 {
1113 // Yes, extract it.
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));
1117 }
1118
1119 // Do we have a second parameter?
1120 if (pwszSecondParameter)
1121 {
1122 // Yes, skip the comma at the beginning.
1123 ++pwszSecondParameter;
1124
1125 // Skip whitespace as well.
1126 while (*pwszSecondParameter == L' ')
1127 ++pwszSecondParameter;
1128 }
1129
1130 // Create a new handle.
1131 pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
1132 if (!pHandle)
1133 {
1134 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1135 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
1136 goto Cleanup;
1137 }
1138
1139 // Now we can finally check the type of handle actually requested.
1140 if (pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Port", 4) == 0)
1141 {
1142 // The caller wants a port handle and provided a string like:
1143 // "LPT1:, Port"
1144 // "\\COMPUTERNAME\LPT1:, Port"
1145
1146 // Look for this port in our Print Monitor Port list.
1147 pPort = FindPort(pwszFirstParameter);
1148 if (!pPort)
1149 {
1150 // The supplied port is unknown to all our Print Monitors.
1151 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
1152 goto Cleanup;
1153 }
1154
1155 pPrintMonitor = pPort->pPrintMonitor;
1156
1157 // Call the monitor's OpenPort function.
1158 if (pPrintMonitor->bIsLevel2)
1159 bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnOpenPort(pPrintMonitor->hMonitor, pwszFirstParameter, &hExternalHandle);
1160 else
1161 bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnOpenPort(pwszFirstParameter, &hExternalHandle);
1162
1163 if (!bReturnValue)
1164 {
1165 // The OpenPort function failed. Return its last error.
1166 dwErrorCode = GetLastError();
1167 goto Cleanup;
1168 }
1169
1170 // Create a new LOCAL_PORT_HANDLE.
1171 pPortHandle = DllAllocSplMem(sizeof(LOCAL_PORT_HANDLE));
1172 if (!pPortHandle)
1173 {
1174 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1175 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
1176 goto Cleanup;
1177 }
1178
1179 pPortHandle->hPort = hExternalHandle;
1180 pPortHandle->pPort = pPort;
1181
1182 // Return the Port handle through our general handle.
1183 pHandle->HandleType = HandleType_Port;
1184 pHandle->pSpecificHandle = pPortHandle;
1185 }
1186 else if (!pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Xcv", 3) == 0)
1187 {
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:"
1193
1194 // Skip the "Xcv" string.
1195 pwszSecondParameter += 3;
1196
1197 // Is XcvMonitor or XcvPort requested?
1198 if (wcsncmp(pwszSecondParameter, L"Monitor ", 8) == 0)
1199 {
1200 // Skip the "Monitor " string.
1201 pwszSecondParameter += 8;
1202
1203 // Look for this monitor in our Print Monitor list.
1204 pPrintMonitor = FindPrintMonitor(pwszSecondParameter);
1205 if (!pPrintMonitor)
1206 {
1207 // The caller supplied a non-existing Monitor name.
1208 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
1209 goto Cleanup;
1210 }
1211 }
1212 else if (wcsncmp(pwszSecondParameter, L"Port ", 5) == 0)
1213 {
1214 // Skip the "Port " string.
1215 pwszSecondParameter += 5;
1216
1217 // Look for this port in our Print Monitor Port list.
1218 pPort = FindPort(pwszFirstParameter);
1219 if (!pPort)
1220 {
1221 // The supplied port is unknown to all our Print Monitors.
1222 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
1223 goto Cleanup;
1224 }
1225
1226 pPrintMonitor = pPort->pPrintMonitor;
1227 }
1228 else
1229 {
1230 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
1231 goto Cleanup;
1232 }
1233
1234 // Call the monitor's XcvOpenPort function.
1235 if (pPrintMonitor->bIsLevel2)
1236 bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnXcvOpenPort(pPrintMonitor->hMonitor, pwszSecondParameter, SERVER_EXECUTE, &hExternalHandle);
1237 else
1238 bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnXcvOpenPort(pwszSecondParameter, SERVER_EXECUTE, &hExternalHandle);
1239
1240 if (!bReturnValue)
1241 {
1242 // The XcvOpenPort function failed. Return its last error.
1243 dwErrorCode = GetLastError();
1244 goto Cleanup;
1245 }
1246
1247 // Create a new LOCAL_XCV_HANDLE.
1248 pXcvHandle = DllAllocSplMem(sizeof(LOCAL_XCV_HANDLE));
1249 if (!pXcvHandle)
1250 {
1251 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1252 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
1253 goto Cleanup;
1254 }
1255
1256 pXcvHandle->hXcv = hExternalHandle;
1257 pXcvHandle->pPrintMonitor = pPrintMonitor;
1258
1259 // Return the Xcv handle through our general handle.
1260 pHandle->HandleType = HandleType_Xcv;
1261 pHandle->pSpecificHandle = pXcvHandle;
1262 }
1263 else
1264 {
1265 // The caller wants a Printer or Printer Job handle and provided a string like:
1266 // "HP DeskJet"
1267 // "\\COMPUTERNAME\HP DeskJet"
1268 // "HP DeskJet, Job 5"
1269 // "\\COMPUTERNAME\HP DeskJet, Job 5"
1270
1271 // Retrieve the printer from the list.
1272 pPrinter = LookupElementSkiplist(&PrinterList, &pwszFirstParameter, NULL);
1273 if (!pPrinter)
1274 {
1275 // The printer does not exist.
1276 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
1277 goto Cleanup;
1278 }
1279
1280 // Create a new LOCAL_PRINTER_HANDLE.
1281 pPrinterHandle = DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE));
1282 if (!pPrinterHandle)
1283 {
1284 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1285 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
1286 goto Cleanup;
1287 }
1288
1289 pPrinterHandle->hSPLFile = INVALID_HANDLE_VALUE;
1290 pPrinterHandle->pPrinter = pPrinter;
1291
1292 // Check if a datatype was given.
1293 if (pDefault && pDefault->pDatatype)
1294 {
1295 // Use the datatype if it's valid.
1296 if (!FindDatatype(pPrinter->pPrintProcessor, pDefault->pDatatype))
1297 {
1298 dwErrorCode = ERROR_INVALID_DATATYPE;
1299 goto Cleanup;
1300 }
1301
1302 pPrinterHandle->pwszDatatype = AllocSplStr(pDefault->pDatatype);
1303 }
1304 else
1305 {
1306 // Use the default datatype.
1307 pPrinterHandle->pwszDatatype = AllocSplStr(pPrinter->pwszDefaultDatatype);
1308 }
1309
1310 // Check if a DevMode was given, otherwise use the default.
1311 if (pDefault && pDefault->pDevMode)
1312 pPrinterHandle->pDevMode = DuplicateDevMode(pDefault->pDevMode);
1313 else
1314 pPrinterHandle->pDevMode = DuplicateDevMode(pPrinter->pDefaultDevMode);
1315
1316 // Check if the caller wants a handle to an existing Print Job.
1317 if (pwszSecondParameter)
1318 {
1319 // The "Job " string has to follow now.
1320 if (wcsncmp(pwszSecondParameter, L"Job ", 4) != 0)
1321 {
1322 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
1323 goto Cleanup;
1324 }
1325
1326 // Skip the "Job " string.
1327 pwszSecondParameter += 4;
1328
1329 // Skip even more whitespace.
1330 while (*pwszSecondParameter == ' ')
1331 ++pwszSecondParameter;
1332
1333 // Finally extract the desired Job ID.
1334 dwJobID = wcstoul(pwszSecondParameter, NULL, 10);
1335 if (!IS_VALID_JOB_ID(dwJobID))
1336 {
1337 // The user supplied an invalid Job ID.
1338 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
1339 goto Cleanup;
1340 }
1341
1342 // Look for this job in the Global Job List.
1343 pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL);
1344 if (!pJob || pJob->pPrinter != pPrinter)
1345 {
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;
1348 goto Cleanup;
1349 }
1350
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)
1355 {
1356 dwErrorCode = GetLastError();
1357 ERR("CreateFileW failed with error %lu for \"%S\"!", dwErrorCode, wszFullPath);
1358 goto Cleanup;
1359 }
1360
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;
1364 }
1365
1366 // Return the Printer handle through our general handle.
1367 pHandle->HandleType = HandleType_Printer;
1368 pHandle->pSpecificHandle = pPrinterHandle;
1369 }
1370
1371 // We were successful! Return the handle.
1372 *phPrinter = (HANDLE)pHandle;
1373 dwErrorCode = ERROR_SUCCESS;
1374
1375 // Don't let the cleanup routines free this.
1376 pHandle = NULL;
1377 pPrinterHandle = NULL;
1378
1379 Cleanup:
1380 if (pHandle)
1381 DllFreeSplMem(pHandle);
1382
1383 if (pPrinterHandle)
1384 {
1385 if (pPrinterHandle->pwszDatatype)
1386 DllFreeSplStr(pPrinterHandle->pwszDatatype);
1387
1388 if (pPrinterHandle->pDevMode)
1389 DllFreeSplMem(pPrinterHandle->pDevMode);
1390
1391 DllFreeSplMem(pPrinterHandle);
1392 }
1393
1394 if (pwszFirstParameter)
1395 DllFreeSplMem(pwszFirstParameter);
1396
1397 SetLastError(dwErrorCode);
1398 return (dwErrorCode == ERROR_SUCCESS);
1399 }
1400
1401 BOOL WINAPI
1402 LocalReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
1403 {
1404 BOOL bReturnValue;
1405 DWORD dwErrorCode;
1406 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1407 PLOCAL_PORT_HANDLE pPortHandle;
1408 PLOCAL_PRINTER_HANDLE pPrinterHandle;
1409
1410 // Sanity checks.
1411 if (!pHandle)
1412 {
1413 dwErrorCode = ERROR_INVALID_HANDLE;
1414 goto Cleanup;
1415 }
1416
1417 // Port handles are an entirely different thing.
1418 if (pHandle->HandleType == HandleType_Port)
1419 {
1420 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1421
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);
1425 else
1426 bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnReadPort(pPortHandle->hPort, pBuf, cbBuf, pNoBytesRead);
1427
1428 if (!bReturnValue)
1429 {
1430 // The ReadPort function failed. Return its last error.
1431 dwErrorCode = GetLastError();
1432 goto Cleanup;
1433 }
1434
1435 // We were successful!
1436 dwErrorCode = ERROR_SUCCESS;
1437 goto Cleanup;
1438 }
1439
1440 // The remaining function deals with Printer handles only.
1441 if (pHandle->HandleType != HandleType_Printer)
1442 {
1443 dwErrorCode = ERROR_INVALID_HANDLE;
1444 goto Cleanup;
1445 }
1446
1447 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1448
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)
1452 {
1453 dwErrorCode = ERROR_INVALID_HANDLE;
1454 goto Cleanup;
1455 }
1456
1457 // Pass the parameters to ReadFile.
1458 if (!ReadFile(pPrinterHandle->hSPLFile, pBuf, cbBuf, pNoBytesRead, NULL))
1459 {
1460 dwErrorCode = GetLastError();
1461 ERR("ReadFile failed with error %lu!\n", dwErrorCode);
1462 goto Cleanup;
1463 }
1464
1465 dwErrorCode = ERROR_SUCCESS;
1466
1467 Cleanup:
1468 SetLastError(dwErrorCode);
1469 return (dwErrorCode == ERROR_SUCCESS);
1470 }
1471
1472 DWORD WINAPI
1473 LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
1474 {
1475 BOOL bReturnValue;
1476 DWORD dwErrorCode;
1477 DWORD dwReturnValue = 0;
1478 PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
1479 PLOCAL_JOB pJob;
1480 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1481 PLOCAL_PORT_HANDLE pPortHandle;
1482 PLOCAL_PRINTER_HANDLE pPrinterHandle;
1483
1484 // Sanity checks.
1485 if (!pHandle)
1486 {
1487 dwErrorCode = ERROR_INVALID_HANDLE;
1488 goto Cleanup;
1489 }
1490
1491 // Port handles are an entirely different thing.
1492 if (pHandle->HandleType == HandleType_Port)
1493 {
1494 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1495
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;
1500 ASSERT(pJob);
1501
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);
1505 else
1506 bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnStartDocPort(pPortHandle->hPort, pJob->pPrinter->pwszPrinterName, pJob->dwJobID, Level, pDocInfo);
1507
1508 if (!bReturnValue)
1509 {
1510 // The StartDocPort function failed. Return its last error.
1511 dwErrorCode = GetLastError();
1512 goto Cleanup;
1513 }
1514
1515 // We were successful!
1516 dwErrorCode = ERROR_SUCCESS;
1517 dwReturnValue = pJob->dwJobID;
1518 goto Cleanup;
1519 }
1520
1521 // The remaining function deals with Printer handles only.
1522 if (pHandle->HandleType != HandleType_Printer)
1523 {
1524 dwErrorCode = ERROR_INVALID_HANDLE;
1525 goto Cleanup;
1526 }
1527
1528 if (!pDocInfo1)
1529 {
1530 dwErrorCode = ERROR_INVALID_PARAMETER;
1531 goto Cleanup;
1532 }
1533
1534 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1535
1536 // pJob may already be occupied if this is a Print Job handle. In this case, StartDocPrinter has to fail.
1537 if (pPrinterHandle->pJob)
1538 {
1539 dwErrorCode = ERROR_INVALID_PARAMETER;
1540 goto Cleanup;
1541 }
1542
1543 // Check the validity of the datatype if we got one.
1544 if (pDocInfo1->pDatatype && !FindDatatype(pPrinterHandle->pJob->pPrintProcessor, pDocInfo1->pDatatype))
1545 {
1546 dwErrorCode = ERROR_INVALID_DATATYPE;
1547 goto Cleanup;
1548 }
1549
1550 // Check if this is the right document information level.
1551 if (Level != 1)
1552 {
1553 dwErrorCode = ERROR_INVALID_LEVEL;
1554 goto Cleanup;
1555 }
1556
1557 // All requirements are met - create a new job.
1558 dwErrorCode = CreateJob(pPrinterHandle);
1559 if (dwErrorCode != ERROR_SUCCESS)
1560 goto Cleanup;
1561
1562 // Use any given datatype.
1563 if (pDocInfo1->pDatatype && !ReallocSplStr(&pPrinterHandle->pJob->pwszDatatype, pDocInfo1->pDatatype))
1564 {
1565 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1566 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1567 goto Cleanup;
1568 }
1569
1570 // Use any given document name.
1571 if (pDocInfo1->pDocName && !ReallocSplStr(&pPrinterHandle->pJob->pwszDocumentName, pDocInfo1->pDocName))
1572 {
1573 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1574 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1575 goto Cleanup;
1576 }
1577
1578 // We were successful!
1579 dwErrorCode = ERROR_SUCCESS;
1580 dwReturnValue = pPrinterHandle->pJob->dwJobID;
1581
1582 Cleanup:
1583 SetLastError(dwErrorCode);
1584 return dwReturnValue;
1585 }
1586
1587 BOOL WINAPI
1588 LocalStartPagePrinter(HANDLE hPrinter)
1589 {
1590 DWORD dwErrorCode;
1591 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1592 PLOCAL_PRINTER_HANDLE pPrinterHandle;
1593
1594 // Sanity checks.
1595 if (!pHandle || pHandle->HandleType != HandleType_Printer)
1596 {
1597 dwErrorCode = ERROR_INVALID_HANDLE;
1598 goto Cleanup;
1599 }
1600
1601 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1602
1603 // We require StartDocPrinter or AddJob to be called first.
1604 if (!pPrinterHandle->bStartedDoc)
1605 {
1606 dwErrorCode = ERROR_SPL_NO_STARTDOC;
1607 goto Cleanup;
1608 }
1609
1610 // Increase the page count.
1611 ++pPrinterHandle->pJob->dwTotalPages;
1612 dwErrorCode = ERROR_SUCCESS;
1613
1614 Cleanup:
1615 SetLastError(dwErrorCode);
1616 return (dwErrorCode == ERROR_SUCCESS);
1617 }
1618
1619 BOOL WINAPI
1620 LocalWritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
1621 {
1622 BOOL bReturnValue;
1623 DWORD dwErrorCode;
1624 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1625 PLOCAL_PORT_HANDLE pPortHandle;
1626 PLOCAL_PRINTER_HANDLE pPrinterHandle;
1627
1628 // Sanity checks.
1629 if (!pHandle)
1630 {
1631 dwErrorCode = ERROR_INVALID_HANDLE;
1632 goto Cleanup;
1633 }
1634
1635 // Port handles are an entirely different thing.
1636 if (pHandle->HandleType == HandleType_Port)
1637 {
1638 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1639
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);
1643 else
1644 bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnWritePort(pPortHandle->hPort, pBuf, cbBuf, pcWritten);
1645
1646 if (!bReturnValue)
1647 {
1648 // The WritePort function failed. Return its last error.
1649 dwErrorCode = GetLastError();
1650 goto Cleanup;
1651 }
1652
1653 // We were successful!
1654 dwErrorCode = ERROR_SUCCESS;
1655 goto Cleanup;
1656 }
1657
1658 // The remaining function deals with Printer handles only.
1659 if (pHandle->HandleType != HandleType_Printer)
1660 {
1661 dwErrorCode = ERROR_INVALID_HANDLE;
1662 goto Cleanup;
1663 }
1664
1665 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1666
1667 // We require StartDocPrinter or AddJob to be called first.
1668 if (!pPrinterHandle->bStartedDoc)
1669 {
1670 dwErrorCode = ERROR_SPL_NO_STARTDOC;
1671 goto Cleanup;
1672 }
1673
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).
1676 #if 0
1677 // Pass the parameters to WriteFile.
1678 if (!WriteFile(SOME_SPOOL_FILE_HANDLE, pBuf, cbBuf, pcWritten, NULL))
1679 {
1680 dwErrorCode = GetLastError();
1681 ERR("WriteFile failed with error %lu!\n", GetLastError());
1682 goto Cleanup;
1683 }
1684 #endif
1685
1686 dwErrorCode = ERROR_SUCCESS;
1687
1688 Cleanup:
1689 SetLastError(dwErrorCode);
1690 return (dwErrorCode == ERROR_SUCCESS);
1691 }
1692
1693 BOOL WINAPI
1694 LocalEndPagePrinter(HANDLE hPrinter)
1695 {
1696 DWORD dwErrorCode;
1697 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1698
1699 // Sanity checks.
1700 if (!pHandle || pHandle->HandleType != HandleType_Printer)
1701 {
1702 dwErrorCode = ERROR_INVALID_HANDLE;
1703 goto Cleanup;
1704 }
1705
1706 // This function doesn't do anything else for now.
1707 dwErrorCode = ERROR_SUCCESS;
1708
1709 Cleanup:
1710 SetLastError(dwErrorCode);
1711 return (dwErrorCode == ERROR_SUCCESS);
1712 }
1713
1714 BOOL WINAPI
1715 LocalEndDocPrinter(HANDLE hPrinter)
1716 {
1717 BOOL bReturnValue;
1718 DWORD dwErrorCode;
1719 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1720 PLOCAL_PORT_HANDLE pPortHandle;
1721 PLOCAL_PRINTER_HANDLE pPrinterHandle;
1722
1723 // Sanity checks.
1724 if (!pHandle)
1725 {
1726 dwErrorCode = ERROR_INVALID_HANDLE;
1727 goto Cleanup;
1728 }
1729
1730 // Port handles are an entirely different thing.
1731 if (pHandle->HandleType == HandleType_Port)
1732 {
1733 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1734
1735 // Call the monitor's EndDocPort function.
1736 if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1737 bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnEndDocPort(pPortHandle->hPort);
1738 else
1739 bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnEndDocPort(pPortHandle->hPort);
1740
1741 if (!bReturnValue)
1742 {
1743 // The EndDocPort function failed. Return its last error.
1744 dwErrorCode = GetLastError();
1745 goto Cleanup;
1746 }
1747
1748 // We were successful!
1749 dwErrorCode = ERROR_SUCCESS;
1750 goto Cleanup;
1751 }
1752
1753 // The remaining function deals with Printer handles only.
1754 if (pHandle->HandleType != HandleType_Printer)
1755 {
1756 dwErrorCode = ERROR_INVALID_HANDLE;
1757 goto Cleanup;
1758 }
1759
1760 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1761
1762 // We require StartDocPrinter or AddJob to be called first.
1763 if (!pPrinterHandle->bStartedDoc)
1764 {
1765 dwErrorCode = ERROR_SPL_NO_STARTDOC;
1766 goto Cleanup;
1767 }
1768
1769 // TODO: Something like ScheduleJob
1770
1771 // Finish the job.
1772 pPrinterHandle->bStartedDoc = FALSE;
1773 pPrinterHandle->pJob = NULL;
1774 dwErrorCode = ERROR_SUCCESS;
1775
1776 Cleanup:
1777 SetLastError(dwErrorCode);
1778 return (dwErrorCode == ERROR_SUCCESS);
1779 }
1780
1781 BOOL WINAPI
1782 LocalClosePrinter(HANDLE hPrinter)
1783 {
1784 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1785 PLOCAL_PORT_HANDLE pPortHandle;
1786 PLOCAL_PRINTER_HANDLE pPrinterHandle;
1787 PLOCAL_XCV_HANDLE pXcvHandle;
1788
1789 if (!pHandle)
1790 {
1791 SetLastError(ERROR_INVALID_HANDLE);
1792 return FALSE;
1793 }
1794
1795 if (pHandle->HandleType == HandleType_Port)
1796 {
1797 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1798
1799 // Call the monitor's ClosePort function.
1800 if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1801 ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnClosePort(pPortHandle->hPort);
1802 else
1803 ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnClosePort(pPortHandle->hPort);
1804 }
1805 else if (pHandle->HandleType == HandleType_Printer)
1806 {
1807 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1808
1809 // Terminate any started job.
1810 if (pPrinterHandle->pJob)
1811 FreeJob(pPrinterHandle->pJob);
1812
1813 // Free memory for the fields.
1814 DllFreeSplMem(pPrinterHandle->pDevMode);
1815 DllFreeSplStr(pPrinterHandle->pwszDatatype);
1816 }
1817 else if (pHandle->HandleType == HandleType_Xcv)
1818 {
1819 pXcvHandle = (PLOCAL_XCV_HANDLE)pHandle->pSpecificHandle;
1820
1821 // Call the monitor's XcvClosePort function.
1822 if (pXcvHandle->pPrintMonitor->bIsLevel2)
1823 ((PMONITOR2)pXcvHandle->pPrintMonitor->pMonitor)->pfnXcvClosePort(pXcvHandle->hXcv);
1824 else
1825 ((LPMONITOREX)pXcvHandle->pPrintMonitor->pMonitor)->Monitor.pfnXcvClosePort(pXcvHandle->hXcv);
1826 }
1827
1828 // Free memory for the handle and the specific handle.
1829 DllFreeSplMem(pHandle->pSpecificHandle);
1830 DllFreeSplMem(pHandle);
1831
1832 return TRUE;
1833 }