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