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