bda9e5375f498f00c18bbbbae30a2cec8587457c
[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 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. Can be NULL to use the current environment.
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 // Use the current environment if none was supplied.
40 if (!pEnvironment)
41 pEnvironment = wszCurrentEnvironment;
42
43 // Construct the registry key of the demanded environment.
44 cchEnvironment = wcslen(pEnvironment);
45 pwszEnvironmentKey = DllAllocSplMem((cchEnvironmentsKey + cchEnvironment + 1) * sizeof(WCHAR));
46 if (!pwszEnvironmentKey)
47 {
48 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
49 goto Cleanup;
50 }
51
52 CopyMemory(pwszEnvironmentKey, wszEnvironmentsKey, cchEnvironmentsKey * sizeof(WCHAR));
53 CopyMemory(&pwszEnvironmentKey[cchEnvironmentsKey], pEnvironment, (cchEnvironment + 1) * sizeof(WCHAR));
54
55 // Open the registry key.
56 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, pwszEnvironmentKey, 0, KEY_READ, hKey);
57 if (dwErrorCode == ERROR_FILE_NOT_FOUND)
58 {
59 dwErrorCode = ERROR_INVALID_ENVIRONMENT;
60 goto Cleanup;
61 }
62 else if (dwErrorCode != ERROR_SUCCESS)
63 {
64 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
65 goto Cleanup;
66 }
67
68 Cleanup:
69 if (pwszEnvironmentKey)
70 DllFreeSplMem(pwszEnvironmentKey);
71
72 return dwErrorCode;
73 }
74
75 BOOL
76 FindDatatype(const PLOCAL_PRINT_PROCESSOR pPrintProcessor, PCWSTR pwszDatatype)
77 {
78 DWORD i;
79 PDATATYPES_INFO_1W pCurrentDatatype = pPrintProcessor->pDatatypesInfo1;
80
81 if (!pwszDatatype)
82 return FALSE;
83
84 for (i = 0; i < pPrintProcessor->dwDatatypeCount; i++)
85 {
86 if (wcsicmp(pCurrentDatatype->pName, pwszDatatype) == 0)
87 return TRUE;
88
89 ++pCurrentDatatype;
90 }
91
92 return FALSE;
93 }
94
95 PLOCAL_PRINT_PROCESSOR
96 FindPrintProcessor(PCWSTR pwszName)
97 {
98 PLIST_ENTRY pEntry;
99 PLOCAL_PRINT_PROCESSOR pPrintProcessor;
100
101 if (!pwszName)
102 return NULL;
103
104 for (pEntry = _PrintProcessorList.Flink; pEntry != &_PrintProcessorList; pEntry = pEntry->Flink)
105 {
106 pPrintProcessor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_PROCESSOR, Entry);
107
108 if (wcsicmp(pPrintProcessor->pwszName, pwszName) == 0)
109 return pPrintProcessor;
110 }
111
112 return NULL;
113 }
114
115 /**
116 * @name InitializePrintProcessorList
117 *
118 * Initializes a singly linked list of locally available Print Processors.
119 */
120 BOOL
121 InitializePrintProcessorList()
122 {
123 DWORD cbDatatypes;
124 DWORD cbFileName;
125 DWORD cchPrintProcessorPath;
126 DWORD cchMaxSubKey;
127 DWORD cchPrintProcessorName;
128 DWORD dwErrorCode;
129 DWORD dwSubKeys;
130 DWORD i;
131 HINSTANCE hinstPrintProcessor;
132 HKEY hKey = NULL;
133 HKEY hSubKey = NULL;
134 HKEY hSubSubKey = NULL;
135 PLOCAL_PRINT_PROCESSOR pPrintProcessor = NULL;
136 WCHAR wszFileName[MAX_PATH];
137 WCHAR wszPrintProcessorPath[MAX_PATH];
138
139 // Initialize an empty list for our Print Processors.
140 InitializeListHead(&_PrintProcessorList);
141
142 // Prepare the path to the Print Processor directory.
143 if (!LocalGetPrintProcessorDirectory(NULL, NULL, 1, (PBYTE)wszPrintProcessorPath, sizeof(wszPrintProcessorPath), &cchPrintProcessorPath))
144 {
145 dwErrorCode = GetLastError();
146 goto Cleanup;
147 }
148
149 // LocalGetPrintProcessorDirectory returns the number of copied bytes. Convert this into a number of characters without the terminating null-character.
150 cchPrintProcessorPath /= sizeof(WCHAR);
151 --cchPrintProcessorPath;
152
153 // Append a trailing backslash.
154 wszPrintProcessorPath[cchPrintProcessorPath] = L'\\';
155 ++cchPrintProcessorPath;
156
157 // Open the environment registry key.
158 dwErrorCode = _OpenEnvironment(NULL, &hKey);
159 if (dwErrorCode != ERROR_SUCCESS)
160 {
161 ERR("_OpenEnvironment failed with error %lu!\n", dwErrorCode);
162 goto Cleanup;
163 }
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 {
495 ERR("_OpenEnvironment failed with error %lu!\n", dwErrorCode);
496 goto Cleanup;
497 }
498
499 // Open the "Print Processors" subkey.
500 dwErrorCode = (DWORD)RegOpenKeyExW(hKey, L"Print Processors", 0, KEY_READ, &hSubKey);
501 if (dwErrorCode != ERROR_SUCCESS)
502 {
503 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
504 goto Cleanup;
505 }
506
507 // Get the number of Print Processors and maximum sub key length.
508 dwErrorCode = (DWORD)RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwPrintProcessorCount, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL);
509 if (dwErrorCode != ERROR_SUCCESS)
510 {
511 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
512 goto Cleanup;
513 }
514
515 // Allocate a temporary buffer to let RegEnumKeyExW succeed.
516 pwszTemp = DllAllocSplMem((cchMaxSubKey + 1) * sizeof(WCHAR));
517 if (!pwszTemp)
518 {
519 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
520 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
521 goto Cleanup;
522 }
523
524 // Determine the required size of the output buffer.
525 *pcbNeeded = 0;
526
527 for (i = 0; i < dwPrintProcessorCount; i++)
528 {
529 // RegEnumKeyExW sucks! Unlike similar API functions, it only returns the actual numbers of characters copied when you supply a buffer large enough.
530 // So use pwszTemp with its size cchMaxSubKey for this.
531 cchPrintProcessor = cchMaxSubKey + 1;
532 dwErrorCode = (DWORD)RegEnumKeyExW(hSubKey, i, pwszTemp, &cchPrintProcessor, NULL, NULL, NULL, NULL);
533 if (dwErrorCode != ERROR_SUCCESS)
534 {
535 ERR("RegEnumKeyExW failed with status %lu!\n", dwErrorCode);
536 goto Cleanup;
537 }
538
539 *pcbNeeded += sizeof(PRINTPROCESSOR_INFO_1W) + (cchPrintProcessor + 1) * sizeof(WCHAR);
540 }
541
542 // Check if the supplied buffer is large enough.
543 if (cbBuf < *pcbNeeded)
544 {
545 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
546 goto Cleanup;
547 }
548
549 // Put the Print Processor strings right after the last PRINTPROCESSOR_INFO_1W structure.
550 pCurrentOutputPrintProcessorInfo = pPrintProcessorInfo;
551 pCurrentOutputPrintProcessor = pPrintProcessorInfo + dwPrintProcessorCount * sizeof(PRINTPROCESSOR_INFO_1W);
552
553 // Copy over all Print Processors.
554 for (i = 0; i < dwPrintProcessorCount; i++)
555 {
556 // This isn't really correct, but doesn't cause any harm, because we've extensively checked the size of the supplied buffer above.
557 cchPrintProcessor = cchMaxSubKey + 1;
558
559 // Copy the Print Processor name.
560 dwErrorCode = (DWORD)RegEnumKeyExW(hSubKey, i, (PWSTR)pCurrentOutputPrintProcessor, &cchPrintProcessor, NULL, NULL, NULL, NULL);
561 if (dwErrorCode != ERROR_SUCCESS)
562 {
563 ERR("RegEnumKeyExW failed with status %lu!\n", dwErrorCode);
564 goto Cleanup;
565 }
566
567 // Fill and copy the PRINTPROCESSOR_INFO_1W structure belonging to this Print Processor.
568 PrintProcessorInfo1.pName = (PWSTR)pCurrentOutputPrintProcessor;
569 CopyMemory(pCurrentOutputPrintProcessorInfo, &PrintProcessorInfo1, sizeof(PRINTPROCESSOR_INFO_1W));
570
571 // Advance to the next PRINTPROCESSOR_INFO_1W location and string location in the output buffer.
572 pCurrentOutputPrintProcessor += (cchPrintProcessor + 1) * sizeof(WCHAR);
573 pCurrentOutputPrintProcessorInfo += sizeof(PRINTPROCESSOR_INFO_1W);
574 }
575
576 // We've finished successfully!
577 *pcReturned = dwPrintProcessorCount;
578 dwErrorCode = ERROR_SUCCESS;
579
580 Cleanup:
581 if (pwszTemp)
582 DllFreeSplMem(pwszTemp);
583
584 if (hSubKey)
585 RegCloseKey(hSubKey);
586
587 if (hKey)
588 RegCloseKey(hKey);
589
590 SetLastError(dwErrorCode);
591 return (dwErrorCode == ERROR_SUCCESS);
592 }
593
594 /**
595 * @name LocalGetPrintProcessorDirectory
596 *
597 * Obtains the path to the local Print Processor directory.
598 * Print Provider function for GetPrintProcessorDirectoryA/GetPrintProcessorDirectoryW.
599 *
600 * @param pName
601 * Server Name. Ignored here, because every caller of LocalGetPrintProcessorDirectory is interested in the local directory.
602 *
603 * @param pEnvironment
604 * One of the predefined operating system and architecture "environment" strings (like "Windows NT x86").
605 * Alternatively, NULL to output the Print Processor directory of the current environment.
606 *
607 * @param Level
608 * The level of the (non-existing) structure supplied through pPrintProcessorInfo. This must be 1.
609 *
610 * @param pPrintProcessorInfo
611 * Pointer to the buffer that receives the full path to the Print Processor directory.
612 * Can be NULL if you just want to know the required size of the buffer.
613 *
614 * @param cbBuf
615 * Size of the buffer you supplied for pPrintProcessorInfo, in bytes.
616 *
617 * @param pcbNeeded
618 * Pointer to a variable that receives the required size of the buffer for pPrintProcessorInfo, in bytes.
619 * This parameter mustn't be NULL!
620 *
621 * @return
622 * TRUE if we successfully copied the directory into pPrintProcessorInfo, FALSE otherwise.
623 * A more specific error code can be obtained through GetLastError.
624 */
625 BOOL WINAPI
626 LocalGetPrintProcessorDirectory(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pPrintProcessorInfo, DWORD cbBuf, PDWORD pcbNeeded)
627 {
628 const WCHAR wszPath[] = L"\\PRTPROCS\\";
629 const DWORD cchPath = _countof(wszPath) - 1;
630
631 DWORD cbDirectoryName;
632 DWORD dwErrorCode;
633 HKEY hKey = NULL;
634 PWSTR pwszDirectory = (PWSTR)pPrintProcessorInfo;
635
636 // Sanity checks
637 if (Level != 1)
638 {
639 dwErrorCode = ERROR_INVALID_LEVEL;
640 goto Cleanup;
641 }
642
643 if (!pcbNeeded)
644 {
645 // This error is also caught by RPC and returned as RPC_X_NULL_REF_POINTER.
646 dwErrorCode = ERROR_INVALID_PARAMETER;
647 goto Cleanup;
648 }
649
650 // Verify pEnvironment and open its registry key.
651 dwErrorCode = _OpenEnvironment(pEnvironment, &hKey);
652 if (dwErrorCode != ERROR_SUCCESS)
653 {
654 ERR("_OpenEnvironment failed with error %lu!\n", dwErrorCode);
655 goto Cleanup;
656 }
657
658 // Determine the size of the required buffer.
659 dwErrorCode = (DWORD)RegQueryValueExW(hKey, L"Directory", NULL, NULL, NULL, &cbDirectoryName);
660 if (dwErrorCode != ERROR_SUCCESS)
661 {
662 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
663 goto Cleanup;
664 }
665
666 *pcbNeeded = (cchSpoolDirectory + cchPath) * sizeof(WCHAR) + cbDirectoryName;
667
668 // Is the supplied buffer large enough?
669 if (cbBuf < *pcbNeeded)
670 {
671 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
672 goto Cleanup;
673 }
674
675 // Copy the path to the "prtprocs" directory into pPrintProcessorInfo
676 CopyMemory(pwszDirectory, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR));
677 CopyMemory(&pwszDirectory[cchSpoolDirectory], wszPath, cchPath * sizeof(WCHAR));
678
679 // Get the directory name from the registry.
680 dwErrorCode = (DWORD)RegQueryValueExW(hKey, L"Directory", NULL, NULL, (PBYTE)&pwszDirectory[cchSpoolDirectory + cchPath], &cbDirectoryName);
681 if (dwErrorCode != ERROR_SUCCESS)
682 {
683 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
684 goto Cleanup;
685 }
686
687 // We've finished successfully!
688 dwErrorCode = ERROR_SUCCESS;
689
690 Cleanup:
691 if (hKey)
692 RegCloseKey(hKey);
693
694 SetLastError(dwErrorCode);
695 return (dwErrorCode == ERROR_SUCCESS);
696 }