[LOCALSPL, WINPRINT]
[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 != &_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 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 + 1;
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 dwPrintProcessorCount;
442 DWORD i;
443 HKEY hKey = NULL;
444 HKEY hSubKey = NULL;
445 PBYTE pCurrentOutputPrintProcessor;
446 PBYTE pCurrentOutputPrintProcessorInfo;
447 PRINTPROCESSOR_INFO_1W PrintProcessorInfo1;
448 PWSTR pwszTemp = NULL;
449
450 // Sanity checks
451 if (Level != 1)
452 {
453 dwErrorCode = ERROR_INVALID_LEVEL;
454 goto Cleanup;
455 }
456
457 if (!pcbNeeded || !pcReturned)
458 {
459 // This error is also caught by RPC and returned as RPC_X_NULL_REF_POINTER.
460 dwErrorCode = ERROR_INVALID_PARAMETER;
461 goto Cleanup;
462 }
463
464 // Verify pEnvironment and open its registry key.
465 // We use the registry and not the PrintProcessorList here, because the caller may request information about a different environment.
466 dwErrorCode = _OpenEnvironment(pEnvironment, &hKey);
467 if (dwErrorCode != ERROR_SUCCESS)
468 goto Cleanup;
469
470 // Open the "Print Processors" subkey.
471 dwErrorCode = (DWORD)RegOpenKeyExW(hKey, L"Print Processors", 0, KEY_READ, &hSubKey);
472 if (dwErrorCode != ERROR_SUCCESS)
473 {
474 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
475 goto Cleanup;
476 }
477
478 // Get the number of Print Processors and maximum sub key length.
479 dwErrorCode = (DWORD)RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwPrintProcessorCount, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL);
480 if (dwErrorCode != ERROR_SUCCESS)
481 {
482 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
483 goto Cleanup;
484 }
485
486 // Allocate a temporary buffer to let RegEnumKeyExW succeed.
487 pwszTemp = DllAllocSplMem((cchMaxSubKey + 1) * sizeof(WCHAR));
488 if (!pwszTemp)
489 {
490 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
491 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
492 goto Cleanup;
493 }
494
495 // Determine the required size of the output buffer.
496 *pcbNeeded = 0;
497
498 for (i = 0; i < dwPrintProcessorCount; i++)
499 {
500 // RegEnumKeyExW sucks! Unlike similar API functions, it only returns the actual numbers of characters copied when you supply a buffer large enough.
501 // So use pwszTemp with its size cchMaxSubKey for this.
502 cchPrintProcessor = cchMaxSubKey + 1;
503 dwErrorCode = (DWORD)RegEnumKeyExW(hSubKey, i, pwszTemp, &cchPrintProcessor, NULL, NULL, NULL, NULL);
504 if (dwErrorCode != ERROR_SUCCESS)
505 {
506 ERR("RegEnumKeyExW failed with status %lu!\n", dwErrorCode);
507 goto Cleanup;
508 }
509
510 *pcbNeeded += sizeof(PRINTPROCESSOR_INFO_1W) + (cchPrintProcessor + 1) * sizeof(WCHAR);
511 }
512
513 // Check if the supplied buffer is large enough.
514 if (cbBuf < *pcbNeeded)
515 {
516 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
517 goto Cleanup;
518 }
519
520 // Put the Print Processor strings right after the last PRINTPROCESSOR_INFO_1W structure.
521 pCurrentOutputPrintProcessorInfo = pPrintProcessorInfo;
522 pCurrentOutputPrintProcessor = pPrintProcessorInfo + dwPrintProcessorCount * sizeof(PRINTPROCESSOR_INFO_1W);
523
524 // Copy over all Print Processors.
525 for (i = 0; i < dwPrintProcessorCount; i++)
526 {
527 // This isn't really correct, but doesn't cause any harm, because we've extensively checked the size of the supplied buffer above.
528 cchPrintProcessor = cchMaxSubKey + 1;
529
530 // Copy the Print Processor name.
531 dwErrorCode = (DWORD)RegEnumKeyExW(hSubKey, i, (PWSTR)pCurrentOutputPrintProcessor, &cchPrintProcessor, NULL, NULL, NULL, NULL);
532 if (dwErrorCode != ERROR_SUCCESS)
533 {
534 ERR("RegEnumKeyExW failed with status %lu!\n", dwErrorCode);
535 goto Cleanup;
536 }
537
538 // Fill and copy the PRINTPROCESSOR_INFO_1W structure belonging to this Print Processor.
539 PrintProcessorInfo1.pName = (PWSTR)pCurrentOutputPrintProcessor;
540 CopyMemory(pCurrentOutputPrintProcessorInfo, &PrintProcessorInfo1, sizeof(PRINTPROCESSOR_INFO_1W));
541
542 // Advance to the next PRINTPROCESSOR_INFO_1W location and string location in the output buffer.
543 pCurrentOutputPrintProcessor += (cchPrintProcessor + 1) * sizeof(WCHAR);
544 pCurrentOutputPrintProcessorInfo += sizeof(PRINTPROCESSOR_INFO_1W);
545 }
546
547 // We've finished successfully!
548 *pcReturned = dwPrintProcessorCount;
549 dwErrorCode = ERROR_SUCCESS;
550
551 Cleanup:
552 if (pwszTemp)
553 DllFreeSplMem(pwszTemp);
554
555 if (hSubKey)
556 RegCloseKey(hSubKey);
557
558 if (hKey)
559 RegCloseKey(hKey);
560
561 SetLastError(dwErrorCode);
562 return (dwErrorCode == ERROR_SUCCESS);
563 }
564
565 /**
566 * @name LocalGetPrintProcessorDirectory
567 *
568 * Obtains the path to the local Print Processor directory.
569 * Print Provider function for GetPrintProcessorDirectoryA/GetPrintProcessorDirectoryW.
570 *
571 * @param pName
572 * Server Name. Ignored here, because every caller of LocalGetPrintProcessorDirectory is interested in the local directory.
573 *
574 * @param pEnvironment
575 * One of the predefined operating system and architecture "environment" strings (like "Windows NT x86").
576 * Alternatively, NULL to output the Print Processor directory of the current environment.
577 *
578 * @param Level
579 * The level of the (non-existing) structure supplied through pPrintProcessorInfo. This must be 1.
580 *
581 * @param pPrintProcessorInfo
582 * Pointer to the buffer that receives the full path to the Print Processor directory.
583 * Can be NULL if you just want to know the required size of the buffer.
584 *
585 * @param cbBuf
586 * Size of the buffer you supplied for pPrintProcessorInfo, in bytes.
587 *
588 * @param pcbNeeded
589 * Pointer to a variable that receives the required size of the buffer for pPrintProcessorInfo, in bytes.
590 * This parameter mustn't be NULL!
591 *
592 * @return
593 * TRUE if we successfully copied the directory into pPrintProcessorInfo, FALSE otherwise.
594 * A more specific error code can be obtained through GetLastError.
595 */
596 BOOL WINAPI
597 LocalGetPrintProcessorDirectory(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded)
598 {
599 const WCHAR wszPath[] = L"\\PRTPROCS\\";
600 const DWORD cchPath = _countof(wszPath) - 1;
601
602 DWORD cbDataWritten;
603 DWORD dwErrorCode;
604 HKEY hKey = NULL;
605
606 // Sanity checks
607 if (Level != 1)
608 {
609 dwErrorCode = ERROR_INVALID_LEVEL;
610 goto Cleanup;
611 }
612
613 if (!pcbNeeded)
614 {
615 // This error is also caught by RPC and returned as RPC_X_NULL_REF_POINTER.
616 dwErrorCode = ERROR_INVALID_PARAMETER;
617 goto Cleanup;
618 }
619
620 // Verify pEnvironment and open its registry key.
621 dwErrorCode = _OpenEnvironment(pEnvironment, &hKey);
622 if (dwErrorCode != ERROR_SUCCESS)
623 goto Cleanup;
624
625 // Determine the size of the required buffer.
626 dwErrorCode = (DWORD)RegQueryValueExW(hKey, L"Directory", NULL, NULL, NULL, pcbNeeded);
627 if (dwErrorCode != ERROR_SUCCESS)
628 {
629 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
630 goto Cleanup;
631 }
632
633 *pcbNeeded += cchSpoolDirectory;
634 *pcbNeeded += cchPath;
635
636 // Is the supplied buffer large enough?
637 if (cbBuf < *pcbNeeded)
638 {
639 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
640 goto Cleanup;
641 }
642
643 // Copy the path to the "prtprocs" directory into pPrintProcessorInfo
644 CopyMemory(pPrintProcessorInfo, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR));
645 CopyMemory(&pPrintProcessorInfo[cchSpoolDirectory], wszPath, cchPath * sizeof(WCHAR));
646
647 // Get the directory name from the registry.
648 dwErrorCode = (DWORD)RegQueryValueExW(hKey, L"Directory", NULL, NULL, &pPrintProcessorInfo[cchSpoolDirectory + cchPath], &cbDataWritten);
649 if (dwErrorCode != ERROR_SUCCESS)
650 {
651 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
652 goto Cleanup;
653 }
654
655 // We've finished successfully!
656 dwErrorCode = ERROR_SUCCESS;
657
658 Cleanup:
659 if (hKey)
660 RegCloseKey(hKey);
661
662 SetLastError(dwErrorCode);
663 return (dwErrorCode == ERROR_SUCCESS);
664 }