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