7fd34a4086630c3fb57f4ed870ea063fc9d4b412
[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 #include <strsafe.h>
12
13 // Local Constants
14
15 /** And the award for the most confusingly named setting goes to "Device", for storing the default printer of the current user.
16 Ok, I admit that this has historical reasons. It's still not straightforward in any way though! */
17 static const WCHAR wszWindowsKey[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows";
18 static const WCHAR wszDeviceValue[] = L"Device";
19
20 static DWORD
21 _StartDocPrinterSpooled(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1, PADDJOB_INFO_1W pAddJobInfo1)
22 {
23 DWORD cbNeeded;
24 DWORD dwErrorCode;
25 PJOB_INFO_1W pJobInfo1 = NULL;
26
27 // Create the spool file.
28 pHandle->hSPLFile = CreateFileW(pAddJobInfo1->Path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
29 if (pHandle->hSPLFile == INVALID_HANDLE_VALUE)
30 {
31 dwErrorCode = GetLastError();
32 ERR("CreateFileW failed for \"%S\" with error %lu!\n", pAddJobInfo1->Path, dwErrorCode);
33 goto Cleanup;
34 }
35
36 // Get the size of the job information.
37 GetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, NULL, 0, &cbNeeded);
38 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
39 {
40 dwErrorCode = GetLastError();
41 ERR("GetJobW failed with error %lu!\n", dwErrorCode);
42 goto Cleanup;
43 }
44
45 // Allocate enough memory for the returned job information.
46 pJobInfo1 = HeapAlloc(hProcessHeap, 0, cbNeeded);
47 if (!pJobInfo1)
48 {
49 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
50 ERR("HeapAlloc failed!\n");
51 goto Cleanup;
52 }
53
54 // Get the job information.
55 if (!GetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, cbNeeded, &cbNeeded))
56 {
57 dwErrorCode = GetLastError();
58 ERR("GetJobW failed with error %lu!\n", dwErrorCode);
59 goto Cleanup;
60 }
61
62 // Add our document information.
63 if (pDocInfo1->pDatatype)
64 pJobInfo1->pDatatype = pDocInfo1->pDatatype;
65
66 pJobInfo1->pDocument = pDocInfo1->pDocName;
67
68 // Set the new job information.
69 if (!SetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, 0))
70 {
71 dwErrorCode = GetLastError();
72 ERR("SetJobW failed with error %lu!\n", dwErrorCode);
73 goto Cleanup;
74 }
75
76 // We were successful!
77 pHandle->dwJobID = pAddJobInfo1->JobId;
78 dwErrorCode = ERROR_SUCCESS;
79
80 Cleanup:
81 if (pJobInfo1)
82 HeapFree(hProcessHeap, 0, pJobInfo1);
83
84 return dwErrorCode;
85 }
86
87 static DWORD
88 _StartDocPrinterWithRPC(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1)
89 {
90 DWORD dwErrorCode;
91 WINSPOOL_DOC_INFO_CONTAINER DocInfoContainer;
92
93 DocInfoContainer.Level = 1;
94 DocInfoContainer.DocInfo.pDocInfo1 = (WINSPOOL_DOC_INFO_1*)pDocInfo1;
95
96 RpcTryExcept
97 {
98 dwErrorCode = _RpcStartDocPrinter(pHandle->hPrinter, &DocInfoContainer, &pHandle->dwJobID);
99 }
100 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
101 {
102 dwErrorCode = RpcExceptionCode();
103 ERR("_RpcStartDocPrinter failed with exception code %lu!\n", dwErrorCode);
104 }
105 RpcEndExcept;
106
107 return dwErrorCode;
108 }
109
110 BOOL WINAPI
111 AbortPrinter(HANDLE hPrinter)
112 {
113 TRACE("AbortPrinter(%p)\n", hPrinter);
114 UNIMPLEMENTED;
115 return FALSE;
116 }
117
118 HANDLE WINAPI
119 AddPrinterA(PSTR pName, DWORD Level, PBYTE pPrinter)
120 {
121 TRACE("AddPrinterA(%s, %lu, %p)\n", pName, Level, pPrinter);
122 UNIMPLEMENTED;
123 return NULL;
124 }
125
126 HANDLE WINAPI
127 AddPrinterW(PWSTR pName, DWORD Level, PBYTE pPrinter)
128 {
129 TRACE("AddPrinterW(%S, %lu, %p)\n", pName, Level, pPrinter);
130 UNIMPLEMENTED;
131 return NULL;
132 }
133
134 BOOL WINAPI
135 ClosePrinter(HANDLE hPrinter)
136 {
137 DWORD dwErrorCode;
138 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
139
140 TRACE("ClosePrinter(%p)\n", hPrinter);
141
142 // Sanity checks.
143 if (!pHandle)
144 {
145 dwErrorCode = ERROR_INVALID_HANDLE;
146 goto Cleanup;
147 }
148
149 // Do the RPC call.
150 RpcTryExcept
151 {
152 dwErrorCode = _RpcClosePrinter(&pHandle->hPrinter);
153 }
154 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
155 {
156 dwErrorCode = RpcExceptionCode();
157 ERR("_RpcClosePrinter failed with exception code %lu!\n", dwErrorCode);
158 }
159 RpcEndExcept;
160
161 // Close any open file handle.
162 if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
163 CloseHandle(pHandle->hSPLFile);
164
165 // Free the memory for the handle.
166 HeapFree(hProcessHeap, 0, pHandle);
167
168 Cleanup:
169 SetLastError(dwErrorCode);
170 return (dwErrorCode == ERROR_SUCCESS);
171 }
172
173 BOOL WINAPI
174 DeletePrinter(HANDLE hPrinter)
175 {
176 TRACE("DeletePrinter(%p)\n", hPrinter);
177 UNIMPLEMENTED;
178 return FALSE;
179 }
180
181 DWORD WINAPI
182 DeviceCapabilitiesA(LPCSTR pDevice, LPCSTR pPort, WORD fwCapability, LPSTR pOutput, const DEVMODEA* pDevMode)
183 {
184 TRACE("DeviceCapabilitiesA(%s, %s, %hu, %p, %p)\n", pDevice, pPort, fwCapability, pOutput, pDevMode);
185 UNIMPLEMENTED;
186 return 0;
187 }
188
189 DWORD WINAPI
190 DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort, WORD fwCapability, LPWSTR pOutput, const DEVMODEW* pDevMode)
191 {
192 TRACE("DeviceCapabilitiesW(%S, %S, %hu, %p, %p)\n", pDevice, pPort, fwCapability, pOutput, pDevMode);
193 UNIMPLEMENTED;
194 return 0;
195 }
196
197 LONG WINAPI
198 DocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName, PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput, DWORD fMode)
199 {
200 TRACE("DocumentPropertiesA(%p, %p, %s, %p, %p, %lu)\n", hWnd, hPrinter, pDeviceName, pDevModeOutput, pDevModeInput, fMode);
201 UNIMPLEMENTED;
202 return -1;
203 }
204
205 static PRINTER_INFO_9W * get_devmodeW(HANDLE hprn)
206 {
207 PRINTER_INFO_9W *pi9 = NULL;
208 DWORD needed = 0;
209 BOOL res;
210
211 res = GetPrinterW(hprn, 9, NULL, 0, &needed);
212 if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
213 {
214 pi9 = HeapAlloc(hProcessHeap, 0, needed);
215 res = GetPrinterW(hprn, 9, (LPBYTE)pi9, needed, &needed);
216 }
217
218 if (res)
219 return pi9;
220
221 ERR("GetPrinterW failed with %u\n", GetLastError());
222 HeapFree(hProcessHeap, 0, pi9);
223 return NULL;
224 }
225
226 LONG WINAPI
227 DocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName, PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput, DWORD fMode)
228 {
229 HANDLE hUseHandle = NULL;
230 PRINTER_INFO_9W *pi9 = NULL;
231 LONG Result = -1, Length;
232
233 TRACE("DocumentPropertiesW(%p, %p, %S, %p, %p, %lu)\n", hWnd, hPrinter, pDeviceName, pDevModeOutput, pDevModeInput, fMode);
234 if (hPrinter)
235 {
236 hUseHandle = hPrinter;
237 }
238 else if (!OpenPrinterW(pDeviceName, &hUseHandle, NULL))
239 {
240 ERR("No handle, and no usable printer name passed in\n");
241 return -1;
242 }
243
244 pi9 = get_devmodeW(hUseHandle);
245
246 if (pi9)
247 {
248 Length = pi9->pDevMode->dmSize + pi9->pDevMode->dmDriverExtra;
249 // See wineps.drv PSDRV_ExtDeviceMode
250 if (fMode)
251 {
252 Result = 1; /* IDOK */
253
254 if (fMode & DM_IN_BUFFER)
255 {
256 FIXME("Merge pDevModeInput with pi9, write back to driver!\n");
257 // See wineps.drv PSDRV_MergeDevmodes
258 }
259
260 if (fMode & DM_IN_PROMPT)
261 {
262 FIXME("Show property sheet!\n");
263 Result = 2; /* IDCANCEL */
264 }
265
266 if (fMode & (DM_OUT_BUFFER | DM_OUT_DEFAULT))
267 {
268 if (pDevModeOutput)
269 {
270 memcpy(pDevModeOutput, pi9->pDevMode, pi9->pDevMode->dmSize + pi9->pDevMode->dmDriverExtra);
271 }
272 else
273 {
274 ERR("No pDevModeOutput\n");
275 Result = -1;
276 }
277 }
278 }
279 else
280 {
281 Result = Length;
282 }
283
284 HeapFree(hProcessHeap, 0, pi9);
285 }
286
287 if (hUseHandle && !hPrinter)
288 ClosePrinter(hUseHandle);
289 return Result;
290 }
291
292 BOOL WINAPI
293 EndDocPrinter(HANDLE hPrinter)
294 {
295 DWORD dwErrorCode;
296 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
297
298 TRACE("EndDocPrinter(%p)\n", hPrinter);
299
300 // Sanity checks.
301 if (!pHandle)
302 {
303 dwErrorCode = ERROR_INVALID_HANDLE;
304 goto Cleanup;
305 }
306
307 if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
308 {
309 // For spooled jobs, the document is finished by calling _RpcScheduleJob.
310 RpcTryExcept
311 {
312 dwErrorCode = _RpcScheduleJob(pHandle->hPrinter, pHandle->dwJobID);
313 }
314 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
315 {
316 dwErrorCode = RpcExceptionCode();
317 ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode);
318 }
319 RpcEndExcept;
320
321 // Close the spool file handle.
322 CloseHandle(pHandle->hSPLFile);
323 }
324 else
325 {
326 // In all other cases, just call _RpcEndDocPrinter.
327 RpcTryExcept
328 {
329 dwErrorCode = _RpcEndDocPrinter(pHandle->hPrinter);
330 }
331 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
332 {
333 dwErrorCode = RpcExceptionCode();
334 ERR("_RpcEndDocPrinter failed with exception code %lu!\n", dwErrorCode);
335 }
336 RpcEndExcept;
337 }
338
339 // A new document can now be started again.
340 pHandle->bStartedDoc = FALSE;
341
342 Cleanup:
343 SetLastError(dwErrorCode);
344 return (dwErrorCode == ERROR_SUCCESS);
345 }
346
347 BOOL WINAPI
348 EndPagePrinter(HANDLE hPrinter)
349 {
350 DWORD dwErrorCode;
351 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
352
353 TRACE("EndPagePrinter(%p)\n", hPrinter);
354
355 // Sanity checks.
356 if (!pHandle)
357 {
358 dwErrorCode = ERROR_INVALID_HANDLE;
359 goto Cleanup;
360 }
361
362 if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
363 {
364 // For spooled jobs, we don't need to do anything.
365 dwErrorCode = ERROR_SUCCESS;
366 }
367 else
368 {
369 // In all other cases, just call _RpcEndPagePrinter.
370 RpcTryExcept
371 {
372 dwErrorCode = _RpcEndPagePrinter(pHandle->hPrinter);
373 }
374 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
375 {
376 dwErrorCode = RpcExceptionCode();
377 ERR("_RpcEndPagePrinter failed with exception code %lu!\n", dwErrorCode);
378 }
379 RpcEndExcept;
380 }
381
382 Cleanup:
383 SetLastError(dwErrorCode);
384 return (dwErrorCode == ERROR_SUCCESS);
385 }
386
387 BOOL WINAPI
388 EnumPrintersA(DWORD Flags, PSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
389 {
390 BOOL bReturnValue = FALSE;
391 DWORD cch;
392 PWSTR pwszName = NULL;
393 PSTR pszPrinterName = NULL;
394 PSTR pszServerName = NULL;
395 PSTR pszDescription = NULL;
396 PSTR pszName = NULL;
397 PSTR pszComment = NULL;
398 PSTR pszShareName = NULL;
399 PSTR pszPortName = NULL;
400 PSTR pszDriverName = NULL;
401 PSTR pszLocation = NULL;
402 PSTR pszSepFile = NULL;
403 PSTR pszPrintProcessor = NULL;
404 PSTR pszDatatype = NULL;
405 PSTR pszParameters = NULL;
406 DWORD i;
407 PPRINTER_INFO_1W ppi1w = NULL;
408 PPRINTER_INFO_1A ppi1a = NULL;
409 PPRINTER_INFO_2W ppi2w = NULL;
410 PPRINTER_INFO_2A ppi2a = NULL;
411 PPRINTER_INFO_4W ppi4w = NULL;
412 PPRINTER_INFO_4A ppi4a = NULL;
413 PPRINTER_INFO_5W ppi5w = NULL;
414 PPRINTER_INFO_5A ppi5a = NULL;
415
416 TRACE("EnumPrintersA(%lu, %s, %lu, %p, %lu, %p, %p)\n", Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
417
418 // Check for invalid levels here for early error return. MSDN says that only 1, 2, 4, and 5 are allowable.
419 if (Level != 1 && Level != 2 && Level != 4 && Level != 5)
420 {
421 SetLastError(ERROR_INVALID_LEVEL);
422 ERR("Invalid Level!\n");
423 goto Cleanup;
424 }
425
426 if (Name)
427 {
428 // Convert pName to a Unicode string pwszName.
429 cch = strlen(Name);
430
431 pwszName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
432 if (!pwszName)
433 {
434 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
435 ERR("HeapAlloc failed!\n");
436 goto Cleanup;
437 }
438
439 MultiByteToWideChar(CP_ACP, 0, Name, -1, pwszName, cch + 1);
440 }
441
442 /* Ref: https://stackoverflow.com/questions/41147180/why-enumprintersa-and-enumprintersw-request-the-same-amount-of-memory */
443 bReturnValue = EnumPrintersW(Flags, pwszName, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
444 HeapFree(hProcessHeap, 0, pwszName);
445
446 TRACE("*pcReturned is '%d' and bReturnValue is '%d' and GetLastError is '%ld'.\n", *pcReturned, bReturnValue, GetLastError());
447
448 /* We are mapping multiple different pointers to the same pPrinterEnum pointer here so that */
449 /* we can do in-place conversion. We read the Unicode response from the EnumPrintersW and */
450 /* then we write back the ANSI conversion into the same buffer for our EnumPrintersA output */
451
452 /* mapping to pPrinterEnum for Unicode (w) characters for Levels 1, 2, 4, and 5 */
453 ppi1w = (PPRINTER_INFO_1W)pPrinterEnum;
454 ppi2w = (PPRINTER_INFO_2W)pPrinterEnum;
455 ppi4w = (PPRINTER_INFO_4W)pPrinterEnum;
456 ppi5w = (PPRINTER_INFO_5W)pPrinterEnum;
457 /* mapping to pPrinterEnum for ANSI (a) characters for Levels 1, 2, 4, and 5 */
458 ppi1a = (PPRINTER_INFO_1A)pPrinterEnum;
459 ppi2a = (PPRINTER_INFO_2A)pPrinterEnum;
460 ppi4a = (PPRINTER_INFO_4A)pPrinterEnum;
461 ppi5a = (PPRINTER_INFO_5A)pPrinterEnum;
462
463 for (i = 0; i < *pcReturned; i++)
464 {
465 switch (Level)
466 {
467 case 1:
468 {
469 if (ppi1w[i].pDescription)
470 {
471 // Convert Unicode pDescription to a ANSI string pszDescription.
472 cch = wcslen(ppi1w[i].pDescription);
473
474 pszDescription = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
475 if (!pszDescription)
476 {
477 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
478 ERR("HeapAlloc failed!\n");
479 goto Cleanup;
480 }
481
482 WideCharToMultiByte(CP_ACP, 0, ppi1w[i].pDescription, -1, pszDescription, cch + 1, NULL, NULL);
483 StringCchCopyA(ppi1a[i].pDescription, cch + 1, pszDescription);
484
485 HeapFree(hProcessHeap, 0, pszDescription);
486 }
487
488 if (ppi1w[i].pName)
489 {
490 // Convert Unicode pName to a ANSI string pszName.
491 cch = wcslen(ppi1w[i].pName);
492
493 pszName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
494 if (!pszName)
495 {
496 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
497 ERR("HeapAlloc failed!\n");
498 goto Cleanup;
499 }
500
501 WideCharToMultiByte(CP_ACP, 0, ppi1w[i].pName, -1, pszName, cch + 1, NULL, NULL);
502 StringCchCopyA(ppi1a[i].pName, cch + 1, pszName);
503
504 HeapFree(hProcessHeap, 0, pszName);
505 }
506
507 if (ppi1w[i].pComment)
508 {
509 // Convert Unicode pComment to a ANSI string pszComment.
510 cch = wcslen(ppi1w[i].pComment);
511
512 pszComment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
513 if (!pszComment)
514 {
515 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
516 ERR("HeapAlloc failed!\n");
517 goto Cleanup;
518 }
519
520 WideCharToMultiByte(CP_ACP, 0, ppi1w[i].pComment, -1, pszComment, cch + 1, NULL, NULL);
521 StringCchCopyA(ppi1a[i].pComment, cch + 1, pszComment);
522
523 HeapFree(hProcessHeap, 0, pszComment);
524 }
525 break;
526 }
527
528
529 case 2:
530 {
531 if (ppi2w[i].pServerName)
532 {
533 // Convert Unicode pServerName to a ANSI string pszServerName.
534 cch = wcslen(ppi2w[i].pServerName);
535
536 pszServerName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
537 if (!pszServerName)
538 {
539 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
540 ERR("HeapAlloc failed!\n");
541 goto Cleanup;
542 }
543
544 WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pServerName, -1, pszServerName, cch + 1, NULL, NULL);
545 StringCchCopyA(ppi2a[i].pServerName, cch + 1, pszServerName);
546
547 HeapFree(hProcessHeap, 0, pszServerName);
548 }
549
550 if (ppi2w[i].pPrinterName)
551 {
552 // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
553 cch = wcslen(ppi2w[i].pPrinterName);
554
555 pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
556 if (!pszPrinterName)
557 {
558 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
559 ERR("HeapAlloc failed!\n");
560 goto Cleanup;
561 }
562
563 WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
564 StringCchCopyA(ppi2a[i].pPrinterName, cch + 1, pszPrinterName);
565
566 HeapFree(hProcessHeap, 0, pszPrinterName);
567 }
568
569 if (ppi2w[i].pShareName)
570 {
571 // Convert Unicode pShareName to a ANSI string pszShareName.
572 cch = wcslen(ppi2w[i].pShareName);
573
574 pszShareName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
575 if (!pszShareName)
576 {
577 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
578 ERR("HeapAlloc failed!\n");
579 goto Cleanup;
580 }
581
582 WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pShareName, -1, pszShareName, cch + 1, NULL, NULL);
583 StringCchCopyA(ppi2a[i].pShareName, cch + 1, pszShareName);
584
585 HeapFree(hProcessHeap, 0, pszShareName);
586 }
587
588 if (ppi2w[i].pPortName)
589 {
590 // Convert Unicode pPortName to a ANSI string pszPortName.
591 cch = wcslen(ppi2w[i].pPortName);
592
593 pszPortName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
594 if (!pszPortName)
595 {
596 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
597 ERR("HeapAlloc failed!\n");
598 goto Cleanup;
599 }
600
601 WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pPortName, -1, pszPortName, cch + 1, NULL, NULL);
602 StringCchCopyA(ppi2a[i].pPortName, cch + 1, pszPortName);
603
604 HeapFree(hProcessHeap, 0, pszPortName);
605 }
606
607 if (ppi2w[i].pDriverName)
608 {
609 // Convert Unicode pDriverName to a ANSI string pszDriverName.
610 cch = wcslen(ppi2w[i].pDriverName);
611
612 pszDriverName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
613 if (!pszDriverName)
614 {
615 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
616 ERR("HeapAlloc failed!\n");
617 goto Cleanup;
618 }
619
620 WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pDriverName, -1, pszDriverName, cch + 1, NULL, NULL);
621 StringCchCopyA(ppi2a[i].pDriverName, cch + 1, pszDriverName);
622
623 HeapFree(hProcessHeap, 0, pszDriverName);
624 }
625
626 if (ppi2w[i].pComment)
627 {
628 // Convert Unicode pComment to a ANSI string pszComment.
629 cch = wcslen(ppi2w[i].pComment);
630
631 pszComment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
632 if (!pszComment)
633 {
634 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
635 ERR("HeapAlloc failed!\n");
636 goto Cleanup;
637 }
638
639 WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pComment, -1, pszComment, cch + 1, NULL, NULL);
640 StringCchCopyA(ppi2a[i].pComment, cch + 1, pszComment);
641
642 HeapFree(hProcessHeap, 0, pszComment);
643 }
644
645 if (ppi2w[i].pLocation)
646 {
647 // Convert Unicode pLocation to a ANSI string pszLocation.
648 cch = wcslen(ppi2w[i].pLocation);
649
650 pszLocation = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
651 if (!pszLocation)
652 {
653 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
654 ERR("HeapAlloc failed!\n");
655 goto Cleanup;
656 }
657
658 WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pLocation, -1, pszLocation, cch + 1, NULL, NULL);
659 StringCchCopyA(ppi2a[i].pLocation, cch + 1, pszLocation);
660
661 HeapFree(hProcessHeap, 0, pszLocation);
662 }
663
664
665 if (ppi2w[i].pSepFile)
666 {
667 // Convert Unicode pSepFile to a ANSI string pszSepFile.
668 cch = wcslen(ppi2w[i].pSepFile);
669
670 pszSepFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
671 if (!pszSepFile)
672 {
673 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
674 ERR("HeapAlloc failed!\n");
675 goto Cleanup;
676 }
677
678 WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pSepFile, -1, pszSepFile, cch + 1, NULL, NULL);
679 StringCchCopyA(ppi2a[i].pSepFile, cch + 1, pszSepFile);
680
681 HeapFree(hProcessHeap, 0, pszSepFile);
682 }
683
684 if (ppi2w[i].pPrintProcessor)
685 {
686 // Convert Unicode pPrintProcessor to a ANSI string pszPrintProcessor.
687 cch = wcslen(ppi2w[i].pPrintProcessor);
688
689 pszPrintProcessor = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
690 if (!pszPrintProcessor)
691 {
692 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
693 ERR("HeapAlloc failed!\n");
694 goto Cleanup;
695 }
696
697 WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pPrintProcessor, -1, pszPrintProcessor, cch + 1, NULL, NULL);
698 StringCchCopyA(ppi2a[i].pPrintProcessor, cch + 1, pszPrintProcessor);
699
700 HeapFree(hProcessHeap, 0, pszPrintProcessor);
701 }
702
703
704 if (ppi2w[i].pDatatype)
705 {
706 // Convert Unicode pDatatype to a ANSI string pszDatatype.
707 cch = wcslen(ppi2w[i].pDatatype);
708
709 pszDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
710 if (!pszDatatype)
711 {
712 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
713 ERR("HeapAlloc failed!\n");
714 goto Cleanup;
715 }
716
717 WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pDatatype, -1, pszDatatype, cch + 1, NULL, NULL);
718 StringCchCopyA(ppi2a[i].pDatatype, cch + 1, pszDatatype);
719
720 HeapFree(hProcessHeap, 0, pszDatatype);
721 }
722
723 if (ppi2w[i].pParameters)
724 {
725 // Convert Unicode pParameters to a ANSI string pszParameters.
726 cch = wcslen(ppi2w[i].pParameters);
727
728 pszParameters = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
729 if (!pszParameters)
730 {
731 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
732 ERR("HeapAlloc failed!\n");
733 goto Cleanup;
734 }
735
736 WideCharToMultiByte(CP_ACP, 0, ppi2w[i].pParameters, -1, pszParameters, cch + 1, NULL, NULL);
737 StringCchCopyA(ppi2a[i].pParameters, cch + 1, pszParameters);
738
739 HeapFree(hProcessHeap, 0, pszParameters);
740 }
741 break;
742
743 }
744
745 case 4:
746 {
747 if (ppi4w[i].pPrinterName)
748 {
749 // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
750 cch = wcslen(ppi4w[i].pPrinterName);
751
752 pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
753 if (!pszPrinterName)
754 {
755 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
756 ERR("HeapAlloc failed!\n");
757 goto Cleanup;
758 }
759
760 WideCharToMultiByte(CP_ACP, 0, ppi4w[i].pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
761 StringCchCopyA(ppi4a[i].pPrinterName, cch + 1, pszPrinterName);
762
763 HeapFree(hProcessHeap, 0, pszPrinterName);
764 }
765
766 if (ppi4w[i].pServerName)
767 {
768 // Convert Unicode pServerName to a ANSI string pszServerName.
769 cch = wcslen(ppi4w[i].pServerName);
770
771 pszServerName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
772 if (!pszServerName)
773 {
774 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
775 ERR("HeapAlloc failed!\n");
776 goto Cleanup;
777 }
778
779 WideCharToMultiByte(CP_ACP, 0, ppi4w[i].pServerName, -1, pszServerName, cch + 1, NULL, NULL);
780 StringCchCopyA(ppi4a[i].pServerName, cch + 1, pszServerName);
781
782 HeapFree(hProcessHeap, 0, pszServerName);
783 }
784 break;
785 }
786
787 case 5:
788 {
789 if (ppi5w[i].pPrinterName)
790 {
791 // Convert Unicode pPrinterName to a ANSI string pszPrinterName.
792 cch = wcslen(ppi5w[i].pPrinterName);
793
794 pszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
795 if (!pszPrinterName)
796 {
797 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
798 ERR("HeapAlloc failed!\n");
799 goto Cleanup;
800 }
801
802 WideCharToMultiByte(CP_ACP, 0, ppi5w[i].pPrinterName, -1, pszPrinterName, cch + 1, NULL, NULL);
803 StringCchCopyA(ppi5a[i].pPrinterName, cch + 1, pszPrinterName);
804
805 HeapFree(hProcessHeap, 0, pszPrinterName);
806 }
807
808 if (ppi5w[i].pPortName)
809 {
810 // Convert Unicode pPortName to a ANSI string pszPortName.
811 cch = wcslen(ppi5w[i].pPortName);
812
813 pszPortName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(CHAR));
814 if (!pszPortName)
815 {
816 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
817 ERR("HeapAlloc failed!\n");
818 goto Cleanup;
819 }
820
821 WideCharToMultiByte(CP_ACP, 0, ppi5w[i].pPortName, -1, pszPortName, cch + 1, NULL, NULL);
822 StringCchCopyA(ppi5a[i].pPortName, cch + 1, pszPortName);
823
824 HeapFree(hProcessHeap, 0, pszPortName);
825 }
826 break;
827 }
828
829 } // switch
830 } // for
831
832 Cleanup:
833
834 return bReturnValue;
835 }
836
837 BOOL WINAPI
838 EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
839 {
840 DWORD dwErrorCode;
841
842 TRACE("EnumPrintersW(%lu, %S, %lu, %p, %lu, %p, %p)\n", Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
843
844 // Dismiss invalid levels already at this point.
845 if (Level == 3 || Level > 5)
846 {
847 dwErrorCode = ERROR_INVALID_LEVEL;
848 goto Cleanup;
849 }
850
851 if (cbBuf && pPrinterEnum)
852 ZeroMemory(pPrinterEnum, cbBuf);
853
854 // Do the RPC call
855 RpcTryExcept
856 {
857 dwErrorCode = _RpcEnumPrinters(Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
858 }
859 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
860 {
861 dwErrorCode = RpcExceptionCode();
862 ERR("_RpcEnumPrinters failed with exception code %lu!\n", dwErrorCode);
863 }
864 RpcEndExcept;
865
866 if (dwErrorCode == ERROR_SUCCESS)
867 {
868 // Replace relative offset addresses in the output by absolute pointers.
869 ASSERT(Level <= 9);
870 MarshallUpStructuresArray(cbBuf, pPrinterEnum, *pcReturned, pPrinterInfoMarshalling[Level]->pInfo, pPrinterInfoMarshalling[Level]->cbStructureSize, TRUE);
871 }
872
873 Cleanup:
874 SetLastError(dwErrorCode);
875 return (dwErrorCode == ERROR_SUCCESS);
876 }
877
878 BOOL WINAPI
879 FlushPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten, DWORD cSleep)
880 {
881 TRACE("FlushPrinter(%p, %p, %lu, %p, %lu)\n", hPrinter, pBuf, cbBuf, pcWritten, cSleep);
882 UNIMPLEMENTED;
883 return FALSE;
884 }
885
886 BOOL WINAPI
887 GetDefaultPrinterA(LPSTR pszBuffer, LPDWORD pcchBuffer)
888 {
889 DWORD dwErrorCode;
890 PWSTR pwszBuffer = NULL;
891
892 TRACE("GetDefaultPrinterA(%p, %p)\n", pszBuffer, pcchBuffer);
893
894 // Sanity check.
895 if (!pcchBuffer)
896 {
897 dwErrorCode = ERROR_INVALID_PARAMETER;
898 goto Cleanup;
899 }
900
901 // Check if an ANSI buffer was given and if so, allocate a Unicode buffer of the same size.
902 if (pszBuffer && *pcchBuffer)
903 {
904 pwszBuffer = HeapAlloc(hProcessHeap, 0, *pcchBuffer * sizeof(WCHAR));
905 if (!pwszBuffer)
906 {
907 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
908 ERR("HeapAlloc failed!\n");
909 goto Cleanup;
910 }
911 }
912
913 if (!GetDefaultPrinterW(pwszBuffer, pcchBuffer))
914 {
915 dwErrorCode = GetLastError();
916 goto Cleanup;
917 }
918
919 // We successfully got a string in pwszBuffer, so convert the Unicode string to ANSI.
920 WideCharToMultiByte(CP_ACP, 0, pwszBuffer, -1, pszBuffer, *pcchBuffer, NULL, NULL);
921
922 dwErrorCode = ERROR_SUCCESS;
923
924 Cleanup:
925 if (pwszBuffer)
926 HeapFree(hProcessHeap, 0, pwszBuffer);
927
928 SetLastError(dwErrorCode);
929 return (dwErrorCode == ERROR_SUCCESS);
930 }
931
932 BOOL WINAPI
933 GetDefaultPrinterW(LPWSTR pszBuffer, LPDWORD pcchBuffer)
934 {
935 DWORD cbNeeded;
936 DWORD cchInputBuffer;
937 DWORD dwErrorCode;
938 HKEY hWindowsKey = NULL;
939 PWSTR pwszDevice = NULL;
940 PWSTR pwszComma;
941
942 TRACE("GetDefaultPrinterW(%p, %p)\n", pszBuffer, pcchBuffer);
943
944 // Sanity check.
945 if (!pcchBuffer)
946 {
947 dwErrorCode = ERROR_INVALID_PARAMETER;
948 goto Cleanup;
949 }
950
951 cchInputBuffer = *pcchBuffer;
952
953 // Open the registry key where the default printer for the current user is stored.
954 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszWindowsKey, 0, KEY_READ, &hWindowsKey);
955 if (dwErrorCode != ERROR_SUCCESS)
956 {
957 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
958 goto Cleanup;
959 }
960
961 // Determine the size of the required buffer.
962 dwErrorCode = (DWORD)RegQueryValueExW(hWindowsKey, wszDeviceValue, NULL, NULL, NULL, &cbNeeded);
963 if (dwErrorCode != ERROR_SUCCESS)
964 {
965 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
966 goto Cleanup;
967 }
968
969 // Allocate it.
970 pwszDevice = HeapAlloc(hProcessHeap, 0, cbNeeded);
971 if (!pwszDevice)
972 {
973 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
974 ERR("HeapAlloc failed!\n");
975 goto Cleanup;
976 }
977
978 // Now get the actual value.
979 dwErrorCode = RegQueryValueExW(hWindowsKey, wszDeviceValue, NULL, NULL, (PBYTE)pwszDevice, &cbNeeded);
980 if (dwErrorCode != ERROR_SUCCESS)
981 {
982 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
983 goto Cleanup;
984 }
985
986 // We get a string "<Printer Name>,winspool,<Port>:".
987 // Extract the printer name from it.
988 pwszComma = wcschr(pwszDevice, L',');
989 if (!pwszComma)
990 {
991 ERR("Found no or invalid default printer: %S!\n", pwszDevice);
992 dwErrorCode = ERROR_INVALID_NAME;
993 goto Cleanup;
994 }
995
996 // Store the length of the Printer Name (including the terminating NUL character!) in *pcchBuffer.
997 *pcchBuffer = pwszComma - pwszDevice + 1;
998
999 // Check if the supplied buffer is large enough.
1000 if (cchInputBuffer < *pcchBuffer)
1001 {
1002 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
1003 goto Cleanup;
1004 }
1005
1006 // Copy the default printer.
1007 *pwszComma = 0;
1008 CopyMemory(pszBuffer, pwszDevice, *pcchBuffer * sizeof(WCHAR));
1009
1010 dwErrorCode = ERROR_SUCCESS;
1011
1012 Cleanup:
1013 if (hWindowsKey)
1014 RegCloseKey(hWindowsKey);
1015
1016 if (pwszDevice)
1017 HeapFree(hProcessHeap, 0, pwszDevice);
1018
1019 SetLastError(dwErrorCode);
1020 return (dwErrorCode == ERROR_SUCCESS);
1021 }
1022
1023 BOOL WINAPI
1024 GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
1025 {
1026 TRACE("GetPrinterA(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
1027 if(pcbNeeded) *pcbNeeded = 0;
1028 return FALSE;
1029 }
1030
1031 BOOL WINAPI
1032 GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
1033 {
1034 TRACE("GetPrinterDriverA(%p, %s, %lu, %p, %lu, %p)\n", hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
1035 if(pcbNeeded) *pcbNeeded = 0;
1036 return FALSE;
1037 }
1038
1039 BOOL WINAPI
1040 GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
1041 {
1042 DWORD dwErrorCode;
1043 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
1044
1045 TRACE("GetPrinterDriverW(%p, %S, %lu, %p, %lu, %p)\n", hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
1046
1047 // Sanity checks.
1048 if (!pHandle)
1049 {
1050 dwErrorCode = ERROR_INVALID_HANDLE;
1051 goto Cleanup;
1052 }
1053
1054 // Dismiss invalid levels already at this point.
1055 if (Level > 8 || Level < 1)
1056 {
1057 dwErrorCode = ERROR_INVALID_LEVEL;
1058 goto Cleanup;
1059 }
1060
1061 if (cbBuf && pDriverInfo)
1062 ZeroMemory(pDriverInfo, cbBuf);
1063
1064 // Do the RPC call
1065 RpcTryExcept
1066 {
1067 dwErrorCode = _RpcGetPrinterDriver(pHandle->hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
1068 }
1069 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1070 {
1071 dwErrorCode = RpcExceptionCode();
1072 ERR("_RpcGetPrinter failed with exception code %lu!\n", dwErrorCode);
1073 }
1074 RpcEndExcept;
1075
1076 if (dwErrorCode == ERROR_SUCCESS)
1077 {
1078 // Replace relative offset addresses in the output by absolute pointers.
1079 ASSERT(Level <= 3);
1080 MarshallUpStructure(cbBuf, pDriverInfo, pPrinterDriverMarshalling[Level]->pInfo, pPrinterDriverMarshalling[Level]->cbStructureSize, TRUE);
1081 }
1082
1083 Cleanup:
1084 SetLastError(dwErrorCode);
1085 return (dwErrorCode == ERROR_SUCCESS);
1086 }
1087
1088 BOOL WINAPI
1089 GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
1090 {
1091 DWORD dwErrorCode;
1092 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
1093
1094 TRACE("GetPrinterW(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
1095
1096 // Sanity checks.
1097 if (!pHandle)
1098 {
1099 dwErrorCode = ERROR_INVALID_HANDLE;
1100 goto Cleanup;
1101 }
1102
1103 // Dismiss invalid levels already at this point.
1104 if (Level > 9)
1105 {
1106 dwErrorCode = ERROR_INVALID_LEVEL;
1107 goto Cleanup;
1108 }
1109
1110 if (cbBuf && pPrinter)
1111 ZeroMemory(pPrinter, cbBuf);
1112
1113 // Do the RPC call
1114 RpcTryExcept
1115 {
1116 dwErrorCode = _RpcGetPrinter(pHandle->hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
1117 }
1118 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1119 {
1120 dwErrorCode = RpcExceptionCode();
1121 ERR("_RpcGetPrinter failed with exception code %lu!\n", dwErrorCode);
1122 }
1123 RpcEndExcept;
1124
1125 if (dwErrorCode == ERROR_SUCCESS)
1126 {
1127 // Replace relative offset addresses in the output by absolute pointers.
1128 ASSERT(Level <= 9);
1129 MarshallUpStructure(cbBuf, pPrinter, pPrinterInfoMarshalling[Level]->pInfo, pPrinterInfoMarshalling[Level]->cbStructureSize, TRUE);
1130 }
1131
1132 Cleanup:
1133 SetLastError(dwErrorCode);
1134 return (dwErrorCode == ERROR_SUCCESS);
1135 }
1136
1137 BOOL WINAPI
1138 OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefault)
1139 {
1140 BOOL bReturnValue = FALSE;
1141 DWORD cch;
1142 PWSTR pwszPrinterName = NULL;
1143 PRINTER_DEFAULTSW wDefault = { 0 };
1144
1145 TRACE("OpenPrinterA(%s, %p, %p)\n", pPrinterName, phPrinter, pDefault);
1146
1147 if (pPrinterName)
1148 {
1149 // Convert pPrinterName to a Unicode string pwszPrinterName
1150 cch = strlen(pPrinterName);
1151
1152 pwszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
1153 if (!pwszPrinterName)
1154 {
1155 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1156 ERR("HeapAlloc failed!\n");
1157 goto Cleanup;
1158 }
1159
1160 MultiByteToWideChar(CP_ACP, 0, pPrinterName, -1, pwszPrinterName, cch + 1);
1161 }
1162
1163 if (pDefault)
1164 {
1165 wDefault.DesiredAccess = pDefault->DesiredAccess;
1166
1167 if (pDefault->pDatatype)
1168 {
1169 // Convert pDefault->pDatatype to a Unicode string wDefault.pDatatype
1170 cch = strlen(pDefault->pDatatype);
1171
1172 wDefault.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
1173 if (!wDefault.pDatatype)
1174 {
1175 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1176 ERR("HeapAlloc failed!\n");
1177 goto Cleanup;
1178 }
1179
1180 MultiByteToWideChar(CP_ACP, 0, pDefault->pDatatype, -1, wDefault.pDatatype, cch + 1);
1181 }
1182
1183 if (pDefault->pDevMode)
1184 wDefault.pDevMode = GdiConvertToDevmodeW(pDefault->pDevMode);
1185 }
1186
1187 bReturnValue = OpenPrinterW(pwszPrinterName, phPrinter, &wDefault);
1188
1189 Cleanup:
1190 if (wDefault.pDatatype)
1191 HeapFree(hProcessHeap, 0, wDefault.pDatatype);
1192
1193 if (wDefault.pDevMode)
1194 HeapFree(hProcessHeap, 0, wDefault.pDevMode);
1195
1196 if (pwszPrinterName)
1197 HeapFree(hProcessHeap, 0, pwszPrinterName);
1198
1199 return bReturnValue;
1200 }
1201
1202 BOOL WINAPI
1203 OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefault)
1204 {
1205 DWORD dwErrorCode;
1206 HANDLE hPrinter;
1207 PSPOOLER_HANDLE pHandle;
1208 PWSTR pDatatype = NULL;
1209 WINSPOOL_DEVMODE_CONTAINER DevModeContainer = { 0 };
1210 ACCESS_MASK AccessRequired = 0;
1211
1212 TRACE("OpenPrinterW(%S, %p, %p)\n", pPrinterName, phPrinter, pDefault);
1213
1214 // Sanity check
1215 if (!phPrinter)
1216 {
1217 dwErrorCode = ERROR_INVALID_PARAMETER;
1218 goto Cleanup;
1219 }
1220
1221 // Prepare the additional parameters in the format required by _RpcOpenPrinter
1222 if (pDefault)
1223 {
1224 pDatatype = pDefault->pDatatype;
1225 DevModeContainer.cbBuf = sizeof(DEVMODEW);
1226 DevModeContainer.pDevMode = (BYTE*)pDefault->pDevMode;
1227 AccessRequired = pDefault->DesiredAccess;
1228 }
1229
1230 // Do the RPC call
1231 RpcTryExcept
1232 {
1233 dwErrorCode = _RpcOpenPrinter(pPrinterName, &hPrinter, pDatatype, &DevModeContainer, AccessRequired);
1234 }
1235 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1236 {
1237 dwErrorCode = RpcExceptionCode();
1238 ERR("_RpcOpenPrinter failed with exception code %lu!\n", dwErrorCode);
1239 }
1240 RpcEndExcept;
1241
1242 if (dwErrorCode == ERROR_SUCCESS)
1243 {
1244 // Create a new SPOOLER_HANDLE structure.
1245 pHandle = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, sizeof(SPOOLER_HANDLE));
1246 if (!pHandle)
1247 {
1248 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1249 ERR("HeapAlloc failed!\n");
1250 goto Cleanup;
1251 }
1252
1253 pHandle->hPrinter = hPrinter;
1254 pHandle->hSPLFile = INVALID_HANDLE_VALUE;
1255
1256 // Return it as phPrinter.
1257 *phPrinter = (HANDLE)pHandle;
1258 }
1259
1260 Cleanup:
1261 SetLastError(dwErrorCode);
1262 return (dwErrorCode == ERROR_SUCCESS);
1263 }
1264
1265 BOOL WINAPI
1266 ReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
1267 {
1268 DWORD dwErrorCode;
1269 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
1270
1271 TRACE("ReadPrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pNoBytesRead);
1272
1273 // Sanity checks.
1274 if (!pHandle)
1275 {
1276 dwErrorCode = ERROR_INVALID_HANDLE;
1277 goto Cleanup;
1278 }
1279
1280 // Do the RPC call
1281 RpcTryExcept
1282 {
1283 dwErrorCode = _RpcReadPrinter(pHandle->hPrinter, pBuf, cbBuf, pNoBytesRead);
1284 }
1285 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1286 {
1287 dwErrorCode = RpcExceptionCode();
1288 ERR("_RpcReadPrinter failed with exception code %lu!\n", dwErrorCode);
1289 }
1290 RpcEndExcept;
1291
1292 Cleanup:
1293 SetLastError(dwErrorCode);
1294 return (dwErrorCode == ERROR_SUCCESS);
1295 }
1296
1297 BOOL WINAPI
1298 ResetPrinterA(HANDLE hPrinter, PPRINTER_DEFAULTSA pDefault)
1299 {
1300 TRACE("ResetPrinterA(%p, %p)\n", hPrinter, pDefault);
1301 UNIMPLEMENTED;
1302 return FALSE;
1303 }
1304
1305 BOOL WINAPI
1306 ResetPrinterW(HANDLE hPrinter, PPRINTER_DEFAULTSW pDefault)
1307 {
1308 TRACE("ResetPrinterW(%p, %p)\n", hPrinter, pDefault);
1309 UNIMPLEMENTED;
1310 return FALSE;
1311 }
1312
1313 BOOL WINAPI
1314 SetDefaultPrinterA(LPCSTR pszPrinter)
1315 {
1316 BOOL bReturnValue = FALSE;
1317 DWORD cch;
1318 PWSTR pwszPrinter = NULL;
1319
1320 TRACE("SetDefaultPrinterA(%s)\n", pszPrinter);
1321
1322 if (pszPrinter)
1323 {
1324 // Convert pszPrinter to a Unicode string pwszPrinter
1325 cch = strlen(pszPrinter);
1326
1327 pwszPrinter = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
1328 if (!pwszPrinter)
1329 {
1330 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1331 ERR("HeapAlloc failed!\n");
1332 goto Cleanup;
1333 }
1334
1335 MultiByteToWideChar(CP_ACP, 0, pszPrinter, -1, pwszPrinter, cch + 1);
1336 }
1337
1338 bReturnValue = SetDefaultPrinterW(pwszPrinter);
1339
1340 Cleanup:
1341 if (pwszPrinter)
1342 HeapFree(hProcessHeap, 0, pwszPrinter);
1343
1344 return bReturnValue;
1345 }
1346
1347 BOOL WINAPI
1348 SetDefaultPrinterW(LPCWSTR pszPrinter)
1349 {
1350 const WCHAR wszDevicesKey[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Devices";
1351
1352 DWORD cbDeviceValueData;
1353 DWORD cbPrinterValueData = 0;
1354 DWORD cchPrinter;
1355 DWORD dwErrorCode;
1356 HKEY hDevicesKey = NULL;
1357 HKEY hWindowsKey = NULL;
1358 PWSTR pwszDeviceValueData = NULL;
1359 WCHAR wszPrinter[MAX_PRINTER_NAME + 1];
1360
1361 TRACE("SetDefaultPrinterW(%S)\n", pszPrinter);
1362
1363 // Open the Devices registry key.
1364 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszDevicesKey, 0, KEY_READ, &hDevicesKey);
1365 if (dwErrorCode != ERROR_SUCCESS)
1366 {
1367 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
1368 goto Cleanup;
1369 }
1370
1371 // Did the caller give us a printer to set as default?
1372 if (pszPrinter && *pszPrinter)
1373 {
1374 // Check if the given printer exists and query the value data size.
1375 dwErrorCode = (DWORD)RegQueryValueExW(hDevicesKey, pszPrinter, NULL, NULL, NULL, &cbPrinterValueData);
1376 if (dwErrorCode == ERROR_FILE_NOT_FOUND)
1377 {
1378 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
1379 goto Cleanup;
1380 }
1381 else if (dwErrorCode != ERROR_SUCCESS)
1382 {
1383 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
1384 goto Cleanup;
1385 }
1386
1387 cchPrinter = wcslen(pszPrinter);
1388 }
1389 else
1390 {
1391 // If there is already a default printer, we're done!
1392 cchPrinter = _countof(wszPrinter);
1393 if (GetDefaultPrinterW(wszPrinter, &cchPrinter))
1394 {
1395 dwErrorCode = ERROR_SUCCESS;
1396 goto Cleanup;
1397 }
1398
1399 // Otherwise, get us the first printer from the "Devices" key to later set it as default and query the value data size.
1400 cchPrinter = _countof(wszPrinter);
1401 dwErrorCode = (DWORD)RegEnumValueW(hDevicesKey, 0, wszPrinter, &cchPrinter, NULL, NULL, NULL, &cbPrinterValueData);
1402 if (dwErrorCode != ERROR_MORE_DATA)
1403 goto Cleanup;
1404
1405 pszPrinter = wszPrinter;
1406 }
1407
1408 // We now need to query the value data, which has the format "winspool,<Port>:"
1409 // and make "<Printer Name>,winspool,<Port>:" out of it.
1410 // Allocate a buffer large enough for the final data.
1411 cbDeviceValueData = (cchPrinter + 1) * sizeof(WCHAR) + cbPrinterValueData;
1412 pwszDeviceValueData = HeapAlloc(hProcessHeap, 0, cbDeviceValueData);
1413 if (!pwszDeviceValueData)
1414 {
1415 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1416 ERR("HeapAlloc failed!\n");
1417 goto Cleanup;
1418 }
1419
1420 // Copy the Printer Name and a comma into it.
1421 CopyMemory(pwszDeviceValueData, pszPrinter, cchPrinter * sizeof(WCHAR));
1422 pwszDeviceValueData[cchPrinter] = L',';
1423
1424 // Append the value data, which has the format "winspool,<Port>:"
1425 dwErrorCode = (DWORD)RegQueryValueExW(hDevicesKey, pszPrinter, NULL, NULL, (PBYTE)&pwszDeviceValueData[cchPrinter + 1], &cbPrinterValueData);
1426 if (dwErrorCode != ERROR_SUCCESS)
1427 goto Cleanup;
1428
1429 // Open the Windows registry key.
1430 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszWindowsKey, 0, KEY_SET_VALUE, &hWindowsKey);
1431 if (dwErrorCode != ERROR_SUCCESS)
1432 {
1433 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
1434 goto Cleanup;
1435 }
1436
1437 // Store our new default printer.
1438 dwErrorCode = (DWORD)RegSetValueExW(hWindowsKey, wszDeviceValue, 0, REG_SZ, (PBYTE)pwszDeviceValueData, cbDeviceValueData);
1439 if (dwErrorCode != ERROR_SUCCESS)
1440 {
1441 ERR("RegSetValueExW failed with status %lu!\n", dwErrorCode);
1442 goto Cleanup;
1443 }
1444
1445 Cleanup:
1446 if (hDevicesKey)
1447 RegCloseKey(hDevicesKey);
1448
1449 if (hWindowsKey)
1450 RegCloseKey(hWindowsKey);
1451
1452 if (pwszDeviceValueData)
1453 HeapFree(hProcessHeap, 0, pwszDeviceValueData);
1454
1455 SetLastError(dwErrorCode);
1456 return (dwErrorCode == ERROR_SUCCESS);
1457 }
1458
1459 BOOL WINAPI
1460 SetPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD Command)
1461 {
1462 TRACE("SetPrinterA(%p, %lu, %p, %lu)\n", hPrinter, Level, pPrinter, Command);
1463 UNIMPLEMENTED;
1464 return FALSE;
1465 }
1466
1467 BOOL WINAPI
1468 SetPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD Command)
1469 {
1470 TRACE("SetPrinterW(%p, %lu, %p, %lu)\n", hPrinter, Level, pPrinter, Command);
1471 UNIMPLEMENTED;
1472 return FALSE;
1473 }
1474
1475 DWORD WINAPI
1476 StartDocPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
1477 {
1478 DOC_INFO_1W wDocInfo1 = { 0 };
1479 DWORD cch;
1480 DWORD dwErrorCode;
1481 DWORD dwReturnValue = 0;
1482 PDOC_INFO_1A pDocInfo1 = (PDOC_INFO_1A)pDocInfo;
1483
1484 TRACE("StartDocPrinterA(%p, %lu, %p)\n", hPrinter, Level, pDocInfo);
1485
1486 // Only check the minimum required for accessing pDocInfo.
1487 // Additional sanity checks are done in StartDocPrinterW.
1488 if (!pDocInfo1)
1489 {
1490 dwErrorCode = ERROR_INVALID_PARAMETER;
1491 goto Cleanup;
1492 }
1493
1494 if (Level != 1)
1495 {
1496 dwErrorCode = ERROR_INVALID_LEVEL;
1497 goto Cleanup;
1498 }
1499
1500 if (pDocInfo1->pDatatype)
1501 {
1502 // Convert pDocInfo1->pDatatype to a Unicode string wDocInfo1.pDatatype
1503 cch = strlen(pDocInfo1->pDatatype);
1504
1505 wDocInfo1.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
1506 if (!wDocInfo1.pDatatype)
1507 {
1508 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1509 ERR("HeapAlloc failed!\n");
1510 goto Cleanup;
1511 }
1512
1513 MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDatatype, -1, wDocInfo1.pDatatype, cch + 1);
1514 }
1515
1516 if (pDocInfo1->pDocName)
1517 {
1518 // Convert pDocInfo1->pDocName to a Unicode string wDocInfo1.pDocName
1519 cch = strlen(pDocInfo1->pDocName);
1520
1521 wDocInfo1.pDocName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
1522 if (!wDocInfo1.pDocName)
1523 {
1524 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1525 ERR("HeapAlloc failed!\n");
1526 goto Cleanup;
1527 }
1528
1529 MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDocName, -1, wDocInfo1.pDocName, cch + 1);
1530 }
1531
1532 if (pDocInfo1->pOutputFile)
1533 {
1534 // Convert pDocInfo1->pOutputFile to a Unicode string wDocInfo1.pOutputFile
1535 cch = strlen(pDocInfo1->pOutputFile);
1536
1537 wDocInfo1.pOutputFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
1538 if (!wDocInfo1.pOutputFile)
1539 {
1540 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1541 ERR("HeapAlloc failed!\n");
1542 goto Cleanup;
1543 }
1544
1545 MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pOutputFile, -1, wDocInfo1.pOutputFile, cch + 1);
1546 }
1547
1548 dwReturnValue = StartDocPrinterW(hPrinter, Level, (PBYTE)&wDocInfo1);
1549 dwErrorCode = GetLastError();
1550
1551 Cleanup:
1552 if (wDocInfo1.pDatatype)
1553 HeapFree(hProcessHeap, 0, wDocInfo1.pDatatype);
1554
1555 if (wDocInfo1.pDocName)
1556 HeapFree(hProcessHeap, 0, wDocInfo1.pDocName);
1557
1558 if (wDocInfo1.pOutputFile)
1559 HeapFree(hProcessHeap, 0, wDocInfo1.pOutputFile);
1560
1561 SetLastError(dwErrorCode);
1562 return dwReturnValue;
1563 }
1564
1565 DWORD WINAPI
1566 StartDocPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
1567 {
1568 DWORD cbAddJobInfo1;
1569 DWORD cbNeeded;
1570 DWORD dwErrorCode;
1571 DWORD dwReturnValue = 0;
1572 PADDJOB_INFO_1W pAddJobInfo1 = NULL;
1573 PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
1574 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
1575
1576 TRACE("StartDocPrinterW(%p, %lu, %p)\n", hPrinter, Level, pDocInfo);
1577
1578 // Sanity checks.
1579 if (!pHandle)
1580 {
1581 dwErrorCode = ERROR_INVALID_HANDLE;
1582 goto Cleanup;
1583 }
1584
1585 if (!pDocInfo1)
1586 {
1587 dwErrorCode = ERROR_INVALID_PARAMETER;
1588 goto Cleanup;
1589 }
1590
1591 if (Level != 1)
1592 {
1593 dwErrorCode = ERROR_INVALID_LEVEL;
1594 goto Cleanup;
1595 }
1596
1597 if (pHandle->bStartedDoc)
1598 {
1599 dwErrorCode = ERROR_INVALID_PRINTER_STATE;
1600 goto Cleanup;
1601 }
1602
1603 // Check if we want to redirect output into a file.
1604 if (pDocInfo1->pOutputFile)
1605 {
1606 // Do a StartDocPrinter RPC call in this case.
1607 dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
1608 }
1609 else
1610 {
1611 // Allocate memory for the ADDJOB_INFO_1W structure and a path.
1612 cbAddJobInfo1 = sizeof(ADDJOB_INFO_1W) + MAX_PATH * sizeof(WCHAR);
1613 pAddJobInfo1 = HeapAlloc(hProcessHeap, 0, cbAddJobInfo1);
1614 if (!pAddJobInfo1)
1615 {
1616 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1617 ERR("HeapAlloc failed!\n");
1618 goto Cleanup;
1619 }
1620
1621 // Try to add a new job.
1622 // This only succeeds if the printer is set to do spooled printing.
1623 if (AddJobW((HANDLE)pHandle, 1, (PBYTE)pAddJobInfo1, cbAddJobInfo1, &cbNeeded))
1624 {
1625 // Do spooled printing.
1626 dwErrorCode = _StartDocPrinterSpooled(pHandle, pDocInfo1, pAddJobInfo1);
1627 }
1628 else if (GetLastError() == ERROR_INVALID_ACCESS)
1629 {
1630 // ERROR_INVALID_ACCESS is returned when the printer is set to do direct printing.
1631 // In this case, we do a StartDocPrinter RPC call.
1632 dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
1633 }
1634 else
1635 {
1636 dwErrorCode = GetLastError();
1637 ERR("AddJobW failed with error %lu!\n", dwErrorCode);
1638 goto Cleanup;
1639 }
1640 }
1641
1642 if (dwErrorCode == ERROR_SUCCESS)
1643 {
1644 pHandle->bStartedDoc = TRUE;
1645 dwReturnValue = pHandle->dwJobID;
1646 }
1647
1648 Cleanup:
1649 if (pAddJobInfo1)
1650 HeapFree(hProcessHeap, 0, pAddJobInfo1);
1651
1652 SetLastError(dwErrorCode);
1653 return dwReturnValue;
1654 }
1655
1656 BOOL WINAPI
1657 StartPagePrinter(HANDLE hPrinter)
1658 {
1659 DWORD dwErrorCode;
1660 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
1661
1662 TRACE("StartPagePrinter(%p)\n", hPrinter);
1663
1664 // Sanity checks.
1665 if (!pHandle)
1666 {
1667 dwErrorCode = ERROR_INVALID_HANDLE;
1668 goto Cleanup;
1669 }
1670
1671 // Do the RPC call
1672 RpcTryExcept
1673 {
1674 dwErrorCode = _RpcStartPagePrinter(pHandle->hPrinter);
1675 }
1676 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1677 {
1678 dwErrorCode = RpcExceptionCode();
1679 ERR("_RpcStartPagePrinter failed with exception code %lu!\n", dwErrorCode);
1680 }
1681 RpcEndExcept;
1682
1683 Cleanup:
1684 SetLastError(dwErrorCode);
1685 return (dwErrorCode == ERROR_SUCCESS);
1686 }
1687
1688 BOOL WINAPI
1689 WritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
1690 {
1691 DWORD dwErrorCode;
1692 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
1693
1694 TRACE("WritePrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
1695
1696 // Sanity checks.
1697 if (!pHandle)
1698 {
1699 dwErrorCode = ERROR_INVALID_HANDLE;
1700 goto Cleanup;
1701 }
1702
1703 if (!pHandle->bStartedDoc)
1704 {
1705 dwErrorCode = ERROR_SPL_NO_STARTDOC;
1706 goto Cleanup;
1707 }
1708
1709 if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
1710 {
1711 // Write to the spool file. This doesn't need an RPC request.
1712 if (!WriteFile(pHandle->hSPLFile, pBuf, cbBuf, pcWritten, NULL))
1713 {
1714 dwErrorCode = GetLastError();
1715 ERR("WriteFile failed with error %lu!\n", dwErrorCode);
1716 goto Cleanup;
1717 }
1718
1719 dwErrorCode = ERROR_SUCCESS;
1720 }
1721 else
1722 {
1723 // TODO: This case (for direct printing or remote printing) has bad performance if multiple small-sized WritePrinter calls are performed.
1724 // We may increase performance by writing into a buffer and only doing a single RPC call when the buffer is full.
1725
1726 // Do the RPC call
1727 RpcTryExcept
1728 {
1729 dwErrorCode = _RpcWritePrinter(pHandle->hPrinter, pBuf, cbBuf, pcWritten);
1730 }
1731 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
1732 {
1733 dwErrorCode = RpcExceptionCode();
1734 ERR("_RpcWritePrinter failed with exception code %lu!\n", dwErrorCode);
1735 }
1736 RpcEndExcept;
1737 }
1738
1739 Cleanup:
1740 SetLastError(dwErrorCode);
1741 return (dwErrorCode == ERROR_SUCCESS);
1742 }
1743
1744 BOOL WINAPI
1745 XcvDataW(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded, PDWORD pdwStatus)
1746 {
1747 TRACE("XcvDataW(%p, %S, %p, %lu, %p, %lu, %p, %p)\n", hXcv, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded, pdwStatus);
1748 return FALSE;
1749 }