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