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