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