[LOCALSPL]
[reactos.git] / reactos / win32ss / printing / providers / localspl / printprocessors.c
index ac9e546..1b994c6 100644 (file)
@@ -2,7 +2,7 @@
  * PROJECT:     ReactOS Local Spooler
  * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
  * PURPOSE:     Functions related to Print Processors
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2016 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.h"
@@ -17,7 +17,7 @@ static LIST_ENTRY _PrintProcessorList;
  * Checks a supplied pEnvironment variable for validity and opens its registry key.
  *
  * @param pEnvironment
- * The pEnvironment variable to check. Can be NULL to use the current environment.
+ * The pEnvironment variable to check.
  *
  * @param hKey
  * On success, this variable will contain a HKEY to the opened registry key of the environment.
@@ -36,9 +36,12 @@ _OpenEnvironment(PCWSTR pEnvironment, PHKEY hKey)
     DWORD dwErrorCode;
     PWSTR pwszEnvironmentKey = NULL;
 
-    // Use the current environment if none was supplied.
+    // Sanity checks
     if (!pEnvironment)
-        pEnvironment = wszCurrentEnvironment;
+    {
+        dwErrorCode = ERROR_INVALID_ENVIRONMENT;
+        goto Cleanup;
+    }
 
     // Construct the registry key of the demanded environment.
     cchEnvironment = wcslen(pEnvironment);
@@ -73,11 +76,14 @@ Cleanup:
 }
 
 BOOL
-FindDatatype(PLOCAL_PRINT_PROCESSOR pPrintProcessor, PWSTR pwszDatatype)
+FindDatatype(const PLOCAL_PRINT_PROCESSOR pPrintProcessor, PCWSTR pwszDatatype)
 {
     DWORD i;
     PDATATYPES_INFO_1W pCurrentDatatype = pPrintProcessor->pDatatypesInfo1;
 
+    if (!pwszDatatype)
+        return FALSE;
+
     for (i = 0; i < pPrintProcessor->dwDatatypeCount; i++)
     {
         if (wcsicmp(pCurrentDatatype->pName, pwszDatatype) == 0)
@@ -90,12 +96,15 @@ FindDatatype(PLOCAL_PRINT_PROCESSOR pPrintProcessor, PWSTR pwszDatatype)
 }
 
 PLOCAL_PRINT_PROCESSOR
-FindPrintProcessor(PWSTR pwszName)
+FindPrintProcessor(PCWSTR pwszName)
 {
     PLIST_ENTRY pEntry;
     PLOCAL_PRINT_PROCESSOR pPrintProcessor;
 
-    for (pEntry = _PrintProcessorList.Flink; pEntry; pEntry = pEntry->Flink)
+    if (!pwszName)
+        return NULL;
+
+    for (pEntry = _PrintProcessorList.Flink; pEntry != &_PrintProcessorList; pEntry = pEntry->Flink)
     {
         pPrintProcessor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_PROCESSOR, Entry);
 
@@ -111,7 +120,7 @@ FindPrintProcessor(PWSTR pwszName)
  *
  * Initializes a singly linked list of locally available Print Processors.
  */
-void
+BOOL
 InitializePrintProcessorList()
 {
     DWORD cbDatatypes;
@@ -119,15 +128,14 @@ InitializePrintProcessorList()
     DWORD cchPrintProcessorPath;
     DWORD cchMaxSubKey;
     DWORD cchPrintProcessorName;
+    DWORD dwErrorCode;
     DWORD dwSubKeys;
     DWORD i;
     HINSTANCE hinstPrintProcessor;
     HKEY hKey = NULL;
     HKEY hSubKey = NULL;
     HKEY hSubSubKey = NULL;
-    LONG lStatus;
     PLOCAL_PRINT_PROCESSOR pPrintProcessor = NULL;
-    PWSTR pwszPrintProcessorName = NULL;
     WCHAR wszFileName[MAX_PATH];
     WCHAR wszPrintProcessorPath[MAX_PATH];
 
@@ -135,37 +143,38 @@ InitializePrintProcessorList()
     InitializeListHead(&_PrintProcessorList);
     
     // Prepare the path to the Print Processor directory.
-    if (!LocalGetPrintProcessorDirectory(NULL, NULL, 1, (PBYTE)wszPrintProcessorPath, sizeof(wszPrintProcessorPath), &cchPrintProcessorPath))
+    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);
-    wszPrintProcessorPath[cchPrintProcessorPath++] = L'\\';
+    --cchPrintProcessorPath;
+
+    // Append a trailing backslash.
+    wszPrintProcessorPath[cchPrintProcessorPath] = L'\\';
+    ++cchPrintProcessorPath;
 
     // Open the environment registry key.
-    if (_OpenEnvironment(NULL, &hKey) != ERROR_SUCCESS)
+    dwErrorCode = _OpenEnvironment(wszCurrentEnvironment, &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, &dwSubKeys, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL);
-    if (lStatus != ERROR_SUCCESS)
-    {
-        ERR("RegQueryInfoKeyW failed with status %ld!\n", lStatus);
-        goto Cleanup;
-    }
-
-    // Allocate a temporary buffer for the Print Processor names.
-    pwszPrintProcessorName = DllAllocSplMem((cchMaxSubKey + 1) * sizeof(WCHAR));
-    if (!pwszPrintProcessorName)
+    dwErrorCode = (DWORD)RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwSubKeys, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL);
+    if (dwErrorCode != ERROR_SUCCESS)
     {
-        ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
+        ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
         goto Cleanup;
     }
 
@@ -191,36 +200,54 @@ InitializePrintProcessorList()
             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 with error %lu!\n", GetLastError());
+            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 with error %lu!\n", GetLastError());
+            goto Cleanup;
+        }
+
         // Get the name of this Print Processor.
