reactos/win32ss/printing/providers/localspl/printprocessors.c

691 lines
24 KiB
C

/*
* PROJECT: ReactOS Local Spooler
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
* PURPOSE: Functions related to Print Processors
* COPYRIGHT: Copyright 2015-2017 Colin Finck (colin@reactos.org)
*/
#include "precomp.h"
// Local Variables
static LIST_ENTRY _PrintProcessorList;
/**
* @name _OpenEnvironment
*
* Checks a supplied pEnvironment variable for validity and opens its registry key.
*
* @param pEnvironment
* The pEnvironment variable to check.
*
* @param hKey
* On success, this variable will contain a HKEY to the opened registry key of the environment.
* You can use it for further tasks and have to close it with RegCloseKey.
*
* @return
* A Windows Error Code indicating success or failure.
*/
static DWORD
_OpenEnvironment(PCWSTR pEnvironment, PHKEY hKey)
{
const WCHAR wszEnvironmentsKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Environments\\";
const DWORD cchEnvironmentsKey = _countof(wszEnvironmentsKey) - 1;
DWORD cchEnvironment;
DWORD dwErrorCode;
PWSTR pwszEnvironmentKey = NULL;
// Sanity checks
if (!pEnvironment)
{
dwErrorCode = ERROR_INVALID_ENVIRONMENT;
goto Cleanup;
}
// Construct the registry key of the demanded environment.
cchEnvironment = wcslen(pEnvironment);
pwszEnvironmentKey = DllAllocSplMem((cchEnvironmentsKey + cchEnvironment + 1) * sizeof(WCHAR));
if (!pwszEnvironmentKey)
{
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
ERR("DllAllocSplMem failed!\n");
goto Cleanup;
}
CopyMemory(pwszEnvironmentKey, wszEnvironmentsKey, cchEnvironmentsKey * sizeof(WCHAR));
CopyMemory(&pwszEnvironmentKey[cchEnvironmentsKey], pEnvironment, (cchEnvironment + 1) * sizeof(WCHAR));
// Open the registry key.
dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, pwszEnvironmentKey, 0, KEY_READ, hKey);
if (dwErrorCode == ERROR_FILE_NOT_FOUND)
{
dwErrorCode = ERROR_INVALID_ENVIRONMENT;
goto Cleanup;
}
else if (dwErrorCode != ERROR_SUCCESS)
{
ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
goto Cleanup;
}
Cleanup:
if (pwszEnvironmentKey)
DllFreeSplMem(pwszEnvironmentKey);
return dwErrorCode;
}
BOOL
FindDatatype(const PLOCAL_PRINT_PROCESSOR pPrintProcessor, PCWSTR pwszDatatype)
{
DWORD i;
PDATATYPES_INFO_1W pCurrentDatatype = pPrintProcessor->pDatatypesInfo1;
TRACE("FindDatatype(%p, %S)\n", pPrintProcessor, pwszDatatype);
if (!pwszDatatype)
return FALSE;
for (i = 0; i < pPrintProcessor->dwDatatypeCount; i++)
{
if (wcsicmp(pCurrentDatatype->pName, pwszDatatype) == 0)
return TRUE;
++pCurrentDatatype;
}
return FALSE;
}
PLOCAL_PRINT_PROCESSOR
FindPrintProcessor(PCWSTR pwszName)
{
PLIST_ENTRY pEntry;
PLOCAL_PRINT_PROCESSOR pPrintProcessor;
TRACE("FindPrintProcessor(%S)\n", pwszName);
if (!pwszName)
return NULL;
for (pEntry = _PrintProcessorList.Flink; pEntry != &_PrintProcessorList; pEntry = pEntry->Flink)
{
pPrintProcessor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_PROCESSOR, Entry);
if (wcsicmp(pPrintProcessor->pwszName, pwszName) == 0)
return pPrintProcessor;
}
return NULL;
}
/**
* @name InitializePrintProcessorList
*
* Initializes a singly linked list of locally available Print Processors.
*/
BOOL
InitializePrintProcessorList(void)
{
DWORD cbDatatypes;
DWORD cbFileName;
DWORD cchPrintProcessorPath;
DWORD cchMaxSubKey;
DWORD cchPrintProcessorName;
DWORD dwErrorCode;
DWORD dwSubKeys;
DWORD i;
HINSTANCE hinstPrintProcessor;
HKEY hKey = NULL;
HKEY hSubKey = NULL;
HKEY hSubSubKey = NULL;
PLOCAL_PRINT_PROCESSOR pPrintProcessor = NULL;
WCHAR wszFileName[MAX_PATH];
WCHAR wszPrintProcessorPath[MAX_PATH];
TRACE("InitializePrintProcessorList()\n");
// Initialize an empty list for our Print Processors.
InitializeListHead(&_PrintProcessorList);
// Prepare the path to the Print Processor directory.
if (!LocalGetPrintProcessorDirectory(NULL, (PWSTR)wszCurrentEnvironment, 1, (PBYTE)wszPrintProcessorPath, sizeof(wszPrintProcessorPath), &cchPrintProcessorPath))
{
dwErrorCode = GetLastError();
goto Cleanup;
}
// LocalGetPrintProcessorDirectory returns the number of copied bytes. Convert this into a number of characters without the terminating null-character.
cchPrintProcessorPath /= sizeof(WCHAR);
--cchPrintProcessorPath;
// Append a trailing backslash.
wszPrintProcessorPath[cchPrintProcessorPath] = L'\\';
++cchPrintProcessorPath;
// Open the environment registry key.
dwErrorCode = _OpenEnvironment(wszCurrentEnvironment, &hKey);
if (dwErrorCode != ERROR_SUCCESS)
goto Cleanup;
// Open the "Print Processors" subkey.
dwErrorCode = (DWORD)RegOpenKeyExW(hKey, L"Print Processors", 0, KEY_READ, &hSubKey);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
goto Cleanup;
}
// Get the number of Print Processors and maximum sub key length.
dwErrorCode = (DWORD)RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwSubKeys, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
goto Cleanup;
}
// Loop through all available local Print Processors.
for (i = 0; i < dwSubKeys; i++)
{
// Cleanup tasks from the previous run
if (hSubSubKey)
{
RegCloseKey(hSubSubKey);
hSubSubKey = NULL;
}
if (pPrintProcessor)
{
if (pPrintProcessor->pwszName)
DllFreeSplStr(pPrintProcessor->pwszName);
if (pPrintProcessor->pDatatypesInfo1)
DllFreeSplMem(pPrintProcessor->pDatatypesInfo1);
DllFreeSplMem(pPrintProcessor);
pPrintProcessor = NULL;
}
// Create a new LOCAL_PRINT_PROCESSOR structure for it.
pPrintProcessor = DllAllocSplMem(sizeof(LOCAL_PRINT_PROCESSOR));
if (!pPrintProcessor)
{
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
ERR("DllAllocSplMem failed!\n");
goto Cleanup;
}
// Allocate memory for the Print Monitor Name.
pPrintProcessor->pwszName = DllAllocSplMem((cchMaxSubKey + 1) * sizeof(WCHAR));
if (!pPrintProcessor->pwszName)
{
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
ERR("DllAllocSplMem failed!\n");
goto Cleanup;
}
// Get the name of this Print Processor.
cchPrintProcessorName = cchMaxSubKey + 1;
dwErrorCode = (DWORD)RegEnumKeyExW(hSubKey, i, pPrintProcessor->pwszName, &cchPrintProcessorName, NULL, NULL, NULL, NULL);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("RegEnumKeyExW failed with status %ld!\n", dwErrorCode);
continue;
}
// Open this Print Processor's registry key.
dwErrorCode = (DWORD)RegOpenKeyExW(hSubKey, pPrintProcessor->pwszName, 0, KEY_READ, &hSubSubKey);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("RegOpenKeyExW failed for Print Processor \"%S\" with status %lu!\n", pPrintProcessor->pwszName, dwErrorCode);
continue;
}
// Get the file name of the Print Processor.
cbFileName = sizeof(wszFileName);
dwErrorCode = (DWORD)RegQueryValueExW(hSubSubKey, L"Driver", NULL, NULL, (PBYTE)wszFileName, &cbFileName);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("RegQueryValueExW failed for Print Processor \"%S\" with status %lu!\n", pPrintProcessor->pwszName, dwErrorCode);
continue;
}
// Verify that our buffer is large enough.
if (cchPrintProcessorPath + cbFileName / sizeof(WCHAR) > MAX_PATH)
{
ERR("Print Processor directory \"%S\" for Print Processor \"%S\" is too long!\n", wszFileName, pPrintProcessor->pwszName);
continue;
}
// Construct the full path to the Print Processor.
CopyMemory(&wszPrintProcessorPath[cchPrintProcessorPath], wszFileName, cbFileName);
// Try to load it.
hinstPrintProcessor = LoadLibraryW(wszPrintProcessorPath);
if (!hinstPrintProcessor)
{
ERR("LoadLibraryW failed for \"%S\" with error %lu!\n", wszPrintProcessorPath, GetLastError());
continue;
}
// Get and verify all its function pointers.
pPrintProcessor->pfnClosePrintProcessor = (PClosePrintProcessor)GetProcAddress(hinstPrintProcessor, "ClosePrintProcessor");
if (!pPrintProcessor->pfnClosePrintProcessor)
{
ERR("Print Processor \"%S\" exports no ClosePrintProcessor!\n", wszPrintProcessorPath);
continue;
}
pPrintProcessor->pfnControlPrintProcessor = (PControlPrintProcessor)GetProcAddress(hinstPrintProcessor, "ControlPrintProcessor");
if (!pPrintProcessor->pfnControlPrintProcessor)
{
ERR("Print Processor \"%S\" exports no ControlPrintProcessor!\n", wszPrintProcessorPath);
continue;
}
pPrintProcessor->pfnEnumPrintProcessorDatatypesW = (PEnumPrintProcessorDatatypesW)GetProcAddress(hinstPrintProcessor, "EnumPrintProcessorDatatypesW");
if (!pPrintProcessor->pfnEnumPrintProcessorDatatypesW)
{
ERR("Print Processor \"%S\" exports no EnumPrintProcessorDatatypesW!\n", wszPrintProcessorPath);
continue;
}
pPrintProcessor->pfnGetPrintProcessorCapabilities = (PGetPrintProcessorCapabilities)GetProcAddress(hinstPrintProcessor, "GetPrintProcessorCapabilities");
if (!pPrintProcessor->pfnGetPrintProcessorCapabilities)
{
ERR("Print Processor \"%S\" exports no GetPrintProcessorCapabilities!\n", wszPrintProcessorPath);
continue;
}
pPrintProcessor->pfnOpenPrintProcessor = (POpenPrintProcessor)GetProcAddress(hinstPrintProcessor, "OpenPrintProcessor");
if (!pPrintProcessor->pfnOpenPrintProcessor)
{
ERR("Print Processor \"%S\" exports no OpenPrintProcessor!\n", wszPrintProcessorPath);
continue;
}
pPrintProcessor->pfnPrintDocumentOnPrintProcessor = (PPrintDocumentOnPrintProcessor)GetProcAddress(hinstPrintProcessor, "PrintDocumentOnPrintProcessor");
if (!pPrintProcessor->pfnPrintDocumentOnPrintProcessor)
{
ERR("Print Processor \"%S\" exports no PrintDocumentOnPrintProcessor!\n", wszPrintProcessorPath);
continue;
}
// Get all supported datatypes.
pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, 0, &cbDatatypes, &pPrintProcessor->dwDatatypeCount);
pPrintProcessor->pDatatypesInfo1 = DllAllocSplMem(cbDatatypes);
if (!pPrintProcessor->pDatatypesInfo1)
{
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
ERR("DllAllocSplMem failed!\n");
goto Cleanup;
}
if (!pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, (PBYTE)pPrintProcessor->pDatatypesInfo1, cbDatatypes, &cbDatatypes, &pPrintProcessor->dwDatatypeCount))
{
ERR("EnumPrintProcessorDatatypesW failed for Print Processor \"%S\" with error %lu!\n", wszPrintProcessorPath, GetLastError());
continue;
}
// Add the Print Processor to the list.
InsertTailList(&_PrintProcessorList, &pPrintProcessor->Entry);
// Don't let the cleanup routines free this.
pPrintProcessor = NULL;
}
dwErrorCode = ERROR_SUCCESS;
Cleanup:
// Inside the loop
if (hSubSubKey)
RegCloseKey(hSubSubKey);
if (pPrintProcessor)
{
if (pPrintProcessor->pwszName)
DllFreeSplStr(pPrintProcessor->pwszName);
if (pPrintProcessor->pDatatypesInfo1)
DllFreeSplMem(pPrintProcessor->pDatatypesInfo1);
DllFreeSplMem(pPrintProcessor);
}
// Outside the loop
if (hSubKey)
RegCloseKey(hSubKey);
if (hKey)
RegCloseKey(hKey);
SetLastError(dwErrorCode);
return (dwErrorCode == ERROR_SUCCESS);
}
/**
* @name LocalEnumPrintProcessorDatatypes
*
* Obtains an array of all datatypes supported by a particular Print Processor.
* Print Provider function for EnumPrintProcessorDatatypesA/EnumPrintProcessorDatatypesW.
*
* @param pName
* Server Name. Ignored here, because every caller of LocalEnumPrintProcessorDatatypes is interested in the local directory.
*
* @param pPrintProcessorName
* The (case-insensitive) name of the Print Processor to query.
*
* @param Level
* The level of the structure supplied through pDatatypes. This must be 1.
*
* @param pDatatypes
* Pointer to the buffer that receives an array of DATATYPES_INFO_1W structures.
* Can be NULL if you just want to know the required size of the buffer.
*
* @param cbBuf
* Size of the buffer you supplied for pDatatypes, in bytes.
*
* @param pcbNeeded
* Pointer to a variable that receives the required size of the buffer for pDatatypes, in bytes.
* This parameter mustn't be NULL!
*
* @param pcReturned
* Pointer to a variable that receives the number of elements of the DATATYPES_INFO_1W array.
* This parameter mustn't be NULL!
*
* @return
* TRUE if we successfully copied the array into pDatatypes, FALSE otherwise.
* A more specific error code can be obtained through GetLastError.
*/
BOOL WINAPI
LocalEnumPrintProcessorDatatypes(LPWSTR pName, LPWSTR pPrintProcessorName, DWORD Level, LPBYTE pDatatypes, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
{
DWORD dwErrorCode;
PLOCAL_PRINT_PROCESSOR pPrintProcessor;
TRACE("LocalEnumPrintProcessorDatatypes(%S, %S, %lu, %p, %lu, %p, %p)\n", pName, pPrintProcessorName, Level, pDatatypes, cbBuf, pcbNeeded, pcReturned);
// Sanity checks
if (Level != 1)
{
dwErrorCode = ERROR_INVALID_LEVEL;
goto Cleanup;
}
// Try to find the Print Processor.
pPrintProcessor = FindPrintProcessor(pPrintProcessorName);
if (!pPrintProcessor)
{
dwErrorCode = ERROR_UNKNOWN_PRINTPROCESSOR;
goto Cleanup;
}
// Call its EnumPrintProcessorDatatypesW function.
if (pPrintProcessor->pfnEnumPrintProcessorDatatypesW(pName, pPrintProcessorName, Level, pDatatypes, cbBuf, pcbNeeded, pcReturned))
dwErrorCode = ERROR_SUCCESS;
else
dwErrorCode = GetLastError();
Cleanup:
SetLastError(dwErrorCode);
return (dwErrorCode == ERROR_SUCCESS);
}
/**
* @name LocalEnumPrintProcessors
*
* Obtains an array of all available Print Processors on this computer.
* Print Provider function for EnumPrintProcessorsA/EnumPrintProcessorsW.
*
* @param pName
* Server Name. Ignored here, because every caller of LocalEnumPrintProcessors is interested in the local directory.
*
* @param pEnvironment
* One of the predefined operating system and architecture "environment" strings (like "Windows NT x86").
* Alternatively, NULL to output the Print Processor directory of the current environment.
*
* @param Level
* The level of the structure supplied through pPrintProcessorInfo. This must be 1.
*
* @param pPrintProcessorInfo
* Pointer to the buffer that receives an array of PRINTPROCESSOR_INFO_1W structures.
* Can be NULL if you just want to know the required size of the buffer.
*
* @param cbBuf
* Size of the buffer you supplied for pPrintProcessorInfo, in bytes.
*
* @param pcbNeeded
* Pointer to a variable that receives the required size of the buffer for pPrintProcessorInfo, in bytes.
* This parameter mustn't be NULL!
*
* @param pcReturned
* Pointer to a variable that receives the number of elements of the PRINTPROCESSOR_INFO_1W array.
* This parameter mustn't be NULL!
*
* @return
* TRUE if we successfully copied the array into pPrintProcessorInfo, FALSE otherwise.
* A more specific error code can be obtained through GetLastError.
*/
BOOL WINAPI
LocalEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
{
DWORD cchMaxSubKey;
DWORD cchPrintProcessor;
DWORD dwErrorCode;
DWORD dwPrintProcessorCount;
DWORD i;
HKEY hKey = NULL;
HKEY hSubKey = NULL;
PBYTE pCurrentOutputPrintProcessor;
PBYTE pCurrentOutputPrintProcessorInfo;
PRINTPROCESSOR_INFO_1W PrintProcessorInfo1;
PWSTR pwszTemp = NULL;
TRACE("LocalEnumPrintProcessors(%S, %S, %lu, %p, %lu, %p, %p)\n", pName, pEnvironment, Level, pPrintProcessorInfo, cbBuf, pcbNeeded, pcReturned);
// Sanity checks
if (Level != 1)
{
dwErrorCode = ERROR_INVALID_LEVEL;
goto Cleanup;
}
if (!pcbNeeded || !pcReturned)
{
// This error is also caught by RPC and returned as RPC_X_NULL_REF_POINTER.
dwErrorCode = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
// Verify pEnvironment and open its registry key.
// We use the registry and not the PrintProcessorList here, because the caller may request information about a different environment.
dwErrorCode = _OpenEnvironment(pEnvironment, &hKey);
if (dwErrorCode != ERROR_SUCCESS)
goto Cleanup;
// Open the "Print Processors" subkey.
dwErrorCode = (DWORD)RegOpenKeyExW(hKey, L"Print Processors", 0, KEY_READ, &hSubKey);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
goto Cleanup;
}
// Get the number of Print Processors and maximum sub key length.
dwErrorCode = (DWORD)RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwPrintProcessorCount, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
goto Cleanup;
}
// Allocate a temporary buffer to let RegEnumKeyExW succeed.
pwszTemp = DllAllocSplMem((cchMaxSubKey + 1) * sizeof(WCHAR));
if (!pwszTemp)
{
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
ERR("DllAllocSplMem failed!\n");
goto Cleanup;
}
// Determine the required size of the output buffer.
*pcbNeeded = 0;
for (i = 0; i < dwPrintProcessorCount; i++)
{
// RegEnumKeyExW sucks! Unlike similar API functions, it only returns the actual numbers of characters copied when you supply a buffer large enough.
// So use pwszTemp with its size cchMaxSubKey for this.
cchPrintProcessor = cchMaxSubKey + 1;
dwErrorCode = (DWORD)RegEnumKeyExW(hSubKey, i, pwszTemp, &cchPrintProcessor, NULL, NULL, NULL, NULL);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("RegEnumKeyExW failed with status %lu!\n", dwErrorCode);
goto Cleanup;
}
*pcbNeeded += sizeof(PRINTPROCESSOR_INFO_1W) + (cchPrintProcessor + 1) * sizeof(WCHAR);
}
// Check if the supplied buffer is large enough.
if (cbBuf < *pcbNeeded)
{
dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
goto Cleanup;
}
// Put the Print Processor strings right after the last PRINTPROCESSOR_INFO_1W structure.
pCurrentOutputPrintProcessorInfo = pPrintProcessorInfo;
pCurrentOutputPrintProcessor = pPrintProcessorInfo + dwPrintProcessorCount * sizeof(PRINTPROCESSOR_INFO_1W);
// Copy over all Print Processors.
for (i = 0; i < dwPrintProcessorCount; i++)
{
// This isn't really correct, but doesn't cause any harm, because we've extensively checked the size of the supplied buffer above.
cchPrintProcessor = cchMaxSubKey + 1;
// Copy the Print Processor name.
dwErrorCode = (DWORD)RegEnumKeyExW(hSubKey, i, (PWSTR)pCurrentOutputPrintProcessor, &cchPrintProcessor, NULL, NULL, NULL, NULL);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("RegEnumKeyExW failed with status %lu!\n", dwErrorCode);
goto Cleanup;
}
// Fill and copy the PRINTPROCESSOR_INFO_1W structure belonging to this Print Processor.
PrintProcessorInfo1.pName = (PWSTR)pCurrentOutputPrintProcessor;
CopyMemory(pCurrentOutputPrintProcessorInfo, &PrintProcessorInfo1, sizeof(PRINTPROCESSOR_INFO_1W));
// Advance to the next PRINTPROCESSOR_INFO_1W location and string location in the output buffer.
pCurrentOutputPrintProcessor += (cchPrintProcessor + 1) * sizeof(WCHAR);
pCurrentOutputPrintProcessorInfo += sizeof(PRINTPROCESSOR_INFO_1W);
}
// We've finished successfully!
*pcReturned = dwPrintProcessorCount;
dwErrorCode = ERROR_SUCCESS;
Cleanup:
if (pwszTemp)
DllFreeSplMem(pwszTemp);
if (hSubKey)
RegCloseKey(hSubKey);
if (hKey)
RegCloseKey(hKey);
SetLastError(dwErrorCode);
return (dwErrorCode == ERROR_SUCCESS);
}
/**
* @name LocalGetPrintProcessorDirectory
*
* Obtains the path to the local Print Processor directory.
* Print Provider function for GetPrintProcessorDirectoryA/GetPrintProcessorDirectoryW.
*
* @param pName
* Server Name. Ignored here, because every caller of LocalGetPrintProcessorDirectory is interested in the local directory.
*
* @param pEnvironment
* One of the predefined operating system and architecture "environment" strings (like "Windows NT x86").
*
* @param Level
* The level of the (non-existing) structure supplied through pPrintProcessorInfo. This must be 1.
*
* @param pPrintProcessorInfo
* Pointer to the buffer that receives the full path to the Print Processor directory.
* Can be NULL if you just want to know the required size of the buffer.
*
* @param cbBuf
* Size of the buffer you supplied for pPrintProcessorInfo, in bytes.
*
* @param pcbNeeded
* Pointer to a variable that receives the required size of the buffer for pPrintProcessorInfo, in bytes.
* This parameter mustn't be NULL!
*
* @return
* TRUE if we successfully copied the directory into pPrintProcessorInfo, FALSE otherwise.
* A more specific error code can be obtained through GetLastError.
*/
BOOL WINAPI
LocalGetPrintProcessorDirectory(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pPrintProcessorInfo, DWORD cbBuf, PDWORD pcbNeeded)
{
const WCHAR wszPath[] = L"\\PRTPROCS\\";
const DWORD cchPath = _countof(wszPath) - 1;
DWORD cbDirectoryName;
DWORD dwErrorCode;
HKEY hKey = NULL;
PWSTR pwszDirectory = (PWSTR)pPrintProcessorInfo;
TRACE("LocalGetPrintProcessorDirectory(%S, %S, %lu, %p, %lu, %p)\n", pName, pEnvironment, Level, pPrintProcessorInfo, cbBuf, pcbNeeded);
// Verify pEnvironment and open its registry key.
dwErrorCode = _OpenEnvironment(pEnvironment, &hKey);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("_OpenEnvironment failed with error %lu!\n", dwErrorCode);
goto Cleanup;
}
// Determine the size of the required buffer.
dwErrorCode = (DWORD)RegQueryValueExW(hKey, L"Directory", NULL, NULL, NULL, &cbDirectoryName);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
goto Cleanup;
}
*pcbNeeded = (cchSpoolDirectory + cchPath) * sizeof(WCHAR) + cbDirectoryName;
// Is the supplied buffer large enough?
if (cbBuf < *pcbNeeded)
{
dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
goto Cleanup;
}
// Copy the path to the "prtprocs" directory into pPrintProcessorInfo
CopyMemory(pwszDirectory, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR));
CopyMemory(&pwszDirectory[cchSpoolDirectory], wszPath, cchPath * sizeof(WCHAR));
// Get the directory name from the registry.
dwErrorCode = (DWORD)RegQueryValueExW(hKey, L"Directory", NULL, NULL, (PBYTE)&pwszDirectory[cchSpoolDirectory + cchPath], &cbDirectoryName);
if (dwErrorCode != ERROR_SUCCESS)
{
ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
goto Cleanup;
}
// We've finished successfully!
dwErrorCode = ERROR_SUCCESS;
Cleanup:
if (hKey)
RegCloseKey(hKey);
SetLastError(dwErrorCode);
return (dwErrorCode == ERROR_SUCCESS);
}