602dd830d4a5955daf299e9ff6a0fd7df4ad18a2
[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 = _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 /**
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 = DllAllocSplMem((cchMaxSubKey + 1) * sizeof(WCHAR));
186 if (!pwszPrintProcessorName)
187 {
188 ERR("DllAllocSplMem 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 DllFreeSplStr(pPrintProcessor->pwszName);
206
207 DllFreeSplMem(pPrintProcessor);
208 pPrintProcessor = NULL;
209 }
210
211 if (pDatatypesInfo1)
212 {
213 DllFreeSplMem(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 = DllAllocSplMem(sizeof(LOCAL_PRINT_PROCESSOR));
263 pPrintProcessor->pwszName = AllocSplStr(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 = DllAllocSplMem(cbDatatypes);
311 if (!pDatatypesInfo1)
312 {
313 ERR("DllAllocSplMem 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 = AllocSplStr(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 DllFreeSplStr(pwszDatatype);
354
355 if (pDatatypesInfo1)
356 DllFreeSplMem(pDatatypesInfo1);
357
358 if (pPrintProcessor)
359 {
360 if (pPrintProcessor->pwszName)
361 DllFreeSplStr(pPrintProcessor->pwszName);
362
363 DllFreeSplMem(pPrintProcessor);
364 }
365
366 if (pwszPrintProcessorName)
367 DllFreeSplStr(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 pwszTemp = NULL;
486
487 // Sanity checks
488 if (Level != 1)
489 {
490 SetLastError(ERROR_INVALID_LEVEL);
491 goto Cleanup;
492 }
493
494 if (!pcbNeeded || !pcReturned)
495 {
496 // This error must be caught by RPC and returned as RPC_X_NULL_REF_POINTER.
497 ERR("pcbNeeded or pcReturned is NULL!\n");
498 goto Cleanup;
499 }
500
501 // Verify pEnvironment and open its registry key.
502 if (!_OpenEnvironment(pEnvironment, &hKey))
503 goto Cleanup;
504
505 // Open the "Print Processors" subkey.
506 lStatus = RegOpenKeyExW(hKey, L"Print Processors", 0, KEY_READ, &hSubKey);
507 if (lStatus != ERROR_SUCCESS)
508 {
509 ERR("RegOpenKeyExW failed with status %ld!\n", lStatus);
510 goto Cleanup;
511 }
512
513 // Get the number of Print Processors and maximum sub key length.
514 lStatus = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, pcReturned, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL);
515 if (lStatus != ERROR_SUCCESS)
516 {
517 ERR("RegQueryInfoKeyW failed with status %ld!\n", lStatus);
518 goto Cleanup;
519 }
520
521 // Allocate a temporary buffer to let RegEnumKeyExW succeed.
522 pwszTemp = DllAllocSplMem((cchMaxSubKey + 1) * sizeof(WCHAR));
523 if (!pwszTemp)
524 {
525 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
526 goto Cleanup;
527 }
528
529 // Determine the required size of the output buffer.
530 *pcbNeeded = 0;
531
532 for (i = 0; i < *pcReturned; i++)
533 {
534 // RegEnumKeyExW sucks! Unlike similar API functions, it only returns the actual numbers of characters copied when you supply a buffer large enough.
535 // So use pwszTemp with its size cchMaxSubKey for this.
536 cchPrintProcessor = cchMaxSubKey;
537 lStatus = RegEnumKeyExW(hSubKey, i, pwszTemp, &cchPrintProcessor, NULL, NULL, NULL, NULL);
538 if (lStatus != ERROR_SUCCESS)
539 {
540 ERR("RegEnumKeyExW failed with status %ld!\n", lStatus);
541 goto Cleanup;
542 }
543
544 *pcbNeeded += sizeof(PRINTPROCESSOR_INFO_1W) + (cchPrintProcessor + 1) * sizeof(WCHAR);
545 }
546
547 // Check if the supplied buffer is large enough.
548 if (cbBuf < *pcbNeeded)
549 {
550 SetLastError(ERROR_INSUFFICIENT_BUFFER);
551 goto Cleanup;
552 }
553
554 // Put the Print Processor strings right after the last PRINTPROCESSOR_INFO_1W structure.
555 pCurrentOutputPrintProcessorInfo = pPrintProcessorInfo;
556 pCurrentOutputPrintProcessor = pPrintProcessorInfo + *pcReturned * sizeof(PRINTPROCESSOR_INFO_1W);
557
558 // Copy over all Print Processors.
559 for (i = 0; i < *pcReturned; i++)
560 {
561 // This isn't really correct, but doesn't cause any harm, because we've extensively checked the size of the supplied buffer above.
562 cchPrintProcessor = cchMaxSubKey;
563
564 // Copy the Print Processor name.
565 lStatus = RegEnumKeyExW(hSubKey, i, (PWSTR)pCurrentOutputPrintProcessor, &cchPrintProcessor, NULL, NULL, NULL, NULL);
566 if (lStatus != ERROR_SUCCESS)
567 {
568 ERR("RegEnumKeyExW failed with status %ld!\n", lStatus);
569 goto Cleanup;
570 }
571
572 // Fill and copy the PRINTPROCESSOR_INFO_1W structure belonging to this Print Processor.
573 PrintProcessorInfo1.pName = (PWSTR)pCurrentOutputPrintProcessor;
574 CopyMemory(pCurrentOutputPrintProcessorInfo, &PrintProcessorInfo1, sizeof(PRINTPROCESSOR_INFO_1W));
575
576 // Advance to the next PRINTPROCESSOR_INFO_1W location and string location in the output buffer.
577 pCurrentOutputPrintProcessor += (cchPrintProcessor + 1) * sizeof(WCHAR);
578 pCurrentOutputPrintProcessorInfo += sizeof(PRINTPROCESSOR_INFO_1W);
579 }
580
581 // We've finished successfully!
582 SetLastError(ERROR_SUCCESS);
583 bReturnValue = TRUE;
584
585 Cleanup:
586 if (pwszTemp)
587 DllFreeSplMem(pwszTemp);
588
589 if (hSubKey)
590 RegCloseKey(hSubKey);
591
592 if (hKey)
593 RegCloseKey(hKey);
594
595 return bReturnValue;
596 }
597
598 /**
599 * @name LocalGetPrintProcessorDirectory
600 *
601 * Obtains the path to the local Print Processor directory.
602 * Print Provider function for GetPrintProcessorDirectoryA/GetPrintProcessorDirectoryW.
603 *
604 * @param pName
605 * Server Name. Ignored here, because every caller of LocalGetPrintProcessorDirectory is interested in the local directory.
606 *
607 * @param pEnvironment
608 * One of the predefined operating system and architecture "environment" strings (like "Windows NT x86").
609 * Alternatively, NULL to output the Print Processor directory of the current environment.
610 *
611 * @param Level
612 * The level of the (non-existing) structure supplied through pPrintProcessorInfo. This must be 1.
613 *
614 * @param pPrintProcessorInfo
615 * Pointer to the buffer that receives the full path to the Print Processor directory.
616 * Can be NULL if you just want to know the required size of the buffer.
617 *
618 * @param cbBuf
619 * Size of the buffer you supplied for pPrintProcessorInfo, in bytes.
620 *
621 * @param pcbNeeded
622 * Pointer to a variable that receives the required size of the buffer for pPrintProcessorInfo, in bytes.
623 * This parameter mustn't be NULL!
624 *
625 * @return
626 * TRUE if we successfully copied the directory into pPrintProcessorInfo, FALSE otherwise.
627 * A more specific error code can be obtained through GetLastError.
628 */
629 BOOL WINAPI
630 LocalGetPrintProcessorDirectory(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded)
631 {
632 const WCHAR wszPath[] = L"\\PRTPROCS\\";
633 const DWORD cchPath = _countof(wszPath) - 1;
634
635 BOOL bReturnValue = FALSE;
636 DWORD cbDataWritten;
637 HKEY hKey = NULL;
638 LONG lStatus;
639
640 // Sanity checks
641 if (Level != 1)
642 {
643 SetLastError(ERROR_INVALID_LEVEL);
644 goto Cleanup;
645 }
646
647 if (!pcbNeeded)
648 {
649 // This error must be caught by RPC and returned as RPC_X_NULL_REF_POINTER.
650 ERR("pcbNeeded is NULL!\n");
651 goto Cleanup;
652 }
653
654 // Verify pEnvironment and open its registry key.
655 if (!_OpenEnvironment(pEnvironment, &hKey))
656 goto Cleanup;
657
658 // Determine the size of the required buffer.
659 lStatus = RegQueryValueExW(hKey, L"Directory", NULL, NULL, NULL, pcbNeeded);
660 if (lStatus != ERROR_SUCCESS)
661 {
662 ERR("RegQueryValueExW failed with status %ld!\n", lStatus);
663 goto Cleanup;
664 }
665
666 *pcbNeeded += cchSpoolDirectory;
667 *pcbNeeded += cchPath;
668
669 // Is the supplied buffer large enough?
670 if (cbBuf < *pcbNeeded)
671 {
672 SetLastError(ERROR_INSUFFICIENT_BUFFER);
673 goto Cleanup;
674 }
675
676 // Copy the path to the "prtprocs" directory into pPrintProcessorInfo
677 CopyMemory(pPrintProcessorInfo, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR));
678 CopyMemory(&pPrintProcessorInfo[cchSpoolDirectory], wszPath, cchPath * sizeof(WCHAR));
679
680 // Get the directory name from the registry.
681 lStatus = RegQueryValueExW(hKey, L"Directory", NULL, NULL, &pPrintProcessorInfo[cchSpoolDirectory + cchPath], &cbDataWritten);
682 if (lStatus != ERROR_SUCCESS)
683 {
684 ERR("RegQueryValueExW failed with status %ld!\n", lStatus);
685 goto Cleanup;
686 }
687
688 // We've finished successfully!
689 SetLastError(ERROR_SUCCESS);
690 bReturnValue = TRUE;
691
692 Cleanup:
693 if (hKey)
694 RegCloseKey(hKey);
695
696 return bReturnValue;
697 }