mirror of
https://github.com/reactos/reactos.git
synced 2024-11-20 14:30:57 +00:00
691 lines
24 KiB
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);
|
|
}
|