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 * A Windows Error Code indicating success or failure.
30 _OpenEnvironment(PCWSTR pEnvironment
, PHKEY hKey
)
32 const WCHAR wszEnvironmentsKey
[] = L
"SYSTEM\\CurrentControlSet\\Control\\Print\\Environments\\";
33 const DWORD cchEnvironmentsKey
= _countof(wszEnvironmentsKey
) - 1;
37 PWSTR pwszEnvironmentKey
= NULL
;
39 // Use the current environment if none was supplied.
41 pEnvironment
= wszCurrentEnvironment
;
43 // Construct the registry key of the demanded environment.
44 cchEnvironment
= wcslen(pEnvironment
);
45 pwszEnvironmentKey
= DllAllocSplMem((cchEnvironmentsKey
+ cchEnvironment
+ 1) * sizeof(WCHAR
));
46 if (!pwszEnvironmentKey
)
48 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
52 CopyMemory(pwszEnvironmentKey
, wszEnvironmentsKey
, cchEnvironmentsKey
* sizeof(WCHAR
));
53 CopyMemory(&pwszEnvironmentKey
[cchEnvironmentsKey
], pEnvironment
, (cchEnvironment
+ 1) * sizeof(WCHAR
));
55 // Open the registry key.
56 dwErrorCode
= (DWORD
)RegOpenKeyExW(HKEY_LOCAL_MACHINE
, pwszEnvironmentKey
, 0, KEY_READ
, hKey
);
57 if (dwErrorCode
== ERROR_FILE_NOT_FOUND
)
59 dwErrorCode
= ERROR_INVALID_ENVIRONMENT
;
62 else if (dwErrorCode
!= ERROR_SUCCESS
)
64 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode
);
69 if (pwszEnvironmentKey
)
70 DllFreeSplMem(pwszEnvironmentKey
);
76 FindDatatype(const PLOCAL_PRINT_PROCESSOR pPrintProcessor
, PCWSTR pwszDatatype
)
79 PDATATYPES_INFO_1W pCurrentDatatype
= pPrintProcessor
->pDatatypesInfo1
;
81 for (i
= 0; i
< pPrintProcessor
->dwDatatypeCount
; i
++)
83 if (wcsicmp(pCurrentDatatype
->pName
, pwszDatatype
) == 0)
92 PLOCAL_PRINT_PROCESSOR
93 FindPrintProcessor(PCWSTR pwszName
)
96 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
98 for (pEntry
= _PrintProcessorList
.Flink
; pEntry
!= &_PrintProcessorList
; pEntry
= pEntry
->Flink
)
100 pPrintProcessor
= CONTAINING_RECORD(pEntry
, LOCAL_PRINT_PROCESSOR
, Entry
);
102 if (wcsicmp(pPrintProcessor
->pwszName
, pwszName
) == 0)
103 return pPrintProcessor
;
110 * @name InitializePrintProcessorList
112 * Initializes a singly linked list of locally available Print Processors.
115 InitializePrintProcessorList()
119 DWORD cchPrintProcessorPath
;
121 DWORD cchPrintProcessorName
;
125 HINSTANCE hinstPrintProcessor
;
128 HKEY hSubSubKey
= NULL
;
129 PLOCAL_PRINT_PROCESSOR pPrintProcessor
= NULL
;
130 WCHAR wszFileName
[MAX_PATH
];
131 WCHAR wszPrintProcessorPath
[MAX_PATH
];
133 // Initialize an empty list for our Print Processors.
134 InitializeListHead(&_PrintProcessorList
);
136 // Prepare the path to the Print Processor directory.
137 if (!LocalGetPrintProcessorDirectory(NULL
, NULL
, 1, (PBYTE
)wszPrintProcessorPath
, sizeof(wszPrintProcessorPath
), &cchPrintProcessorPath
))
139 dwErrorCode
= GetLastError();
143 cchPrintProcessorPath
/= sizeof(WCHAR
);
144 wszPrintProcessorPath
[cchPrintProcessorPath
++] = L
'\\';
146 // Open the environment registry key.
147 dwErrorCode
= _OpenEnvironment(NULL
, &hKey
);
148 if (dwErrorCode
!= ERROR_SUCCESS
)
151 // Open the "Print Processors" subkey.
152 dwErrorCode
= (DWORD
)RegOpenKeyExW(hKey
, L
"Print Processors", 0, KEY_READ
, &hSubKey
);
153 if (dwErrorCode
!= ERROR_SUCCESS
)
155 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode
);
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
)
163 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode
);
167 // Loop through all available local Print Processors.
168 for (i
= 0; i
< dwSubKeys
; i
++)
170 // Cleanup tasks from the previous run
173 RegCloseKey(hSubSubKey
);
179 if (pPrintProcessor
->pwszName
)
180 DllFreeSplStr(pPrintProcessor
->pwszName
);
182 if (pPrintProcessor
->pDatatypesInfo1
)
183 DllFreeSplMem(pPrintProcessor
->pDatatypesInfo1
);
185 DllFreeSplMem(pPrintProcessor
);
186 pPrintProcessor
= NULL
;
189 // Create a new LOCAL_PRINT_PROCESSOR structure for it.
190 pPrintProcessor
= DllAllocSplMem(sizeof(LOCAL_PRINT_PROCESSOR
));
191 if (!pPrintProcessor
)
193 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
194 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
198 // Allocate memory for the Print Monitor Name.
199 pPrintProcessor
->pwszName
= DllAllocSplMem((cchMaxSubKey
+ 1) * sizeof(WCHAR
));
200 if (!pPrintProcessor
->pwszName
)
202 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
203 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
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
)
212 ERR("RegEnumKeyExW failed with status %ld!\n", dwErrorCode
);
216 // Open this Print Processor's registry key.
217 dwErrorCode
= (DWORD
)RegOpenKeyExW(hSubKey
, pPrintProcessor
->pwszName
, 0, KEY_READ
, &hSubSubKey
);
218 if (dwErrorCode
!= ERROR_SUCCESS
)
220 ERR("RegOpenKeyExW failed for Print Processor \"%S\" with status %lu!\n", pPrintProcessor
->pwszName
, dwErrorCode
);
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
)
229 ERR("RegQueryValueExW failed for Print Processor \"%S\" with status %lu!\n", pPrintProcessor
->pwszName
, dwErrorCode
);
233 // Verify that our buffer is large enough.
234 if (cchPrintProcessorPath
+ cbFileName
/ sizeof(WCHAR
) > MAX_PATH
)
236 ERR("Print Processor directory \"%S\" for Print Processor \"%S\" is too long!\n", wszFileName
, pPrintProcessor
->pwszName
);
240 // Construct the full path to the Print Processor.
241 CopyMemory(&wszPrintProcessorPath
[cchPrintProcessorPath
], wszFileName
, cbFileName
);
244 hinstPrintProcessor
= LoadLibraryW(wszPrintProcessorPath
);
245 if (!hinstPrintProcessor
)
247 ERR("LoadLibraryW failed for \"%S\" with error %lu!\n", wszPrintProcessorPath
, GetLastError());
251 // Get and verify all its function pointers.
252 pPrintProcessor
->pfnClosePrintProcessor
= (PClosePrintProcessor
)GetProcAddress(hinstPrintProcessor
, "ClosePrintProcessor");
253 if (!pPrintProcessor
->pfnClosePrintProcessor
)
255 ERR("Print Processor \"%S\" exports no ClosePrintProcessor!\n", wszPrintProcessorPath
);
259 pPrintProcessor
->pfnControlPrintProcessor
= (PControlPrintProcessor
)GetProcAddress(hinstPrintProcessor
, "ControlPrintProcessor");
260 if (!pPrintProcessor
->pfnControlPrintProcessor
)
262 ERR("Print Processor \"%S\" exports no ControlPrintProcessor!\n", wszPrintProcessorPath
);
266 pPrintProcessor
->pfnEnumPrintProcessorDatatypesW
= (PEnumPrintProcessorDatatypesW
)GetProcAddress(hinstPrintProcessor
, "EnumPrintProcessorDatatypesW");
267 if (!pPrintProcessor
->pfnEnumPrintProcessorDatatypesW
)
269 ERR("Print Processor \"%S\" exports no EnumPrintProcessorDatatypesW!\n", wszPrintProcessorPath
);
273 pPrintProcessor
->pfnGetPrintProcessorCapabilities
= (PGetPrintProcessorCapabilities
)GetProcAddress(hinstPrintProcessor
, "GetPrintProcessorCapabilities");
274 if (!pPrintProcessor
->pfnGetPrintProcessorCapabilities
)
276 ERR("Print Processor \"%S\" exports no GetPrintProcessorCapabilities!\n", wszPrintProcessorPath
);
280 pPrintProcessor
->pfnOpenPrintProcessor
= (POpenPrintProcessor
)GetProcAddress(hinstPrintProcessor
, "OpenPrintProcessor");
281 if (!pPrintProcessor
->pfnOpenPrintProcessor
)
283 ERR("Print Processor \"%S\" exports no OpenPrintProcessor!\n", wszPrintProcessorPath
);
287 pPrintProcessor
->pfnPrintDocumentOnPrintProcessor
= (PPrintDocumentOnPrintProcessor
)GetProcAddress(hinstPrintProcessor
, "PrintDocumentOnPrintProcessor");
288 if (!pPrintProcessor
->pfnPrintDocumentOnPrintProcessor
)
290 ERR("Print Processor \"%S\" exports no PrintDocumentOnPrintProcessor!\n", wszPrintProcessorPath
);
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
)
299 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
300 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
304 if (!pPrintProcessor
->pfnEnumPrintProcessorDatatypesW(NULL
, NULL
, 1, (PBYTE
)pPrintProcessor
->pDatatypesInfo1
, cbDatatypes
, &cbDatatypes
, &pPrintProcessor
->dwDatatypeCount
))
306 ERR("EnumPrintProcessorDatatypesW failed for Print Processor \"%S\" with error %lu!\n", wszPrintProcessorPath
, GetLastError());
310 // Add the Print Processor to the list.
311 InsertTailList(&_PrintProcessorList
, &pPrintProcessor
->Entry
);
313 // Don't let the cleanup routines free this.
314 pPrintProcessor
= NULL
;
317 dwErrorCode
= ERROR_SUCCESS
;
322 RegCloseKey(hSubSubKey
);
326 if (pPrintProcessor
->pwszName
)
327 DllFreeSplStr(pPrintProcessor
->pwszName
);
329 if (pPrintProcessor
->pDatatypesInfo1
)
330 DllFreeSplMem(pPrintProcessor
->pDatatypesInfo1
);
332 DllFreeSplMem(pPrintProcessor
);
337 RegCloseKey(hSubKey
);
342 SetLastError(dwErrorCode
);
343 return (dwErrorCode
== ERROR_SUCCESS
);
347 * @name LocalEnumPrintProcessorDatatypes
349 * Obtains an array of all datatypes supported by a particular Print Processor.
350 * Print Provider function for EnumPrintProcessorDatatypesA/EnumPrintProcessorDatatypesW.
353 * Server Name. Ignored here, because every caller of LocalEnumPrintProcessorDatatypes is interested in the local directory.
355 * @param pPrintProcessorName
356 * The (case-insensitive) name of the Print Processor to query.
359 * The level of the structure supplied through pDatatypes. This must be 1.
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.
366 * Size of the buffer you supplied for pDatatypes, in bytes.
369 * Pointer to a variable that receives the required size of the buffer for pDatatypes, in bytes.
370 * This parameter mustn't be NULL!
373 * Pointer to a variable that receives the number of elements of the DATATYPES_INFO_1W array.
374 * This parameter mustn't be NULL!
377 * TRUE if we successfully copied the array into pDatatypes, FALSE otherwise.
378 * A more specific error code can be obtained through GetLastError.
381 LocalEnumPrintProcessorDatatypes(LPWSTR pName
, LPWSTR pPrintProcessorName
, DWORD Level
, LPBYTE pDatatypes
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
384 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
389 dwErrorCode
= ERROR_INVALID_LEVEL
;
393 // Try to find the Print Processor.
394 pPrintProcessor
= FindPrintProcessor(pPrintProcessorName
);
395 if (!pPrintProcessor
)
397 dwErrorCode
= ERROR_UNKNOWN_PRINTPROCESSOR
;
401 // Call its EnumPrintProcessorDatatypesW function.
402 if (pPrintProcessor
->pfnEnumPrintProcessorDatatypesW(pName
, pPrintProcessorName
, Level
, pDatatypes
, cbBuf
, pcbNeeded
, pcReturned
))
403 dwErrorCode
= ERROR_SUCCESS
;
405 dwErrorCode
= GetLastError();
408 SetLastError(dwErrorCode
);
409 return (dwErrorCode
== ERROR_SUCCESS
);
413 * @name LocalEnumPrintProcessors
415 * Obtains an array of all available Print Processors on this computer.
416 * Print Provider function for EnumPrintProcessorsA/EnumPrintProcessorsW.
419 * Server Name. Ignored here, because every caller of LocalEnumPrintProcessors is interested in the local directory.
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.
426 * The level of the structure supplied through pPrintProcessorInfo. This must be 1.
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.
433 * Size of the buffer you supplied for pPrintProcessorInfo, in bytes.
436 * Pointer to a variable that receives the required size of the buffer for pPrintProcessorInfo, in bytes.
437 * This parameter mustn't be NULL!
440 * Pointer to a variable that receives the number of elements of the PRINTPROCESSOR_INFO_1W array.
441 * This parameter mustn't be NULL!
444 * TRUE if we successfully copied the array into pPrintProcessorInfo, FALSE otherwise.
445 * A more specific error code can be obtained through GetLastError.
448 LocalEnumPrintProcessors(LPWSTR pName
, LPWSTR pEnvironment
, DWORD Level
, LPBYTE pPrintProcessorInfo
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
451 DWORD cchPrintProcessor
;
453 DWORD dwPrintProcessorCount
;
457 PBYTE pCurrentOutputPrintProcessor
;
458 PBYTE pCurrentOutputPrintProcessorInfo
;
459 PRINTPROCESSOR_INFO_1W PrintProcessorInfo1
;
460 PWSTR pwszTemp
= NULL
;
465 dwErrorCode
= ERROR_INVALID_LEVEL
;
469 if (!pcbNeeded
|| !pcReturned
)
471 // This error is also caught by RPC and returned as RPC_X_NULL_REF_POINTER.
472 dwErrorCode
= ERROR_INVALID_PARAMETER
;
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
)
482 // Open the "Print Processors" subkey.
483 dwErrorCode
= (DWORD
)RegOpenKeyExW(hKey
, L
"Print Processors", 0, KEY_READ
, &hSubKey
);
484 if (dwErrorCode
!= ERROR_SUCCESS
)
486 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode
);
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
)
494 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode
);
498 // Allocate a temporary buffer to let RegEnumKeyExW succeed.
499 pwszTemp
= DllAllocSplMem((cchMaxSubKey
+ 1) * sizeof(WCHAR
));
502 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
503 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
507 // Determine the required size of the output buffer.
510 for (i
= 0; i
< dwPrintProcessorCount
; i
++)
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
)
518 ERR("RegEnumKeyExW failed with status %lu!\n", dwErrorCode
);
522 *pcbNeeded
+= sizeof(PRINTPROCESSOR_INFO_1W
) + (cchPrintProcessor
+ 1) * sizeof(WCHAR
);
525 // Check if the supplied buffer is large enough.
526 if (cbBuf
< *pcbNeeded
)
528 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
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
);
536 // Copy over all Print Processors.
537 for (i
= 0; i
< dwPrintProcessorCount
; i
++)
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;
542 // Copy the Print Processor name.
543 dwErrorCode
= (DWORD
)RegEnumKeyExW(hSubKey
, i
, (PWSTR
)pCurrentOutputPrintProcessor
, &cchPrintProcessor
, NULL
, NULL
, NULL
, NULL
);
544 if (dwErrorCode
!= ERROR_SUCCESS
)
546 ERR("RegEnumKeyExW failed with status %lu!\n", dwErrorCode
);
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
));
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
);
559 // We've finished successfully!
560 *pcReturned
= dwPrintProcessorCount
;
561 dwErrorCode
= ERROR_SUCCESS
;
565 DllFreeSplMem(pwszTemp
);
568 RegCloseKey(hSubKey
);
573 SetLastError(dwErrorCode
);
574 return (dwErrorCode
== ERROR_SUCCESS
);
578 * @name LocalGetPrintProcessorDirectory
580 * Obtains the path to the local Print Processor directory.
581 * Print Provider function for GetPrintProcessorDirectoryA/GetPrintProcessorDirectoryW.
584 * Server Name. Ignored here, because every caller of LocalGetPrintProcessorDirectory is interested in the local directory.
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.
591 * The level of the (non-existing) structure supplied through pPrintProcessorInfo. This must be 1.
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.
598 * Size of the buffer you supplied for pPrintProcessorInfo, in bytes.
601 * Pointer to a variable that receives the required size of the buffer for pPrintProcessorInfo, in bytes.
602 * This parameter mustn't be NULL!
605 * TRUE if we successfully copied the directory into pPrintProcessorInfo, FALSE otherwise.
606 * A more specific error code can be obtained through GetLastError.
609 LocalGetPrintProcessorDirectory(LPWSTR pName
, LPWSTR pEnvironment
, DWORD Level
, LPBYTE pPrintProcessorInfo
, DWORD cbBuf
, LPDWORD pcbNeeded
)
611 const WCHAR wszPath
[] = L
"\\PRTPROCS\\";
612 const DWORD cchPath
= _countof(wszPath
) - 1;
621 dwErrorCode
= ERROR_INVALID_LEVEL
;
627 // This error is also caught by RPC and returned as RPC_X_NULL_REF_POINTER.
628 dwErrorCode
= ERROR_INVALID_PARAMETER
;
632 // Verify pEnvironment and open its registry key.
633 dwErrorCode
= _OpenEnvironment(pEnvironment
, &hKey
);
634 if (dwErrorCode
!= ERROR_SUCCESS
)
637 // Determine the size of the required buffer.
638 dwErrorCode
= (DWORD
)RegQueryValueExW(hKey
, L
"Directory", NULL
, NULL
, NULL
, pcbNeeded
);
639 if (dwErrorCode
!= ERROR_SUCCESS
)
641 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode
);
645 *pcbNeeded
+= cchSpoolDirectory
;
646 *pcbNeeded
+= cchPath
;
648 // Is the supplied buffer large enough?
649 if (cbBuf
< *pcbNeeded
)
651 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
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
));
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
)
663 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode
);
667 // We've finished successfully!
668 dwErrorCode
= ERROR_SUCCESS
;
674 SetLastError(dwErrorCode
);
675 return (dwErrorCode
== ERROR_SUCCESS
);