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