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(PLOCAL_PRINT_PROCESSOR pPrintProcessor
, PWSTR 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(PWSTR 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
;
124 HINSTANCE hinstPrintProcessor
;
127 HKEY hSubSubKey
= NULL
;
129 PLOCAL_PRINT_PROCESSOR pPrintProcessor
= NULL
;
130 PWSTR pwszPrintProcessorName
= NULL
;
131 WCHAR wszFileName
[MAX_PATH
];
132 WCHAR wszPrintProcessorPath
[MAX_PATH
];
134 // Initialize an empty list for our Print Processors.
135 InitializeListHead(&_PrintProcessorList
);
137 // Prepare the path to the Print Processor directory.
138 if (!LocalGetPrintProcessorDirectory(NULL
, NULL
, 1, (PBYTE
)wszPrintProcessorPath
, sizeof(wszPrintProcessorPath
), &cchPrintProcessorPath
))
141 cchPrintProcessorPath
/= sizeof(WCHAR
);
142 wszPrintProcessorPath
[cchPrintProcessorPath
++] = L
'\\';
144 // Open the environment registry key.
145 if (_OpenEnvironment(NULL
, &hKey
) != ERROR_SUCCESS
)
148 // Open the "Print Processors" subkey.
149 lStatus
= RegOpenKeyExW(hKey
, L
"Print Processors", 0, KEY_READ
, &hSubKey
);
150 if (lStatus
!= ERROR_SUCCESS
)
152 ERR("RegOpenKeyExW failed with status %ld!\n", lStatus
);
156 // Get the number of Print Processors and maximum sub key length.
157 lStatus
= RegQueryInfoKeyW(hSubKey
, NULL
, NULL
, NULL
, &dwSubKeys
, &cchMaxSubKey
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
158 if (lStatus
!= ERROR_SUCCESS
)
160 ERR("RegQueryInfoKeyW failed with status %ld!\n", lStatus
);
164 // Allocate a temporary buffer for the Print Processor names.
165 pwszPrintProcessorName
= DllAllocSplMem((cchMaxSubKey
+ 1) * sizeof(WCHAR
));
166 if (!pwszPrintProcessorName
)
168 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
172 // Loop through all available local Print Processors.
173 for (i
= 0; i
< dwSubKeys
; i
++)
175 // Cleanup tasks from the previous run
178 RegCloseKey(hSubSubKey
);
184 if (pPrintProcessor
->pwszName
)
185 DllFreeSplStr(pPrintProcessor
->pwszName
);
187 if (pPrintProcessor
->pDatatypesInfo1
)
188 DllFreeSplMem(pPrintProcessor
->pDatatypesInfo1
);
190 DllFreeSplMem(pPrintProcessor
);
191 pPrintProcessor
= NULL
;
194 // Get the name of this Print Processor.
195 cchPrintProcessorName
= cchMaxSubKey
+ 1;
196 lStatus
= RegEnumKeyExW(hSubKey
, i
, pwszPrintProcessorName
, &cchPrintProcessorName
, NULL
, NULL
, NULL
, NULL
);
197 if (lStatus
!= ERROR_SUCCESS
)
199 ERR("RegEnumKeyExW failed with status %ld!\n", lStatus
);
203 // Open this Print Processor's registry key.
204 lStatus
= RegOpenKeyExW(hSubKey
, pwszPrintProcessorName
, 0, KEY_READ
, &hSubSubKey
);
205 if (lStatus
!= ERROR_SUCCESS
)
207 ERR("RegOpenKeyExW failed for Print Processor \"%S\" with status %ld!\n", pwszPrintProcessorName
, lStatus
);
211 // Get the file name of the Print Processor.
212 cbFileName
= sizeof(wszFileName
);
213 lStatus
= RegQueryValueExW(hSubSubKey
, L
"Driver", NULL
, NULL
, (PBYTE
)wszFileName
, &cbFileName
);
214 if (lStatus
!= ERROR_SUCCESS
)
216 ERR("RegQueryValueExW failed for Print Processor \"%S\" with status %ld!\n", pwszPrintProcessorName
, lStatus
);
220 // Verify that our buffer is large enough.
221 if (cchPrintProcessorPath
+ cbFileName
/ sizeof(WCHAR
) > MAX_PATH
)
223 ERR("Print Processor directory \"%S\" for Print Processor \"%S\" is too long!\n", wszFileName
, pwszPrintProcessorName
);
227 // Construct the full path to the Print Processor.
228 CopyMemory(&wszPrintProcessorPath
[cchPrintProcessorPath
], wszFileName
, cbFileName
);
231 hinstPrintProcessor
= LoadLibraryW(wszPrintProcessorPath
);
232 if (lStatus
!= ERROR_SUCCESS
)
234 ERR("LoadLibraryW failed for \"%S\" with error %lu!\n", wszPrintProcessorPath
, GetLastError());
238 // Create a new LOCAL_PRINT_PROCESSOR structure for it.
239 pPrintProcessor
= DllAllocSplMem(sizeof(LOCAL_PRINT_PROCESSOR
));
240 pPrintProcessor
->pwszName
= AllocSplStr(pwszPrintProcessorName
);
242 // Get and verify all its function pointers.
243 pPrintProcessor
->pfnClosePrintProcessor
= (PClosePrintProcessor
)GetProcAddress(hinstPrintProcessor
, "ClosePrintProcessor");
244 if (!pPrintProcessor
->pfnClosePrintProcessor
)
246 ERR("Print Processor \"%S\" exports no ClosePrintProcessor!\n", wszPrintProcessorPath
);
250 pPrintProcessor
->pfnControlPrintProcessor
= (PControlPrintProcessor
)GetProcAddress(hinstPrintProcessor
, "ControlPrintProcessor");
251 if (!pPrintProcessor
->pfnControlPrintProcessor
)
253 ERR("Print Processor \"%S\" exports no ControlPrintProcessor!\n", wszPrintProcessorPath
);
257 pPrintProcessor
->pfnEnumPrintProcessorDatatypesW
= (PEnumPrintProcessorDatatypesW
)GetProcAddress(hinstPrintProcessor
, "EnumPrintProcessorDatatypesW");
258 if (!pPrintProcessor
->pfnEnumPrintProcessorDatatypesW
)
260 ERR("Print Processor \"%S\" exports no EnumPrintProcessorDatatypesW!\n", wszPrintProcessorPath
);
264 pPrintProcessor
->pfnGetPrintProcessorCapabilities
= (PGetPrintProcessorCapabilities
)GetProcAddress(hinstPrintProcessor
, "GetPrintProcessorCapabilities");
265 if (!pPrintProcessor
->pfnGetPrintProcessorCapabilities
)
267 ERR("Print Processor \"%S\" exports no GetPrintProcessorCapabilities!\n", wszPrintProcessorPath
);
271 pPrintProcessor
->pfnOpenPrintProcessor
= (POpenPrintProcessor
)GetProcAddress(hinstPrintProcessor
, "OpenPrintProcessor");
272 if (!pPrintProcessor
->pfnOpenPrintProcessor
)
274 ERR("Print Processor \"%S\" exports no OpenPrintProcessor!\n", wszPrintProcessorPath
);
278 pPrintProcessor
->pfnPrintDocumentOnPrintProcessor
= (PPrintDocumentOnPrintProcessor
)GetProcAddress(hinstPrintProcessor
, "PrintDocumentOnPrintProcessor");
279 if (!pPrintProcessor
->pfnPrintDocumentOnPrintProcessor
)
281 ERR("Print Processor \"%S\" exports no PrintDocumentOnPrintProcessor!\n", wszPrintProcessorPath
);
285 // Get all supported datatypes.
286 pPrintProcessor
->pfnEnumPrintProcessorDatatypesW(NULL
, NULL
, 1, NULL
, 0, &cbDatatypes
, &pPrintProcessor
->dwDatatypeCount
);
287 pPrintProcessor
->pDatatypesInfo1
= DllAllocSplMem(cbDatatypes
);
288 if (!pPrintProcessor
->pDatatypesInfo1
)
290 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
294 if (!pPrintProcessor
->pfnEnumPrintProcessorDatatypesW(NULL
, NULL
, 1, (PBYTE
)pPrintProcessor
->pDatatypesInfo1
, cbDatatypes
, &cbDatatypes
, &pPrintProcessor
->dwDatatypeCount
))
296 ERR("EnumPrintProcessorDatatypesW failed for Print Processor \"%S\" with error %lu!\n", wszPrintProcessorPath
, GetLastError());
300 // Add the Print Processor to the list.
301 InsertTailList(&_PrintProcessorList
, &pPrintProcessor
->Entry
);
303 // Don't let the cleanup routines free this.
304 pPrintProcessor
= NULL
;
310 RegCloseKey(hSubSubKey
);
314 if (pPrintProcessor
->pwszName
)
315 DllFreeSplStr(pPrintProcessor
->pwszName
);
317 if (pPrintProcessor
->pDatatypesInfo1
)
318 DllFreeSplMem(pPrintProcessor
->pDatatypesInfo1
);
320 DllFreeSplMem(pPrintProcessor
);
324 if (pwszPrintProcessorName
)
325 DllFreeSplStr(pwszPrintProcessorName
);
328 RegCloseKey(hSubKey
);
335 * @name LocalEnumPrintProcessorDatatypes
337 * Obtains an array of all datatypes supported by a particular Print Processor.
338 * Print Provider function for EnumPrintProcessorDatatypesA/EnumPrintProcessorDatatypesW.
341 * Server Name. Ignored here, because every caller of LocalEnumPrintProcessorDatatypes is interested in the local directory.
343 * @param pPrintProcessorName
344 * The (case-insensitive) name of the Print Processor to query.
347 * The level of the structure supplied through pDatatypes. This must be 1.
350 * Pointer to the buffer that receives an array of DATATYPES_INFO_1W structures.
351 * Can be NULL if you just want to know the required size of the buffer.
354 * Size of the buffer you supplied for pDatatypes, in bytes.
357 * Pointer to a variable that receives the required size of the buffer for pDatatypes, in bytes.
358 * This parameter mustn't be NULL!
361 * Pointer to a variable that receives the number of elements of the DATATYPES_INFO_1W array.
362 * This parameter mustn't be NULL!
365 * TRUE if we successfully copied the array into pDatatypes, FALSE otherwise.
366 * A more specific error code can be obtained through GetLastError.
369 LocalEnumPrintProcessorDatatypes(LPWSTR pName
, LPWSTR pPrintProcessorName
, DWORD Level
, LPBYTE pDatatypes
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
372 PLOCAL_PRINT_PROCESSOR pPrintProcessor
;
377 dwErrorCode
= ERROR_INVALID_LEVEL
;
381 // Try to find the Print Processor.
382 pPrintProcessor
= FindPrintProcessor(pPrintProcessorName
);
383 if (!pPrintProcessor
)
385 dwErrorCode
= ERROR_UNKNOWN_PRINTPROCESSOR
;
389 // Call its EnumPrintProcessorDatatypesW function.
390 if (pPrintProcessor
->pfnEnumPrintProcessorDatatypesW(pName
, pPrintProcessorName
, Level
, pDatatypes
, cbBuf
, pcbNeeded
, pcReturned
))
391 dwErrorCode
= ERROR_SUCCESS
;
393 dwErrorCode
= GetLastError();
396 SetLastError(dwErrorCode
);
397 return (dwErrorCode
== ERROR_SUCCESS
);
401 * @name LocalEnumPrintProcessors
403 * Obtains an array of all available Print Processors on this computer.
404 * Print Provider function for EnumPrintProcessorsA/EnumPrintProcessorsW.
407 * Server Name. Ignored here, because every caller of LocalEnumPrintProcessors is interested in the local directory.
409 * @param pEnvironment
410 * One of the predefined operating system and architecture "environment" strings (like "Windows NT x86").
411 * Alternatively, NULL to output the Print Processor directory of the current environment.
414 * The level of the structure supplied through pPrintProcessorInfo. This must be 1.
416 * @param pPrintProcessorInfo
417 * Pointer to the buffer that receives an array of PRINTPROCESSOR_INFO_1W structures.
418 * Can be NULL if you just want to know the required size of the buffer.
421 * Size of the buffer you supplied for pPrintProcessorInfo, in bytes.
424 * Pointer to a variable that receives the required size of the buffer for pPrintProcessorInfo, in bytes.
425 * This parameter mustn't be NULL!
428 * Pointer to a variable that receives the number of elements of the PRINTPROCESSOR_INFO_1W array.
429 * This parameter mustn't be NULL!
432 * TRUE if we successfully copied the array into pPrintProcessorInfo, FALSE otherwise.
433 * A more specific error code can be obtained through GetLastError.
436 LocalEnumPrintProcessors(LPWSTR pName
, LPWSTR pEnvironment
, DWORD Level
, LPBYTE pPrintProcessorInfo
, DWORD cbBuf
, LPDWORD pcbNeeded
, LPDWORD pcReturned
)
439 DWORD cchPrintProcessor
;
444 PBYTE pCurrentOutputPrintProcessor
;
445 PBYTE pCurrentOutputPrintProcessorInfo
;
446 PRINTPROCESSOR_INFO_1W PrintProcessorInfo1
;
447 PWSTR pwszTemp
= NULL
;
452 dwErrorCode
= ERROR_INVALID_LEVEL
;
456 if (!pcbNeeded
|| !pcReturned
)
458 // This error is also caught by RPC and returned as RPC_X_NULL_REF_POINTER.
459 dwErrorCode
= ERROR_INVALID_PARAMETER
;
463 // Verify pEnvironment and open its registry key.
464 // We use the registry and not the PrintProcessorList here, because the caller may request information about a different environment.
465 dwErrorCode
= _OpenEnvironment(pEnvironment
, &hKey
);
466 if (dwErrorCode
!= ERROR_SUCCESS
)
469 // Open the "Print Processors" subkey.
470 dwErrorCode
= (DWORD
)RegOpenKeyExW(hKey
, L
"Print Processors", 0, KEY_READ
, &hSubKey
);
471 if (dwErrorCode
!= ERROR_SUCCESS
)
473 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode
);
477 // Get the number of Print Processors and maximum sub key length.
478 dwErrorCode
= (DWORD
)RegQueryInfoKeyW(hSubKey
, NULL
, NULL
, NULL
, pcReturned
, &cchMaxSubKey
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
479 if (dwErrorCode
!= ERROR_SUCCESS
)
481 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode
);
485 // Allocate a temporary buffer to let RegEnumKeyExW succeed.
486 pwszTemp
= DllAllocSplMem((cchMaxSubKey
+ 1) * sizeof(WCHAR
));
489 dwErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
490 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
494 // Determine the required size of the output buffer.
497 for (i
= 0; i
< *pcReturned
; i
++)
499 // RegEnumKeyExW sucks! Unlike similar API functions, it only returns the actual numbers of characters copied when you supply a buffer large enough.
500 // So use pwszTemp with its size cchMaxSubKey for this.
501 cchPrintProcessor
= cchMaxSubKey
+ 1;
502 dwErrorCode
= (DWORD
)RegEnumKeyExW(hSubKey
, i
, pwszTemp
, &cchPrintProcessor
, NULL
, NULL
, NULL
, NULL
);
503 if (dwErrorCode
!= ERROR_SUCCESS
)
505 ERR("RegEnumKeyExW failed with status %lu!\n", dwErrorCode
);
509 *pcbNeeded
+= sizeof(PRINTPROCESSOR_INFO_1W
) + (cchPrintProcessor
+ 1) * sizeof(WCHAR
);
512 // Check if the supplied buffer is large enough.
513 if (cbBuf
< *pcbNeeded
)
515 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
519 // Put the Print Processor strings right after the last PRINTPROCESSOR_INFO_1W structure.
520 pCurrentOutputPrintProcessorInfo
= pPrintProcessorInfo
;
521 pCurrentOutputPrintProcessor
= pPrintProcessorInfo
+ *pcReturned
* sizeof(PRINTPROCESSOR_INFO_1W
);
523 // Copy over all Print Processors.
524 for (i
= 0; i
< *pcReturned
; i
++)
526 // This isn't really correct, but doesn't cause any harm, because we've extensively checked the size of the supplied buffer above.
527 cchPrintProcessor
= cchMaxSubKey
+ 1;
529 // Copy the Print Processor name.
530 dwErrorCode
= (DWORD
)RegEnumKeyExW(hSubKey
, i
, (PWSTR
)pCurrentOutputPrintProcessor
, &cchPrintProcessor
, NULL
, NULL
, NULL
, NULL
);
531 if (dwErrorCode
!= ERROR_SUCCESS
)
533 ERR("RegEnumKeyExW failed with status %lu!\n", dwErrorCode
);
537 // Fill and copy the PRINTPROCESSOR_INFO_1W structure belonging to this Print Processor.
538 PrintProcessorInfo1
.pName
= (PWSTR
)pCurrentOutputPrintProcessor
;
539 CopyMemory(pCurrentOutputPrintProcessorInfo
, &PrintProcessorInfo1
, sizeof(PRINTPROCESSOR_INFO_1W
));
541 // Advance to the next PRINTPROCESSOR_INFO_1W location and string location in the output buffer.
542 pCurrentOutputPrintProcessor
+= (cchPrintProcessor
+ 1) * sizeof(WCHAR
);
543 pCurrentOutputPrintProcessorInfo
+= sizeof(PRINTPROCESSOR_INFO_1W
);
546 // We've finished successfully!
547 dwErrorCode
= ERROR_SUCCESS
;
551 DllFreeSplMem(pwszTemp
);
554 RegCloseKey(hSubKey
);
559 SetLastError(dwErrorCode
);
560 return (dwErrorCode
== ERROR_SUCCESS
);
564 * @name LocalGetPrintProcessorDirectory
566 * Obtains the path to the local Print Processor directory.
567 * Print Provider function for GetPrintProcessorDirectoryA/GetPrintProcessorDirectoryW.
570 * Server Name. Ignored here, because every caller of LocalGetPrintProcessorDirectory is interested in the local directory.
572 * @param pEnvironment
573 * One of the predefined operating system and architecture "environment" strings (like "Windows NT x86").
574 * Alternatively, NULL to output the Print Processor directory of the current environment.
577 * The level of the (non-existing) structure supplied through pPrintProcessorInfo. This must be 1.
579 * @param pPrintProcessorInfo
580 * Pointer to the buffer that receives the full path to the Print Processor directory.
581 * Can be NULL if you just want to know the required size of the buffer.
584 * Size of the buffer you supplied for pPrintProcessorInfo, in bytes.
587 * Pointer to a variable that receives the required size of the buffer for pPrintProcessorInfo, in bytes.
588 * This parameter mustn't be NULL!
591 * TRUE if we successfully copied the directory into pPrintProcessorInfo, FALSE otherwise.
592 * A more specific error code can be obtained through GetLastError.
595 LocalGetPrintProcessorDirectory(LPWSTR pName
, LPWSTR pEnvironment
, DWORD Level
, LPBYTE pPrintProcessorInfo
, DWORD cbBuf
, LPDWORD pcbNeeded
)
597 const WCHAR wszPath
[] = L
"\\PRTPROCS\\";
598 const DWORD cchPath
= _countof(wszPath
) - 1;
607 dwErrorCode
= ERROR_INVALID_LEVEL
;
613 // This error is also caught by RPC and returned as RPC_X_NULL_REF_POINTER.
614 dwErrorCode
= ERROR_INVALID_PARAMETER
;
618 // Verify pEnvironment and open its registry key.
619 dwErrorCode
= _OpenEnvironment(pEnvironment
, &hKey
);
620 if (dwErrorCode
!= ERROR_SUCCESS
)
623 // Determine the size of the required buffer.
624 dwErrorCode
= (DWORD
)RegQueryValueExW(hKey
, L
"Directory", NULL
, NULL
, NULL
, pcbNeeded
);
625 if (dwErrorCode
!= ERROR_SUCCESS
)
627 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode
);
631 *pcbNeeded
+= cchSpoolDirectory
;
632 *pcbNeeded
+= cchPath
;
634 // Is the supplied buffer large enough?
635 if (cbBuf
< *pcbNeeded
)
637 dwErrorCode
= ERROR_INSUFFICIENT_BUFFER
;
641 // Copy the path to the "prtprocs" directory into pPrintProcessorInfo
642 CopyMemory(pPrintProcessorInfo
, wszSpoolDirectory
, cchSpoolDirectory
* sizeof(WCHAR
));
643 CopyMemory(&pPrintProcessorInfo
[cchSpoolDirectory
], wszPath
, cchPath
* sizeof(WCHAR
));
645 // Get the directory name from the registry.
646 dwErrorCode
= (DWORD
)RegQueryValueExW(hKey
, L
"Directory", NULL
, NULL
, &pPrintProcessorInfo
[cchSpoolDirectory
+ cchPath
], &cbDataWritten
);
647 if (dwErrorCode
!= ERROR_SUCCESS
)
649 ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode
);
653 // We've finished successfully!
654 dwErrorCode
= ERROR_SUCCESS
;
660 SetLastError(dwErrorCode
);
661 return (dwErrorCode
== ERROR_SUCCESS
);