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