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