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