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