#include "precomp.h"
-// Global Variables
-RTL_GENERIC_TABLE PrintProcessorTable;
+// Local Variables
+static LIST_ENTRY _PrintProcessorList;
/**
* @name _OpenEnvironment
* You can use it for further tasks and have to close it with RegCloseKey.
*
* @return
- * TRUE if the environment is valid and a registry key was opened, FALSE otherwise.
- * In case of failure, Last Error will be set to ERROR_INVALID_ENVIRONMENT.
+ * A Windows Error Code indicating success or failure.
*/
-static BOOL
+static DWORD
_OpenEnvironment(PCWSTR pEnvironment, PHKEY hKey)
{
const WCHAR wszEnvironmentsKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Environments\\";
const DWORD cchEnvironmentsKey = _countof(wszEnvironmentsKey) - 1;
- BOOL bReturnValue = FALSE;
DWORD cchEnvironment;
- LONG lStatus;
+ DWORD dwErrorCode;
PWSTR pwszEnvironmentKey = NULL;
// Use the current environment if none was supplied.
CopyMemory(&pwszEnvironmentKey[cchEnvironmentsKey], pEnvironment, (cchEnvironment + 1) * sizeof(WCHAR));
// Open the registry key.
- lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE, pwszEnvironmentKey, 0, KEY_READ, hKey);
- if (lStatus == ERROR_FILE_NOT_FOUND)
+ dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, pwszEnvironmentKey, 0, KEY_READ, hKey);
+ if (dwErrorCode == ERROR_FILE_NOT_FOUND)
{
- SetLastError(ERROR_INVALID_ENVIRONMENT);
+ dwErrorCode = ERROR_INVALID_ENVIRONMENT;
goto Cleanup;
}
- else if (lStatus != ERROR_SUCCESS)
+ else if (dwErrorCode != ERROR_SUCCESS)
{
- ERR("RegOpenKeyExW failed with status %ld!\n", lStatus);
+ ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
goto Cleanup;
}
- bReturnValue = TRUE;
-
Cleanup:
if (pwszEnvironmentKey)
DllFreeSplMem(pwszEnvironmentKey);
- return bReturnValue;
+ return dwErrorCode;
}
-/**
- * @name _PrinterTableCompareRoutine
- *
- * RTL_GENERIC_COMPARE_ROUTINE for the Print Processor Table.
- * Does a case-insensitive comparison, because e.g. LocalEnumPrintProcessorDatatypes doesn't match the case when looking for Print Processors.
- */
-static RTL_GENERIC_COMPARE_RESULTS NTAPI
-_PrintProcessorTableCompareRoutine(PRTL_GENERIC_TABLE Table, PVOID FirstStruct, PVOID SecondStruct)
+BOOL
+FindDatatype(PLOCAL_PRINT_PROCESSOR pPrintProcessor, PWSTR pwszDatatype)
{
- PLOCAL_PRINT_PROCESSOR A = (PLOCAL_PRINT_PROCESSOR)FirstStruct;
- PLOCAL_PRINT_PROCESSOR B = (PLOCAL_PRINT_PROCESSOR)SecondStruct;
+ DWORD i;
+ PDATATYPES_INFO_1W pCurrentDatatype = pPrintProcessor->pDatatypesInfo1;
- int iResult = wcsicmp(A->pwszName, B->pwszName);
+ for (i = 0; i < pPrintProcessor->dwDatatypeCount; i++)
+ {
+ if (wcsicmp(pCurrentDatatype->pName, pwszDatatype) == 0)
+ return TRUE;
- if (iResult < 0)
- return GenericLessThan;
- else if (iResult > 0)
- return GenericGreaterThan;
- else
- return GenericEqual;
+ ++pCurrentDatatype;
+ }
+
+ return FALSE;
}
-/**
- * @name _DatatypeTableCompareRoutine
- *
- * RTL_GENERIC_COMPARE_ROUTINE for the Datatype Table.
- * Does a case-insensitive comparison, because e.g. LocalOpenPrinter doesn't match the case when looking for Datatypes.
- */
-static RTL_GENERIC_COMPARE_RESULTS NTAPI
-_DatatypeTableCompareRoutine(PRTL_GENERIC_TABLE Table, PVOID FirstStruct, PVOID SecondStruct)
+PLOCAL_PRINT_PROCESSOR
+FindPrintProcessor(PWSTR pwszName)
{
- PWSTR A = (PWSTR)FirstStruct;
- PWSTR B = (PWSTR)SecondStruct;
+ PLIST_ENTRY pEntry;
+ PLOCAL_PRINT_PROCESSOR pPrintProcessor;
- int iResult = wcsicmp(A, B);
+ for (pEntry = _PrintProcessorList.Flink; pEntry; pEntry = pEntry->Flink)
+ {
+ pPrintProcessor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_PROCESSOR, Entry);
- if (iResult < 0)
- return GenericLessThan;
- else if (iResult > 0)
- return GenericGreaterThan;
- else
- return GenericEqual;
+ if (wcsicmp(pPrintProcessor->pwszName, pwszName) == 0)
+ return pPrintProcessor;
+ }
+
+ return NULL;
}
/**
- * @name InitializePrintProcessorTable
+ * @name InitializePrintProcessorList
*
- * Initializes a RTL_GENERIC_TABLE of locally available Print Processors.
- * The table is searchable by name and returns pointers to the functions of the loaded Print Processor DLL.
+ * Initializes a singly linked list of locally available Print Processors.
*/
void
-InitializePrintProcessorTable()
+InitializePrintProcessorList()
{
DWORD cbDatatypes;
DWORD cbFileName;
DWORD cchPrintProcessorPath;
DWORD cchMaxSubKey;
DWORD cchPrintProcessorName;
- DWORD dwDatatypes;
DWORD dwSubKeys;
DWORD i;
- DWORD j;
HINSTANCE hinstPrintProcessor;
HKEY hKey = NULL;
HKEY hSubKey = NULL;
HKEY hSubSubKey = NULL;
LONG lStatus;
- PDATATYPES_INFO_1W pDatatypesInfo1 = NULL;
PLOCAL_PRINT_PROCESSOR pPrintProcessor = NULL;
- PWSTR pwszDatatype = NULL;
PWSTR pwszPrintProcessorName = NULL;
WCHAR wszFileName[MAX_PATH];
WCHAR wszPrintProcessorPath[MAX_PATH];
- // Initialize an empty table for our Print Processors.
- // We will search it by Print Processor name.
- RtlInitializeGenericTable(&PrintProcessorTable, _PrintProcessorTableCompareRoutine, GenericTableAllocateRoutine, GenericTableFreeRoutine, NULL);
+ // Initialize an empty list for our Print Processors.
+ InitializeListHead(&_PrintProcessorList);
// Prepare the path to the Print Processor directory.
if (!LocalGetPrintProcessorDirectory(NULL, NULL, 1, (PBYTE)wszPrintProcessorPath, sizeof(wszPrintProcessorPath), &cchPrintProcessorPath))
wszPrintProcessorPath[cchPrintProcessorPath++] = L'\\';
// Open the environment registry key.
- if (!_OpenEnvironment(NULL, &hKey))
+ if (_OpenEnvironment(NULL, &hKey) != ERROR_SUCCESS)
goto Cleanup;
// Open the "Print Processors" subkey.
if (pPrintProcessor->pwszName)
DllFreeSplStr(pPrintProcessor->pwszName);
+ if (pPrintProcessor->pDatatypesInfo1)
+ DllFreeSplMem(pPrintProcessor->pDatatypesInfo1);
+
DllFreeSplMem(pPrintProcessor);
pPrintProcessor = NULL;
}
- if (pDatatypesInfo1)
- {
- DllFreeSplMem(pDatatypesInfo1);
- pDatatypesInfo1 = NULL;
- }
-
// Get the name of this Print Processor.
cchPrintProcessorName = cchMaxSubKey;
lStatus = RegEnumKeyExW(hSubKey, i, pwszPrintProcessorName, &cchPrintProcessorName, NULL, NULL, NULL, NULL);
}
// Get all supported datatypes.
- pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, 0, &cbDatatypes, &dwDatatypes);
- pDatatypesInfo1 = DllAllocSplMem(cbDatatypes);
- if (!pDatatypesInfo1)
+ pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, 0, &cbDatatypes, &pPrintProcessor->dwDatatypeCount);
+ pPrintProcessor->pDatatypesInfo1 = DllAllocSplMem(cbDatatypes);
+ if (!pPrintProcessor->pDatatypesInfo1)
{
ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
goto Cleanup;
}
- if (!pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, (PBYTE)pDatatypesInfo1, cbDatatypes, &cbDatatypes, &dwDatatypes))
+ 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 supported datatypes to the datatype table.
- RtlInitializeGenericTable(&pPrintProcessor->DatatypeTable, _DatatypeTableCompareRoutine, GenericTableAllocateRoutine, GenericTableFreeRoutine, NULL);
-
- for (j = 0; j < dwDatatypes; j++)
- {
- pwszDatatype = AllocSplStr(pDatatypesInfo1->pName);
-
- if (!RtlInsertElementGenericTable(&pPrintProcessor->DatatypeTable, pDatatypesInfo1->pName, sizeof(PWSTR), NULL))
- {
- ERR("RtlInsertElementGenericTable failed for iteration %lu with error %lu!\n", j, GetLastError());
- goto Cleanup;
- }
-
- ++pDatatypesInfo1;
- }
-
- // Add the Print Processor to the table.
- if (!RtlInsertElementGenericTable(&PrintProcessorTable, pPrintProcessor, sizeof(LOCAL_PRINT_PROCESSOR), NULL))
- {
- ERR("RtlInsertElementGenericTable failed for iteration %lu with error %lu!\n", i, GetLastError());
- goto Cleanup;
- }
+ // Add the Print Processor to the list.
+ InsertTailList(&_PrintProcessorList, &pPrintProcessor->Entry);
// Don't let the cleanup routines free this.
- pwszDatatype = NULL;
pPrintProcessor = NULL;
}
Cleanup:
- if (pwszDatatype)
- DllFreeSplStr(pwszDatatype);
-
- if (pDatatypesInfo1)
- DllFreeSplMem(pDatatypesInfo1);
+ // 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 (pwszPrintProcessorName)
DllFreeSplStr(pwszPrintProcessorName);
- if (hSubSubKey)
- RegCloseKey(hSubSubKey);
-
if (hSubKey)
RegCloseKey(hSubKey);
BOOL WINAPI
LocalEnumPrintProcessorDatatypes(LPWSTR pName, LPWSTR pPrintProcessorName, DWORD Level, LPBYTE pDatatypes, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
{
+ DWORD dwErrorCode;
PLOCAL_PRINT_PROCESSOR pPrintProcessor;
// Sanity checks
if (Level != 1)
{
- SetLastError(ERROR_INVALID_LEVEL);
- return FALSE;
+ dwErrorCode = ERROR_INVALID_LEVEL;
+ goto Cleanup;
}
// Try to find the Print Processor.
- pPrintProcessor = RtlLookupElementGenericTable(&PrintProcessorTable, pPrintProcessorName);
+ pPrintProcessor = FindPrintProcessor(pPrintProcessorName);
if (!pPrintProcessor)
{
- SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
- return FALSE;
+ dwErrorCode = ERROR_UNKNOWN_PRINTPROCESSOR;
+ goto Cleanup;
}
// Call its EnumPrintProcessorDatatypesW function.
- return pPrintProcessor->pfnEnumPrintProcessorDatatypesW(pName, pPrintProcessorName, Level, pDatatypes, cbBuf, pcbNeeded, pcReturned);
+ if (pPrintProcessor->pfnEnumPrintProcessorDatatypesW(pName, pPrintProcessorName, Level, pDatatypes, cbBuf, pcbNeeded, pcReturned))
+ dwErrorCode = ERROR_SUCCESS;
+ else
+ dwErrorCode = GetLastError();
+
+Cleanup:
+ SetLastError(dwErrorCode);
+ return (dwErrorCode == ERROR_SUCCESS);
}
/**
BOOL WINAPI
LocalEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
{
- BOOL bReturnValue = FALSE;
DWORD cchMaxSubKey;
DWORD cchPrintProcessor;
+ DWORD dwErrorCode;
DWORD i;
HKEY hKey = NULL;
HKEY hSubKey = NULL;
- LONG lStatus;
PBYTE pCurrentOutputPrintProcessor;
PBYTE pCurrentOutputPrintProcessorInfo;
PRINTPROCESSOR_INFO_1W PrintProcessorInfo1;
// Sanity checks
if (Level != 1)
{
- SetLastError(ERROR_INVALID_LEVEL);
+ dwErrorCode = ERROR_INVALID_LEVEL;
goto Cleanup;
}
if (!pcbNeeded || !pcReturned)
{
- // This error must be caught by RPC and returned as RPC_X_NULL_REF_POINTER.
- ERR("pcbNeeded or pcReturned is NULL!\n");
+ // 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.
- if (!_OpenEnvironment(pEnvironment, &hKey))
+ // 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.
- lStatus = RegOpenKeyExW(hKey, L"Print Processors", 0, KEY_READ, &hSubKey);
- if (lStatus != ERROR_SUCCESS)
+ dwErrorCode = (DWORD)RegOpenKeyExW(hKey, L"Print Processors", 0, KEY_READ, &hSubKey);
+ if (dwErrorCode != ERROR_SUCCESS)
{
- ERR("RegOpenKeyExW failed with status %ld!\n", lStatus);
+ ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
goto Cleanup;
}
// Get the number of Print Processors and maximum sub key length.
- lStatus = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, pcReturned, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL);
- if (lStatus != ERROR_SUCCESS)
+ dwErrorCode = (DWORD)RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, pcReturned, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (dwErrorCode != ERROR_SUCCESS)
{
- ERR("RegQueryInfoKeyW failed with status %ld!\n", lStatus);
+ ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
goto Cleanup;
}
pwszTemp = DllAllocSplMem((cchMaxSubKey + 1) * sizeof(WCHAR));
if (!pwszTemp)
{
+ dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
goto Cleanup;
}
// 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;
- lStatus = RegEnumKeyExW(hSubKey, i, pwszTemp, &cchPrintProcessor, NULL, NULL, NULL, NULL);
- if (lStatus != ERROR_SUCCESS)
+ dwErrorCode = (DWORD)RegEnumKeyExW(hSubKey, i, pwszTemp, &cchPrintProcessor, NULL, NULL, NULL, NULL);
+ if (dwErrorCode != ERROR_SUCCESS)
{
- ERR("RegEnumKeyExW failed with status %ld!\n", lStatus);
+ ERR("RegEnumKeyExW failed with status %lu!\n", dwErrorCode);
goto Cleanup;
}
// Check if the supplied buffer is large enough.
if (cbBuf < *pcbNeeded)
{
- SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
goto Cleanup;
}
cchPrintProcessor = cchMaxSubKey;
// Copy the Print Processor name.
- lStatus = RegEnumKeyExW(hSubKey, i, (PWSTR)pCurrentOutputPrintProcessor, &cchPrintProcessor, NULL, NULL, NULL, NULL);
- if (lStatus != ERROR_SUCCESS)
+ dwErrorCode = (DWORD)RegEnumKeyExW(hSubKey, i, (PWSTR)pCurrentOutputPrintProcessor, &cchPrintProcessor, NULL, NULL, NULL, NULL);
+ if (dwErrorCode != ERROR_SUCCESS)
{
- ERR("RegEnumKeyExW failed with status %ld!\n", lStatus);
+ ERR("RegEnumKeyExW failed with status %lu!\n", dwErrorCode);
goto Cleanup;
}
}
// We've finished successfully!
- SetLastError(ERROR_SUCCESS);
- bReturnValue = TRUE;
+ dwErrorCode = ERROR_SUCCESS;
Cleanup:
if (pwszTemp)
if (hKey)
RegCloseKey(hKey);
- return bReturnValue;
+ SetLastError(dwErrorCode);
+ return (dwErrorCode == ERROR_SUCCESS);
}
/**
const WCHAR wszPath[] = L"\\PRTPROCS\\";
const DWORD cchPath = _countof(wszPath) - 1;
- BOOL bReturnValue = FALSE;
DWORD cbDataWritten;
+ DWORD dwErrorCode;
HKEY hKey = NULL;
- LONG lStatus;
// Sanity checks
if (Level != 1)
{
- SetLastError(ERROR_INVALID_LEVEL);
+ dwErrorCode = ERROR_INVALID_LEVEL;
goto Cleanup;
}
if (!pcbNeeded)
{
- // This error must be caught by RPC and returned as RPC_X_NULL_REF_POINTER.
- ERR("pcbNeeded is NULL!\n");
+ // 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.
- if (!_OpenEnvironment(pEnvironment, &hKey))
+ dwErrorCode = _OpenEnvironment(pEnvironment, &hKey);
+ if (dwErrorCode != ERROR_SUCCESS)
goto Cleanup;
// Determine the size of the required buffer.
- lStatus = RegQueryValueExW(hKey, L"Directory", NULL, NULL, NULL, pcbNeeded);
- if (lStatus != ERROR_SUCCESS)
+ dwErrorCode = (DWORD)RegQueryValueExW(hKey, L"Directory", NULL, NULL, NULL, pcbNeeded);
+ if (dwErrorCode != ERROR_SUCCESS)
{
- ERR("RegQueryValueExW failed with status %ld!\n", lStatus);
+ ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
goto Cleanup;
}
// Is the supplied buffer large enough?
if (cbBuf < *pcbNeeded)
{
- SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
goto Cleanup;
}
CopyMemory(&pPrintProcessorInfo[cchSpoolDirectory], wszPath, cchPath * sizeof(WCHAR));
// Get the directory name from the registry.
- lStatus = RegQueryValueExW(hKey, L"Directory", NULL, NULL, &pPrintProcessorInfo[cchSpoolDirectory + cchPath], &cbDataWritten);
- if (lStatus != ERROR_SUCCESS)
+ dwErrorCode = (DWORD)RegQueryValueExW(hKey, L"Directory", NULL, NULL, &pPrintProcessorInfo[cchSpoolDirectory + cchPath], &cbDataWritten);
+ if (dwErrorCode != ERROR_SUCCESS)
{
- ERR("RegQueryValueExW failed with status %ld!\n", lStatus);
+ ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
goto Cleanup;
}
// We've finished successfully!
- SetLastError(ERROR_SUCCESS);
- bReturnValue = TRUE;
+ dwErrorCode = ERROR_SUCCESS;
Cleanup:
if (hKey)
RegCloseKey(hKey);
- return bReturnValue;
+ SetLastError(dwErrorCode);
+ return (dwErrorCode == ERROR_SUCCESS);
}