-        cchPrintProcessorName = cchMaxSubKey;
-        lStatus = RegEnumKeyExW(hSubKey, i, pwszPrintProcessorName, &cchPrintProcessorName, NULL, NULL, NULL, NULL);
-        if (lStatus != ERROR_SUCCESS)
+        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", lStatus);
+            ERR("RegEnumKeyExW failed with status %ld!\n", dwErrorCode);
             continue;
         }
 
         // Open this Print Processor's registry key.
-        lStatus = RegOpenKeyExW(hSubKey, pwszPrintProcessorName, 0, KEY_READ, &hSubSubKey);
-        if (lStatus != ERROR_SUCCESS)
+        dwErrorCode = (DWORD)RegOpenKeyExW(hSubKey, pPrintProcessor->pwszName, 0, KEY_READ, &hSubSubKey);
+        if (dwErrorCode != ERROR_SUCCESS)
         {
-            ERR("RegOpenKeyExW failed for Print Processor \"%S\" with status %ld!\n", pwszPrintProcessorName, lStatus);
+            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);
-        lStatus = RegQueryValueExW(hSubSubKey, L"Driver", NULL, NULL, (PBYTE)wszFileName, &cbFileName);
-        if (lStatus != ERROR_SUCCESS)
+        dwErrorCode = (DWORD)RegQueryValueExW(hSubSubKey, L"Driver", NULL, NULL, (PBYTE)wszFileName, &cbFileName);
+        if (dwErrorCode != ERROR_SUCCESS)
         {
-            ERR("RegQueryValueExW failed for Print Processor \"%S\" with status %ld!\n", pwszPrintProcessorName, lStatus);
+            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, pwszPrintProcessorName);
+            ERR("Print Processor directory \"%S\" for Print Processor \"%S\" is too long!\n", wszFileName, pPrintProcessor->pwszName);
             continue;
         }
 
@@ -229,16 +256,12 @@ InitializePrintProcessorList()
 
         // Try to load it.
         hinstPrintProcessor = LoadLibraryW(wszPrintProcessorPath);
-        if (lStatus != ERROR_SUCCESS)
+        if (!hinstPrintProcessor)
         {
             ERR("LoadLibraryW failed for \"%S\" with error %lu!\n", wszPrintProcessorPath, GetLastError());
             continue;
         }
 
-        // Create a new LOCAL_PRINT_PROCESSOR structure for it.
-        pPrintProcessor = DllAllocSplMem(sizeof(LOCAL_PRINT_PROCESSOR));
-        pPrintProcessor->pwszName = AllocSplStr(pwszPrintProcessorName);
-
         // Get and verify all its function pointers.
         pPrintProcessor->pfnClosePrintProcessor = (PClosePrintProcessor)GetProcAddress(hinstPrintProcessor, "ClosePrintProcessor");
         if (!pPrintProcessor->pfnClosePrintProcessor)
@@ -287,6 +310,7 @@ InitializePrintProcessorList()
         pPrintProcessor->pDatatypesInfo1 = DllAllocSplMem(cbDatatypes);
         if (!pPrintProcessor->pDatatypesInfo1)
         {
+            dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
             ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
             goto Cleanup;
         }
@@ -304,6 +328,8 @@ InitializePrintProcessorList()
         pPrintProcessor = NULL;
     }
 
