1 /* 2 * PROJECT: ReactOS Local Spooler 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Functions related to Print Processors 5 * COPYRIGHT: Copyright 2015-2017 Colin Finck (colin@reactos.org) 6 */ 7 8 #include "precomp.h" 9 10 11 // Local Variables 12 static LIST_ENTRY _PrintProcessorList; 13 14 /** 15 * @name _OpenEnvironment 16 * 17 * Checks a supplied pEnvironment variable for validity and opens its registry key. 18 * 19 * @param pEnvironment 20 * The pEnvironment variable to check. 21 * 22 * @param hKey 23 * On success, this variable will contain a HKEY to the opened registry key of the environment. 24 * You can use it for further tasks and have to close it with RegCloseKey. 25 * 26 * @return 27 * A Windows Error Code indicating success or failure. 28 */ 29 static DWORD 30 _OpenEnvironment(PCWSTR pEnvironment, PHKEY hKey) 31 { 32 const WCHAR wszEnvironmentsKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Environments\\"; 33 const DWORD cchEnvironmentsKey = _countof(wszEnvironmentsKey) - 1; 34 35 DWORD cchEnvironment; 36 DWORD dwErrorCode; 37 PWSTR pwszEnvironmentKey = NULL; 38 39 // Sanity checks 40 if (!pEnvironment) 41 { 42 dwErrorCode = ERROR_INVALID_ENVIRONMENT; 43 goto Cleanup; 44 } 45 46 // Construct the registry key of the demanded environment. 47 cchEnvironment = wcslen(pEnvironment); 48 pwszEnvironmentKey = DllAllocSplMem((cchEnvironmentsKey + cchEnvironment + 1) * sizeof(WCHAR)); 49 if (!pwszEnvironmentKey) 50 { 51 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; 52 ERR("DllAllocSplMem failed!\n"); 53 goto Cleanup; 54 } 55 56 CopyMemory(pwszEnvironmentKey, wszEnvironmentsKey, cchEnvironmentsKey * sizeof(WCHAR)); 57 CopyMemory(&pwszEnvironmentKey[cchEnvironmentsKey], pEnvironment, (cchEnvironment + 1) * sizeof(WCHAR)); 58 59 // Open the registry key. 60 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, pwszEnvironmentKey, 0, KEY_READ, hKey); 61 if (dwErrorCode == ERROR_FILE_NOT_FOUND) 62 { 63 dwErrorCode = ERROR_INVALID_ENVIRONMENT; 64 goto Cleanup; 65 } 66 else if (dwErrorCode != ERROR_SUCCESS) 67 { 68 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode); 69 goto Cleanup; 70 } 71 72 Cleanup: 73 if (pwszEnvironmentKey) 74 DllFreeSplMem(pwszEnvironmentKey); 75 76 return dwErrorCode; 77 } 78 79 BOOL 80 FindDatatype(const PLOCAL_PRINT_PROCESSOR pPrintProcessor, PCWSTR pwszDatatype) 81 { 82 DWORD i; 83 PDATATYPES_INFO_1W pCurrentDatatype = pPrintProcessor->pDatatypesInfo1; 84 85 TRACE("FindDatatype(%p, %S)\n", pPrintProcessor, pwszDatatype); 86 87 if (!pwszDatatype) 88 return FALSE; 89 90 for (i = 0; i < pPrintProcessor->dwDatatypeCount; i++) 91 { 92 if (wcsicmp(pCurrentDatatype->pName, pwszDatatype) == 0) 93 return TRUE; 94 95 ++pCurrentDatatype; 96 } 97 98 return FALSE; 99 } 100 101 PLOCAL_PRINT_PROCESSOR 102 FindPrintProcessor(PCWSTR pwszName) 103 { 104 PLIST_ENTRY pEntry; 105 PLOCAL_PRINT_PROCESSOR pPrintProcessor; 106 107 TRACE("FindPrintProcessor(%S)\n", pwszName); 108 109 if (!pwszName) 110 return NULL; 111 112 for (pEntry = _PrintProcessorList.Flink; pEntry != &_PrintProcessorList; pEntry = pEntry->Flink) 113 { 114 pPrintProcessor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_PROCESSOR, Entry); 115 116 if (wcsicmp(pPrintProcessor->pwszName, pwszName) == 0) 117 return pPrintProcessor; 118 } 119 120 return NULL; 121 } 122 123 /** 124 * @name InitializePrintProcessorList 125 * 126 * Initializes a singly linked list of locally available Print Processors. 127 */ 128 BOOL 129 InitializePrintProcessorList(void) 130 { 131 DWORD cbDatatypes; 132 DWORD cbFileName; 133 DWORD cchPrintProcessorPath; 134 DWORD cchMaxSubKey; 135 DWORD cchPrintProcessorName; 136 DWORD dwErrorCode; 137 DWORD dwSubKeys; 138 DWORD i; 139 HINSTANCE hinstPrintProcessor; 140 HKEY hKey = NULL; 141 HKEY hSubKey = NULL; 142 HKEY hSubSubKey = NULL; 143 PLOCAL_PRINT_PROCESSOR pPrintProcessor = NULL; 144 WCHAR wszFileName[MAX_PATH]; 145 WCHAR wszPrintProcessorPath[MAX_PATH]; 146 147 TRACE("InitializePrintProcessorList()\n"); 148 149 // Initialize an empty list for our Print Processors. 150 InitializeListHead(&_PrintProcessorList); 151 152 // Prepare the path to the Print Processor directory. 153 if (!LocalGetPrintProcessorDirectory(NULL, (PWSTR)wszCurrentEnvironment, 1, (PBYTE)wszPrintProcessorPath, sizeof(wszPrintProcessorPath), &cchPrintProcessorPath)) 154 { 155 dwErrorCode = GetLastError(); 156 goto Cleanup; 157 } 158 159 // LocalGetPrintProcessorDirectory returns the number of copied bytes. Convert this into a number of characters without the terminating null-character. 160 cchPrintProcessorPath /= sizeof(WCHAR); 161 --cchPrintProcessorPath; 162 163 // Append a trailing backslash. 164 wszPrintProcessorPath[cchPrintProcessorPath] = L'\\'; 165 ++cchPrintProcessorPath; 166 167 // Open the environment registry key. 168 dwErrorCode = _OpenEnvironment(wszCurrentEnvironment, &hKey); 169 if (dwErrorCode != ERROR_SUCCESS) 170 goto Cleanup; 171 172 // Open the "Print Processors" subkey. 173 dwErrorCode = (DWORD)RegOpenKeyExW(hKey, L"Print Processors", 0, KEY_READ, &hSubKey); 174 if (dwErrorCode != ERROR_SUCCESS) 175 { 176 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode); 177 goto Cleanup; 178 } 179 180 // Get the number of Print Processors and maximum sub key length. 181 dwErrorCode = (DWORD)RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwSubKeys, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL); 182 if (dwErrorCode != ERROR_SUCCESS) 183 { 184 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode); 185 goto Cleanup; 186 } 187 188 // Loop through all available local Print Processors. 189 for (i = 0; i < dwSubKeys; i++) 190 { 191 // Cleanup tasks from the previous run 192 if (hSubSubKey) 193 { 194 RegCloseKey(hSubSubKey); 195 hSubSubKey = NULL; 196 } 197 198 if (pPrintProcessor) 199 { 200 if (pPrintProcessor->pwszName) 201 DllFreeSplStr(pPrintProcessor->pwszName); 202 203 if (pPrintProcessor->pDatatypesInfo1) 204 DllFreeSplMem(pPrintProcessor->pDatatypesInfo1); 205 206 DllFreeSplMem(pPrintProcessor); 207 pPrintProcessor = NULL; 208 } 209 210 // Create a new LOCAL_PRINT_PROCESSOR structure for it. 211 pPrintProcessor = DllAllocSplMem(sizeof(LOCAL_PRINT_PROCESSOR)); 212 if (!pPrintProcessor) 213 { 214 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; 215 ERR("DllAllocSplMem failed!\n"); 216 goto Cleanup; 217 } 218 219 // Allocate memory for the Print Monitor Name. 220 pPrintProcessor->pwszName = DllAllocSplMem((cchMaxSubKey + 1) * sizeof(WCHAR)); 221 if (!pPrintProcessor->pwszName) 222 { 223 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; 224 ERR("DllAllocSplMem failed!\n"); 225 goto Cleanup; 226 } 227 228 // Get the name of this Print Processor. 229 cchPrintProcessorName = cchMaxSubKey + 1; 230 dwErrorCode = (DWORD)RegEnumKeyExW(hSubKey, i, pPrintProcessor->pwszName, &cchPrintProcessorName, NULL, NULL, NULL, NULL); 231 if (dwErrorCode != ERROR_SUCCESS) 232 { 233 ERR("RegEnumKeyExW failed with status %ld!\n", dwErrorCode); 234 continue; 235 } 236 237 // Open this Print Processor's registry key. 238 dwErrorCode = (DWORD)RegOpenKeyExW(hSubKey, pPrintProcessor->pwszName, 0, KEY_READ, &hSubSubKey); 239 if (dwErrorCode != ERROR_SUCCESS) 240 { 241 ERR("RegOpenKeyExW failed for Print Processor \"%S\" with status %lu!\n", pPrintProcessor->pwszName, dwErrorCode); 242 continue; 243 } 244 245 // Get the file name of the Print Processor. 246 cbFileName = sizeof(wszFileName); 247 dwErrorCode = (DWORD)RegQueryValueExW(hSubSubKey, L"Driver", NULL, NULL, (PBYTE)wszFileName, &cbFileName); 248 if (dwErrorCode != ERROR_SUCCESS) 249 { 250 ERR("RegQueryValueExW failed for Print Processor \"%S\" with status %lu!\n", pPrintProcessor->pwszName, dwErrorCode); 251 continue; 252 } 253 254 // Verify that our buffer is large enough. 255 if (cchPrintProcessorPath + cbFileName / sizeof(WCHAR) > MAX_PATH) 256 { 257 ERR("Print Processor directory \"%S\" for Print Processor \"%S\" is too long!\n", wszFileName, pPrintProcessor->pwszName); 258 continue; 259 } 260 261 // Construct the full path to the Print Processor. 262 CopyMemory(&wszPrintProcessorPath[cchPrintProcessorPath], wszFileName, cbFileName); 263 264 // Try to load it. 265 hinstPrintProcessor = LoadLibraryW(wszPrintProcessorPath); 266 if (!hinstPrintProcessor) 267 { 268 ERR("LoadLibraryW failed for \"%S\" with error %lu!\n", wszPrintProcessorPath, GetLastError()); 269 continue; 270 } 271 272 // Get and verify all its function pointers. 273 pPrintProcessor->pfnClosePrintProcessor = (PClosePrintProcessor)GetProcAddress(hinstPrintProcessor, "ClosePrintProcessor"); 274 if (!pPrintProcessor->pfnClosePrintProcessor) 275 { 276 ERR("Print Processor \"%S\" exports no ClosePrintProcessor!\n", wszPrintProcessorPath); 277 continue; 278 } 279 280 pPrintProcessor->pfnControlPrintProcessor = (PControlPrintProcessor)GetProcAddress(hinstPrintProcessor, "ControlPrintProcessor"); 281 if (!pPrintProcessor->pfnControlPrintProcessor) 282 { 283 ERR("Print Processor \"%S\" exports no ControlPrintProcessor!\n", wszPrintProcessorPath); 284 continue; 285 } 286 287 pPrintProcessor->pfnEnumPrintProcessorDatatypesW = (PEnumPrintProcessorDatatypesW)GetProcAddress(hinstPrintProcessor, "EnumPrintProcessorDatatypesW"); 288 if (!pPrintProcessor->pfnEnumPrintProcessorDatatypesW) 289 { 290 ERR("Print Processor \"%S\" exports no EnumPrintProcessorDatatypesW!\n", wszPrintProcessorPath); 291 continue; 292 } 293 294 pPrintProcessor->pfnGetPrintProcessorCapabilities = (PGetPrintProcessorCapabilities)GetProcAddress(hinstPrintProcessor, "GetPrintProcessorCapabilities"); 295 if (!pPrintProcessor->pfnGetPrintProcessorCapabilities) 296 { 297 ERR("Print Processor \"%S\" exports no GetPrintProcessorCapabilities!\n", wszPrintProcessorPath); 298 continue; 299 } 300 301 pPrintProcessor->pfnOpenPrintProcessor = (POpenPrintProcessor)GetProcAddress(hinstPrintProcessor, "OpenPrintProcessor"); 302 if (!pPrintProcessor->pfnOpenPrintProcessor) 303 { 304 ERR("Print Processor \"%S\" exports no OpenPrintProcessor!\n", wszPrintProcessorPath); 305 continue; 306 } 307 308 pPrintProcessor->pfnPrintDocumentOnPrintProcessor = (PPrintDocumentOnPrintProcessor)GetProcAddress(hinstPrintProcessor, "PrintDocumentOnPrintProcessor"); 309 if (!pPrintProcessor->pfnPrintDocumentOnPrintProcessor) 310 { 311 ERR("Print Processor \"%S\" exports no PrintDocumentOnPrintProcessor!\n", wszPrintProcessorPath); 312 continue; 313 } 314 315 // Get all supported datatypes. 316 pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, 0, &cbDatatypes, &pPrintProcessor->dwDatatypeCount); 317 pPrintProcessor->pDatatypesInfo1 = DllAllocSplMem(cbDatatypes); 318 if (!pPrintProcessor->pDatatypesInfo1) 319 { 320 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; 321 ERR("DllAllocSplMem failed!\n"); 322 goto Cleanup; 323 } 324 325 if (!pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, (PBYTE)pPrintProcessor->pDatatypesInfo1, cbDatatypes, &cbDatatypes, &pPrintProcessor->dwDatatypeCount)) 326 { 327 ERR("EnumPrintProcessorDatatypesW failed for Print Processor \"%S\" with error %lu!\n", wszPrintProcessorPath, GetLastError()); 328 continue; 329 } 330 331 // Add the Print Processor to the list. 332 InsertTailList(&_PrintProcessorList, &pPrintProcessor->Entry); 333 334 // Don't let the cleanup routines free this. 335 pPrintProcessor = NULL; 336 } 337 338 dwErrorCode = ERROR_SUCCESS; 339 340 Cleanup: 341 // Inside the loop 342 if (hSubSubKey) 343 RegCloseKey(hSubSubKey); 344 345 if (pPrintProcessor) 346 { 347 if (pPrintProcessor->pwszName) 348 DllFreeSplStr(pPrintProcessor->pwszName); 349 350 if (pPrintProcessor->pDatatypesInfo1) 351 DllFreeSplMem(pPrintProcessor->pDatatypesInfo1); 352 353 DllFreeSplMem(pPrintProcessor); 354 } 355 356 // Outside the loop 357 if (hSubKey) 358 RegCloseKey(hSubKey); 359 360 if (hKey) 361 RegCloseKey(hKey); 362 363 SetLastError(dwErrorCode); 364 return (dwErrorCode == ERROR_SUCCESS); 365 } 366 367 /** 368 * @name LocalEnumPrintProcessorDatatypes 369 * 370 * Obtains an array of all datatypes supported by a particular Print Processor. 371 * Print Provider function for EnumPrintProcessorDatatypesA/EnumPrintProcessorDatatypesW. 372 * 373 * @param pName 374 * Server Name. Ignored here, because every caller of LocalEnumPrintProcessorDatatypes is interested in the local directory. 375 * 376 * @param pPrintProcessorName 377 * The (case-insensitive) name of the Print Processor to query. 378 * 379 * @param Level 380 * The level of the structure supplied through pDatatypes. This must be 1. 381 * 382 * @param pDatatypes 383 * Pointer to the buffer that receives an array of DATATYPES_INFO_1W structures. 384 * Can be NULL if you just want to know the required size of the buffer. 385 * 386 * @param cbBuf 387 * Size of the buffer you supplied for pDatatypes, in bytes. 388 * 389 * @param pcbNeeded 390 * Pointer to a variable that receives the required size of the buffer for pDatatypes, in bytes. 391 * This parameter mustn't be NULL! 392 * 393 * @param pcReturned 394 * Pointer to a variable that receives the number of elements of the DATATYPES_INFO_1W array. 395 * This parameter mustn't be NULL! 396 * 397 * @return 398 * TRUE if we successfully copied the array into pDatatypes, FALSE otherwise. 399 * A more specific error code can be obtained through GetLastError. 400 */ 401 BOOL WINAPI 402 LocalEnumPrintProcessorDatatypes(LPWSTR pName, LPWSTR pPrintProcessorName, DWORD Level, LPBYTE pDatatypes, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned) 403 { 404 DWORD dwErrorCode; 405 PLOCAL_PRINT_PROCESSOR pPrintProcessor; 406 407 TRACE("LocalEnumPrintProcessorDatatypes(%S, %S, %lu, %p, %lu, %p, %p)\n", pName, pPrintProcessorName, Level, pDatatypes, cbBuf, pcbNeeded, pcReturned); 408 409 // Sanity checks 410 if (Level != 1) 411 { 412 dwErrorCode = ERROR_INVALID_LEVEL; 413 goto Cleanup; 414 } 415 416 // Try to find the Print Processor. 417 pPrintProcessor = FindPrintProcessor(pPrintProcessorName); 418 if (!pPrintProcessor) 419 { 420 dwErrorCode = ERROR_UNKNOWN_PRINTPROCESSOR; 421 goto Cleanup; 422 } 423 424 // Call its EnumPrintProcessorDatatypesW function. 425 if (pPrintProcessor->pfnEnumPrintProcessorDatatypesW(pName, pPrintProcessorName, Level, pDatatypes, cbBuf, pcbNeeded, pcReturned)) 426 dwErrorCode = ERROR_SUCCESS; 427 else 428 dwErrorCode = GetLastError(); 429 430 Cleanup: 431 SetLastError(dwErrorCode); 432 return (dwErrorCode == ERROR_SUCCESS); 433 } 434 435 /** 436 * @name LocalEnumPrintProcessors 437 * 438 * Obtains an array of all available Print Processors on this computer. 439 * Print Provider function for EnumPrintProcessorsA/EnumPrintProcessorsW. 440 * 441 * @param pName 442 * Server Name. Ignored here, because every caller of LocalEnumPrintProcessors is interested in the local directory. 443 * 444 * @param pEnvironment 445 * One of the predefined operating system and architecture "environment" strings (like "Windows NT x86"). 446 * Alternatively, NULL to output the Print Processor directory of the current environment. 447 * 448 * @param Level 449 * The level of the structure supplied through pPrintProcessorInfo. This must be 1. 450 * 451 * @param pPrintProcessorInfo 452 * Pointer to the buffer that receives an array of PRINTPROCESSOR_INFO_1W structures. 453 * Can be NULL if you just want to know the required size of the buffer. 454 * 455 * @param cbBuf 456 * Size of the buffer you supplied for pPrintProcessorInfo, in bytes. 457 * 458 * @param pcbNeeded 459 * Pointer to a variable that receives the required size of the buffer for pPrintProcessorInfo, in bytes. 460 * This parameter mustn't be NULL! 461 * 462 * @param pcReturned 463 * Pointer to a variable that receives the number of elements of the PRINTPROCESSOR_INFO_1W array. 464 * This parameter mustn't be NULL! 465 * 466 * @return 467 * TRUE if we successfully copied the array into pPrintProcessorInfo, FALSE otherwise. 468 * A more specific error code can be obtained through GetLastError. 469 */ 470 BOOL WINAPI 471 LocalEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned) 472 { 473 DWORD cchMaxSubKey; 474 DWORD cchPrintProcessor; 475 DWORD dwErrorCode; 476 DWORD dwPrintProcessorCount; 477 DWORD i; 478 HKEY hKey = NULL; 479 HKEY hSubKey = NULL; 480 PBYTE pCurrentOutputPrintProcessor; 481 PBYTE pCurrentOutputPrintProcessorInfo; 482 PRINTPROCESSOR_INFO_1W PrintProcessorInfo1; 483 PWSTR pwszTemp = NULL; 484 485 TRACE("LocalEnumPrintProcessors(%S, %S, %lu, %p, %lu, %p, %p)\n", pName, pEnvironment, Level, pPrintProcessorInfo, cbBuf, pcbNeeded, pcReturned); 486 487 // Sanity checks 488 if (Level != 1) 489 { 490 dwErrorCode = ERROR_INVALID_LEVEL; 491 goto Cleanup; 492 } 493 494 if (!pcbNeeded || !pcReturned) 495 { 496 // This error is also caught by RPC and returned as RPC_X_NULL_REF_POINTER. 497 dwErrorCode = ERROR_INVALID_PARAMETER; 498 goto Cleanup; 499 } 500 501 // Verify pEnvironment and open its registry key. 502 // We use the registry and not the PrintProcessorList here, because the caller may request information about a different environment. 503 dwErrorCode = _OpenEnvironment(pEnvironment, &hKey); 504 if (dwErrorCode != ERROR_SUCCESS) 505 goto Cleanup; 506 507 // Open the "Print Processors" subkey. 508 dwErrorCode = (DWORD)RegOpenKeyExW(hKey, L"Print Processors", 0, KEY_READ, &hSubKey); 509 if (dwErrorCode != ERROR_SUCCESS) 510 { 511 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode); 512 goto Cleanup; 513 } 514 515 // Get the number of Print Processors and maximum sub key length. 516 dwErrorCode = (DWORD)RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwPrintProcessorCount, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL); 517 if (dwErrorCode != ERROR_SUCCESS) 518 { 519 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode); 520 goto Cleanup; 521 } 522 523 // Allocate a temporary buffer to let RegEnumKeyExW succeed. 524 pwszTemp = DllAllocSplMem((cchMaxSubKey + 1) * sizeof(WCHAR)); 525 if (!pwszTemp) 526 { 527 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; 528 ERR("DllAllocSplMem failed!\n"); 529 goto Cleanup; 530 } 531 532 // Determine the required size of the output buffer. 533 *pcbNeeded = 0; 534 535 for (i = 0; i < dwPrintProcessorCount; i++) 536 { 537 // RegEnumKeyExW sucks! Unlike similar API functions, it only returns the actual numbers of characters copied when you supply a buffer large enough. 538 // So use pwszTemp with its size cchMaxSubKey for this. 539 cchPrintProcessor = cchMaxSubKey + 1; 540 dwErrorCode = (DWORD)RegEnumKeyExW(hSubKey, i, pwszTemp, &cchPrintProcessor, NULL, NULL, NULL, NULL); 541 if (dwErrorCode != ERROR_SUCCESS) 542 { 543 ERR("RegEnumKeyExW failed with status %lu!\n", dwErrorCode); 544 goto Cleanup; 545 } 546 547 *pcbNeeded += sizeof(PRINTPROCESSOR_INFO_1W) + (cchPrintProcessor + 1) * sizeof(WCHAR); 548 } 549 550 // Check if the supplied buffer is large enough. 551 if (cbBuf < *pcbNeeded) 552 { 553 dwErrorCode = ERROR_INSUFFICIENT_BUFFER; 554 goto Cleanup; 555 } 556 557 // Put the Print Processor strings right after the last PRINTPROCESSOR_INFO_1W structure. 558 pCurrentOutputPrintProcessorInfo = pPrintProcessorInfo; 559 pCurrentOutputPrintProcessor = pPrintProcessorInfo + dwPrintProcessorCount * sizeof(PRINTPROCESSOR_INFO_1W); 560 561 // Copy over all Print Processors. 562 for (i = 0; i < dwPrintProcessorCount; i++) 563 { 564 // This isn't really correct, but doesn't cause any harm, because we've extensively checked the size of the supplied buffer above. 565 cchPrintProcessor = cchMaxSubKey + 1; 566 567 // Copy the Print Processor name. 568 dwErrorCode = (DWORD)RegEnumKeyExW(hSubKey, i, (PWSTR)pCurrentOutputPrintProcessor, &cchPrintProcessor, NULL, NULL, NULL, NULL); 569 if (dwErrorCode != ERROR_SUCCESS) 570 { 571 ERR("RegEnumKeyExW failed with status %lu!\n", dwErrorCode); 572 goto Cleanup; 573 } 574 575 // Fill and copy the PRINTPROCESSOR_INFO_1W structure belonging to this Print Processor. 576 PrintProcessorInfo1.pName = (PWSTR)pCurrentOutputPrintProcessor; 577 CopyMemory(pCurrentOutputPrintProcessorInfo, &PrintProcessorInfo1, sizeof(PRINTPROCESSOR_INFO_1W)); 578 579 // Advance to the next PRINTPROCESSOR_INFO_1W location and string location in the output buffer. 580 pCurrentOutputPrintProcessor += (cchPrintProcessor + 1) * sizeof(WCHAR); 581 pCurrentOutputPrintProcessorInfo += sizeof(PRINTPROCESSOR_INFO_1W); 582 } 583 584 // We've finished successfully! 585 *pcReturned = dwPrintProcessorCount; 586 dwErrorCode = ERROR_SUCCESS; 587 588 Cleanup: 589 if (pwszTemp) 590 DllFreeSplMem(pwszTemp); 591 592 if (hSubKey) 593 RegCloseKey(hSubKey); 594 595 if (hKey) 596 RegCloseKey(hKey); 597 598 SetLastError(dwErrorCode); 599 return (dwErrorCode == ERROR_SUCCESS); 600 } 601 602 /** 603 * @name LocalGetPrintProcessorDirectory 604 * 605 * Obtains the path to the local Print Processor directory. 606 * Print Provider function for GetPrintProcessorDirectoryA/GetPrintProcessorDirectoryW. 607 * 608 * @param pName 609 * Server Name. Ignored here, because every caller of LocalGetPrintProcessorDirectory is interested in the local directory. 610 * 611 * @param pEnvironment 612 * One of the predefined operating system and architecture "environment" strings (like "Windows NT x86"). 613 * 614 * @param Level 615 * The level of the (non-existing) structure supplied through pPrintProcessorInfo. This must be 1. 616 * 617 * @param pPrintProcessorInfo 618 * Pointer to the buffer that receives the full path to the Print Processor directory. 619 * Can be NULL if you just want to know the required size of the buffer. 620 * 621 * @param cbBuf 622 * Size of the buffer you supplied for pPrintProcessorInfo, in bytes. 623 * 624 * @param pcbNeeded 625 * Pointer to a variable that receives the required size of the buffer for pPrintProcessorInfo, in bytes. 626 * This parameter mustn't be NULL! 627 * 628 * @return 629 * TRUE if we successfully copied the directory into pPrintProcessorInfo, FALSE otherwise. 630 * A more specific error code can be obtained through GetLastError. 631 */ 632 BOOL WINAPI 633 LocalGetPrintProcessorDirectory(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pPrintProcessorInfo, DWORD cbBuf, PDWORD pcbNeeded) 634 { 635 const WCHAR wszPath[] = L"\\PRTPROCS\\"; 636 const DWORD cchPath = _countof(wszPath) - 1; 637 638 DWORD cbDirectoryName; 639 DWORD dwErrorCode; 640 HKEY hKey = NULL; 641 PWSTR pwszDirectory = (PWSTR)pPrintProcessorInfo; 642 643 TRACE("LocalGetPrintProcessorDirectory(%S, %S, %lu, %p, %lu, %p)\n", pName, pEnvironment, Level, pPrintProcessorInfo, cbBuf, pcbNeeded); 644 645 // Verify pEnvironment and open its registry key. 646 dwErrorCode = _OpenEnvironment(pEnvironment, &hKey); 647 if (dwErrorCode != ERROR_SUCCESS) 648 { 649 ERR("_OpenEnvironment failed with error %lu!\n", dwErrorCode); 650 goto Cleanup; 651 } 652 653 // Determine the size of the required buffer. 654 dwErrorCode = (DWORD)RegQueryValueExW(hKey, L"Directory", NULL, NULL, NULL, &cbDirectoryName); 655 if (dwErrorCode != ERROR_SUCCESS) 656 { 657 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode); 658 goto Cleanup; 659 } 660 661 *pcbNeeded = (cchSpoolDirectory + cchPath) * sizeof(WCHAR) + cbDirectoryName; 662 663 // Is the supplied buffer large enough? 664 if (cbBuf < *pcbNeeded) 665 { 666 dwErrorCode = ERROR_INSUFFICIENT_BUFFER; 667 goto Cleanup; 668 } 669 670 // Copy the path to the "prtprocs" directory into pPrintProcessorInfo 671 CopyMemory(pwszDirectory, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR)); 672 CopyMemory(&pwszDirectory[cchSpoolDirectory], wszPath, cchPath * sizeof(WCHAR)); 673 674 // Get the directory name from the registry. 675 dwErrorCode = (DWORD)RegQueryValueExW(hKey, L"Directory", NULL, NULL, (PBYTE)&pwszDirectory[cchSpoolDirectory + cchPath], &cbDirectoryName); 676 if (dwErrorCode != ERROR_SUCCESS) 677 { 678 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode); 679 goto Cleanup; 680 } 681 682 // We've finished successfully! 683 dwErrorCode = ERROR_SUCCESS; 684 685 Cleanup: 686 if (hKey) 687 RegCloseKey(hKey); 688 689 SetLastError(dwErrorCode); 690 return (dwErrorCode == ERROR_SUCCESS); 691 } 692