[PRINTING] Replace all my custom marshalling code by calls to the newly implemented...
[reactos.git] / win32ss / printing / base / winspool / printers.c
1 /*
2 * PROJECT: ReactOS Spooler API
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Functions related to Printers and printing
5 * COPYRIGHT: Copyright 2015-2018 Colin Finck (colin@reactos.org)
6 */
7
8 #include "precomp.h"
9 #include <marshalling/printers.h>
10
11 // Local Constants
12
13 /** And the award for the most confusingly named setting goes to "Device", for storing the default printer of the current user.
14 Ok, I admit that this has historical reasons. It's still not straightforward in any way though! */
15 static const WCHAR wszWindowsKey[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows";
16 static const WCHAR wszDeviceValue[] = L"Device";
17
18 static DWORD
19 _StartDocPrinterSpooled(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1, PADDJOB_INFO_1W pAddJobInfo1)
20 {
21 DWORD cbNeeded;
22 DWORD dwErrorCode;
23 PJOB_INFO_1W pJobInfo1 = NULL;
24
25 // Create the spool file.
26 pHandle->hSPLFile = CreateFileW(pAddJobInfo1->Path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
27 if (pHandle->hSPLFile == INVALID_HANDLE_VALUE)
28 {
29 dwErrorCode = GetLastError();
30 ERR("CreateFileW failed for \"%S\" with error %lu!\n", pAddJobInfo1->Path, dwErrorCode);
31 goto Cleanup;
32 }
33
34 // Get the size of the job information.
35 GetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, NULL, 0, &cbNeeded);
36 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
37 {
38 dwErrorCode = GetLastError();
39 ERR("GetJobW failed with error %lu!\n", dwErrorCode);
40 goto Cleanup;
41 }
42
43 // Allocate enough memory for the returned job information.
44 pJobInfo1 = HeapAlloc(hProcessHeap, 0, cbNeeded);
45 if (!pJobInfo1)
46 {
47 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
48 ERR("HeapAlloc failed!\n");
49 goto Cleanup;
50 }
51
52 // Get the job information.
53 if (!GetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, cbNeeded, &cbNeeded))
54 {
55 dwErrorCode = GetLastError();
56 ERR("GetJobW failed with error %lu!\n", dwErrorCode);
57 goto Cleanup;
58 }
59
60 // Add our document information.
61 if (pDocInfo1->pDatatype)
62 pJobInfo1->pDatatype = pDocInfo1->pDatatype;
63
64 pJobInfo1->pDocument = pDocInfo1->pDocName;
65
66 // Set the new job information.
67 if (!SetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, 0))
68 {
69 dwErrorCode = GetLastError();
70 ERR("SetJobW failed with error %lu!\n", dwErrorCode);
71 goto Cleanup;
72 }
73
74 // We were successful!
75 pHandle->dwJobID = pAddJobInfo1->JobId;
76 dwErrorCode = ERROR_SUCCESS;
77
78 Cleanup:
79 if (pJobInfo1)
80 HeapFree(hProcessHeap, 0, pJobInfo1);
81
82 return dwErrorCode;
83 }
84
85 static DWORD
86 _StartDocPrinterWithRPC(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1)
87 {
88 DWORD dwErrorCode;
89 WINSPOOL_DOC_INFO_CONTAINER DocInfoContainer;
90
91 DocInfoContainer.Level = 1;
92 DocInfoContainer.DocInfo.pDocInfo1 = (WINSPOOL_DOC_INFO_1*)pDocInfo1;
93
94 RpcTryExcept
95 {
96 dwErrorCode = _RpcStartDocPrinter(pHandle->hPrinter, &DocInfoContainer, &pHandle->dwJobID);
97 }
98 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
99 {
100 dwErrorCode = RpcExceptionCode();
101 ERR("_RpcStartDocPrinter failed with exception code %lu!\n", dwErrorCode);
102 }
103 RpcEndExcept;
104
105 return dwErrorCode;
106 }
107
108 BOOL WINAPI
109 AbortPrinter(HANDLE hPrinter)
110 {
111 TRACE("AbortPrinter(%p)\n", hPrinter);
112 UNIMPLEMENTED;
113 return FALSE;
114 }
115
116 HANDLE WINAPI
117 AddPrinterA(PSTR pName, DWORD Level, PBYTE pPrinter)
118 {
119 TRACE("AddPrinterA(%s, %lu, %p)\n", pName, Level, pPrinter);
120 UNIMPLEMENTED;
121 return NULL;
122 }
123
124 HANDLE WINAPI
125 AddPrinterW(PWSTR pName, DWORD Level, PBYTE pPrinter)
126 {
127 TRACE("AddPrinterW(%S, %lu, %p)\n", pName, Level, pPrinter);
128 UNIMPLEMENTED;
129 return NULL;
130 }
131
132 BOOL WINAPI
133 ClosePrinter(HANDLE hPrinter)
134 {
135 DWORD dwErrorCode;
136 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
137
138 TRACE("ClosePrinter(%p)\n", hPrinter);
139
140 // Sanity checks.
141 if (!pHandle)
142 {
143 dwErrorCode = ERROR_INVALID_HANDLE;
144 goto Cleanup;
145 }
146
147 // Do the RPC call.
148 RpcTryExcept
149 {
150 dwErrorCode = _RpcClosePrinter(&pHandle->hPrinter);
151 }
152 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
153 {
154 dwErrorCode = RpcExceptionCode();
155 ERR("_RpcClosePrinter failed with exception code %lu!\n", dwErrorCode);
156 }
157 RpcEndExcept;
158
159 // Close any open file handle.
160 if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
161 CloseHandle(pHandle->hSPLFile);
162
163 // Free the memory for the handle.
164 HeapFree(hProcessHeap, 0, pHandle);
165
166 Cleanup:
167 SetLastError(dwErrorCode);
168 return (dwErrorCode == ERROR_SUCCESS);
169 }
170
171 BOOL WINAPI
172 DeletePrinter(HANDLE hPrinter)
173 {
174 TRACE("DeletePrinter(%p)\n", hPrinter);
175 UNIMPLEMENTED;
176 return FALSE;
177 }
178
179 DWORD WINAPI
180 DeviceCapabilitiesA(LPCSTR pDevice, LPCSTR pPort, WORD fwCapability, LPSTR pOutput, const DEVMODEA* pDevMode)
181 {
182 TRACE("DeviceCapabilitiesA(%s, %s, %hu, %p, %p)\n", pDevice, pPort, fwCapability, pOutput, pDevMode);
183 UNIMPLEMENTED;
184 return 0;
185 }
186
187 DWORD WINAPI
188 DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort, WORD fwCapability, LPWSTR pOutput, const DEVMODEW* pDevMode)
189 {
190 TRACE("DeviceCapabilitiesW(%S, %S, %hu, %p, %p)\n", pDevice, pPort, fwCapability, pOutput, pDevMode);
191 UNIMPLEMENTED;
192 return 0;
193 }
194
195 LONG WINAPI
196 DocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName, PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput, DWORD fMode)
197 {
198 TRACE("DocumentPropertiesA(%p, %p, %s, %p, %p, %lu)\n", hWnd, hPrinter, pDeviceName, pDevModeOutput, pDevModeInput, fMode);
199 UNIMPLEMENTED;
200 return -1;
201 }
202
203 LONG WINAPI
204 DocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName, PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput, DWORD fMode)
205 {
206 TRACE("DocumentPropertiesW(%p, %p, %S, %p, %p, %lu)\n", hWnd, hPrinter, pDeviceName, pDevModeOutput, pDevModeInput, fMode);
207 UNIMPLEMENTED;
208 return -1;
209 }
210
211 BOOL WINAPI
212 EndDocPrinter(HANDLE hPrinter)
213 {
214 DWORD dwErrorCode;
215 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
216
217 TRACE("EndDocPrinter(%p)\n", hPrinter);
218
219 // Sanity checks.
220 if (!pHandle)
221 {
222 dwErrorCode = ERROR_INVALID_HANDLE;
223 goto Cleanup;
224 }
225
226 if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
227 {
228 // For spooled jobs, the document is finished by calling _RpcScheduleJob.
229 RpcTryExcept
230 {
231 dwErrorCode = _RpcScheduleJob(pHandle->hPrinter, pHandle->dwJobID);
232 }
233 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
234 {
235 dwErrorCode = RpcExceptionCode();
236 ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode);
237 }
238 RpcEndExcept;
239
240 // Close the spool file handle.
241 CloseHandle(pHandle->hSPLFile);
242 }
243 else
244 {
245 // In all other cases, just call _RpcEndDocPrinter.
246 RpcTryExcept
247 {
248 dwErrorCode = _RpcEndDocPrinter(pHandle->hPrinter);
249 }
250 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
251 {
252 dwErrorCode = RpcExceptionCode();
253 ERR("_RpcEndDocPrinter failed with exception code %lu!\n", dwErrorCode);
254 }
255 RpcEndExcept;
256 }
257
258 // A new document can now be started again.
259 pHandle->bStartedDoc = FALSE;
260
261 Cleanup:
262 SetLastError(dwErrorCode);
263 return (dwErrorCode == ERROR_SUCCESS);
264 }
265
266 BOOL WINAPI
267 EndPagePrinter(HANDLE hPrinter)
268 {
269 DWORD dwErrorCode;
270 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
271
272 TRACE("EndPagePrinter(%p)\n", hPrinter);
273
274 // Sanity checks.
275 if (!pHandle)
276 {
277 dwErrorCode = ERROR_INVALID_HANDLE;
278 goto Cleanup;
279 }
280
281 if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
282 {
283 // For spooled jobs, we don't need to do anything.
284 dwErrorCode = ERROR_SUCCESS;
285 }
286 else
287 {
288 // In all other cases, just call _RpcEndPagePrinter.
289 RpcTryExcept
290 {
291 dwErrorCode = _RpcEndPagePrinter(pHandle->hPrinter);
292 }
293 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
294 {
295 dwErrorCode = RpcExceptionCode();
296 ERR("_RpcEndPagePrinter failed with exception code %lu!\n", dwErrorCode);
297 }
298 RpcEndExcept;
299 }
300
301 Cleanup:
302 SetLastError(dwErrorCode);
303 return (dwErrorCode == ERROR_SUCCESS);
304 }
305
306 BOOL WINAPI
307 EnumPrintersA(DWORD Flags, PSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
308 {
309 TRACE("EnumPrintersA(%lu, %s, %lu, %p, %lu, %p, %p)\n", Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
310 return FALSE;
311 }
312
313 BOOL WINAPI
314 EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
315 {
316 DWORD dwErrorCode;
317
318 TRACE("EnumPrintersW(%lu, %S, %lu, %p, %lu, %p, %p)\n", Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
319
320 // Dismiss invalid levels already at this point.
321 if (Level == 3 || Level > 5)
322 {
323 dwErrorCode = ERROR_INVALID_LEVEL;
324 goto Cleanup;
325 }
326
327 if (cbBuf && pPrinterEnum)
328 ZeroMemory(pPrinterEnum, cbBuf);
329
330 // Do the RPC call
331 RpcTryExcept
332 {
333 dwErrorCode = _RpcEnumPrinters(Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
334 }
335 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
336 {
337 dwErrorCode = RpcExceptionCode();
338 ERR("_RpcEnumPrinters failed with exception code %lu!\n", dwErrorCode);
339 }
340 RpcEndExcept;
341
342 if (dwErrorCode == ERROR_SUCCESS)
343 {
344 // Replace relative offset addresses in the output by absolute pointers.
345 ASSERT(Level <= 9);
346 MarshallUpStructuresArray(cbBuf, pPrinterEnum, *pcReturned, pPrinterInfoMarshalling[Level]->pInfo, pPrinterInfoMarshalling[Level]->cbStructureSize, TRUE);
347 }
348
349 Cleanup:
350 SetLastError(dwErrorCode);
351 return (dwErrorCode == ERROR_SUCCESS);
352 }
353
354 BOOL WINAPI
355 FlushPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten, DWORD cSleep)
356 {
357 TRACE("FlushPrinter(%p, %p, %lu, %p, %lu)\n", hPrinter, pBuf, cbBuf, pcWritten, cSleep);
358 UNIMPLEMENTED;
359 return FALSE;
360 }
361
362 BOOL WINAPI
363 GetDefaultPrinterA(LPSTR pszBuffer, LPDWORD pcchBuffer)
364 {
365 DWORD dwErrorCode;
366 PWSTR pwszBuffer = NULL;
367
368 TRACE("GetDefaultPrinterA(%p, %p)\n", pszBuffer, pcchBuffer);
369
370 // Sanity check.
371 if (!pcchBuffer)
372 {
373 dwErrorCode = ERROR_INVALID_PARAMETER;
374 goto Cleanup;
375 }
376
377 // Check if an ANSI buffer was given and if so, allocate a Unicode buffer of the same size.
378 if (pszBuffer && *pcchBuffer)
379 {
380 pwszBuffer = HeapAlloc(hProcessHeap, 0, *pcchBuffer * sizeof(WCHAR));
381 if (!pwszBuffer)
382 {
383 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
384 ERR("HeapAlloc failed!\n");
385 goto Cleanup;
386 }
387 }
388
389 if (!GetDefaultPrinterW(pwszBuffer, pcchBuffer))
390 {
391 dwErrorCode = GetLastError();
392 goto Cleanup;
393 }
394
395 // We successfully got a string in pwszBuffer, so convert the Unicode string to ANSI.
396 WideCharToMultiByte(CP_ACP, 0, pwszBuffer, -1, pszBuffer, *pcchBuffer, NULL, NULL);
397
398 dwErrorCode = ERROR_SUCCESS;
399
400 Cleanup:
401 if (pwszBuffer)
402 HeapFree(hProcessHeap, 0, pwszBuffer);
403
404 SetLastError(dwErrorCode);
405 return (dwErrorCode == ERROR_SUCCESS);
406 }
407
408 BOOL WINAPI
409 GetDefaultPrinterW(LPWSTR pszBuffer, LPDWORD pcchBuffer)
410 {
411 DWORD cbNeeded;
412 DWORD cchInputBuffer;
413 DWORD dwErrorCode;
414 HKEY hWindowsKey = NULL;
415 PWSTR pwszDevice = NULL;
416 PWSTR pwszComma;
417
418 TRACE("GetDefaultPrinterW(%p, %p)\n", pszBuffer, pcchBuffer);
419
420 // Sanity check.
421 if (!pcchBuffer)
422 {
423 dwErrorCode = ERROR_INVALID_PARAMETER;
424 goto Cleanup;
425 }
426
427 cchInputBuffer = *pcchBuffer;
428
429 // Open the registry key where the default printer for the current user is stored.
430 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszWindowsKey, 0, KEY_READ, &hWindowsKey);
431 if (dwErrorCode != ERROR_SUCCESS)
432 {
433 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
434 goto Cleanup;
435 }
436
437 // Determine the size of the required buffer.
438 dwErrorCode = (DWORD)RegQueryValueExW(hWindowsKey, wszDeviceValue, NULL, NULL, NULL, &cbNeeded);
439 if (dwErrorCode != ERROR_SUCCESS)
440 {
441 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
442 goto Cleanup;
443 }
444
445 // Allocate it.
446 pwszDevice = HeapAlloc(hProcessHeap, 0, cbNeeded);
447 if (!pwszDevice)
448 {
449 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
450 ERR("HeapAlloc failed!\n");
451 goto Cleanup;
452 }
453
454 // Now get the actual value.
455 dwErrorCode = RegQueryValueExW(hWindowsKey, wszDeviceValue, NULL, NULL, (PBYTE)pwszDevice, &cbNeeded);
456 if (dwErrorCode != ERROR_SUCCESS)
457 {
458 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
459 goto Cleanup;
460 }
461
462 // We get a string "<Printer Name>,winspool,<Port>:".
463 // Extract the printer name from it.
464 pwszComma = wcschr(pwszDevice, L',');
465 if (!pwszComma)
466 {
467 ERR("Found no or invalid default printer: %S!\n", pwszDevice);
468 dwErrorCode = ERROR_INVALID_NAME;
469 goto Cleanup;
470 }
471
472 // Store the length of the Printer Name (including the terminating NUL character!) in *pcchBuffer.
473 *pcchBuffer = pwszComma - pwszDevice + 1;
474
475 // Check if the supplied buffer is large enough.
476 if (cchInputBuffer < *pcchBuffer)
477 {
478 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
479 goto Cleanup;
480 }
481
482 // Copy the default printer.
483 *pwszComma = 0;
484 CopyMemory(pszBuffer, pwszDevice, *pcchBuffer * sizeof(WCHAR));
485
486 dwErrorCode = ERROR_SUCCESS;
487
488 Cleanup:
489 if (hWindowsKey)
490 RegCloseKey(hWindowsKey);
491
492 if (pwszDevice)
493 HeapFree(hProcessHeap, 0, pwszDevice);
494
495 SetLastError(dwErrorCode);
496 return (dwErrorCode == ERROR_SUCCESS);
497 }
498
499 BOOL WINAPI
500 GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
501 {
502 TRACE("GetPrinterA(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
503 return FALSE;
504 }
505
506 BOOL WINAPI
507 GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
508 {
509 TRACE("GetPrinterDriverA(%p, %s, %lu, %p, %lu, %p)\n", hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
510 return FALSE;
511 }
512
513 BOOL WINAPI
514 GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
515 {
516 TRACE("GetPrinterDriverW(%p, %S, %lu, %p, %lu, %p)\n", hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
517 return FALSE;
518 }
519
520 BOOL WINAPI
521 GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
522 {
523 DWORD dwErrorCode;
524 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
525
526 TRACE("GetPrinterW(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
527
528 // Sanity checks.
529 if (!pHandle)
530 {
531 dwErrorCode = ERROR_INVALID_HANDLE;
532 goto Cleanup;
533 }
534
535 // Dismiss invalid levels already at this point.
536 if (Level > 9)
537 {
538 dwErrorCode = ERROR_INVALID_LEVEL;
539 goto Cleanup;
540 }
541
542 if (cbBuf && pPrinter)
543 ZeroMemory(pPrinter, cbBuf);
544
545 // Do the RPC call
546 RpcTryExcept
547 {
548 dwErrorCode = _RpcGetPrinter(pHandle->hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
549 }
550 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
551 {
552 dwErrorCode = RpcExceptionCode();
553 ERR("_RpcGetPrinter failed with exception code %lu!\n", dwErrorCode);
554 }
555 RpcEndExcept;
556
557 if (dwErrorCode == ERROR_SUCCESS)
558 {
559 // Replace relative offset addresses in the output by absolute pointers.
560 ASSERT(Level <= 9);
561 MarshallUpStructure(cbBuf, pPrinter, pPrinterInfoMarshalling[Level]->pInfo, pPrinterInfoMarshalling[Level]->cbStructureSize, TRUE);
562 }
563
564 Cleanup:
565 SetLastError(dwErrorCode);
566 return (dwErrorCode == ERROR_SUCCESS);
567 }
568
569 BOOL WINAPI
570 OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefault)
571 {
572 BOOL bReturnValue = FALSE;
573 DWORD cch;
574 PWSTR pwszPrinterName = NULL;
575 PRINTER_DEFAULTSW wDefault = { 0 };
576
577 TRACE("OpenPrinterA(%s, %p, %p)\n", pPrinterName, phPrinter, pDefault);
578
579 if (pPrinterName)
580 {
581 // Convert pPrinterName to a Unicode string pwszPrinterName
582 cch = strlen(pPrinterName);
583
584 pwszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
585 if (!pwszPrinterName)
586 {
587 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
588 ERR("HeapAlloc failed!\n");
589 goto Cleanup;
590 }
591
592 MultiByteToWideChar(CP_ACP, 0, pPrinterName, -1, pwszPrinterName, cch + 1);
593 }
594
595 if (pDefault)
596 {
597 wDefault.DesiredAccess = pDefault->DesiredAccess;
598
599 if (pDefault->pDatatype)
600 {
601 // Convert pDefault->pDatatype to a Unicode string wDefault.pDatatype
602 cch = strlen(pDefault->pDatatype);
603
604 wDefault.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
605 if (!wDefault.pDatatype)
606 {
607 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
608 ERR("HeapAlloc failed!\n");
609 goto Cleanup;
610 }
611
612 MultiByteToWideChar(CP_ACP, 0, pDefault->pDatatype, -1, wDefault.pDatatype, cch + 1);
613 }
614
615 if (pDefault->pDevMode)
616 wDefault.pDevMode = GdiConvertToDevmodeW(pDefault->pDevMode);
617 }
618
619 bReturnValue = OpenPrinterW(pwszPrinterName, phPrinter, &wDefault);
620
621 Cleanup:
622 if (wDefault.pDatatype)
623 HeapFree(hProcessHeap, 0, wDefault.pDatatype);
624
625 if (wDefault.pDevMode)
626 HeapFree(hProcessHeap, 0, wDefault.pDevMode);
627
628 if (pwszPrinterName)
629 HeapFree(hProcessHeap, 0, pwszPrinterName);
630
631 return bReturnValue;
632 }
633
634 BOOL WINAPI
635 OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefault)
636 {
637 DWORD dwErrorCode;
638 HANDLE hPrinter;
639 PSPOOLER_HANDLE pHandle;
640 PWSTR pDatatype = NULL;
641 WINSPOOL_DEVMODE_CONTAINER DevModeContainer = { 0 };
642 ACCESS_MASK AccessRequired = 0;
643
644 TRACE("OpenPrinterW(%S, %p, %p)\n", pPrinterName, phPrinter, pDefault);
645
646 // Sanity check
647 if (!phPrinter)
648 {
649 dwErrorCode = ERROR_INVALID_PARAMETER;
650 goto Cleanup;
651 }
652
653 // Prepare the additional parameters in the format required by _RpcOpenPrinter
654 if (pDefault)
655 {
656 pDatatype = pDefault->pDatatype;
657 DevModeContainer.cbBuf = sizeof(DEVMODEW);
658 DevModeContainer.pDevMode = (BYTE*)pDefault->pDevMode;
659 AccessRequired = pDefault->DesiredAccess;
660 }
661
662 // Do the RPC call
663 RpcTryExcept
664 {
665 dwErrorCode = _RpcOpenPrinter(pPrinterName, &hPrinter, pDatatype, &DevModeContainer, AccessRequired);
666 }
667 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
668 {
669 dwErrorCode = RpcExceptionCode();
670 ERR("_RpcOpenPrinter failed with exception code %lu!\n", dwErrorCode);
671 }
672 RpcEndExcept;
673
674 if (dwErrorCode == ERROR_SUCCESS)
675 {
676 // Create a new SPOOLER_HANDLE structure.
677 pHandle = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, sizeof(SPOOLER_HANDLE));
678 if (!pHandle)
679 {
680 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
681 ERR("HeapAlloc failed!\n");
682 goto Cleanup;
683 }
684
685 pHandle->hPrinter = hPrinter;
686 pHandle->hSPLFile = INVALID_HANDLE_VALUE;
687
688 // Return it as phPrinter.
689 *phPrinter = (HANDLE)pHandle;
690 }
691
692 Cleanup:
693 SetLastError(dwErrorCode);
694 return (dwErrorCode == ERROR_SUCCESS);
695 }
696
697 BOOL WINAPI
698 ReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
699 {
700 DWORD dwErrorCode;
701 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
702
703 TRACE("ReadPrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pNoBytesRead);
704
705 // Sanity checks.
706 if (!pHandle)
707 {
708 dwErrorCode = ERROR_INVALID_HANDLE;
709 goto Cleanup;
710 }
711
712 // Do the RPC call
713 RpcTryExcept
714 {
715 dwErrorCode = _RpcReadPrinter(pHandle->hPrinter, pBuf, cbBuf, pNoBytesRead);
716 }
717 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
718 {
719 dwErrorCode = RpcExceptionCode();
720 ERR("_RpcReadPrinter failed with exception code %lu!\n", dwErrorCode);
721 }
722 RpcEndExcept;
723
724 Cleanup:
725 SetLastError(dwErrorCode);
726 return (dwErrorCode == ERROR_SUCCESS);
727 }
728
729 BOOL WINAPI
730 ResetPrinterA(HANDLE hPrinter, PPRINTER_DEFAULTSA pDefault)
731 {
732 TRACE("ResetPrinterA(%p, %p)\n", hPrinter, pDefault);
733 UNIMPLEMENTED;
734 return FALSE;
735 }
736
737 BOOL WINAPI
738 ResetPrinterW(HANDLE hPrinter, PPRINTER_DEFAULTSW pDefault)
739 {
740 TRACE("ResetPrinterW(%p, %p)\n", hPrinter, pDefault);
741 UNIMPLEMENTED;
742 return FALSE;
743 }
744
745 BOOL WINAPI
746 SetDefaultPrinterA(LPCSTR pszPrinter)
747 {
748 BOOL bReturnValue = FALSE;
749 DWORD cch;
750 PWSTR pwszPrinter = NULL;
751
752 TRACE("SetDefaultPrinterA(%s)\n", pszPrinter);
753
754 if (pszPrinter)
755 {
756 // Convert pszPrinter to a Unicode string pwszPrinter
757 cch = strlen(pszPrinter);
758
759 pwszPrinter = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
760 if (!pwszPrinter)
761 {
762 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
763 ERR("HeapAlloc failed!\n");
764 goto Cleanup;
765 }
766
767 MultiByteToWideChar(CP_ACP, 0, pszPrinter, -1, pwszPrinter, cch + 1);
768 }
769
770 bReturnValue = SetDefaultPrinterW(pwszPrinter);
771
772 Cleanup:
773 if (pwszPrinter)
774 HeapFree(hProcessHeap, 0, pwszPrinter);
775
776 return bReturnValue;
777 }
778
779 BOOL WINAPI
780 SetDefaultPrinterW(LPCWSTR pszPrinter)
781 {
782 const WCHAR wszDevicesKey[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Devices";
783
784 DWORD cbDeviceValueData;
785 DWORD cbPrinterValueData = 0;
786 DWORD cchPrinter;
787 DWORD dwErrorCode;
788 HKEY hDevicesKey = NULL;
789 HKEY hWindowsKey = NULL;
790 PWSTR pwszDeviceValueData = NULL;
791 WCHAR wszPrinter[MAX_PRINTER_NAME + 1];
792
793 TRACE("SetDefaultPrinterW(%S)\n", pszPrinter);
794
795 // Open the Devices registry key.
796 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszDevicesKey, 0, KEY_READ, &hDevicesKey);
797 if (dwErrorCode != ERROR_SUCCESS)
798 {
799 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
800 goto Cleanup;
801 }
802
803 // Did the caller give us a printer to set as default?
804 if (pszPrinter && *pszPrinter)
805 {
806 // Check if the given printer exists and query the value data size.
807 dwErrorCode = (DWORD)RegQueryValueExW(hDevicesKey, pszPrinter, NULL, NULL, NULL, &cbPrinterValueData);
808 if (dwErrorCode == ERROR_FILE_NOT_FOUND)
809 {
810 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
811 goto Cleanup;
812 }
813 else if (dwErrorCode != ERROR_SUCCESS)
814 {
815 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
816 goto Cleanup;
817 }
818
819 cchPrinter = wcslen(pszPrinter);
820 }
821 else
822 {
823 // If there is already a default printer, we're done!
824 cchPrinter = _countof(wszPrinter);
825 if (GetDefaultPrinterW(wszPrinter, &cchPrinter))
826 {
827 dwErrorCode = ERROR_SUCCESS;
828 goto Cleanup;
829 }
830
831 // Otherwise, get us the first printer from the "Devices" key to later set it as default and query the value data size.
832 cchPrinter = _countof(wszPrinter);
833 dwErrorCode = (DWORD)RegEnumValueW(hDevicesKey, 0, wszPrinter, &cchPrinter, NULL, NULL, NULL, &cbPrinterValueData);
834 if (dwErrorCode != ERROR_MORE_DATA)
835 goto Cleanup;
836
837 pszPrinter = wszPrinter;
838 }
839
840 // We now need to query the value data, which has the format "winspool,<Port>:"
841 // and make "<Printer Name>,winspool,<Port>:" out of it.
842 // Allocate a buffer large enough for the final data.
843 cbDeviceValueData = (cchPrinter + 1) * sizeof(WCHAR) + cbPrinterValueData;
844 pwszDeviceValueData = HeapAlloc(hProcessHeap, 0, cbDeviceValueData);
845 if (!pwszDeviceValueData)
846 {
847 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
848 ERR("HeapAlloc failed!\n");
849 goto Cleanup;
850 }
851
852 // Copy the Printer Name and a comma into it.
853 CopyMemory(pwszDeviceValueData, pszPrinter, cchPrinter * sizeof(WCHAR));
854 pwszDeviceValueData[cchPrinter] = L',';
855
856 // Append the value data, which has the format "winspool,<Port>:"
857 dwErrorCode = (DWORD)RegQueryValueExW(hDevicesKey, pszPrinter, NULL, NULL, (PBYTE)&pwszDeviceValueData[cchPrinter + 1], &cbPrinterValueData);
858 if (dwErrorCode != ERROR_SUCCESS)
859 goto Cleanup;
860
861 // Open the Windows registry key.
862 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszWindowsKey, 0, KEY_SET_VALUE, &hWindowsKey);
863 if (dwErrorCode != ERROR_SUCCESS)
864 {
865 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
866 goto Cleanup;
867 }
868
869 // Store our new default printer.
870 dwErrorCode = (DWORD)RegSetValueExW(hWindowsKey, wszDeviceValue, 0, REG_SZ, (PBYTE)pwszDeviceValueData, cbDeviceValueData);
871 if (dwErrorCode != ERROR_SUCCESS)
872 {
873 ERR("RegSetValueExW failed with status %lu!\n", dwErrorCode);
874 goto Cleanup;
875 }
876
877 Cleanup:
878 if (hDevicesKey)
879 RegCloseKey(hDevicesKey);
880
881 if (hWindowsKey)
882 RegCloseKey(hWindowsKey);
883
884 if (pwszDeviceValueData)
885 HeapFree(hProcessHeap, 0, pwszDeviceValueData);
886
887 SetLastError(dwErrorCode);
888 return (dwErrorCode == ERROR_SUCCESS);
889 }
890
891 BOOL WINAPI
892 SetPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD Command)
893 {
894 TRACE("SetPrinterA(%p, %lu, %p, %lu)\n", hPrinter, Level, pPrinter, Command);
895 UNIMPLEMENTED;
896 return FALSE;
897 }
898
899 BOOL WINAPI
900 SetPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD Command)
901 {
902 TRACE("SetPrinterW(%p, %lu, %p, %lu)\n", hPrinter, Level, pPrinter, Command);
903 UNIMPLEMENTED;
904 return FALSE;
905 }
906
907 DWORD WINAPI
908 StartDocPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
909 {
910 DOC_INFO_1W wDocInfo1 = { 0 };
911 DWORD cch;
912 DWORD dwErrorCode;
913 DWORD dwReturnValue = 0;
914 PDOC_INFO_1A pDocInfo1 = (PDOC_INFO_1A)pDocInfo;
915
916 TRACE("StartDocPrinterA(%p, %lu, %p)\n", hPrinter, Level, pDocInfo);
917
918 // Only check the minimum required for accessing pDocInfo.
919 // Additional sanity checks are done in StartDocPrinterW.
920 if (!pDocInfo1)
921 {
922 dwErrorCode = ERROR_INVALID_PARAMETER;
923 goto Cleanup;
924 }
925
926 if (Level != 1)
927 {
928 dwErrorCode = ERROR_INVALID_LEVEL;
929 goto Cleanup;
930 }
931
932 if (pDocInfo1->pDatatype)
933 {
934 // Convert pDocInfo1->pDatatype to a Unicode string wDocInfo1.pDatatype
935 cch = strlen(pDocInfo1->pDatatype);
936
937 wDocInfo1.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
938 if (!wDocInfo1.pDatatype)
939 {
940 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
941 ERR("HeapAlloc failed!\n");
942 goto Cleanup;
943 }
944
945 MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDatatype, -1, wDocInfo1.pDatatype, cch + 1);
946 }
947
948 if (pDocInfo1->pDocName)
949 {
950 // Convert pDocInfo1->pDocName to a Unicode string wDocInfo1.pDocName
951 cch = strlen(pDocInfo1->pDocName);
952
953 wDocInfo1.pDocName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
954 if (!wDocInfo1.pDocName)
955 {
956 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
957 ERR("HeapAlloc failed!\n");
958 goto Cleanup;
959 }
960
961 MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDocName, -1, wDocInfo1.pDocName, cch + 1);
962 }
963
964 if (pDocInfo1->pOutputFile)
965 {
966 // Convert pDocInfo1->pOutputFile to a Unicode string wDocInfo1.pOutputFile
967 cch = strlen(pDocInfo1->pOutputFile);
968
969 wDocInfo1.pOutputFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
970 if (!wDocInfo1.pOutputFile)
971 {
972 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
973 ERR("HeapAlloc failed!\n");
974 goto Cleanup;
975 }
976
977 MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pOutputFile, -1, wDocInfo1.pOutputFile, cch + 1);
978 }
979
980 dwReturnValue = StartDocPrinterW(hPrinter, Level, (PBYTE)&wDocInfo1);
981 dwErrorCode = GetLastError();
982
983 Cleanup:
984 if (wDocInfo1.pDatatype)
985 HeapFree(hProcessHeap, 0, wDocInfo1.pDatatype);
986
987 if (wDocInfo1.pDocName)
988 HeapFree(hProcessHeap, 0, wDocInfo1.pDocName);
989
990 if (wDocInfo1.pOutputFile)
991 HeapFree(hProcessHeap, 0, wDocInfo1.pOutputFile);
992
993 SetLastError(dwErrorCode);
994 return dwReturnValue;
995 }
996
997 DWORD WINAPI
998 StartDocPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
999 {
1000 DWORD cbAddJobInfo1;
1001 DWORD cbNeeded;
1002 DWORD dwErrorCode;
1003 DWORD dwReturnValue = 0;
1004 PADDJOB_INFO_1W pAddJobInfo1 = NULL;
1005 PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
1006 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
1007
1008 TRACE("StartDocPrinterW(%p, %lu, %p)\n", hPrinter, Level, pDocInfo);
1009
1010 // Sanity checks.
1011 if (!pHandle)
1012 {
1013 dwErrorCode = ERROR_INVALID_HANDLE;
1014 goto Cleanup;
1015 }
1016
1017 if (!pDocInfo1)
1018 {
1019 dwErrorCode = ERROR_INVALID_PARAMETER;
1020 goto Cleanup;
1021 }
1022
1023 if (Level != 1)
1024 {
1025 dwErrorCode = ERROR_INVALID_LEVEL;
1026 goto Cleanup;
1027 }
1028
1029 if (pHandle->bStartedDoc)
1030 {
1031 dwErrorCode = ERROR_INVALID_PRINTER_STATE;
1032 goto Cleanup;
1033 }
1034
1035 // Check if we want to redirect output into a file.
1036 if (pDocInfo1->pOutputFile)
1037 {
1038 // Do a StartDocPrinter RPC call in this case.
1039 dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
1040 }
1041 else
1042 {
1043 // Allocate memory for the ADDJOB_INFO_1W structure and a path.
1044 cbAddJobInfo1 = sizeof(ADDJOB_INFO_1W) + MAX_PATH * sizeof(WCHAR);
1045 pAddJobInfo1 = HeapAlloc(hProcessHeap, 0, cbAddJobInfo1);
1046 if (!pAddJobInfo1)
1047 {
1048 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1049 ERR("HeapAlloc failed!\n");
1050 goto Cleanup;
1051 }
1052
1053 // Try to add a new job.
1054 // This only succeeds if the printer is set to do spooled printing.
1055 if (AddJobW((HANDLE)pHandle, 1, (PBYTE)pAddJobInfo1, cbAddJobInfo1, &cbNeeded))
1056 {
1057 // Do spooled printing.
1058 dwErrorCode = _StartDocPrinterSpooled(pHandle, pDocInfo1, pAddJobInfo1);
1059 }
1060 else if (GetLastError() == ERROR_INVALID_ACCESS)
1061 {
1062 // ERROR_INVALID_ACCESS is returned when the printer is set to do direct printing.
1063 // In this case, we do a StartDocPrinter RPC call.
1064 dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
1065 }
1066 else
1067 {
1068 dwErrorCode = GetLastError();
1069 ERR("AddJobW failed with error %lu!\n", dwErrorCode);
1070 goto Cleanup;
1071 }
1072 }
1073
1074 if (dwErrorCode == ERROR_SUCCESS)
1075 {
1076 pHandle->bStartedDoc = TRUE;
1077 dwReturnValue = pHandle->dwJobID;
1078 }
1079
1080 Cleanup:
1081 if (pAddJobInfo1)
1082 HeapFree(hProcessHeap, 0, pAddJobInfo1);
1083
1084 SetLastError(dwErrorCode);
1085 return dwReturnValue;
1086 }
1087
1088 BOOL WINAPI
1089 StartPagePrinter(HANDLE hPrinter)
1090 {
1091 DWORD dwErrorCode;
1092 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
1093
1094 TRACE("StartPagePrinter(%p)\n", hPrinter);
1095
1096 // Sanity checks.
1097 if (!pHandle)
1098 {
1099 dwErrorCode = ERROR_INVALID_HANDLE;
1100 goto Cleanup;
1101 }
1102
1103 // Do the RPC call
1104 RpcTryExcept
1105 {
1106 dwErrorCode = _RpcStartPagePrinter(pHandle->hPrinter);
1107 }
1108 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1109 {
1110 dwErrorCode = RpcExceptionCode();
1111 ERR("_RpcStartPagePrinter failed with exception code %lu!\n", dwErrorCode);
1112 }
1113 RpcEndExcept;
1114
1115 Cleanup:
1116 SetLastError(dwErrorCode);
1117 return (dwErrorCode == ERROR_SUCCESS);
1118 }
1119
1120 BOOL WINAPI
1121 WritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
1122 {
1123 DWORD dwErrorCode;
1124 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
1125
1126 TRACE("WritePrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
1127
1128 // Sanity checks.
1129 if (!pHandle)
1130 {
1131 dwErrorCode = ERROR_INVALID_HANDLE;
1132 goto Cleanup;
1133 }
1134
1135 if (!pHandle->bStartedDoc)
1136 {
1137 dwErrorCode = ERROR_SPL_NO_STARTDOC;
1138 goto Cleanup;
1139 }
1140
1141 if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
1142 {
1143 // Write to the spool file. This doesn't need an RPC request.
1144 if (!WriteFile(pHandle->hSPLFile, pBuf, cbBuf, pcWritten, NULL))
1145 {
1146 dwErrorCode = GetLastError();
1147 ERR("WriteFile failed with error %lu!\n", dwErrorCode);
1148 goto Cleanup;
1149 }
1150
1151 dwErrorCode = ERROR_SUCCESS;
1152 }
1153 else
1154 {
1155 // TODO: This case (for direct printing or remote printing) has bad performance if multiple small-sized WritePrinter calls are performed.
1156 // We may increase performance by writing into a buffer and only doing a single RPC call when the buffer is full.
1157
1158 // Do the RPC call
1159 RpcTryExcept
1160 {
1161 dwErrorCode = _RpcWritePrinter(pHandle->hPrinter, pBuf, cbBuf, pcWritten);
1162 }
1163 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1164 {
1165 dwErrorCode = RpcExceptionCode();
1166 ERR("_RpcWritePrinter failed with exception code %lu!\n", dwErrorCode);
1167 }
1168 RpcEndExcept;
1169 }
1170
1171 Cleanup:
1172 SetLastError(dwErrorCode);
1173 return (dwErrorCode == ERROR_SUCCESS);
1174 }
1175
1176 BOOL WINAPI
1177 XcvDataW(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded, PDWORD pdwStatus)
1178 {
1179 TRACE("XcvDataW(%p, %S, %p, %lu, %p, %lu, %p, %p)\n", hXcv, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded, pdwStatus);
1180 return FALSE;
1181 }