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