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