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