[LOCALSPL]
[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-2017 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 // Dismiss invalid levels already at this point.
313 if (Level == 3 || Level > 5)
314 {
315 dwErrorCode = ERROR_INVALID_LEVEL;
316 goto Cleanup;
317 }
318
319 if (cbBuf && pPrinterEnum)
320 ZeroMemory(pPrinterEnum, cbBuf);
321
322 // Do the RPC call
323 RpcTryExcept
324 {
325 dwErrorCode = _RpcEnumPrinters(Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
326 }
327 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
328 {
329 dwErrorCode = RpcExceptionCode();
330 ERR("_RpcEnumPrinters failed with exception code %lu!\n", dwErrorCode);
331 }
332 RpcEndExcept;
333
334 if (dwErrorCode == ERROR_SUCCESS)
335 {
336 // Replace relative offset addresses in the output by absolute pointers.
337 for (i = 0; i < *pcReturned; i++)
338 {
339 _MarshallUpPrinterInfo(p, Level);
340
341 if (Level == 1)
342 p += sizeof(PRINTER_INFO_1W);
343 else if (Level == 2)
344 p += sizeof(PRINTER_INFO_2W);
345 }
346 }
347
348 Cleanup:
349 SetLastError(dwErrorCode);
350 return (dwErrorCode == ERROR_SUCCESS);
351 }
352
353 BOOL WINAPI
354 GetDefaultPrinterA(LPSTR pszBuffer, LPDWORD pcchBuffer)
355 {
356 return FALSE;
357 }
358
359 BOOL WINAPI
360 GetDefaultPrinterW(LPWSTR pszBuffer, LPDWORD pcchBuffer)
361 {
362 return FALSE;
363 }
364
365 BOOL WINAPI
366 GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
367 {
368 return FALSE;
369 }
370
371 BOOL WINAPI
372 GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
373 {
374 return FALSE;
375 }
376
377 BOOL WINAPI
378 GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
379 {
380 return FALSE;
381 }
382
383 BOOL WINAPI
384 GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
385 {
386 return FALSE;
387 }
388
389 BOOL WINAPI
390 OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefault)
391 {
392 BOOL bReturnValue = FALSE;
393 DWORD cch;
394 PWSTR pwszPrinterName = NULL;
395 PRINTER_DEFAULTSW wDefault = { 0 };
396
397 if (pPrinterName)
398 {
399 // Convert pPrinterName to a Unicode string pwszPrinterName
400 cch = strlen(pPrinterName);
401
402 pwszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
403 if (!pwszPrinterName)
404 {
405 ERR("HeapAlloc failed for pwszPrinterName with last error %lu!\n", GetLastError());
406 goto Cleanup;
407 }
408
409 MultiByteToWideChar(CP_ACP, 0, pPrinterName, -1, pwszPrinterName, cch + 1);
410 }
411
412 if (pDefault)
413 {
414 wDefault.DesiredAccess = pDefault->DesiredAccess;
415
416 if (pDefault->pDatatype)
417 {
418 // Convert pDefault->pDatatype to a Unicode string wDefault.pDatatype
419 cch = strlen(pDefault->pDatatype);
420
421 wDefault.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
422 if (!wDefault.pDatatype)
423 {
424 ERR("HeapAlloc failed for wDefault.pDatatype with last error %lu!\n", GetLastError());
425 goto Cleanup;
426 }
427
428 MultiByteToWideChar(CP_ACP, 0, pDefault->pDatatype, -1, wDefault.pDatatype, cch + 1);
429 }
430
431 if (pDefault->pDevMode)
432 wDefault.pDevMode = GdiConvertToDevmodeW(pDefault->pDevMode);
433 }
434
435 bReturnValue = OpenPrinterW(pwszPrinterName, phPrinter, &wDefault);
436
437 Cleanup:
438 if (wDefault.pDatatype)
439 HeapFree(hProcessHeap, 0, wDefault.pDatatype);
440
441 if (wDefault.pDevMode)
442 HeapFree(hProcessHeap, 0, wDefault.pDevMode);
443
444 if (pwszPrinterName)
445 HeapFree(hProcessHeap, 0, pwszPrinterName);
446
447 return bReturnValue;
448 }
449
450 BOOL WINAPI
451 OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefault)
452 {
453 DWORD dwErrorCode;
454 HANDLE hPrinter;
455 PSPOOLER_HANDLE pHandle;
456 PWSTR pDatatype = NULL;
457 WINSPOOL_DEVMODE_CONTAINER DevModeContainer = { 0 };
458 ACCESS_MASK AccessRequired = 0;
459
460 // Prepare the additional parameters in the format required by _RpcOpenPrinter
461 if (pDefault)
462 {
463 pDatatype = pDefault->pDatatype;
464 DevModeContainer.cbBuf = sizeof(DEVMODEW);
465 DevModeContainer.pDevMode = (BYTE*)pDefault->pDevMode;
466 AccessRequired = pDefault->DesiredAccess;
467 }
468
469 // Do the RPC call
470 RpcTryExcept
471 {
472 dwErrorCode = _RpcOpenPrinter(pPrinterName, &hPrinter, pDatatype, &DevModeContainer, AccessRequired);
473 }
474 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
475 {
476 dwErrorCode = RpcExceptionCode();
477 ERR("_RpcOpenPrinter failed with exception code %lu!\n", dwErrorCode);
478 }
479 RpcEndExcept;
480
481 if (dwErrorCode == ERROR_SUCCESS)
482 {
483 // Create a new SPOOLER_HANDLE structure.
484 pHandle = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, sizeof(SPOOLER_HANDLE));
485 if (!pHandle)
486 {
487 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
488 ERR("HeapAlloc failed with error %lu!\n", GetLastError());
489 goto Cleanup;
490 }
491
492 pHandle->hPrinter = hPrinter;
493 pHandle->hSPLFile = INVALID_HANDLE_VALUE;
494
495 // Return it as phPrinter.
496 *phPrinter = (HANDLE)pHandle;
497 }
498
499 Cleanup:
500 SetLastError(dwErrorCode);
501 return (dwErrorCode == ERROR_SUCCESS);
502 }
503
504 BOOL WINAPI
505 ReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
506 {
507 DWORD dwErrorCode;
508 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
509
510 // Sanity checks.
511 if (!pHandle)
512 {
513 dwErrorCode = ERROR_INVALID_HANDLE;
514 goto Cleanup;
515 }
516
517 // Do the RPC call
518 RpcTryExcept
519 {
520 dwErrorCode = _RpcReadPrinter(pHandle->hPrinter, pBuf, cbBuf, pNoBytesRead);
521 }
522 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
523 {
524 dwErrorCode = RpcExceptionCode();
525 ERR("_RpcReadPrinter failed with exception code %lu!\n", dwErrorCode);
526 }
527 RpcEndExcept;
528
529 Cleanup:
530 SetLastError(dwErrorCode);
531 return (dwErrorCode == ERROR_SUCCESS);
532 }
533
534 BOOL WINAPI
535 ResetPrinterW(HANDLE hPrinter, PPRINTER_DEFAULTSW pDefault)
536 {
537 UNIMPLEMENTED;
538 return FALSE;
539 }
540
541 BOOL WINAPI
542 SetPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD Command)
543 {
544 UNIMPLEMENTED;
545 return FALSE;
546 }
547
548 DWORD WINAPI
549 StartDocPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
550 {
551 DOC_INFO_1W wDocInfo1 = { 0 };
552 DWORD cch;
553 DWORD dwErrorCode;
554 DWORD dwReturnValue = 0;
555 PDOC_INFO_1A pDocInfo1 = (PDOC_INFO_1A)pDocInfo;
556
557 // Only check the minimum required for accessing pDocInfo.
558 // Additional sanity checks are done in StartDocPrinterW.
559 if (!pDocInfo1)
560 {
561 dwErrorCode = ERROR_INVALID_PARAMETER;
562 goto Cleanup;
563 }
564
565 if (Level != 1)
566 {
567 dwErrorCode = ERROR_INVALID_LEVEL;
568 goto Cleanup;
569 }
570
571 if (pDocInfo1->pDatatype)
572 {
573 // Convert pDocInfo1->pDatatype to a Unicode string wDocInfo1.pDatatype
574 cch = strlen(pDocInfo1->pDatatype);
575
576 wDocInfo1.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
577 if (!wDocInfo1.pDatatype)
578 {
579 ERR("HeapAlloc failed for wDocInfo1.pDatatype with last error %lu!\n", GetLastError());
580 goto Cleanup;
581 }
582
583 MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDatatype, -1, wDocInfo1.pDatatype, cch + 1);
584 }
585
586 if (pDocInfo1->pDocName)
587 {
588 // Convert pDocInfo1->pDocName to a Unicode string wDocInfo1.pDocName
589 cch = strlen(pDocInfo1->pDocName);
590
591 wDocInfo1.pDocName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
592 if (!wDocInfo1.pDocName)
593 {
594 ERR("HeapAlloc failed for wDocInfo1.pDocName with last error %lu!\n", GetLastError());
595 goto Cleanup;
596 }
597
598 MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDocName, -1, wDocInfo1.pDocName, cch + 1);
599 }
600
601 if (pDocInfo1->pOutputFile)
602 {
603 // Convert pDocInfo1->pOutputFile to a Unicode string wDocInfo1.pOutputFile
604 cch = strlen(pDocInfo1->pOutputFile);
605
606 wDocInfo1.pOutputFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
607 if (!wDocInfo1.pOutputFile)
608 {
609 ERR("HeapAlloc failed for wDocInfo1.pOutputFile with last error %lu!\n", GetLastError());
610 goto Cleanup;
611 }
612
613 MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pOutputFile, -1, wDocInfo1.pOutputFile, cch + 1);
614 }
615
616 dwReturnValue = StartDocPrinterW(hPrinter, Level, (PBYTE)&wDocInfo1);
617 dwErrorCode = GetLastError();
618
619 Cleanup:
620 if (wDocInfo1.pDatatype)
621 HeapFree(hProcessHeap, 0, wDocInfo1.pDatatype);
622
623 if (wDocInfo1.pDocName)
624 HeapFree(hProcessHeap, 0, wDocInfo1.pDocName);
625
626 if (wDocInfo1.pOutputFile)
627 HeapFree(hProcessHeap, 0, wDocInfo1.pOutputFile);
628
629 SetLastError(dwErrorCode);
630 return dwReturnValue;
631 }
632
633 DWORD WINAPI
634 StartDocPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
635 {
636 DWORD cbAddJobInfo1;
637 DWORD cbNeeded;
638 DWORD dwErrorCode;
639 DWORD dwReturnValue = 0;
640 PADDJOB_INFO_1W pAddJobInfo1 = NULL;
641 PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
642 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
643
644 // Sanity checks.
645 if (!pHandle)
646 {
647 dwErrorCode = ERROR_INVALID_HANDLE;
648 goto Cleanup;
649 }
650
651 if (!pDocInfo1)
652 {
653 dwErrorCode = ERROR_INVALID_PARAMETER;
654 goto Cleanup;
655 }
656
657 if (Level != 1)
658 {
659 dwErrorCode = ERROR_INVALID_LEVEL;
660 goto Cleanup;
661 }
662
663 if (pHandle->bStartedDoc)
664 {
665 dwErrorCode = ERROR_INVALID_PRINTER_STATE;
666 goto Cleanup;
667 }
668
669 // Check if we want to redirect output into a file.
670 if (pDocInfo1->pOutputFile)
671 {
672 // Do a StartDocPrinter RPC call in this case.
673 dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
674 }
675 else
676 {
677 // Allocate memory for the ADDJOB_INFO_1W structure and a path.
678 cbAddJobInfo1 = sizeof(ADDJOB_INFO_1W) + MAX_PATH * sizeof(WCHAR);
679 pAddJobInfo1 = HeapAlloc(hProcessHeap, 0, cbAddJobInfo1);
680 if (!pAddJobInfo1)
681 {
682 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
683 ERR("HeapAlloc failed with error %lu!\n", GetLastError());
684 goto Cleanup;
685 }
686
687 // Try to add a new job.
688 // This only succeeds if the printer is set to do spooled printing.
689 if (AddJobW((HANDLE)pHandle, 1, (PBYTE)pAddJobInfo1, cbAddJobInfo1, &cbNeeded))
690 {
691 // Do spooled printing.
692 dwErrorCode = _StartDocPrinterSpooled(pHandle, pDocInfo1, pAddJobInfo1);
693 }
694 else if (GetLastError() == ERROR_INVALID_ACCESS)
695 {
696 // ERROR_INVALID_ACCESS is returned when the printer is set to do direct printing.
697 // In this case, we do a StartDocPrinter RPC call.
698 dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
699 }
700 else
701 {
702 dwErrorCode = GetLastError();
703 ERR("AddJobW failed with error %lu!\n", dwErrorCode);
704 goto Cleanup;
705 }
706 }
707
708 if (dwErrorCode == ERROR_SUCCESS)
709 {
710 pHandle->bStartedDoc = TRUE;
711 dwReturnValue = pHandle->dwJobID;
712 }
713
714 Cleanup:
715 if (pAddJobInfo1)
716 HeapFree(hProcessHeap, 0, pAddJobInfo1);
717
718 SetLastError(dwErrorCode);
719 return dwReturnValue;
720 }
721
722 BOOL WINAPI
723 StartPagePrinter(HANDLE hPrinter)
724 {
725 DWORD dwErrorCode;
726 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
727
728 // Sanity checks.
729 if (!pHandle)
730 {
731 dwErrorCode = ERROR_INVALID_HANDLE;
732 goto Cleanup;
733 }
734
735 // Do the RPC call
736 RpcTryExcept
737 {
738 dwErrorCode = _RpcStartPagePrinter(pHandle->hPrinter);
739 }
740 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
741 {
742 dwErrorCode = RpcExceptionCode();
743 ERR("_RpcStartPagePrinter failed with exception code %lu!\n", dwErrorCode);
744 }
745 RpcEndExcept;
746
747 Cleanup:
748 SetLastError(dwErrorCode);
749 return (dwErrorCode == ERROR_SUCCESS);
750 }
751
752 BOOL WINAPI
753 WritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
754 {
755 DWORD dwErrorCode;
756 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
757
758 // Sanity checks.
759 if (!pHandle)
760 {
761 dwErrorCode = ERROR_INVALID_HANDLE;
762 goto Cleanup;
763 }
764
765 if (!pHandle->bStartedDoc)
766 {
767 dwErrorCode = ERROR_SPL_NO_STARTDOC;
768 goto Cleanup;
769 }
770
771 if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
772 {
773 // Write to the spool file. This doesn't need an RPC request.
774 if (!WriteFile(pHandle->hSPLFile, pBuf, cbBuf, pcWritten, NULL))
775 {
776 dwErrorCode = GetLastError();
777 ERR("WriteFile failed with error %lu!\n", dwErrorCode);
778 goto Cleanup;
779 }
780
781 dwErrorCode = ERROR_SUCCESS;
782 }
783 else
784 {
785 // TODO: This case (for direct printing or remote printing) has bad performance if multiple small-sized WritePrinter calls are performed.
786 // We may increase performance by writing into a buffer and only doing a single RPC call when the buffer is full.
787
788 // Do the RPC call
789 RpcTryExcept
790 {
791 dwErrorCode = _RpcWritePrinter(pHandle->hPrinter, pBuf, cbBuf, pcWritten);
792 }
793 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
794 {
795 dwErrorCode = RpcExceptionCode();
796 ERR("_RpcWritePrinter failed with exception code %lu!\n", dwErrorCode);
797 }
798 RpcEndExcept;
799 }
800
801 Cleanup:
802 SetLastError(dwErrorCode);
803 return (dwErrorCode == ERROR_SUCCESS);
804 }
805
806 BOOL WINAPI
807 XcvDataW(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded, PDWORD pdwStatus)
808 {
809 return FALSE;
810 }