[SPOOLSS]
[reactos.git] / reactos / win32ss / printing / base / winspool / printers.c
1 /*
2 * PROJECT: ReactOS Spooler API
3 * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation
4 * PURPOSE: Functions related to Printers and printing
5 * COPYRIGHT: Copyright 2015 Colin Finck <colin@reactos.org>
6 */
7
8 #include "precomp.h"
9
10 static void
11 _MarshallUpPrinterInfo(PBYTE pPrinterInfo, DWORD Level)
12 {
13 PPRINTER_INFO_1W pPrinterInfo1;
14 PPRINTER_INFO_2W pPrinterInfo2;
15
16 // Replace relative offset addresses in the output by absolute pointers.
17 if (Level == 1)
18 {
19 pPrinterInfo1 = (PPRINTER_INFO_1W)pPrinterInfo;
20
21 pPrinterInfo1->pName = (PWSTR)((ULONG_PTR)pPrinterInfo1->pName + (ULONG_PTR)pPrinterInfo1);
22 pPrinterInfo1->pDescription = (PWSTR)((ULONG_PTR)pPrinterInfo1->pDescription + (ULONG_PTR)pPrinterInfo1);
23 pPrinterInfo1->pComment = (PWSTR)((ULONG_PTR)pPrinterInfo1->pComment + (ULONG_PTR)pPrinterInfo1);
24 }
25 else if (Level == 2)
26 {
27 pPrinterInfo2 = (PPRINTER_INFO_2W)pPrinterInfo;
28
29 pPrinterInfo2->pPrinterName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pPrinterName + (ULONG_PTR)pPrinterInfo2);
30 pPrinterInfo2->pShareName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pShareName + (ULONG_PTR)pPrinterInfo2);
31 pPrinterInfo2->pPortName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pPortName + (ULONG_PTR)pPrinterInfo2);
32 pPrinterInfo2->pDriverName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pDriverName + (ULONG_PTR)pPrinterInfo2);
33 pPrinterInfo2->pComment = (PWSTR)((ULONG_PTR)pPrinterInfo2->pComment + (ULONG_PTR)pPrinterInfo2);
34 pPrinterInfo2->pLocation = (PWSTR)((ULONG_PTR)pPrinterInfo2->pLocation + (ULONG_PTR)pPrinterInfo2);
35 pPrinterInfo2->pDevMode = (PDEVMODEW)((ULONG_PTR)pPrinterInfo2->pDevMode + (ULONG_PTR)pPrinterInfo2);
36 pPrinterInfo2->pSepFile = (PWSTR)((ULONG_PTR)pPrinterInfo2->pSepFile + (ULONG_PTR)pPrinterInfo2);
37 pPrinterInfo2->pPrintProcessor = (PWSTR)((ULONG_PTR)pPrinterInfo2->pPrintProcessor + (ULONG_PTR)pPrinterInfo2);
38 pPrinterInfo2->pDatatype = (PWSTR)((ULONG_PTR)pPrinterInfo2->pDatatype + (ULONG_PTR)pPrinterInfo2);
39 pPrinterInfo2->pParameters = (PWSTR)((ULONG_PTR)pPrinterInfo2->pParameters + (ULONG_PTR)pPrinterInfo2);
40
41 if (pPrinterInfo2->pServerName)
42 pPrinterInfo2->pServerName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pServerName + (ULONG_PTR)pPrinterInfo2);
43
44 if (pPrinterInfo2->pSecurityDescriptor)
45 pPrinterInfo2->pSecurityDescriptor = (PWSTR)((ULONG_PTR)pPrinterInfo2->pSecurityDescriptor + (ULONG_PTR)pPrinterInfo2);
46 }
47 }
48
49 static DWORD
50 _StartDocPrinterSpooled(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1, PADDJOB_INFO_1W pAddJobInfo1)
51 {
52 DWORD cbNeeded;
53 DWORD dwErrorCode;
54 PJOB_INFO_1W pJobInfo1 = NULL;
55
56 // Create the spool file.
57 pHandle->hSPLFile = CreateFileW(pAddJobInfo1->Path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
58 if (pHandle->hSPLFile == INVALID_HANDLE_VALUE)
59 {
60 dwErrorCode = GetLastError();
61 ERR("CreateFileW failed for \"%S\" with error %lu!\n", pAddJobInfo1->Path, dwErrorCode);
62 goto Cleanup;
63 }
64
65 // Get the size of the job information.
66 GetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, NULL, 0, &cbNeeded);
67 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
68 {
69 dwErrorCode = GetLastError();
70 ERR("GetJobW failed with error %lu!\n", dwErrorCode);
71 goto Cleanup;
72 }
73
74 // Allocate enough memory for the returned job information.
75 pJobInfo1 = HeapAlloc(hProcessHeap, 0, cbNeeded);
76 if (!pJobInfo1)
77 {
78 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
79 ERR("HeapAlloc failed with error %lu!\n", GetLastError());
80 goto Cleanup;
81 }
82
83 // Get the job information.
84 if (!GetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, cbNeeded, &cbNeeded))
85 {
86 dwErrorCode = GetLastError();
87 ERR("GetJobW failed with error %lu!\n", dwErrorCode);
88 goto Cleanup;
89 }
90
91 // Add our document information.
92 if (pDocInfo1->pDatatype)
93 pJobInfo1->pDatatype = pDocInfo1->pDatatype;
94
95 pJobInfo1->pDocument = pDocInfo1->pDocName;
96
97 // Set the new job information.
98 if (!SetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, 0))
99 {
100 dwErrorCode = GetLastError();
101 ERR("SetJobW failed with error %lu!\n", dwErrorCode);
102 goto Cleanup;
103 }
104
105 // We were successful!
106 pHandle->dwJobID = pAddJobInfo1->JobId;
107 dwErrorCode = ERROR_SUCCESS;
108
109 Cleanup:
110 if (pJobInfo1)
111 HeapFree(hProcessHeap, 0, pJobInfo1);
112
113 return dwErrorCode;
114 }
115
116 static DWORD
117 _StartDocPrinterWithRPC(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1)
118 {
119 DWORD dwErrorCode;
120 WINSPOOL_DOC_INFO_CONTAINER DocInfoContainer;
121
122 DocInfoContainer.Level = 1;
123 DocInfoContainer.DocInfo.pDocInfo1 = (WINSPOOL_DOC_INFO_1*)pDocInfo1;
124
125 RpcTryExcept
126 {
127 dwErrorCode = _RpcStartDocPrinter(pHandle->hPrinter, &DocInfoContainer, &pHandle->dwJobID);
128 }
129 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
130 {
131 dwErrorCode = RpcExceptionCode();
132 ERR("_RpcStartDocPrinter failed with exception code %lu!\n", dwErrorCode);
133 }
134 RpcEndExcept;
135
136 return dwErrorCode;
137 }
138
139 HANDLE WINAPI
140 AddPrinterW(PWSTR pName, DWORD Level, PBYTE pPrinter)
141 {
142 UNIMPLEMENTED;
143 return NULL;
144 }
145
146 BOOL WINAPI
147 ClosePrinter(HANDLE hPrinter)
148 {
149 DWORD dwErrorCode;
150 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
151
152 // Sanity checks.
153 if (!pHandle)
154 {
155 dwErrorCode = ERROR_INVALID_HANDLE;
156 goto Cleanup;
157 }
158
159 // Do the RPC call.
160 RpcTryExcept
161 {
162 dwErrorCode = _RpcClosePrinter(pHandle->hPrinter);
163 }
164 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
165 {
166 dwErrorCode = RpcExceptionCode();
167 ERR("_RpcClosePrinter failed with exception code %lu!\n", dwErrorCode);
168 }
169 RpcEndExcept;
170
171 // Close any open file handle.
172 if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
173 CloseHandle(pHandle->hSPLFile);
174
175 // Free the memory for the handle.
176 HeapFree(hProcessHeap, 0, pHandle);
177
178 Cleanup:
179 SetLastError(dwErrorCode);
180 return (dwErrorCode == ERROR_SUCCESS);
181
182 }
183
184 DWORD WINAPI
185 DeviceCapabilitiesA(LPCSTR pDevice, LPCSTR pPort, WORD fwCapability, LPSTR pOutput, const DEVMODEA* pDevMode)
186 {
187 return 0;
188 }
189
190 DWORD WINAPI
191 DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort, WORD fwCapability, LPWSTR pOutput, const DEVMODEW* pDevMode)
192 {
193 return 0;
194 }
195
196 LONG WINAPI
197 DocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName, PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput, DWORD fMode)
198 {
199 return 0;
200 }
201
202 LONG WINAPI
203 DocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName, PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput, DWORD fMode)
204 {
205 return 0;
206 }
207
208 BOOL WINAPI
209 EndDocPrinter(HANDLE hPrinter)
210 {
211 DWORD dwErrorCode;
212 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
213
214 // Sanity checks.
215 if (!pHandle)
216 {
217 dwErrorCode = ERROR_INVALID_HANDLE;
218 goto Cleanup;
219 }
220
221 if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
222 {
223 // For spooled jobs, the document is finished by calling _RpcScheduleJob.
224 RpcTryExcept
225 {
226 dwErrorCode = _RpcScheduleJob(pHandle->hPrinter, pHandle->dwJobID);
227 }
228 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
229 {
230 dwErrorCode = RpcExceptionCode();
231 ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode);
232 }
233 RpcEndExcept;
234
235 // Close the spool file handle.
236 CloseHandle(pHandle->hSPLFile);
237 }
238 else
239 {
240 // In all other cases, just call _RpcEndDocPrinter.
241 RpcTryExcept
242 {
243 dwErrorCode = _RpcEndDocPrinter(pHandle->hPrinter);
244 }
245 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
246 {
247 dwErrorCode = RpcExceptionCode();
248 ERR("_RpcEndDocPrinter failed with exception code %lu!\n", dwErrorCode);
249 }
250 RpcEndExcept;
251 }
252
253 // A new document can now be started again.
254 pHandle->bStartedDoc = FALSE;
255
256 Cleanup:
257 SetLastError(dwErrorCode);
258 return (dwErrorCode == ERROR_SUCCESS);
259 }
260
261 BOOL WINAPI
262 EndPagePrinter(HANDLE hPrinter)
263 {
264 DWORD dwErrorCode;
265 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
266
267 // Sanity checks.
268 if (!pHandle)
269 {
270 dwErrorCode = ERROR_INVALID_HANDLE;
271 goto Cleanup;
272 }
273
274 if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
275 {
276 // For spooled jobs, we don't need to do anything.
277 dwErrorCode = ERROR_SUCCESS;
278 }
279 else
280 {
281 // In all other cases, just call _RpcEndPagePrinter.
282 RpcTryExcept
283 {
284 dwErrorCode = _RpcEndPagePrinter(pHandle->hPrinter);
285 }
286 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
287 {
288 dwErrorCode = RpcExceptionCode();
289 ERR("_RpcEndPagePrinter failed with exception code %lu!\n", dwErrorCode);
290 }
291 RpcEndExcept;
292 }
293
294 Cleanup:
295 SetLastError(dwErrorCode);
296 return (dwErrorCode == ERROR_SUCCESS);
297 }
298
299 BOOL WINAPI
300 EnumPrintersA(DWORD Flags, PSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
301 {
302 return FALSE;
303 }
304
305 BOOL WINAPI
306 EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
307 {
308 DWORD dwErrorCode;
309 DWORD i;
310 PBYTE p = pPrinterEnum;
311
312 // Do the RPC call
313 RpcTryExcept
314 {
315 dwErrorCode = _RpcEnumPrinters(Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
316 }
317 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
318 {
319 dwErrorCode = RpcExceptionCode();
320 ERR("_RpcEnumPrinters failed with exception code %lu!\n", dwErrorCode);
321 }
322 RpcEndExcept;
323
324 if (dwErrorCode == ERROR_SUCCESS)
325 {
326 // Replace relative offset addresses in the output by absolute pointers.
327 for (i = 0; i < *pcReturned; i++)
328 {
329 _MarshallUpPrinterInfo(p, Level);
330
331 if (Level == 1)
332 p += sizeof(PRINTER_INFO_1W);
333 else if (Level == 2)
334 p += sizeof(PRINTER_INFO_2W);
335 }
336 }
337
338 SetLastError(dwErrorCode);
339 return (dwErrorCode == ERROR_SUCCESS);
340 }
341
342 BOOL WINAPI
343 GetDefaultPrinterA(LPSTR pszBuffer, LPDWORD pcchBuffer)
344 {
345 return FALSE;
346 }
347
348 BOOL WINAPI
349 GetDefaultPrinterW(LPWSTR pszBuffer, LPDWORD pcchBuffer)
350 {
351 return FALSE;
352 }
353
354 BOOL WINAPI
355 GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
356 {
357 return FALSE;
358 }
359
360 BOOL WINAPI
361 GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
362 {
363 return FALSE;
364 }
365
366 BOOL WINAPI
367 GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
368 {
369 return FALSE;
370 }
371
372 BOOL WINAPI
373 GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
374 {
375 return FALSE;
376 }
377
378 BOOL WINAPI
379 OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefault)
380 {
381 BOOL bReturnValue = FALSE;
382 DWORD cch;
383 PWSTR pwszPrinterName = NULL;
384 PRINTER_DEFAULTSW wDefault = { 0 };
385
386 if (pPrinterName)
387 {
388 // Convert pPrinterName to a Unicode string pwszPrinterName
389 cch = strlen(pPrinterName);
390
391 pwszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
392 if (!pwszPrinterName)
393 {
394 ERR("HeapAlloc failed for pwszPrinterName with last error %lu!\n", GetLastError());
395 goto Cleanup;
396 }
397
398 MultiByteToWideChar(CP_ACP, 0, pPrinterName, -1, pwszPrinterName, cch + 1);
399 }
400
401 if (pDefault)
402 {
403 wDefault.DesiredAccess = pDefault->DesiredAccess;
404
405 if (pDefault->pDatatype)
406 {
407 // Convert pDefault->pDatatype to a Unicode string wDefault.pDatatype
408 cch = strlen(pDefault->pDatatype);
409
410 wDefault.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
411 if (!wDefault.pDatatype)
412 {
413 ERR("HeapAlloc failed for wDefault.pDatatype with last error %lu!\n", GetLastError());
414 goto Cleanup;
415 }
416
417 MultiByteToWideChar(CP_ACP, 0, pDefault->pDatatype, -1, wDefault.pDatatype, cch + 1);
418 }
419
420 if (pDefault->pDevMode)
421 wDefault.pDevMode = GdiConvertToDevmodeW(pDefault->pDevMode);
422 }
423
424 bReturnValue = OpenPrinterW(pwszPrinterName, phPrinter, &wDefault);
425
426 Cleanup:
427 if (wDefault.pDatatype)
428 HeapFree(hProcessHeap, 0, wDefault.pDatatype);
429
430 if (wDefault.pDevMode)
431 HeapFree(hProcessHeap, 0, wDefault.pDevMode);
432
433 if (pwszPrinterName)
434 HeapFree(hProcessHeap, 0, pwszPrinterName);
435
436 return bReturnValue;
437 }
438
439 BOOL WINAPI
440 OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefault)
441 {
442 DWORD dwErrorCode;
443 HANDLE hPrinter;
444 PSPOOLER_HANDLE pHandle;
445 PWSTR pDatatype = NULL;
446 WINSPOOL_DEVMODE_CONTAINER DevModeContainer = { 0 };
447 ACCESS_MASK AccessRequired = 0;
448
449 // Prepare the additional parameters in the format required by _RpcOpenPrinter
450 if (pDefault)
451 {
452 pDatatype = pDefault->pDatatype;
453 DevModeContainer.cbBuf = sizeof(DEVMODEW);
454 DevModeContainer.pDevMode = (BYTE*)pDefault->pDevMode;
455 AccessRequired = pDefault->DesiredAccess;
456 }
457
458 // Do the RPC call
459 RpcTryExcept
460 {
461 dwErrorCode = _RpcOpenPrinter(pPrinterName, &hPrinter, pDatatype, &DevModeContainer, AccessRequired);
462 }
463 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
464 {
465 dwErrorCode = RpcExceptionCode();
466 ERR("_RpcOpenPrinter failed with exception code %lu!\n", dwErrorCode);
467 }
468 RpcEndExcept;
469
470 if (dwErrorCode == ERROR_SUCCESS)
471 {
472 // Create a new SPOOLER_HANDLE structure.
473 pHandle = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, sizeof(SPOOLER_HANDLE));
474 if (!pHandle)
475 {
476 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
477 ERR("HeapAlloc failed with error %lu!\n", GetLastError());
478 goto Cleanup;
479 }
480
481 pHandle->hPrinter = hPrinter;
482 pHandle->hSPLFile = INVALID_HANDLE_VALUE;
483
484 // Return it as phPrinter.
485 *phPrinter = (HANDLE)pHandle;
486 }
487
488 Cleanup:
489 SetLastError(dwErrorCode);
490 return (dwErrorCode == ERROR_SUCCESS);
491 }
492
493 BOOL WINAPI
494 ReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
495 {
496 DWORD dwErrorCode;
497 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
498
499 // Sanity checks.
500 if (!pHandle)
501 {
502 dwErrorCode = ERROR_INVALID_HANDLE;
503 goto Cleanup;
504 }
505
506 // Do the RPC call
507 RpcTryExcept
508 {
509 dwErrorCode = _RpcReadPrinter(pHandle->hPrinter, pBuf, cbBuf, pNoBytesRead);
510 }
511 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
512 {
513 dwErrorCode = RpcExceptionCode();
514 ERR("_RpcReadPrinter failed with exception code %lu!\n", dwErrorCode);
515 }
516 RpcEndExcept;
517
518 Cleanup:
519 SetLastError(dwErrorCode);
520 return (dwErrorCode == ERROR_SUCCESS);
521 }
522
523 BOOL WINAPI
524 ResetPrinterW(HANDLE hPrinter, PPRINTER_DEFAULTSW pDefault)
525 {
526 UNIMPLEMENTED;
527 return FALSE;
528 }
529
530 BOOL WINAPI
531 SetPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD Command)
532 {
533 UNIMPLEMENTED;
534 return FALSE;
535 }
536
537 DWORD WINAPI
538 StartDocPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
539 {
540 DOC_INFO_1W wDocInfo1 = { 0 };
541 DWORD cch;
542 DWORD dwErrorCode;
543 DWORD dwReturnValue = 0;
544 PDOC_INFO_1A pDocInfo1 = (PDOC_INFO_1A)pDocInfo;
545
546 // Only check the minimum required for accessing pDocInfo.
547 // Additional sanity checks are done in StartDocPrinterW.
548 if (!pDocInfo1)
549 {
550 dwErrorCode = ERROR_INVALID_PARAMETER;
551 goto Cleanup;
552 }
553
554 if (Level != 1)
555 {
556 dwErrorCode = ERROR_INVALID_LEVEL;
557 goto Cleanup;
558 }
559
560 if (pDocInfo1->pDatatype)
561 {
562 // Convert pDocInfo1->pDatatype to a Unicode string wDocInfo1.pDatatype
563 cch = strlen(pDocInfo1->pDatatype);
564
565 wDocInfo1.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
566 if (!wDocInfo1.pDatatype)
567 {
568 ERR("HeapAlloc failed for wDocInfo1.pDatatype with last error %lu!\n", GetLastError());
569 goto Cleanup;
570 }
571
572 MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDatatype, -1, wDocInfo1.pDatatype, cch + 1);
573 }
574
575 if (pDocInfo1->pDocName)
576 {
577 // Convert pDocInfo1->pDocName to a Unicode string wDocInfo1.pDocName
578 cch = strlen(pDocInfo1->pDocName);
579
580 wDocInfo1.pDocName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
581 if (!wDocInfo1.pDocName)
582 {
583 ERR("HeapAlloc failed for wDocInfo1.pDocName with last error %lu!\n", GetLastError());
584 goto Cleanup;
585 }
586
587 MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDocName, -1, wDocInfo1.pDocName, cch + 1);
588 }
589
590 if (pDocInfo1->pOutputFile)
591 {
592 // Convert pDocInfo1->pOutputFile to a Unicode string wDocInfo1.pOutputFile
593 cch = strlen(pDocInfo1->pOutputFile);
594
595 wDocInfo1.pOutputFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
596 if (!wDocInfo1.pOutputFile)
597 {
598 ERR("HeapAlloc failed for wDocInfo1.pOutputFile with last error %lu!\n", GetLastError());
599 goto Cleanup;
600 }
601
602 MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pOutputFile, -1, wDocInfo1.pOutputFile, cch + 1);
603 }
604
605 dwReturnValue = StartDocPrinterW(hPrinter, Level, (PBYTE)&wDocInfo1);
606 dwErrorCode = GetLastError();
607
608 Cleanup:
609 if (wDocInfo1.pDatatype)
610 HeapFree(hProcessHeap, 0, wDocInfo1.pDatatype);
611
612 if (wDocInfo1.pDocName)
613 HeapFree(hProcessHeap, 0, wDocInfo1.pDocName);
614
615 if (wDocInfo1.pOutputFile)
616 HeapFree(hProcessHeap, 0, wDocInfo1.pOutputFile);
617
618 SetLastError(dwErrorCode);
619 return dwReturnValue;
620 }
621
622 DWORD WINAPI
623 StartDocPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
624 {
625 DWORD cbAddJobInfo1;
626 DWORD cbNeeded;
627 DWORD dwErrorCode;
628 DWORD dwReturnValue = 0;
629 PADDJOB_INFO_1W pAddJobInfo1 = NULL;
630 PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
631 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
632
633 // Sanity checks.
634 if (!pHandle)
635 {
636 dwErrorCode = ERROR_INVALID_HANDLE;
637 goto Cleanup;
638 }
639
640 if (!pDocInfo1)
641 {
642 dwErrorCode = ERROR_INVALID_PARAMETER;
643 goto Cleanup;
644 }
645
646 if (Level != 1)
647 {
648 dwErrorCode = ERROR_INVALID_LEVEL;
649 goto Cleanup;
650 }
651
652 if (pHandle->bStartedDoc)
653 {
654 dwErrorCode = ERROR_INVALID_PRINTER_STATE;
655 goto Cleanup;
656 }
657
658 // Check if we want to redirect output into a file.
659 if (pDocInfo1->pOutputFile)
660 {
661 // Do a StartDocPrinter RPC call in this case.
662 dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
663 }
664 else
665 {
666 // Allocate memory for the ADDJOB_INFO_1W structure and a path.
667 cbAddJobInfo1 = sizeof(ADDJOB_INFO_1W) + MAX_PATH * sizeof(WCHAR);
668 pAddJobInfo1 = HeapAlloc(hProcessHeap, 0, cbAddJobInfo1);
669 if (!pAddJobInfo1)
670 {
671 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
672 ERR("HeapAlloc failed with error %lu!\n", GetLastError());
673 goto Cleanup;
674 }
675
676 // Try to add a new job.
677 // This only succeeds if the printer is set to do spooled printing.
678 if (AddJobW((HANDLE)pHandle, 1, (PBYTE)pAddJobInfo1, cbAddJobInfo1, &cbNeeded))
679 {
680 // Do spooled printing.
681 dwErrorCode = _StartDocPrinterSpooled(pHandle, pDocInfo1, pAddJobInfo1);
682 }
683 else if (GetLastError() == ERROR_INVALID_ACCESS)
684 {
685 // ERROR_INVALID_ACCESS is returned when the printer is set to do direct printing.
686 // In this case, we do a StartDocPrinter RPC call.
687 dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
688 }
689 else
690 {
691 dwErrorCode = GetLastError();
692 ERR("AddJobW failed with error %lu!\n", dwErrorCode);
693 goto Cleanup;
694 }
695 }
696
697 if (dwErrorCode == ERROR_SUCCESS)
698 {
699 pHandle->bStartedDoc = TRUE;
700 dwReturnValue = pHandle->dwJobID;
701 }
702
703 Cleanup:
704 if (pAddJobInfo1)
705 HeapFree(hProcessHeap, 0, pAddJobInfo1);
706
707 SetLastError(dwErrorCode);
708 return dwReturnValue;
709 }
710
711 BOOL WINAPI
712 StartPagePrinter(HANDLE hPrinter)
713 {
714 DWORD dwErrorCode;
715 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
716
717 // Sanity checks.
718 if (!pHandle)
719 {
720 dwErrorCode = ERROR_INVALID_HANDLE;
721 goto Cleanup;
722 }
723
724 // Do the RPC call
725 RpcTryExcept
726 {
727 dwErrorCode = _RpcStartPagePrinter(pHandle->hPrinter);
728 }
729 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
730 {
731 dwErrorCode = RpcExceptionCode();
732 ERR("_RpcStartPagePrinter failed with exception code %lu!\n", dwErrorCode);
733 }
734 RpcEndExcept;
735
736 Cleanup:
737 SetLastError(dwErrorCode);
738 return (dwErrorCode == ERROR_SUCCESS);
739 }
740
741 BOOL WINAPI
742 WritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
743 {
744 DWORD dwErrorCode;
745 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
746
747 // Sanity checks.
748 if (!pHandle)
749 {
750 dwErrorCode = ERROR_INVALID_HANDLE;
751 goto Cleanup;
752 }
753
754 if (!pHandle->bStartedDoc)
755 {
756 dwErrorCode = ERROR_SPL_NO_STARTDOC;
757 goto Cleanup;
758 }
759
760 if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
761 {
762 // Write to the spool file. This doesn't need an RPC request.
763 if (!WriteFile(pHandle->hSPLFile, pBuf, cbBuf, pcWritten, NULL))
764 {
765 dwErrorCode = GetLastError();
766 ERR("WriteFile failed with error %lu!\n", dwErrorCode);
767 goto Cleanup;
768 }
769
770 dwErrorCode = ERROR_SUCCESS;
771 }
772 else
773 {
774 // TODO: This case (for direct printing or remote printing) has bad performance if multiple small-sized WritePrinter calls are performed.
775 // We may increase performance by writing into a buffer and only doing a single RPC call when the buffer is full.
776
777 // Do the RPC call
778 RpcTryExcept
779 {
780 dwErrorCode = _RpcWritePrinter(pHandle->hPrinter, pBuf, cbBuf, pcWritten);
781 }
782 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
783 {
784 dwErrorCode = RpcExceptionCode();
785 ERR("_RpcWritePrinter failed with exception code %lu!\n", dwErrorCode);
786 }
787 RpcEndExcept;
788 }
789
790 Cleanup:
791 SetLastError(dwErrorCode);
792 return (dwErrorCode == ERROR_SUCCESS);
793 }
794
795 BOOL WINAPI
796 XcvDataW(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded, PDWORD pdwStatus)
797 {
798 return FALSE;
799 }