[LOCALSPL]
[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 // Local Constants
14 static DWORD dwPrinterInfo1Offsets[] = {
15 FIELD_OFFSET(PRINTER_INFO_1W, pName),
16 FIELD_OFFSET(PRINTER_INFO_1W, pComment),
17 FIELD_OFFSET(PRINTER_INFO_1W, pDescription),
18 MAXDWORD
19 };
20
21 /**
22 * @name _PrinterListCompareRoutine
23 *
24 * SKIPLIST_COMPARE_ROUTINE for the Printer List.
25 * Does a case-insensitive comparison, because e.g. LocalOpenPrinter doesn't match the case when looking for Printers.
26 */
27 static int WINAPI
28 _PrinterListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
29 {
30 PLOCAL_PRINTER A = (PLOCAL_PRINTER)FirstStruct;
31 PLOCAL_PRINTER B = (PLOCAL_PRINTER)SecondStruct;
32
33 return wcsicmp(A->pwszPrinterName, B->pwszPrinterName);
34 }
35
36 /**
37 * @name InitializePrinterList
38 *
39 * Initializes a list of locally available Printers.
40 * The list is searchable by name and returns information about the printers, including their job queues.
41 * During this process, the job queues are also initialized.
42 */
43 BOOL
44 InitializePrinterList()
45 {
46 const WCHAR wszPrintersKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Printers";
47
48 DWORD cbData;
49 DWORD cchPrinterName;
50 DWORD dwErrorCode;
51 DWORD dwSubKeys;
52 DWORD i;
53 HKEY hKey = NULL;
54 HKEY hSubKey = NULL;
55 PLOCAL_PORT pPort;
56 PLOCAL_PRINTER pPrinter = NULL;
57 PLOCAL_PRINT_PROCESSOR pPrintProcessor;
58 PWSTR pwszPort = NULL;
59 PWSTR pwszPrintProcessor = NULL;
60 WCHAR wszPrinterName[MAX_PRINTER_NAME + 1];
61
62 // Initialize an empty list for our printers.
63 InitializeSkiplist(&PrinterList, DllAllocSplMem, _PrinterListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
64
65 // Open our printers registry key. Each subkey is a local printer there.
66 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszPrintersKey, 0, KEY_READ, &hKey);
67 if (dwErrorCode != ERROR_SUCCESS)
68 {
69 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
70 goto Cleanup;
71 }
72
73 // Get the number of subkeys.
74 dwErrorCode = (DWORD)RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
75 if (dwErrorCode != ERROR_SUCCESS)
76 {
77 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
78 goto Cleanup;
79 }
80
81 // Loop through all available local printers.
82 for (i = 0; i < dwSubKeys; i++)
83 {
84 // Cleanup tasks from the previous run
85 if (hSubKey)
86 {
87 RegCloseKey(hSubKey);
88 hSubKey = NULL;
89 }
90
91 if (pPrinter)
92 {
93 if (pPrinter->pDefaultDevMode)
94 DllFreeSplMem(pPrinter->pDefaultDevMode);
95
96 if (pPrinter->pwszDefaultDatatype)
97 DllFreeSplStr(pPrinter->pwszDefaultDatatype);
98
99 if (pPrinter->pwszDescription)
100 DllFreeSplStr(pPrinter->pwszDescription);
101
102 if (pPrinter->pwszPrinterDriver)
103 DllFreeSplStr(pPrinter->pwszPrinterDriver);
104
105 if (pPrinter->pwszPrinterName)
106 DllFreeSplStr(pPrinter->pwszPrinterName);
107
108 DllFreeSplMem(pPrinter);
109 pPrinter = NULL;
110 }
111
112 if (pwszPrintProcessor)
113 {
114 DllFreeSplStr(pwszPrintProcessor);
115 pwszPrintProcessor = NULL;
116 }
117
118 // Get the name of this printer.
119 cchPrinterName = _countof(wszPrinterName);
120 dwErrorCode = (DWORD)RegEnumKeyExW(hKey, i, wszPrinterName, &cchPrinterName, NULL, NULL, NULL, NULL);
121 if (dwErrorCode == ERROR_MORE_DATA)
122 {
123 // This printer name exceeds the maximum length and is invalid.
124 continue;
125 }
126 else if (dwErrorCode != ERROR_SUCCESS)
127 {
128 ERR("RegEnumKeyExW failed for iteration %lu with status %lu!\n", i, dwErrorCode);
129 continue;
130 }
131
132 // Open this Printer's registry key.
133 dwErrorCode = (DWORD)RegOpenKeyExW(hKey, wszPrinterName, 0, KEY_READ, &hSubKey);
134 if (dwErrorCode != ERROR_SUCCESS)
135 {
136 ERR("RegOpenKeyExW failed for Printer \"%S\" with status %lu!\n", wszPrinterName, dwErrorCode);
137 continue;
138 }
139
140 // Get the Print Processor.
141 pwszPrintProcessor = AllocAndRegQueryWSZ(hSubKey, L"Print Processor");
142 if (!pwszPrintProcessor)
143 continue;
144
145 // Try to find it in the Print Processor List.
146 pPrintProcessor = FindPrintProcessor(pwszPrintProcessor);
147 if (!pPrintProcessor)
148 {
149 ERR("Invalid Print Processor \"%S\" for Printer \"%S\"!\n", pwszPrintProcessor, wszPrinterName);
150 continue;
151 }
152
153 // Get the Port.
154 pwszPort = AllocAndRegQueryWSZ(hSubKey, L"Port");
155 if (!pwszPort)
156 continue;
157
158 // Try to find it in the Port List.
159 pPort = FindPort(pwszPort);
160 if (!pPort)
161 {
162 ERR("Invalid Port \"%S\" for Printer \"%S\"!\n", pwszPort, wszPrinterName);
163 continue;
164 }
165
166 // Create a new LOCAL_PRINTER structure for it.
167 pPrinter = DllAllocSplMem(sizeof(LOCAL_PRINTER));
168 if (!pPrinter)
169 {
170 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
171 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
172 goto Cleanup;
173 }
174
175 pPrinter->pwszPrinterName = AllocSplStr(wszPrinterName);
176 pPrinter->pPrintProcessor = pPrintProcessor;
177 pPrinter->pPort = pPort;
178 InitializePrinterJobList(pPrinter);
179
180 // Get the location.
181 pPrinter->pwszLocation = AllocAndRegQueryWSZ(hSubKey, L"Location");
182 if (!pPrinter->pwszLocation)
183 continue;
184
185 // Get the printer driver.
186 pPrinter->pwszPrinterDriver = AllocAndRegQueryWSZ(hSubKey, L"Printer Driver");
187 if (!pPrinter->pwszPrinterDriver)
188 continue;
189
190 // Get the description.
191 pPrinter->pwszDescription = AllocAndRegQueryWSZ(hSubKey, L"Description");
192 if (!pPrinter->pwszDescription)
193 continue;
194
195 // Get the default datatype.
196 pPrinter->pwszDefaultDatatype = AllocAndRegQueryWSZ(hSubKey, L"Datatype");
197 if (!pPrinter->pwszDefaultDatatype)
198 continue;
199
200 // Verify that it's valid.
201 if (!FindDatatype(pPrintProcessor, pPrinter->pwszDefaultDatatype))
202 {
203 ERR("Invalid default datatype \"%S\" for Printer \"%S\"!\n", pPrinter->pwszDefaultDatatype, wszPrinterName);
204 continue;
205 }
206
207 // Determine the size of the DevMode.
208 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, NULL, &cbData);
209 if (dwErrorCode != ERROR_SUCCESS)
210 {
211 ERR("Couldn't query the size of the DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName, dwErrorCode, cbData);
212 continue;
213 }
214
215 // Allocate enough memory for the DevMode.
216 pPrinter->pDefaultDevMode = DllAllocSplMem(cbData);
217 if (!pPrinter->pDefaultDevMode)
218 {
219 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
220 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
221 goto Cleanup;
222 }
223
224 // Get the default DevMode.
225 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, (PBYTE)pPrinter->pDefaultDevMode, &cbData);
226 if (dwErrorCode != ERROR_SUCCESS)
227 {
228 ERR("Couldn't query a DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName, dwErrorCode, cbData);
229 continue;
230 }
231
232 // Get the Attributes.
233 cbData = sizeof(DWORD);
234 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Attributes", NULL, NULL, (PBYTE)&pPrinter->dwAttributes, &cbData);
235 if (dwErrorCode != ERROR_SUCCESS)
236 {
237 ERR("Couldn't query Attributes for Printer \"%S\", status is %lu!\n", wszPrinterName, dwErrorCode);
238 continue;
239 }
240
241 // Get the Status.
242 cbData = sizeof(DWORD);
243 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Status", NULL, NULL, (PBYTE)&pPrinter->dwStatus, &cbData);
244 if (dwErrorCode != ERROR_SUCCESS)
245 {
246 ERR("Couldn't query Status for Printer \"%S\", status is %lu!\n", wszPrinterName, dwErrorCode);
247 continue;
248 }
249
250 // Add this printer to the printer list.
251 if (!InsertElementSkiplist(&PrinterList, pPrinter))
252 {
253 ERR("InsertElementSkiplist failed for Printer \"%S\"!\n", pPrinter->pwszPrinterName);
254 goto Cleanup;
255 }
256
257 // Don't let the cleanup routines free this.
258 pPrinter = NULL;
259 }
260
261 dwErrorCode = ERROR_SUCCESS;
262
263 Cleanup:
264 // Inside the loop
265 if (hSubKey)
266 RegCloseKey(hSubKey);
267
268 if (pPrinter)
269 {
270 if (pPrinter->pDefaultDevMode)
271 DllFreeSplMem(pPrinter->pDefaultDevMode);
272
273 if (pPrinter->pwszDefaultDatatype)
274 DllFreeSplStr(pPrinter->pwszDefaultDatatype);
275
276 if (pPrinter->pwszDescription)
277 DllFreeSplStr(pPrinter->pwszDescription);
278
279 if (pPrinter->pwszPrinterDriver)
280 DllFreeSplStr(pPrinter->pwszPrinterDriver);
281
282 if (pPrinter->pwszPrinterName)
283 DllFreeSplStr(pPrinter->pwszPrinterName);
284
285 DllFreeSplMem(pPrinter);
286 }
287
288 if (pwszPrintProcessor)
289 DllFreeSplStr(pwszPrintProcessor);
290
291 // Outside the loop
292 if (hKey)
293 RegCloseKey(hKey);
294
295 SetLastError(dwErrorCode);
296 return (dwErrorCode == ERROR_SUCCESS);
297 }
298
299 /**
300 * @name _IsLocalComputerName
301 *
302 * Checks if the given Computer Name matches the local Computer Name.
303 *
304 * @param Name
305 * Computer Name prepended with two backslashes to check.
306 *
307 * @param pwszComputerName
308 * Pointer to a string able to hold 2 + MAX_COMPUTERNAME_LENGTH + 1 + 1 characters.
309 * Will contain a string "\\COMPUTERNAME\" on success that can be prepended in EnumPrinters.
310 *
311 * @param pcchComputerName
312 * On success, this pointer receives the length in characters of pwszComputerName.
313 *
314 * @return
315 * ERROR_SUCCESS on success or an error code on failure.
316 */
317 static DWORD
318 _IsLocalComputerName(PCWSTR Name, PWSTR pwszComputerName, PDWORD pcchComputerName)
319 {
320 DWORD dwErrorCode;
321
322 // Prepend slashes to the computer name.
323 pwszComputerName[0] = L'\\';
324 pwszComputerName[1] = L'\\';
325
326 // Get the local computer name for comparison.
327 *pcchComputerName = MAX_COMPUTERNAME_LENGTH + 1;
328 if (!GetComputerNameW(&pwszComputerName[2], pcchComputerName))
329 {
330 dwErrorCode = GetLastError();
331 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
332 goto Cleanup;
333 }
334
335 // Add the leading slashes to the total length.
336 *pcchComputerName += 2;
337
338 // Now compare this with the local computer name and reject it with ERROR_INVALID_NAME if it doesn't match.
339 if (wcsicmp(&Name[2], &pwszComputerName[2]) != 0)
340 {
341 dwErrorCode = ERROR_INVALID_NAME;
342 goto Cleanup;
343 }
344
345 // Add a trailing backslash to pwszComputerName, which will later be prepended in front of the printer names.
346 pwszComputerName[(*pcchComputerName)++] = L'\\';
347 pwszComputerName[*pcchComputerName] = 0;
348
349 dwErrorCode = ERROR_SUCCESS;
350
351 Cleanup:
352 return dwErrorCode;
353 }
354
355 static DWORD
356 _DumpLevel1PrintProviderInformation(PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
357 {
358 int i;
359
360 // Count the needed bytes for Print Provider information.
361 *pcbNeeded = sizeof(PRINTER_INFO_1W);
362
363 for (i = 0; i < 3; i++)
364 *pcbNeeded += (wcslen(wszPrintProviderInfo[i]) + 1) * sizeof(WCHAR);
365
366 // Check if the supplied buffer is large enough.
367 if (cbBuf < *pcbNeeded)
368 return ERROR_INSUFFICIENT_BUFFER;
369
370 // Copy over the Print Provider information.
371 ((PPRINTER_INFO_1W)pPrinterEnum)->Flags = 0;
372 PackStrings(wszPrintProviderInfo, pPrinterEnum, dwPrinterInfo1Offsets, &pPrinterEnum[*pcbNeeded]);
373 *pcReturned = 1;
374
375 return ERROR_SUCCESS;
376 }
377
378 static DWORD
379 _LocalEnumPrintersLevel0(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
380 {
381 return ERROR_INVALID_LEVEL;
382 }
383
384 static DWORD
385 _LocalEnumPrintersLevel1(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
386 {
387 const WCHAR wszComma[] = L",";
388
389 DWORD cbName;
390 DWORD cbComment;
391 DWORD cbDescription;
392 DWORD cchComputerName = 0;
393 DWORD dwErrorCode;
394 DWORD i;
395 PBYTE pPrinterInfo;
396 PBYTE pPrinterStrings;
397 PSKIPLIST_NODE pNode;
398 PLOCAL_PRINTER pPrinter;
399 PWSTR p;
400 PWSTR pwszStrings[3];
401 WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1] = { 0 };
402
403 if (Flags & PRINTER_ENUM_NAME)
404 {
405 if (Name)
406 {
407 // The user supplied a Computer Name (with leading double backslashes) or Print Provider Name.
408 // Only process what's directed at us.
409 if (Name[0] == L'\\' && Name[1] == L'\\')
410 {
411 dwErrorCode = _IsLocalComputerName(Name, wszComputerName, &cchComputerName);
412 if (dwErrorCode != ERROR_SUCCESS)
413 goto Cleanup;
414 }
415 else if (wcsicmp(Name, wszPrintProviderInfo[0]) != 0)
416 {
417 // The user supplied a name that cannot be processed by the Local Print Provider.
418 dwErrorCode = ERROR_INVALID_NAME;
419 goto Cleanup;
420 }
421 }
422 else
423 {
424 // The caller wants information about this Print Provider.
425 // spoolss packs this into an array of information about all Print Providers.
426 dwErrorCode = _DumpLevel1PrintProviderInformation(pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
427 goto Cleanup;
428 }
429 }
430
431 // Count the required buffer size and the number of printers.
432 i = 0;
433
434 for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
435 {
436 pPrinter = (PLOCAL_PRINTER)pNode->Element;
437
438 // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
439 if (Flags & PRINTER_ENUM_SHARED)
440 continue;
441
442 // Attention: pComment equals the "Description" registry value while pDescription is concatenated out of several strings.
443 // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
444 cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
445 cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
446 cbDescription = cbName + (wcslen(pPrinter->pwszPrinterDriver) + 1 + wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR);
447
448 *pcbNeeded += sizeof(PRINTER_INFO_1W) + cbName + cbComment + cbDescription;
449 i++;
450 }
451
452 // Check if the supplied buffer is large enough.
453 if (cbBuf < *pcbNeeded)
454 {
455 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
456 goto Cleanup;
457 }
458
459 // Initialize the variables for filling the output buffer using PackStrings.
460 pPrinterInfo = pPrinterEnum;
461 pPrinterStrings = &pPrinterEnum[*pcbNeeded];
462
463 // Copy over the Printer information.
464 for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
465 {
466 pPrinter = (PLOCAL_PRINTER)pNode->Element;
467
468 // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
469 if (Flags & PRINTER_ENUM_SHARED)
470 continue;
471
472 // Indicate that this is a Printer.
473 ((PPRINTER_INFO_1W)pPrinterInfo)->Flags = PRINTER_ENUM_ICON8;
474
475 // Calculate the string lengths.
476 cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
477 cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
478 cbDescription = cbName + (wcslen(pPrinter->pwszPrinterDriver) + 1 + wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR);
479
480 // Copy the Printer Name.
481 pwszStrings[0] = DllAllocSplMem(cbName);
482 p = pwszStrings[0];
483 StringCbCopyExW(p, cbName, wszComputerName, &p, &cbName, 0);
484 StringCbCopyExW(p, cbName, pPrinter->pwszPrinterName, &p, &cbName, 0);
485
486 // Copy the Printer comment (equals the "Description" registry value).
487 pwszStrings[1] = pPrinter->pwszDescription;
488
489 // Copy the description, which for PRINTER_INFO_1W has the form "Name,Printer Driver,Location"
490 pwszStrings[2] = DllAllocSplMem(cbDescription);
491 p = pwszStrings[2];
492 StringCbCopyExW(p, cbDescription, wszComputerName, &p, &cbDescription, 0);
493 StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterName, &p, &cbDescription, 0);
494 StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0);
495 StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterDriver, &p, &cbDescription, 0);
496 StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0);
497 StringCbCopyExW(p, cbDescription, pPrinter->pwszLocation, &p, &cbDescription, 0);
498
499 // Finally copy the structure and advance to the next one in the output buffer.
500 pPrinterStrings = PackStrings(pwszStrings, pPrinterInfo, dwPrinterInfo1Offsets, pPrinterStrings);
501 pPrinterInfo += sizeof(PRINTER_INFO_1W);
502
503 // Free the memory for temporary strings.
504 DllFreeSplMem(pwszStrings[0]);
505 DllFreeSplMem(pwszStrings[2]);
506 }
507
508 *pcReturned = i;
509 dwErrorCode = ERROR_SUCCESS;
510
511 Cleanup:
512 return dwErrorCode;
513 }
514
515 static DWORD
516 _LocalEnumPrintersLevel2(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
517 {
518 return ERROR_INVALID_LEVEL;
519 }
520
521 static DWORD
522 _LocalEnumPrintersLevel4(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
523 {
524 return ERROR_INVALID_LEVEL;
525 }
526
527 static DWORD
528 _LocalEnumPrintersLevel5(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
529 {
530 return ERROR_INVALID_LEVEL;
531 }
532
533 BOOL WINAPI
534 LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
535 {
536 DWORD dwErrorCode;
537
538 // Do no sanity checks here. This is verified by localspl_apitest!
539
540 // Begin counting.
541 *pcbNeeded = 0;
542 *pcReturned = 0;
543
544 if (Flags & PRINTER_ENUM_CONNECTIONS || Flags & PRINTER_ENUM_REMOTE || Flags & PRINTER_ENUM_NETWORK)
545 {
546 // If the flags for the Network Print Provider are given, bail out with ERROR_INVALID_NAME.
547 // This is the internal way for a Print Provider to signal that it doesn't handle this request.
548 dwErrorCode = ERROR_INVALID_NAME;
549 goto Cleanup;
550 }
551
552 if (!(Flags & PRINTER_ENUM_LOCAL || Flags & PRINTER_ENUM_NAME))
553 {
554 // The Local Print Provider is the right destination for the request, but without any of these flags,
555 // there is no information that can be returned.
556 // So just signal a successful request.
557 dwErrorCode = ERROR_SUCCESS;
558 goto Cleanup;
559 }
560
561 if (Level == 0)
562 {
563 dwErrorCode = _LocalEnumPrintersLevel0(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
564 }
565 else if (Level == 1)
566 {
567 dwErrorCode = _LocalEnumPrintersLevel1(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
568 }
569 else if (Level == 2)
570 {
571 dwErrorCode = _LocalEnumPrintersLevel2(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
572 }
573 else if (Level == 4)
574 {
575 dwErrorCode = _LocalEnumPrintersLevel4(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
576 }
577 else if (Level == 5)
578 {
579 dwErrorCode = _LocalEnumPrintersLevel5(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
580 }
581 else
582 {
583 // The caller supplied an invalid level.
584 dwErrorCode = ERROR_INVALID_LEVEL;
585 }
586
587 Cleanup:
588 SetLastError(dwErrorCode);
589 return (dwErrorCode == ERROR_SUCCESS);
590 }
591
592 BOOL WINAPI
593 LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault)
594 {
595 BOOL bReturnValue;
596 DWORD cchComputerName;
597 DWORD cchFirstParameter;
598 DWORD dwErrorCode;
599 DWORD dwJobID;
600 HANDLE hExternalHandle;
601 PWSTR p = lpPrinterName;
602 PWSTR pwszFirstParameter = NULL;
603 PWSTR pwszSecondParameter = NULL;
604 PLOCAL_JOB pJob;
605 PLOCAL_HANDLE pHandle = NULL;
606 PLOCAL_PORT pPort;
607 PLOCAL_PORT_HANDLE pPortHandle = NULL;
608 PLOCAL_PRINT_MONITOR pPrintMonitor;
609 PLOCAL_PRINTER pPrinter;
610 PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL;
611 PLOCAL_XCV_HANDLE pXcvHandle = NULL;
612 WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
613 WCHAR wszFullPath[MAX_PATH];
614
615 // TODO: lpPrinterName == NULL is supported and means access to the local printer server.
616 // Not sure yet if that is passed down to localspl.dll or processed in advance.
617
618 // Sanity checks
619 if (!lpPrinterName || !phPrinter)
620 {
621 dwErrorCode = ERROR_INVALID_PARAMETER;
622 goto Cleanup;
623 }
624
625 *phPrinter = NULL;
626
627 // Skip any server name in the first parameter.
628 // Does lpPrinterName begin with two backslashes to indicate a server name?
629 if (lpPrinterName[0] == L'\\' && lpPrinterName[1] == L'\\')
630 {
631 // Skip these two backslashes.
632 lpPrinterName += 2;
633
634 // Look for the closing backslash.
635 p = wcschr(lpPrinterName, L'\\');
636 if (!p)
637 {
638 // We didn't get a proper server name.
639 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
640 goto Cleanup;
641 }
642
643 // Get the local computer name for comparison.
644 cchComputerName = _countof(wszComputerName);
645 if (!GetComputerNameW(wszComputerName, &cchComputerName))
646 {
647 dwErrorCode = GetLastError();
648 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
649 goto Cleanup;
650 }
651
652 // Now compare this string excerpt with the local computer name.
653 // The input parameter may not be writable, so we can't null-terminate the input string at this point.
654 // This print provider only supports local printers, so both strings have to match.
655 if (p - lpPrinterName != cchComputerName || _wcsnicmp(lpPrinterName, wszComputerName, cchComputerName) != 0)
656 {
657 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
658 goto Cleanup;
659 }
660
661 // We have checked the server name and don't need it anymore.
662 lpPrinterName = p + 1;
663 }
664
665 // Look for a comma. If it exists, it indicates the end of the first parameter.
666 pwszSecondParameter = wcschr(lpPrinterName, L',');
667 if (pwszSecondParameter)
668 cchFirstParameter = pwszSecondParameter - p;
669 else
670 cchFirstParameter = wcslen(lpPrinterName);
671
672 // We must have at least one parameter.
673 if (!cchFirstParameter && !pwszSecondParameter)
674 {
675 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
676 goto Cleanup;
677 }
678
679 // Do we have a first parameter?
680 if (cchFirstParameter)
681 {
682 // Yes, extract it.
683 // No null-termination is necessary here, because DllAllocSplMem returns a zero-initialized buffer.
684 pwszFirstParameter = DllAllocSplMem((cchFirstParameter + 1) * sizeof(WCHAR));
685 CopyMemory(pwszFirstParameter, lpPrinterName, cchFirstParameter * sizeof(WCHAR));
686 }
687
688 // Do we have a second parameter?
689 if (pwszSecondParameter)
690 {
691 // Yes, skip the comma at the beginning.
692 ++pwszSecondParameter;
693
694 // Skip whitespace as well.
695 while (*pwszSecondParameter == L' ')
696 ++pwszSecondParameter;
697 }
698
699 // Create a new handle.
700 pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
701 if (!pHandle)
702 {
703 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
704 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
705 goto Cleanup;
706 }
707
708 // Now we can finally check the type of handle actually requested.
709 if (pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Port", 4) == 0)
710 {
711 // The caller wants a port handle and provided a string like:
712 // "LPT1:, Port"
713 // "\\COMPUTERNAME\LPT1:, Port"
714
715 // Look for this port in our Print Monitor Port list.
716 pPort = FindPort(pwszFirstParameter);
717 if (!pPort)
718 {
719 // The supplied port is unknown to all our Print Monitors.
720 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
721 goto Cleanup;
722 }
723
724 pPrintMonitor = pPort->pPrintMonitor;
725
726 // Call the monitor's OpenPort function.
727 if (pPrintMonitor->bIsLevel2)
728 bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnOpenPort(pPrintMonitor->hMonitor, pwszFirstParameter, &hExternalHandle);
729 else
730 bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnOpenPort(pwszFirstParameter, &hExternalHandle);
731
732 if (!bReturnValue)
733 {
734 // The OpenPort function failed. Return its last error.
735 dwErrorCode = GetLastError();
736 goto Cleanup;
737 }
738
739 // Create a new LOCAL_PORT_HANDLE.
740 pPortHandle = DllAllocSplMem(sizeof(LOCAL_PORT_HANDLE));
741 if (!pPortHandle)
742 {
743 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
744 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
745 goto Cleanup;
746 }
747
748 pPortHandle->hPort = hExternalHandle;
749 pPortHandle->pPort = pPort;
750
751 // Return the Port handle through our general handle.
752 pHandle->HandleType = HandleType_Port;
753 pHandle->pSpecificHandle = pPortHandle;
754 }
755 else if (!pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Xcv", 3) == 0)
756 {
757 // The caller wants an Xcv handle and provided a string like:
758 // ", XcvMonitor Local Port"
759 // "\\COMPUTERNAME\, XcvMonitor Local Port"
760 // ", XcvPort LPT1:"
761 // "\\COMPUTERNAME\, XcvPort LPT1:"
762
763 // Skip the "Xcv" string.
764 pwszSecondParameter += 3;
765
766 // Is XcvMonitor or XcvPort requested?
767 if (wcsncmp(pwszSecondParameter, L"Monitor ", 8) == 0)
768 {
769 // Skip the "Monitor " string.
770 pwszSecondParameter += 8;
771
772 // Look for this monitor in our Print Monitor list.
773 pPrintMonitor = FindPrintMonitor(pwszSecondParameter);
774 if (!pPrintMonitor)
775 {
776 // The caller supplied a non-existing Monitor name.
777 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
778 goto Cleanup;
779 }
780 }
781 else if (wcsncmp(pwszSecondParameter, L"Port ", 5) == 0)
782 {
783 // Skip the "Port " string.
784 pwszSecondParameter += 5;
785
786 // Look for this port in our Print Monitor Port list.
787 pPort = FindPort(pwszFirstParameter);
788 if (!pPort)
789 {
790 // The supplied port is unknown to all our Print Monitors.
791 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
792 goto Cleanup;
793 }
794
795 pPrintMonitor = pPort->pPrintMonitor;
796 }
797 else
798 {
799 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
800 goto Cleanup;
801 }
802
803 // Call the monitor's XcvOpenPort function.
804 if (pPrintMonitor->bIsLevel2)
805 bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnXcvOpenPort(pPrintMonitor->hMonitor, pwszSecondParameter, SERVER_EXECUTE, &hExternalHandle);
806 else
807 bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnXcvOpenPort(pwszSecondParameter, SERVER_EXECUTE, &hExternalHandle);
808
809 if (!bReturnValue)
810 {
811 // The XcvOpenPort function failed. Return its last error.
812 dwErrorCode = GetLastError();
813 goto Cleanup;
814 }
815
816 // Create a new LOCAL_XCV_HANDLE.
817 pXcvHandle = DllAllocSplMem(sizeof(LOCAL_XCV_HANDLE));
818 if (!pXcvHandle)
819 {
820 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
821 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
822 goto Cleanup;
823 }
824
825 pXcvHandle->hXcv = hExternalHandle;
826 pXcvHandle->pPrintMonitor = pPrintMonitor;
827
828 // Return the Xcv handle through our general handle.
829 pHandle->HandleType = HandleType_Xcv;
830 pHandle->pSpecificHandle = pXcvHandle;
831 }
832 else
833 {
834 // The caller wants a Printer or Printer Job handle and provided a string like:
835 // "HP DeskJet"
836 // "\\COMPUTERNAME\HP DeskJet"
837 // "HP DeskJet, Job 5"
838 // "\\COMPUTERNAME\HP DeskJet, Job 5"
839
840 // Retrieve the printer from the list.
841 pPrinter = LookupElementSkiplist(&PrinterList, &pwszFirstParameter, NULL);
842 if (!pPrinter)
843 {
844 // The printer does not exist.
845 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
846 goto Cleanup;
847 }
848
849 // Create a new LOCAL_PRINTER_HANDLE.
850 pPrinterHandle = DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE));
851 if (!pPrinterHandle)
852 {
853 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
854 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
855 goto Cleanup;
856 }
857
858 pPrinterHandle->hSPLFile = INVALID_HANDLE_VALUE;
859 pPrinterHandle->pPrinter = pPrinter;
860
861 // Check if a datatype was given.
862 if (pDefault && pDefault->pDatatype)
863 {
864 // Use the datatype if it's valid.
865 if (!FindDatatype(pPrinter->pPrintProcessor, pDefault->pDatatype))
866 {
867 dwErrorCode = ERROR_INVALID_DATATYPE;
868 goto Cleanup;
869 }
870
871 pPrinterHandle->pwszDatatype = AllocSplStr(pDefault->pDatatype);
872 }
873 else
874 {
875 // Use the default datatype.
876 pPrinterHandle->pwszDatatype = AllocSplStr(pPrinter->pwszDefaultDatatype);
877 }
878
879 // Check if a DevMode was given, otherwise use the default.
880 if (pDefault && pDefault->pDevMode)
881 pPrinterHandle->pDevMode = DuplicateDevMode(pDefault->pDevMode);
882 else
883 pPrinterHandle->pDevMode = DuplicateDevMode(pPrinter->pDefaultDevMode);
884
885 // Check if the caller wants a handle to an existing Print Job.
886 if (pwszSecondParameter)
887 {
888 // The "Job " string has to follow now.
889 if (wcsncmp(pwszSecondParameter, L"Job ", 4) != 0)
890 {
891 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
892 goto Cleanup;
893 }
894
895 // Skip the "Job " string.
896 pwszSecondParameter += 4;
897
898 // Skip even more whitespace.
899 while (*pwszSecondParameter == ' ')
900 ++pwszSecondParameter;
901
902 // Finally extract the desired Job ID.
903 dwJobID = wcstoul(pwszSecondParameter, NULL, 10);
904 if (!IS_VALID_JOB_ID(dwJobID))
905 {
906 // The user supplied an invalid Job ID.
907 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
908 goto Cleanup;
909 }
910
911 // Look for this job in the Global Job List.
912 pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL);
913 if (!pJob || pJob->pPrinter != pPrinter)
914 {
915 // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
916 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
917 goto Cleanup;
918 }
919
920 // Try to open its SPL file.
921 GetJobFilePath(L"SPL", dwJobID, wszFullPath);
922 pPrinterHandle->hSPLFile = CreateFileW(wszFullPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
923 if (pPrinterHandle->hSPLFile == INVALID_HANDLE_VALUE)
924 {
925 dwErrorCode = GetLastError();
926 ERR("CreateFileW failed with error %lu for \"%S\"!", dwErrorCode, wszFullPath);
927 goto Cleanup;
928 }
929
930 // Associate the job to our Printer Handle, but don't set bStartedDoc.
931 // This prevents the caller from doing further StartDocPrinter, WritePrinter, etc. calls on it.
932 pPrinterHandle->pJob = pJob;
933 }
934
935 // Return the Printer handle through our general handle.
936 pHandle->HandleType = HandleType_Printer;
937 pHandle->pSpecificHandle = pPrinterHandle;
938 }
939
940 // We were successful! Return the handle.
941 *phPrinter = (HANDLE)pHandle;
942 dwErrorCode = ERROR_SUCCESS;
943
944 // Don't let the cleanup routines free this.
945 pHandle = NULL;
946 pPrinterHandle = NULL;
947
948 Cleanup:
949 if (pHandle)
950 DllFreeSplMem(pHandle);
951
952 if (pPrinterHandle)
953 {
954 if (pPrinterHandle->pwszDatatype)
955 DllFreeSplStr(pPrinterHandle->pwszDatatype);
956
957 if (pPrinterHandle->pDevMode)
958 DllFreeSplMem(pPrinterHandle->pDevMode);
959
960 DllFreeSplMem(pPrinterHandle);
961 }
962
963 if (pwszFirstParameter)
964 DllFreeSplMem(pwszFirstParameter);
965
966 SetLastError(dwErrorCode);
967 return (dwErrorCode == ERROR_SUCCESS);
968 }
969
970 BOOL WINAPI
971 LocalReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
972 {
973 BOOL bReturnValue;
974 DWORD dwErrorCode;
975 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
976 PLOCAL_PORT_HANDLE pPortHandle;
977 PLOCAL_PRINTER_HANDLE pPrinterHandle;
978
979 // Sanity checks.
980 if (!pHandle)
981 {
982 dwErrorCode = ERROR_INVALID_HANDLE;
983 goto Cleanup;
984 }
985
986 // Port handles are an entirely different thing.
987 if (pHandle->HandleType == HandleType_Port)
988 {
989 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
990
991 // Call the monitor's ReadPort function.
992 if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
993 bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnReadPort(pPortHandle->hPort, pBuf, cbBuf, pNoBytesRead);
994 else
995 bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnReadPort(pPortHandle->hPort, pBuf, cbBuf, pNoBytesRead);
996
997 if (!bReturnValue)
998 {
999 // The ReadPort function failed. Return its last error.
1000 dwErrorCode = GetLastError();
1001 goto Cleanup;
1002 }
1003
1004 // We were successful!
1005 dwErrorCode = ERROR_SUCCESS;
1006 goto Cleanup;
1007 }
1008
1009 // The remaining function deals with Printer handles only.
1010 if (pHandle->HandleType != HandleType_Printer)
1011 {
1012 dwErrorCode = ERROR_INVALID_HANDLE;
1013 goto Cleanup;
1014 }
1015
1016 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1017
1018 // ReadPrinter needs an opened SPL file to work.
1019 // This only works if a Printer Job Handle was requested in OpenPrinter.
1020 if (pPrinterHandle->hSPLFile == INVALID_HANDLE_VALUE)
1021 {
1022 dwErrorCode = ERROR_INVALID_HANDLE;
1023 goto Cleanup;
1024 }
1025
1026 // Pass the parameters to ReadFile.
1027 if (!ReadFile(pPrinterHandle->hSPLFile, pBuf, cbBuf, pNoBytesRead, NULL))
1028 {
1029 dwErrorCode = GetLastError();
1030 ERR("ReadFile failed with error %lu!\n", dwErrorCode);
1031 goto Cleanup;
1032 }
1033
1034 dwErrorCode = ERROR_SUCCESS;
1035
1036 Cleanup:
1037 SetLastError(dwErrorCode);
1038 return (dwErrorCode == ERROR_SUCCESS);
1039 }
1040
1041 DWORD WINAPI
1042 LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
1043 {
1044 BOOL bReturnValue;
1045 DWORD dwErrorCode;
1046 DWORD dwReturnValue = 0;
1047 PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
1048 PLOCAL_JOB pJob;
1049 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1050 PLOCAL_PORT_HANDLE pPortHandle;
1051 PLOCAL_PRINTER_HANDLE pPrinterHandle;
1052
1053 // Sanity checks.
1054 if (!pHandle)
1055 {
1056 dwErrorCode = ERROR_INVALID_HANDLE;
1057 goto Cleanup;
1058 }
1059
1060 // Port handles are an entirely different thing.
1061 if (pHandle->HandleType == HandleType_Port)
1062 {
1063 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1064
1065 // This call should come from a Print Processor and the job this port is going to print was assigned to us before opening the Print Processor.
1066 // Claim it exclusively for this port handle.
1067 pJob = pPortHandle->pPort->pNextJobToProcess;
1068 pPortHandle->pPort->pNextJobToProcess = NULL;
1069 ASSERT(pJob);
1070
1071 // Call the monitor's StartDocPort function.
1072 if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1073 bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnStartDocPort(pPortHandle->hPort, pJob->pPrinter->pwszPrinterName, pJob->dwJobID, Level, pDocInfo);
1074 else
1075 bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnStartDocPort(pPortHandle->hPort, pJob->pPrinter->pwszPrinterName, pJob->dwJobID, Level, pDocInfo);
1076
1077 if (!bReturnValue)
1078 {
1079 // The StartDocPort function failed. Return its last error.
1080 dwErrorCode = GetLastError();
1081 goto Cleanup;
1082 }
1083
1084 // We were successful!
1085 dwErrorCode = ERROR_SUCCESS;
1086 dwReturnValue = pJob->dwJobID;
1087 goto Cleanup;
1088 }
1089
1090 // The remaining function deals with Printer handles only.
1091 if (pHandle->HandleType != HandleType_Printer)
1092 {
1093 dwErrorCode = ERROR_INVALID_HANDLE;
1094 goto Cleanup;
1095 }
1096
1097 if (!pDocInfo1)
1098 {
1099 dwErrorCode = ERROR_INVALID_PARAMETER;
1100 goto Cleanup;
1101 }
1102
1103 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1104
1105 // pJob may already be occupied if this is a Print Job handle. In this case, StartDocPrinter has to fail.
1106 if (pPrinterHandle->pJob)
1107 {
1108 dwErrorCode = ERROR_INVALID_PARAMETER;
1109 goto Cleanup;
1110 }
1111
1112 // Check the validity of the datatype if we got one.
1113 if (pDocInfo1->pDatatype && !FindDatatype(pPrinterHandle->pJob->pPrintProcessor, pDocInfo1->pDatatype))
1114 {
1115 dwErrorCode = ERROR_INVALID_DATATYPE;
1116 goto Cleanup;
1117 }
1118
1119 // Check if this is the right document information level.
1120 if (Level != 1)
1121 {
1122 dwErrorCode = ERROR_INVALID_LEVEL;
1123 goto Cleanup;
1124 }
1125
1126 // All requirements are met - create a new job.
1127 dwErrorCode = CreateJob(pPrinterHandle);
1128 if (dwErrorCode != ERROR_SUCCESS)
1129 goto Cleanup;
1130
1131 // Use any given datatype.
1132 if (pDocInfo1->pDatatype && !ReallocSplStr(&pPrinterHandle->pJob->pwszDatatype, pDocInfo1->pDatatype))
1133 {
1134 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1135 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1136 goto Cleanup;
1137 }
1138
1139 // Use any given document name.
1140 if (pDocInfo1->pDocName && !ReallocSplStr(&pPrinterHandle->pJob->pwszDocumentName, pDocInfo1->pDocName))
1141 {
1142 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1143 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1144 goto Cleanup;
1145 }
1146
1147 // We were successful!
1148 dwErrorCode = ERROR_SUCCESS;
1149 dwReturnValue = pPrinterHandle->pJob->dwJobID;
1150
1151 Cleanup:
1152 SetLastError(dwErrorCode);
1153 return dwReturnValue;
1154 }
1155
1156 BOOL WINAPI
1157 LocalStartPagePrinter(HANDLE hPrinter)
1158 {
1159 DWORD dwErrorCode;
1160 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1161 PLOCAL_PRINTER_HANDLE pPrinterHandle;
1162
1163 // Sanity checks.
1164 if (!pHandle || pHandle->HandleType != HandleType_Printer)
1165 {
1166 dwErrorCode = ERROR_INVALID_HANDLE;
1167 goto Cleanup;
1168 }
1169
1170 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1171
1172 // We require StartDocPrinter or AddJob to be called first.
1173 if (!pPrinterHandle->bStartedDoc)
1174 {
1175 dwErrorCode = ERROR_SPL_NO_STARTDOC;
1176 goto Cleanup;
1177 }
1178
1179 // Increase the page count.
1180 ++pPrinterHandle->pJob->dwTotalPages;
1181 dwErrorCode = ERROR_SUCCESS;
1182
1183 Cleanup:
1184 SetLastError(dwErrorCode);
1185 return (dwErrorCode == ERROR_SUCCESS);
1186 }
1187
1188 BOOL WINAPI
1189 LocalWritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
1190 {
1191 BOOL bReturnValue;
1192 DWORD dwErrorCode;
1193 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1194 PLOCAL_PORT_HANDLE pPortHandle;
1195 PLOCAL_PRINTER_HANDLE pPrinterHandle;
1196
1197 // Sanity checks.
1198 if (!pHandle)
1199 {
1200 dwErrorCode = ERROR_INVALID_HANDLE;
1201 goto Cleanup;
1202 }
1203
1204 // Port handles are an entirely different thing.
1205 if (pHandle->HandleType == HandleType_Port)
1206 {
1207 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1208
1209 // Call the monitor's WritePort function.
1210 if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1211 bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnWritePort(pPortHandle->hPort, pBuf, cbBuf, pcWritten);
1212 else
1213 bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnWritePort(pPortHandle->hPort, pBuf, cbBuf, pcWritten);
1214
1215 if (!bReturnValue)
1216 {
1217 // The WritePort function failed. Return its last error.
1218 dwErrorCode = GetLastError();
1219 goto Cleanup;
1220 }
1221
1222 // We were successful!
1223 dwErrorCode = ERROR_SUCCESS;
1224 goto Cleanup;
1225 }
1226
1227 // The remaining function deals with Printer handles only.
1228 if (pHandle->HandleType != HandleType_Printer)
1229 {
1230 dwErrorCode = ERROR_INVALID_HANDLE;
1231 goto Cleanup;
1232 }
1233
1234 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1235
1236 // We require StartDocPrinter or AddJob to be called first.
1237 if (!pPrinterHandle->bStartedDoc)
1238 {
1239 dwErrorCode = ERROR_SPL_NO_STARTDOC;
1240 goto Cleanup;
1241 }
1242
1243 // TODO: This function is only called when doing non-spooled printing.
1244 // This needs to be investigated further. We can't just use pPrinterHandle->hSPLFile here, because that's currently reserved for Printer Job handles (see LocalReadPrinter).
1245 #if 0
1246 // Pass the parameters to WriteFile.
1247 if (!WriteFile(SOME_SPOOL_FILE_HANDLE, pBuf, cbBuf, pcWritten, NULL))
1248 {
1249 dwErrorCode = GetLastError();
1250 ERR("WriteFile failed with error %lu!\n", GetLastError());
1251 goto Cleanup;
1252 }
1253 #endif
1254
1255 dwErrorCode = ERROR_SUCCESS;
1256
1257 Cleanup:
1258 SetLastError(dwErrorCode);
1259 return (dwErrorCode == ERROR_SUCCESS);
1260 }
1261
1262 BOOL WINAPI
1263 LocalEndPagePrinter(HANDLE hPrinter)
1264 {
1265 DWORD dwErrorCode;
1266 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1267
1268 // Sanity checks.
1269 if (!pHandle || pHandle->HandleType != HandleType_Printer)
1270 {
1271 dwErrorCode = ERROR_INVALID_HANDLE;
1272 goto Cleanup;
1273 }
1274
1275 // This function doesn't do anything else for now.
1276 dwErrorCode = ERROR_SUCCESS;
1277
1278 Cleanup:
1279 SetLastError(dwErrorCode);
1280 return (dwErrorCode == ERROR_SUCCESS);
1281 }
1282
1283 BOOL WINAPI
1284 LocalEndDocPrinter(HANDLE hPrinter)
1285 {
1286 BOOL bReturnValue;
1287 DWORD dwErrorCode;
1288 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1289 PLOCAL_PORT_HANDLE pPortHandle;
1290 PLOCAL_PRINTER_HANDLE pPrinterHandle;
1291
1292 // Sanity checks.
1293 if (!pHandle)
1294 {
1295 dwErrorCode = ERROR_INVALID_HANDLE;
1296 goto Cleanup;
1297 }
1298
1299 // Port handles are an entirely different thing.
1300 if (pHandle->HandleType == HandleType_Port)
1301 {
1302 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1303
1304 // Call the monitor's EndDocPort function.
1305 if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1306 bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnEndDocPort(pPortHandle->hPort);
1307 else
1308 bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnEndDocPort(pPortHandle->hPort);
1309
1310 if (!bReturnValue)
1311 {
1312 // The EndDocPort function failed. Return its last error.
1313 dwErrorCode = GetLastError();
1314 goto Cleanup;
1315 }
1316
1317 // We were successful!
1318 dwErrorCode = ERROR_SUCCESS;
1319 goto Cleanup;
1320 }
1321
1322 // The remaining function deals with Printer handles only.
1323 if (pHandle->HandleType != HandleType_Printer)
1324 {
1325 dwErrorCode = ERROR_INVALID_HANDLE;
1326 goto Cleanup;
1327 }
1328
1329 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1330
1331 // We require StartDocPrinter or AddJob to be called first.
1332 if (!pPrinterHandle->bStartedDoc)
1333 {
1334 dwErrorCode = ERROR_SPL_NO_STARTDOC;
1335 goto Cleanup;
1336 }
1337
1338 // TODO: Something like ScheduleJob
1339
1340 // Finish the job.
1341 pPrinterHandle->bStartedDoc = FALSE;
1342 pPrinterHandle->pJob = NULL;
1343 dwErrorCode = ERROR_SUCCESS;
1344
1345 Cleanup:
1346 SetLastError(dwErrorCode);
1347 return (dwErrorCode == ERROR_SUCCESS);
1348 }
1349
1350 BOOL WINAPI
1351 LocalClosePrinter(HANDLE hPrinter)
1352 {
1353 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1354 PLOCAL_PORT_HANDLE pPortHandle;
1355 PLOCAL_PRINTER_HANDLE pPrinterHandle;
1356 PLOCAL_XCV_HANDLE pXcvHandle;
1357
1358 if (!pHandle)
1359 {
1360 SetLastError(ERROR_INVALID_HANDLE);
1361 return FALSE;
1362 }
1363
1364 if (pHandle->HandleType == HandleType_Port)
1365 {
1366 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1367
1368 // Call the monitor's ClosePort function.
1369 if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1370 ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnClosePort(pPortHandle->hPort);
1371 else
1372 ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnClosePort(pPortHandle->hPort);
1373 }
1374 else if (pHandle->HandleType == HandleType_Printer)
1375 {
1376 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1377
1378 // Terminate any started job.
1379 if (pPrinterHandle->pJob)
1380 FreeJob(pPrinterHandle->pJob);
1381
1382 // Free memory for the fields.
1383 DllFreeSplMem(pPrinterHandle->pDevMode);
1384 DllFreeSplStr(pPrinterHandle->pwszDatatype);
1385 }
1386 else if (pHandle->HandleType == HandleType_Xcv)
1387 {
1388 pXcvHandle = (PLOCAL_XCV_HANDLE)pHandle->pSpecificHandle;
1389
1390 // Call the monitor's XcvClosePort function.
1391 if (pXcvHandle->pPrintMonitor->bIsLevel2)
1392 ((PMONITOR2)pXcvHandle->pPrintMonitor->pMonitor)->pfnXcvClosePort(pXcvHandle->hXcv);
1393 else
1394 ((LPMONITOREX)pXcvHandle->pPrintMonitor->pMonitor)->Monitor.pfnXcvClosePort(pXcvHandle->hXcv);
1395 }
1396
1397 // Free memory for the handle and the specific handle.
1398 DllFreeSplMem(pHandle->pSpecificHandle);
1399 DllFreeSplMem(pHandle);
1400
1401 return TRUE;
1402 }