c4b95ea8b8dbfca3a5a748827965168c4fc132d1
[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 DWORD
11 _StartDocPrinterSpooled(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1, PADDJOB_INFO_1W pAddJobInfo1)
12 {
13 DWORD cbNeeded;
14 DWORD dwErrorCode;
15 PJOB_INFO_1W pJobInfo1 = NULL;
16
17 // Create the spool file.
18 pHandle->hSPLFile = CreateFileW(pAddJobInfo1->Path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
19 if (pHandle->hSPLFile == INVALID_HANDLE_VALUE)
20 {
21 dwErrorCode = GetLastError();
22 ERR("CreateFileW failed for \"%S\" with error %lu!\n", pAddJobInfo1->Path, dwErrorCode);
23 goto Cleanup;
24 }
25
26 // Get the size of the job information.
27 GetJobW(pHandle->hPrinter, pAddJobInfo1->JobId, 1, NULL, 0, &cbNeeded);
28 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
29 {
30 dwErrorCode = GetLastError();
31 ERR("GetJobW failed with error %lu!\n", dwErrorCode);
32 goto Cleanup;
33 }
34
35 // Allocate enough memory for the returned job information.
36 pJobInfo1 = HeapAlloc(hProcessHeap, 0, cbNeeded);
37 if (!pJobInfo1)
38 {
39 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
40 ERR("HeapAlloc failed with error %lu!\n", GetLastError());
41 goto Cleanup;
42 }
43
44 // Get the job information.
45 if (!GetJobW(pHandle->hPrinter, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, cbNeeded, &cbNeeded))
46 {
47 dwErrorCode = GetLastError();
48 ERR("GetJobW failed with error %lu!\n", dwErrorCode);
49 goto Cleanup;
50 }
51
52 // Add our document information.
53 pJobInfo1->pDatatype = pDocInfo1->pDatatype;
54 pJobInfo1->pDocument = pDocInfo1->pDocName;
55
56 // Set the new job information.
57 if (!SetJobW(pHandle->hPrinter, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, 0))
58 {
59 dwErrorCode = GetLastError();
60 ERR("SetJobW failed with error %lu!\n", dwErrorCode);
61 goto Cleanup;
62 }
63
64 // We were successful!
65 pHandle->dwJobID = pAddJobInfo1->JobId;
66 dwErrorCode = ERROR_SUCCESS;
67
68 Cleanup:
69 if (pJobInfo1)
70 HeapFree(hProcessHeap, 0, pJobInfo1);
71
72 return dwErrorCode;
73 }
74
75 static DWORD
76 _StartDocPrinterWithRPC(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1)
77 {
78 DWORD dwErrorCode;
79 WINSPOOL_DOC_INFO_CONTAINER DocInfoContainer;
80
81 DocInfoContainer.Level = 1;
82 DocInfoContainer.DocInfo.pDocInfo1 = (WINSPOOL_DOC_INFO_1*)pDocInfo1;
83
84 RpcTryExcept
85 {
86 dwErrorCode = _RpcStartDocPrinter(pHandle->hPrinter, &DocInfoContainer, &pHandle->dwJobID);
87 }
88 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
89 {
90 dwErrorCode = RpcExceptionCode();
91 ERR("_RpcStartDocPrinter failed with exception code %lu!\n", dwErrorCode);
92 }
93 RpcEndExcept;
94
95 return dwErrorCode;
96 }
97
98 BOOL WINAPI
99 EnumPrintersA(DWORD Flags, LPSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
100 {
101 return FALSE;
102 }
103
104 BOOL WINAPI
105 EnumPrintersW(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
106 {
107 DWORD dwErrorCode;
108
109 // Do the RPC call
110 RpcTryExcept
111 {
112 dwErrorCode = _RpcEnumPrinters(Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
113 }
114 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
115 {
116 dwErrorCode = RpcExceptionCode();
117 ERR("_RpcEnumPrinters failed with exception code %lu!\n", dwErrorCode);
118 }
119 RpcEndExcept;
120
121 SetLastError(dwErrorCode);
122 return (dwErrorCode == ERROR_SUCCESS);
123 }
124
125 BOOL WINAPI
126 ClosePrinter(HANDLE hPrinter)
127 {
128 DWORD dwErrorCode;
129 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
130
131 // Sanity checks.
132 if (!pHandle)
133 {
134 dwErrorCode = ERROR_INVALID_HANDLE;
135 goto Cleanup;
136 }
137
138 // Do the RPC call.
139 RpcTryExcept
140 {
141 dwErrorCode = _RpcClosePrinter(pHandle->hPrinter);
142 }
143 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
144 {
145 dwErrorCode = RpcExceptionCode();
146 ERR("_RpcClosePrinter failed with exception code %lu!\n", dwErrorCode);
147 }
148 RpcEndExcept;
149
150 // Close any open file handle.
151 if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
152 CloseHandle(pHandle->hSPLFile);
153
154 // Free the memory for the handle.
155 HeapFree(hProcessHeap, 0, pHandle);
156
157 Cleanup:
158 SetLastError(dwErrorCode);
159 return (dwErrorCode == ERROR_SUCCESS);
160
161 }
162
163 DWORD WINAPI
164 DeviceCapabilitiesA(LPCSTR pDevice, LPCSTR pPort, WORD fwCapability, LPSTR pOutput, const DEVMODEA* pDevMode)
165 {
166 return 0;
167 }
168
169 DWORD WINAPI
170 DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort, WORD fwCapability, LPWSTR pOutput, const DEVMODEW* pDevMode)
171 {
172 return 0;
173 }
174
175 LONG WINAPI
176 DocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName, PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput, DWORD fMode)
177 {
178 return 0;
179 }
180
181 LONG WINAPI
182 DocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName, PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput, DWORD fMode)
183 {
184 return 0;
185 }
186
187 BOOL WINAPI
188 EndDocPrinter(HANDLE hPrinter)
189 {
190 DWORD dwErrorCode;
191 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
192
193 // Sanity checks.
194 if (!pHandle)
195 {
196 dwErrorCode = ERROR_INVALID_HANDLE;
197 goto Cleanup;
198 }
199
200 if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
201 {
202 // For spooled jobs, the document is finished by calling _RpcScheduleJob.
203 RpcTryExcept
204 {
205 dwErrorCode = _RpcScheduleJob(pHandle->hPrinter, pHandle->dwJobID);
206 }
207 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
208 {
209 dwErrorCode = RpcExceptionCode();
210 ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode);
211 }
212 RpcEndExcept;
213
214 // Close the spool file handle.
215 CloseHandle(pHandle->hSPLFile);
216 }
217 else
218 {
219 // In all other cases, just call _RpcEndDocPrinter.
220 RpcTryExcept
221 {
222 dwErrorCode = _RpcEndDocPrinter(pHandle->hPrinter);
223 }
224 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
225 {
226 dwErrorCode = RpcExceptionCode();
227 ERR("_RpcEndDocPrinter failed with exception code %lu!\n", dwErrorCode);
228 }
229 RpcEndExcept;
230 }
231
232 // A new document can now be started again.
233 pHandle->bStartedDoc = FALSE;
234
235 Cleanup:
236 SetLastError(dwErrorCode);
237 return (dwErrorCode == ERROR_SUCCESS);
238 }
239
240 BOOL WINAPI
241 EndPagePrinter(HANDLE hPrinter)
242 {
243 DWORD dwErrorCode;
244 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
245
246 // Sanity checks.
247 if (!pHandle)
248 {
249 dwErrorCode = ERROR_INVALID_HANDLE;
250 goto Cleanup;
251 }
252
253 if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
254 {
255 // For spooled jobs, we don't need to do anything.
256 dwErrorCode = ERROR_SUCCESS;
257 }
258 else
259 {
260 // In all other cases, just call _RpcEndPagePrinter.
261 RpcTryExcept
262 {
263 dwErrorCode = _RpcEndPagePrinter(pHandle->hPrinter);
264 }
265 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
266 {
267 dwErrorCode = RpcExceptionCode();
268 ERR("_RpcEndPagePrinter failed with exception code %lu!\n", dwErrorCode);
269 }
270 RpcEndExcept;
271 }
272
273 Cleanup:
274 SetLastError(dwErrorCode);
275 return (dwErrorCode == ERROR_SUCCESS);
276 }
277
278 BOOL WINAPI
279 GetDefaultPrinterA(LPSTR pszBuffer, LPDWORD pcchBuffer)
280 {
281 return FALSE;
282 }
283
284 BOOL WINAPI
285 GetDefaultPrinterW(LPWSTR pszBuffer, LPDWORD pcchBuffer)
286 {
287 return FALSE;
288 }
289
290 BOOL WINAPI
291 GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
292 {
293 return FALSE;
294 }
295
296 BOOL WINAPI
297 GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
298 {
299 return FALSE;
300 }
301
302 BOOL WINAPI
303 GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
304 {
305 return FALSE;
306 }
307
308 BOOL WINAPI
309 GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
310 {
311 return FALSE;
312 }
313
314 BOOL WINAPI
315 OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefault)
316 {
317 BOOL bReturnValue = FALSE;
318 PWSTR pwszPrinterName = NULL;
319 PWSTR pwszDatatype = NULL;
320 PRINTER_DEFAULTSW wDefault = { 0 };
321 size_t StringLength;
322
323 if (pPrinterName)
324 {
325 // Convert pPrinterName to a Unicode string pwszPrinterName
326 StringLength = strlen(pPrinterName) + 1;
327
328 pwszPrinterName = HeapAlloc(hProcessHeap, 0, StringLength * sizeof(WCHAR));
329 if (!pwszPrinterName)
330 {
331 ERR("HeapAlloc failed for pwszPrinterName with last error %lu!\n", GetLastError());
332 goto Cleanup;
333 }
334
335 MultiByteToWideChar(CP_ACP, 0, pPrinterName, -1, pwszPrinterName, StringLength);
336 }
337
338 if (pDefault)
339 {
340 wDefault.DesiredAccess = pDefault->DesiredAccess;
341
342 if (pDefault->pDatatype)
343 {
344 // Convert pDefault->pDatatype to a Unicode string pwszDatatype that later becomes wDefault.pDatatype
345 StringLength = strlen(pDefault->pDatatype) + 1;
346
347 pwszDatatype = HeapAlloc(hProcessHeap, 0, StringLength * sizeof(WCHAR));
348 if (!pwszDatatype)
349 {
350 ERR("HeapAlloc failed for pwszDatatype with last error %lu!\n", GetLastError());
351 goto Cleanup;
352 }
353
354 MultiByteToWideChar(CP_ACP, 0, pDefault->pDatatype, -1, pwszDatatype, StringLength);
355 wDefault.pDatatype = pwszDatatype;
356 }
357
358 if (pDefault->pDevMode)
359 wDefault.pDevMode = GdiConvertToDevmodeW(pDefault->pDevMode);
360 }
361
362 bReturnValue = OpenPrinterW(pwszPrinterName, phPrinter, &wDefault);
363
364 Cleanup:
365 if (wDefault.pDevMode)
366 HeapFree(hProcessHeap, 0, wDefault.pDevMode);
367
368 if (pwszPrinterName)
369 HeapFree(hProcessHeap, 0, pwszPrinterName);
370
371 if (pwszDatatype)
372 HeapFree(hProcessHeap, 0, pwszDatatype);
373
374 return bReturnValue;
375 }
376
377 BOOL WINAPI
378 OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefault)
379 {
380 DWORD dwErrorCode;
381 HANDLE hPrinter;
382 PSPOOLER_HANDLE pHandle;
383 PWSTR pDatatype = NULL;
384 WINSPOOL_DEVMODE_CONTAINER DevModeContainer;
385 WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer = NULL;
386 ACCESS_MASK AccessRequired = 0;
387
388 // Prepare the additional parameters in the format required by _RpcOpenPrinter
389 if (pDefault)
390 {
391 pDatatype = pDefault->pDatatype;
392 DevModeContainer.cbBuf = sizeof(DEVMODEW);
393 DevModeContainer.pDevMode = (BYTE*)pDefault->pDevMode;
394 pDevModeContainer = &DevModeContainer;
395 AccessRequired = pDefault->DesiredAccess;
396 }
397
398 // Do the RPC call
399 RpcTryExcept
400 {
401 dwErrorCode = _RpcOpenPrinter(pPrinterName, &hPrinter, pDatatype, pDevModeContainer, AccessRequired);
402 }
403 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
404 {
405 dwErrorCode = RpcExceptionCode();
406 ERR("_RpcOpenPrinter failed with exception code %lu!\n", dwErrorCode);
407 }
408 RpcEndExcept;
409
410 if (dwErrorCode == ERROR_SUCCESS)
411 {
412 // Create a new SPOOLER_HANDLE structure.
413 pHandle = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, sizeof(SPOOLER_HANDLE));
414 if (!pHandle)
415 {
416 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
417 ERR("HeapAlloc failed with error %lu!\n", GetLastError());
418 goto Cleanup;
419 }
420
421 pHandle->hPrinter = hPrinter;
422 pHandle->hSPLFile = INVALID_HANDLE_VALUE;
423
424 // Return it as phPrinter.
425 *phPrinter = (HANDLE)pHandle;
426 }
427
428 Cleanup:
429 SetLastError(dwErrorCode);
430 return (dwErrorCode == ERROR_SUCCESS);
431 }
432
433 BOOL WINAPI
434 ReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
435 {
436 DWORD dwErrorCode;
437 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
438
439 // Sanity checks.
440 if (!pHandle)
441 {
442 dwErrorCode = ERROR_INVALID_HANDLE;
443 goto Cleanup;
444 }
445
446 // Do the RPC call
447 RpcTryExcept
448 {
449 dwErrorCode = _RpcReadPrinter(pHandle->hPrinter, pBuf, cbBuf, pNoBytesRead);
450 }
451 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
452 {
453 dwErrorCode = RpcExceptionCode();
454 ERR("_RpcReadPrinter failed with exception code %lu!\n", dwErrorCode);
455 }
456 RpcEndExcept;
457
458 Cleanup:
459 SetLastError(dwErrorCode);
460 return (dwErrorCode == ERROR_SUCCESS);
461 }
462
463 DWORD WINAPI
464 StartDocPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
465 {
466 DWORD cbAddJobInfo1;
467 DWORD cbNeeded;
468 DWORD dwErrorCode;
469 PADDJOB_INFO_1W pAddJobInfo1 = NULL;
470 PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
471 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
472
473 // Sanity checks.
474 if (!pHandle)
475 {
476 dwErrorCode = ERROR_INVALID_HANDLE;
477 goto Cleanup;
478 }
479
480 if (!pDocInfo1)
481 {
482 dwErrorCode = ERROR_INVALID_PARAMETER;
483 goto Cleanup;
484 }
485
486 if (Level != 1)
487 {
488 dwErrorCode = ERROR_INVALID_LEVEL;
489 goto Cleanup;
490 }
491
492 if (pHandle->bStartedDoc)
493 {
494 dwErrorCode = ERROR_INVALID_PRINTER_STATE;
495 goto Cleanup;
496 }
497
498 // Check if we want to redirect output into a file.
499 if (pDocInfo1->pOutputFile)
500 {
501 // Do a StartDocPrinter RPC call in this case.
502 dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
503 }
504 else
505 {
506 // Allocate memory for the ADDJOB_INFO_1W structure and a path.
507 cbAddJobInfo1 = sizeof(ADDJOB_INFO_1W) + MAX_PATH * sizeof(WCHAR);
508 pAddJobInfo1 = HeapAlloc(hProcessHeap, 0, cbAddJobInfo1);
509 if (!pAddJobInfo1)
510 {
511 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
512 ERR("HeapAlloc failed with error %lu!\n", GetLastError());
513 goto Cleanup;
514 }
515
516 // Try to add a new job.
517 // This only succeeds if the printer is set to do spooled printing.
518 if (AddJobW(pHandle->hPrinter, 1, (PBYTE)pAddJobInfo1, cbAddJobInfo1, &cbNeeded))
519 {
520 // Do spooled printing.
521 dwErrorCode = _StartDocPrinterSpooled(pHandle, pDocInfo1, pAddJobInfo1);
522 }
523 else if (GetLastError() == ERROR_INVALID_ACCESS)
524 {
525 // ERROR_INVALID_ACCESS is returned when the printer is set to do direct printing.
526 // In this case, we do a StartDocPrinter RPC call.
527 dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
528 }
529 else
530 {
531 dwErrorCode = GetLastError();
532 ERR("AddJobW failed with error %lu!\n", dwErrorCode);
533 goto Cleanup;
534 }
535 }
536
537 if (dwErrorCode == ERROR_SUCCESS)
538 pHandle->bStartedDoc = TRUE;
539
540 Cleanup:
541 if (pAddJobInfo1)
542 HeapFree(hProcessHeap, 0, pAddJobInfo1);
543
544 SetLastError(dwErrorCode);
545 return pHandle->dwJobID;
546 }
547
548 BOOL WINAPI
549 StartPagePrinter(HANDLE hPrinter)
550 {
551 DWORD dwErrorCode;
552 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
553
554 // Sanity checks.
555 if (!pHandle)
556 {
557 dwErrorCode = ERROR_INVALID_HANDLE;
558 goto Cleanup;
559 }
560
561 // Do the RPC call
562 RpcTryExcept
563 {
564 dwErrorCode = _RpcStartPagePrinter(pHandle->hPrinter);
565 }
566 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
567 {
568 dwErrorCode = RpcExceptionCode();
569 ERR("_RpcStartPagePrinter failed with exception code %lu!\n", dwErrorCode);
570 }
571 RpcEndExcept;
572
573 Cleanup:
574 SetLastError(dwErrorCode);
575 return (dwErrorCode == ERROR_SUCCESS);
576 }
577
578 BOOL WINAPI
579 WritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
580 {
581 DWORD dwErrorCode;
582 PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
583
584 // Sanity checks.
585 if (!pHandle)
586 {
587 dwErrorCode = ERROR_INVALID_HANDLE;
588 goto Cleanup;
589 }
590
591 if (!pHandle->bStartedDoc)
592 {
593 dwErrorCode = ERROR_SPL_NO_STARTDOC;
594 goto Cleanup;
595 }
596
597 if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
598 {
599 // Write to the spool file. This doesn't need an RPC request.
600 if (!WriteFile(pHandle->hSPLFile, pBuf, cbBuf, pcWritten, NULL))
601 {
602 dwErrorCode = GetLastError();
603 ERR("WriteFile failed with error %lu!\n", dwErrorCode);
604 goto Cleanup;
605 }
606
607 dwErrorCode = ERROR_SUCCESS;
608 }
609 else
610 {
611 // TODO: This case (for direct printing or remote printing) has bad performance if multiple small-sized WritePrinter calls are performed.
612 // We may increase performance by writing into a buffer and only doing a single RPC call when the buffer is full.
613
614 // Do the RPC call
615 RpcTryExcept
616 {
617 dwErrorCode = _RpcWritePrinter(pHandle->hPrinter, pBuf, cbBuf, pcWritten);
618 }
619 RpcExcept(EXCEPTION_EXECUTE_HANDLER)
620 {
621 dwErrorCode = RpcExceptionCode();
622 ERR("_RpcWritePrinter failed with exception code %lu!\n", dwErrorCode);
623 }
624 RpcEndExcept;
625 }
626
627 Cleanup:
628 SetLastError(dwErrorCode);
629 return (dwErrorCode == ERROR_SUCCESS);
630 }
631
632 BOOL WINAPI
633 XcvDataW(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded, PDWORD pdwStatus)
634 {
635 return FALSE;
636 }