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>
12 static LIST_ENTRY _PrintProcessorList
;
15 * @name _OpenEnvironment
17 * Checks a supplied pEnvironment variable for validity and opens its registry key.
20 * The pEnvironment variable to check. Can be NULL to use the current environment.
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.
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.
31 _OpenEnvironment(PCWSTR pEnvironment
, PHKEY hKey
)
33 const WCHAR wszEnvironmentsKey
[] = L
"SYSTEM\\CurrentControlSet\\Control\\Print\\Environments\\";
34 const DWORD cchEnvironmentsKey
= _countof(wszEnvironmentsKey
) - 1;
36 BOOL bReturnValue
= FALSE
;
39 PWSTR pwszEnvironmentKey
= NULL
;
41 // Use the current environment if none was supplied.
43 pEnvironment
= wszCurrentEnvironment
;
45 // Construct the registry key of the demanded environment.
46 cchEnvironment
= wcslen(pEnvironment
);
47 pwszEnvironmentKey
= DllAllocSplMem((cchEnvironmentsKey
+ cchEnvironment
+ 1) * sizeof(WCHAR
));
48 if (!pwszEnvironmentKey
)
50 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
54 CopyMemory(pwszEnvironmentKey
, wszEnvironmentsKey
, cchEnvironmentsKey
* sizeof(WCHAR
));
55 CopyMemory(&pwszEnvironmentKey
[cchEnvironmentsKey
], pEnvironment
, (cchEnvironment
+ 1) * sizeof(WCHAR
));
57 // Open the registry key.
58 lStatus
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, pwszEnvironmentKey
, 0, KEY_READ
, hKey
);
59 if (lStatus
== ERROR_FILE_NOT_FOUND
)
61 SetLastError(ERROR_INVALID_ENVIRONMENT
);
64 else if (lStatus
!= ERROR_SUCCESS
)
66 ERR("RegOpenKeyExW failed with status %ld!\n", lStatus
);
73 if (pwszEnvironmentKey
)
74 DllFreeSplMem(pwszEnvironmentKey
);
80 FindDatatype(PLOCAL_PRINT_PROCESSOR pPrintProcessor
, PWSTR pwszDatatype
)
83 PDATATYPES_INFO_1W pCurrentDatatype
= pPrintProcessor
->pDatatypesInfo1
;
85 for (i
= 0; i
< pPrintProcessor
->dwDatatypeCount
; i
++)
87 if (wcsicmp(pCurrentDatatype
->pName
, pwszDatatype
) == 0)
96 PLOCAL_PRINT_PROCESSOR
97 FindPrintProcessor(PWSTR pwszName
)
100 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
102 for (pEntry
= _PrintProcessorList
.Flink
; pEntry
; pEntry
= pEntry
->Flink
)
104 pPrintProcessor
= CONTAINING_RECORD(pEntry
, LOCAL_PRINT_PROCESSOR
, Entry
);
106 if (wcsicmp(pPrintProcessor
->pwszName
, pwszName
) == 0)
107 return pPrintProcessor
;
114 * @name InitializePrintProcessorList
116 * Initializes a singly linked list of locally available Print Processors.
119 InitializePrintProcessorList()
123 DWORD cchPrintProcessorPath
;
125 DWORD cchPrintProcessorName
;
128 HINSTANCE hinstPrintProcessor
;
131 HKEY hSubSubKey
= NULL
;
133 PLOCAL_PRINT_PROCESSOR pPrintProcessor
= NULL
;
134 PWSTR pwszPrintProcessorName
= NULL
;
135 WCHAR wszFileName
[MAX_PATH
];
136 WCHAR wszPrintProcessorPath
[MAX_PATH
];
138 // Initialize an empty list for our Print Processors.
139 InitializeListHead(&_PrintProcessorList
);
141 // Prepare the path to the Print Processor directory.
142 if (!LocalGetPrintProcessorDirectory(NULL
, NULL
, 1, (PBYTE
)wszPrintProcessorPath
, sizeof(wszPrintProcessorPath
), &cchPrintProcessorPath
))
145 cchPrintProcessorPath
/= sizeof(WCHAR
);
146 wszPrintProcessorPath
[cchPrintProcessorPath
++] = L
'\\';
148 // Open the environment registry key.
149 if (!_OpenEnvironment(NULL
, &hKey
))
152 // Open the "Print Processors" subkey.
153 lStatus
= RegOpenKeyExW(hKey
, L
"Print Processors", 0, KEY_READ
, &hSubKey
);
154 if (lStatus
!= ERROR_SUCCESS
)
156 ERR("RegOpenKeyExW failed with status %ld!\n", lStatus
);
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
)
164 ERR("RegQueryInfoKeyW failed with status %ld!\n", lStatus
);
168 // Allocate a temporary buffer for the Print Processor names.
169 pwszPrintProcessorName
= DllAllocSplMem((cchMaxSubKey
+ 1) * sizeof(WCHAR
));
170 if (!pwszPrintProcessorName
)
172 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
176 // Loop through all available local Print Processors.
177 for (i
= 0; i
< dwSubKeys
; i
++)
179 // Cleanup tasks from the previous run
182 RegCloseKey(hSubSubKey
);
188 if (pPrintProcessor
->pwszName
)
189 DllFreeSplStr(pPrintProcessor
->pwszName
);
191 if (pPrintProcessor
->pDatatypesInfo1
)
192 DllFreeSplMem(pPrintProcessor
->pDatatypesInfo1
);
194 DllFreeSplMem(pPrintProcessor
);
195 pPrintProcessor
= NULL
;
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
)
203 ERR("RegEnumKeyExW failed with status %ld!\n", lStatus
);
207 // Open this Print Processor's registry key.
208 lStatus
= RegOpenKeyExW(hSubKey
, pwszPrintProcessorName
, 0, KEY_READ
, &hSubSubKey
);
209 if (lStatus
!= ERROR_SUCCESS
)
211 ERR("RegOpenKeyExW failed for Print Processor \"%S\" with status %ld!\n", pwszPrintProcessorName
, lStatus
);
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
)
220 ERR("RegQueryValueExW failed for Print Processor \"%S\" with status %ld!\n", pwszPrintProcessorName
, lStatus
);
224 // Verify that our buffer is large enough.
225 if (cchPrintProcessorPath
+ cbFileName
/ sizeof(WCHAR
) > MAX_PATH
)
227 ERR("Print Processor directory \"%S\" for Print Processor \"%S\" is too long!\n", wszFileName
, pwszPrintProcessorName
);
231 // Construct the full path to the Print Processor.
232 CopyMemory(&wszPrintProcessorPath
[cchPrintProcessorPath
], wszFileName
, cbFileName
);
235 hinstPrintProcessor
= LoadLibraryW(wszPrintProcessorPath
);
236 if (lStatus
!= ERROR_SUCCESS
)
238 ERR("LoadLibraryW failed for \"%S\" with error %lu!\n", wszPrintProcessorPath
, GetLastError());
242 // Create a new LOCAL_PRINT_PROCESSOR structure for it.
243 pPrintProcessor
= DllAllocSplMem(sizeof(LOCAL_PRINT_PROCESSOR
));
244 pPrintProcessor
->pwszName
= AllocSplStr(pwszPrintProcessorName
);
246 // Get and verify all its function pointers.
247 pPrintProcessor
->pfnClosePrintProcessor
= (PClosePrintProcessor
)GetProcAddress(hinstPrintProcessor
, "ClosePrintProcessor");
248 if (!pPrintProcessor
->pfnClosePrintProcessor
)
250 ERR("Print Processor \"%S\" exports no ClosePrintProcessor!\n", wszPrintProcessorPath
);
254 pPrintProcessor
->pfnControlPrintProcessor
= (PControlPrintProcessor
)GetProcAddress(hinstPrintProcessor
, "ControlPrintProcessor");
255 if (!pPrintProcessor
->pfnControlPrintProcessor
)
257 ERR("Print Processor \"%S\" exports no ControlPrintProcessor!\n", wszPrintProcessorPath
);
261 pPrintProcessor
->pfnEnumPrintProcessorDatatypesW
= (PEnumPrintProcessorDatatypesW
)GetProcAddress(hinstPrintProcessor
, "EnumPrintProcessorDatatypesW");
262 if (!pPrintProcessor
->pfnEnumPrintProcessorDatatypesW
)
264 ERR("Print Processor \"%S\" exports no EnumPrintProcessorDatatypesW!\n", wszPrintProcessorPath
);
268 pPrintProcessor
->pfnGetPrintProcessorCapabilities
= (PGetPrintProcessorCapabilities
)GetProcAddress(hinstPrintProcessor
, "GetPrintProcessorCapabilities");
269 if (!pPrintProcessor
->pfnGetPrintProcessorCapabilities
)
271 ERR("Print Processor \"%S\" exports no GetPrintProcessorCapabilities!\n", wszPrintProcessorPath
);
275 pPrintProcessor
->pfnOpenPrintProcessor
= (POpenPrintProcessor
)GetProcAddress(hinstPrintProcessor
, "OpenPrintProcessor");
276 if (!pPrintProcessor
->pfnOpenPrintProcessor
)
278 ERR("Print Processor \"%S\" exports no OpenPrintProcessor!\n", wszPrintProcessorPath
);
282 pPrintProcessor
->pfnPrintDocumentOnPrintProcessor
= (PPrintDocumentOnPrintProcessor
)GetProcAddress(hinstPrintProcessor
, "PrintDocumentOnPrintProcessor");
283 if (!pPrintProcessor
->pfnPrintDocumentOnPrintProcessor
)
285 ERR("Print Processor \"%S\" exports no PrintDocumentOnPrintProcessor!\n", wszPrintProcessorPath
);
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
)
294 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
298 if (!pPrintProcessor
->pfnEnumPrintProcessorDatatypesW(NULL
, NULL
, 1, (PBYTE
)pPrintProcessor
->pDatatypesInfo1
, cbDatatypes
, &cbDatatypes
, &pPrintProcessor
->dwDatatypeCount
))
300 ERR("EnumPrintProcessorDatatypesW failed for Print Processor \"%S\" with error %lu!\n", wszPrintProcessorPath
, GetLastError());
304 // Add the Print Processor to the list.
305 InsertTailList(&_PrintProcessorList
, &pPrintProcessor
->Entry
);
307 // Don't let the cleanup routines free this.
308 pPrintProcessor
= NULL
;
314 RegCloseKey(hSubSubKey
);
318 if (pPrintProcessor
->pwszName
)
319 DllFreeSplStr(pPrintProcessor
->pwszName
);
321 if (pPrintProcessor
->pDatatypesInfo1
)
322 DllFreeSplMem(pPrintProcessor
->pDatatypesInfo1
);
324 DllFreeSplMem(pPrintProcessor
);
328 if (pwszPrintProcessorName
)
329 DllFreeSplStr(pwszPrintProcessorName
);
332 RegCloseKey(hSubKey
);
339 * @name LocalEnumPrintProcessorDatatypes
341 * Obtains an array of all datatypes supported by a particular Print Processor.
342 * Print Provider function for EnumPrintProcessorDatatypesA/EnumPrintProcessorDatatypesW.
345 * Server Name. Ignored here, because every caller of LocalEnumPrintProcessorDatatypes is interested in the local directory.
347 * @param pPrintProcessorName
348 * The (case-insensitive) name of the Print Processor to query.
351 * The level of the structure supplied through pDatatypes. This must be 1.
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.
358 * Size of the buffer you supplied for pDatatypes, in bytes.
361 * Pointer to a variable that receives the required size of the buffer for pDatatypes, in bytes.
362 * This parameter mustn't be NULL!
365 * Pointer to a variable that receives the number of elements of the DATATYPES_INFO_1W array.
366 * This parameter mustn't be NULL!
369 * TRUE if we successfully copied the array into pDatatypes, FALSE otherwise.
370 * A more specific error code can be obtained through GetLastError.
373 LocalEnumPrintProcessorDatatypes(LPWSTR pName
, LPWSTR pPrintProcessorName
, DWORD Level
, LPBYTE pDatatypes
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
375 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
380 SetLastError(ERROR_INVALID_LEVEL
);
384 // Try to find the Print Processor.
385 pPrintProcessor
= FindPrintProcessor(pPrintProcessorName
);
386 if (!pPrintProcessor
)
388 SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR
);
392 // Call its EnumPrintProcessorDatatypesW function.
393 return pPrintProcessor
->pfnEnumPrintProcessorDatatypesW(pName
, pPrintProcessorName
, Level
, pDatatypes
, cbBuf
, pcbNeeded
, pcReturned
);
397 * @name LocalEnumPrintProcessors
399 * Obtains an array of all available Print Processors on this computer.
400 * Print Provider function for EnumPrintProcessorsA/EnumPrintProcessorsW.
403 * Server Name. Ignored here, because every caller of LocalEnumPrintProcessors is interested in the local directory.
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.
410 * The level of the structure supplied through pPrintProcessorInfo. This must be 1.
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.
417 * Size of the buffer you supplied for pPrintProcessorInfo, in bytes.
420 * Pointer to a variable that receives the required size of the buffer for pPrintProcessorInfo, in bytes.
421 * This parameter mustn't be NULL!
424 * Pointer to a variable that receives the number of elements of the PRINTPROCESSOR_INFO_1W array.
425 * This parameter mustn't be NULL!
428 * TRUE if we successfully copied the array into pPrintProcessorInfo, FALSE otherwise.
429 * A more specific error code can be obtained through GetLastError.
432 LocalEnumPrintProcessors(LPWSTR pName
, LPWSTR pEnvironment
, DWORD Level
, LPBYTE pPrintProcessorInfo
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
434 BOOL bReturnValue
= FALSE
;
436 DWORD cchPrintProcessor
;
441 PBYTE pCurrentOutputPrintProcessor
;
442 PBYTE pCurrentOutputPrintProcessorInfo
;
443 PRINTPROCESSOR_INFO_1W PrintProcessorInfo1
;
444 PWSTR pwszTemp
= NULL
;
449 SetLastError(ERROR_INVALID_LEVEL
);
453 if (!pcbNeeded
|| !pcReturned
)
455 // This error must be caught by RPC and returned as RPC_X_NULL_REF_POINTER.
456 ERR("pcbNeeded or pcReturned is NULL!\n");
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
))
465 // Open the "Print Processors" subkey.
466 lStatus
= RegOpenKeyExW(hKey
, L
"Print Processors", 0, KEY_READ
, &hSubKey
);
467 if (lStatus
!= ERROR_SUCCESS
)
469 ERR("RegOpenKeyExW failed with status %ld!\n", lStatus
);
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
)
477 ERR("RegQueryInfoKeyW failed with status %ld!\n", lStatus
);
481 // Allocate a temporary buffer to let RegEnumKeyExW succeed.
482 pwszTemp
= DllAllocSplMem((cchMaxSubKey
+ 1) * sizeof(WCHAR
));
485 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
489 // Determine the required size of the output buffer.
492 for (i
= 0; i
< *pcReturned
; i
++)
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
)
500 ERR("RegEnumKeyExW failed with status %ld!\n", lStatus
);
504 *pcbNeeded
+= sizeof(PRINTPROCESSOR_INFO_1W
) + (cchPrintProcessor
+ 1) * sizeof(WCHAR
);
507 // Check if the supplied buffer is large enough.
508 if (cbBuf
< *pcbNeeded
)
510 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
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
);
518 // Copy over all Print Processors.
519 for (i
= 0; i
< *pcReturned
; i
++)
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
;
524 // Copy the Print Processor name.
525 lStatus
= RegEnumKeyExW(hSubKey
, i
, (PWSTR
)pCurrentOutputPrintProcessor
, &cchPrintProcessor
, NULL
, NULL
, NULL
, NULL
);
526 if (lStatus
!= ERROR_SUCCESS
)
528 ERR("RegEnumKeyExW failed with status %ld!\n", lStatus
);
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
));
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
);
541 // We've finished successfully!
542 SetLastError(ERROR_SUCCESS
);
547 DllFreeSplMem(pwszTemp
);
550 RegCloseKey(hSubKey
);
559 * @name LocalGetPrintProcessorDirectory
561 * Obtains the path to the local Print Processor directory.
562 * Print Provider function for GetPrintProcessorDirectoryA/GetPrintProcessorDirectoryW.
565 * Server Name. Ignored here, because every caller of LocalGetPrintProcessorDirectory is interested in the local directory.
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.
572 * The level of the (non-existing) structure supplied through pPrintProcessorInfo. This must be 1.
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.
579 * Size of the buffer you supplied for pPrintProcessorInfo, in bytes.
582 * Pointer to a variable that receives the required size of the buffer for pPrintProcessorInfo, in bytes.
583 * This parameter mustn't be NULL!
586 * TRUE if we successfully copied the directory into pPrintProcessorInfo, FALSE otherwise.
587 * A more specific error code can be obtained through GetLastError.
590 LocalGetPrintProcessorDirectory(LPWSTR pName
, LPWSTR pEnvironment
, DWORD Level
, LPBYTE pPrintProcessorInfo
, DWORD cbBuf
, LPDWORD pcbNeeded
)
592 const WCHAR wszPath
[] = L
"\\PRTPROCS\\";
593 const DWORD cchPath
= _countof(wszPath
) - 1;
595 BOOL bReturnValue
= FALSE
;
603 SetLastError(ERROR_INVALID_LEVEL
);
609 // This error must be caught by RPC and returned as RPC_X_NULL_REF_POINTER.
610 ERR("pcbNeeded is NULL!\n");
614 // Verify pEnvironment and open its registry key.
615 if (!_OpenEnvironment(pEnvironment
, &hKey
))
618 // Determine the size of the required buffer.
619 lStatus
= RegQueryValueExW(hKey
, L
"Directory", NULL
, NULL
, NULL
, pcbNeeded
);
620 if (lStatus
!= ERROR_SUCCESS
)
622 ERR("RegQueryValueExW failed with status %ld!\n", lStatus
);
626 *pcbNeeded
+= cchSpoolDirectory
;
627 *pcbNeeded
+= cchPath
;
629 // Is the supplied buffer large enough?
630 if (cbBuf
< *pcbNeeded
)
632 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
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
));
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
)
644 ERR("RegQueryValueExW failed with status %ld!\n", lStatus
);
648 // We've finished successfully!
649 SetLastError(ERROR_SUCCESS
);