+    dwErrorCode = ERROR_SUCCESS;
+
 Cleanup:
     // Inside the loop
     if (hSubSubKey)
@@ -321,14 +347,14 @@ Cleanup:
     }
 
     // Outside the loop
-    if (pwszPrintProcessorName)
-        DllFreeSplStr(pwszPrintProcessorName);
-
     if (hSubKey)
         RegCloseKey(hSubKey);
 
     if (hKey)
         RegCloseKey(hKey);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
 }
 
 /**
@@ -438,6 +464,7 @@ LocalEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE
     DWORD cchMaxSubKey;
     DWORD cchPrintProcessor;
     DWORD dwErrorCode;
+    DWORD dwPrintProcessorCount;
     DWORD i;
     HKEY hKey = NULL;
     HKEY hSubKey = NULL;
@@ -475,7 +502,7 @@ LocalEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE
     }
 
     // Get the number of Print Processors and maximum sub key length.
-    dwErrorCode = (DWORD)RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, pcReturned, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL);
+    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);
@@ -494,11 +521,11 @@ LocalEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE
     // Determine the required size of the output buffer.
     *pcbNeeded = 0;
 
-    for (i = 0; i < *pcReturned; i++)
+    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;
+        cchPrintProcessor = cchMaxSubKey + 1;
         dwErrorCode = (DWORD)RegEnumKeyExW(hSubKey, i, pwszTemp, &cchPrintProcessor, NULL, NULL, NULL, NULL);
         if (dwErrorCode != ERROR_SUCCESS)
         {
@@ -518,13 +545,13 @@ LocalEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE
 
     // Put the Print Processor strings right after the last PRINTPROCESSOR_INFO_1W structure.
     pCurrentOutputPrintProcessorInfo = pPrintProcessorInfo;
-    pCurrentOutputPrintProcessor = pPrintProcessorInfo + *pcReturned * sizeof(PRINTPROCESSOR_INFO_1W);
+    pCurrentOutputPrintProcessor = pPrintProcessorInfo + dwPrintProcessorCount * sizeof(PRINTPROCESSOR_INFO_1W);
 
     // Copy over all Print Processors.
-    for (i = 0; i < *pcReturned; i++)
+    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;
+        cchPrintProcessor = cchMaxSubKey + 1;
 
         // Copy the Print Processor name.
         dwErrorCode = (DWORD)RegEnumKeyExW(hSubKey, i, (PWSTR)pCurrentOutputPrintProcessor, &cchPrintProcessor, NULL, NULL, NULL, NULL);
@@ -544,6 +571,7 @@ LocalEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE
     }
 
     // We've finished successfully!
+    *pcReturned = dwPrintProcessorCount;
     dwErrorCode = ERROR_SUCCESS;
 
 Cleanup:
@@ -571,7 +599,6 @@ Cleanup:
  *
  * @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 (non-existing) structure supplied through pPrintProcessorInfo. This must be 1.
@@ -592,44 +619,33 @@ Cleanup:
  * A more specific error code can be obtained through GetLastError.
  */
 BOOL WINAPI
-LocalGetPrintProcessorDirectory(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded)
+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 cbDataWritten;
+    DWORD cbDirectoryName;
     DWORD dwErrorCode;
     HKEY hKey = NULL;
-
-    // Sanity checks
-    if (Level != 1)
-    {
-        dwErrorCode = ERROR_INVALID_LEVEL;
-        goto Cleanup;
-    }
-
-    if (!pcbNeeded)
-    {
-        // This error is also caught by RPC and returned as RPC_X_NULL_REF_POINTER.
-        dwErrorCode = ERROR_INVALID_PARAMETER;
-        goto Cleanup;
-    }
+    PWSTR pwszDirectory = (PWSTR)pPrintProcessorInfo;
 
     // 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, pcbNeeded);
+    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;
-    *pcbNeeded += cchPath;
+    *pcbNeeded = (cchSpoolDirectory + cchPath) * sizeof(WCHAR) + cbDirectoryName;
 
     // Is the supplied buffer large enough?
     if (cbBuf < *pcbNeeded)
@@ -639,11 +655,11 @@ LocalGetPrintProcessorDirectory(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
     }
 
     // Copy the path to the "prtprocs" directory into pPrintProcessorInfo
-    CopyMemory(pPrintProcessorInfo, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR));
-    CopyMemory(&pPrintProcessorInfo[cchSpoolDirectory], wszPath, cchPath * sizeof(WCHAR));
+    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, &pPrintProcessorInfo[cchSpoolDirectory + cchPath], &cbDataWritten);
+    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);