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