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