[SERVICES] Simplify the implementation of RGetServiceDisplayNameA/W() and RGetService... 874/head
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Sat, 22 Sep 2018 13:41:17 +0000 (15:41 +0200)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Sun, 23 Sep 2018 16:09:10 +0000 (18:09 +0200)
Also comment about the observed behaviour of the returned number of "characters"
returned by the ANSI versions of these APIs (which is tested by advapi32_winetest:service).

[ADVAPI32:SCM] Add a comment concerning wide characters vs. bytes mismatch.

base/system/services/rpcserver.c
dll/win32/advapi32/service/scm.c

index 454181b..f21df10 100644 (file)
@@ -2947,7 +2947,7 @@ ROpenServiceW(
     lpService = ScmGetServiceEntryByName(lpServiceName);
     if (lpService == NULL)
     {
-        DPRINT("Could not find a service!\n");
+        DPRINT("Could not find the service!\n");
         dwError = ERROR_SERVICE_DOES_NOT_EXIST;
         goto Done;
     }
@@ -3310,8 +3310,9 @@ RGetServiceDisplayNameW(
     LPWSTR lpDisplayName,
     DWORD *lpcchBuffer)
 {
-//    PMANAGER_HANDLE hManager;
+    // PMANAGER_HANDLE hManager;
     PSERVICE lpService;
+    LPCWSTR lpSvcDisplayName;
     DWORD dwLength;
     DWORD dwError;
 
@@ -3321,56 +3322,42 @@ RGetServiceDisplayNameW(
     DPRINT("lpDisplayName: %p\n", lpDisplayName);
     DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
 
-//    hManager = (PMANAGER_HANDLE)hSCManager;
-//    if (hManager->Handle.Tag != MANAGER_TAG)
-//    {
-//        DPRINT("Invalid manager handle!\n");
-//        return ERROR_INVALID_HANDLE;
-//    }
+#if 0
+    hManager = (PMANAGER_HANDLE)hSCManager;
+    if (hManager->Handle.Tag != MANAGER_TAG)
+    {
+        DPRINT("Invalid manager handle!\n");
+        return ERROR_INVALID_HANDLE;
+    }
+#endif
 
     /* Get service database entry */
     lpService = ScmGetServiceEntryByName(lpServiceName);
     if (lpService == NULL)
     {
-        DPRINT("Could not find a service!\n");
-
-        /* If the service could not be found and lpcchBuffer is less than 2, windows
-           puts null in lpDisplayName and puts 2 in lpcchBuffer */
-        if (*lpcchBuffer < sizeof(WCHAR))
-        {
-            *lpcchBuffer = sizeof(WCHAR);
-            if (lpDisplayName != NULL)
-            {
-                *lpDisplayName = 0;
-            }
-        }
-
+        DPRINT("Could not find the service!\n");
         return ERROR_SERVICE_DOES_NOT_EXIST;
     }
 
-    if (!lpService->lpDisplayName)
+    if (lpService->lpDisplayName)
+        lpSvcDisplayName = lpService->lpDisplayName;
+    else
+        lpSvcDisplayName = lpService->lpServiceName;
+
+    dwLength = (DWORD)wcslen(lpSvcDisplayName);
+
+    if (*lpcchBuffer > dwLength)
     {
-        dwLength = (DWORD)wcslen(lpService->lpServiceName);
+        if (lpDisplayName != NULL)
+            wcscpy(lpDisplayName, lpSvcDisplayName);
 
-        if (lpDisplayName != NULL &&
-            *lpcchBuffer > dwLength)
-        {
-            wcscpy(lpDisplayName, lpService->lpServiceName);
-        }
+        dwError = ERROR_SUCCESS;
     }
     else
     {
-        dwLength = (DWORD)wcslen(lpService->lpDisplayName);
-
-        if (lpDisplayName != NULL &&
-            *lpcchBuffer > dwLength)
-        {
-            wcscpy(lpDisplayName, lpService->lpDisplayName);
-        }
+        dwError = ERROR_INSUFFICIENT_BUFFER;
     }
 
-    dwError = (*lpcchBuffer > dwLength) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER;
-
     *lpcchBuffer = dwLength;
 
     return dwError;
@@ -3386,7 +3373,7 @@ RGetServiceKeyNameW(
     LPWSTR lpServiceName,
     DWORD *lpcchBuffer)
 {
-//    PMANAGER_HANDLE hManager;
+    // PMANAGER_HANDLE hManager;
     PSERVICE lpService;
     DWORD dwLength;
     DWORD dwError;
@@ -3397,44 +3384,36 @@ RGetServiceKeyNameW(
     DPRINT("lpServiceName: %p\n", lpServiceName);
     DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
 
-//    hManager = (PMANAGER_HANDLE)hSCManager;
-//    if (hManager->Handle.Tag != MANAGER_TAG)
-//    {
-//        DPRINT("Invalid manager handle!\n");
-//        return ERROR_INVALID_HANDLE;
-//    }
+#if 0
+    hManager = (PMANAGER_HANDLE)hSCManager;
+    if (hManager->Handle.Tag != MANAGER_TAG)
+    {
+        DPRINT("Invalid manager handle!\n");
+        return ERROR_INVALID_HANDLE;
+    }
+#endif
 
     /* Get service database entry */
     lpService = ScmGetServiceEntryByDisplayName(lpDisplayName);
     if (lpService == NULL)
     {
-        DPRINT("Could not find a service!\n");
-
-        /* If the service could not be found and lpcchBuffer is less than 2, windows
-           puts null in lpDisplayName and puts 2 in lpcchBuffer */
-        if (*lpcchBuffer < sizeof(WCHAR))
-        {
-            *lpcchBuffer = sizeof(WCHAR);
-            if (lpServiceName != NULL)
-            {
-                *lpServiceName = 0;
-            }
-        }
-
+        DPRINT("Could not find the service!\n");
         return ERROR_SERVICE_DOES_NOT_EXIST;
     }
 
     dwLength = (DWORD)wcslen(lpService->lpServiceName);
 
-    if (lpServiceName != NULL &&
-        *lpcchBuffer > dwLength)
+    if (*lpcchBuffer > dwLength)
     {
-        wcscpy(lpServiceName, lpService->lpServiceName);
-        *lpcchBuffer = dwLength;
-        return ERROR_SUCCESS;
-    }
+        if (lpServiceName != NULL)
+            wcscpy(lpServiceName, lpService->lpServiceName);
 
-    dwError = (*lpcchBuffer > dwLength) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER;
+        dwError = ERROR_SUCCESS;
+    }
+    else
+    {
+        dwError = ERROR_INSUFFICIENT_BUFFER;
+    }
 
     *lpcchBuffer = dwLength;
 
@@ -4474,11 +4453,11 @@ RGetServiceDisplayNameA(
     LPSTR lpDisplayName,
     LPBOUNDED_DWORD_4K lpcchBuffer)
 {
-//    PMANAGER_HANDLE hManager;
+    // PMANAGER_HANDLE hManager;
     PSERVICE lpService = NULL;
-    DWORD dwLength;
-    DWORD dwError;
+    LPCWSTR lpSvcDisplayName;
     LPWSTR lpServiceNameW;
+    DWORD dwLength;
 
     DPRINT("RGetServiceDisplayNameA() called\n");
     DPRINT("hSCManager = %p\n", hSCManager);
@@ -4486,13 +4465,16 @@ RGetServiceDisplayNameA(
     DPRINT("lpDisplayName: %p\n", lpDisplayName);
     DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
 
-//    hManager = (PMANAGER_HANDLE)hSCManager;
-//    if (hManager->Handle.Tag != MANAGER_TAG)
-//    {
-//        DPRINT("Invalid manager handle!\n");
-//        return ERROR_INVALID_HANDLE;
-//    }
+#if 0
+    hManager = (PMANAGER_HANDLE)hSCManager;
+    if (hManager->Handle.Tag != MANAGER_TAG)
+    {
+        DPRINT("Invalid manager handle!\n");
+        return ERROR_INVALID_HANDLE;
+    }
+#endif
 
+    /* Get service database entry */
     if (lpServiceName != NULL)
     {
         dwLength = (DWORD)(strlen(lpServiceName) + 1);
@@ -4516,61 +4498,71 @@ RGetServiceDisplayNameA(
 
     if (lpService == NULL)
     {
-        DPRINT("Could not find a service!\n");
-
-        /* If the service could not be found and lpcchBuffer is 0, windows
-           puts null in lpDisplayName and puts 1 in lpcchBuffer */
-        if (*lpcchBuffer == 0)
-        {
-            *lpcchBuffer = sizeof(CHAR);
-            if (lpDisplayName != NULL)
-            {
-                *lpDisplayName = 0;
-            }
-        }
+        DPRINT("Could not find the service!\n");
         return ERROR_SERVICE_DOES_NOT_EXIST;
     }
 
-    if (!lpService->lpDisplayName)
+    if (lpService->lpDisplayName)
+        lpSvcDisplayName = lpService->lpDisplayName;
+    else
+        lpSvcDisplayName = lpService->lpServiceName;
+
+    /*
+     * NOTE: On Windows the comparison on *lpcchBuffer is made against
+     * the number of (wide) characters of the UNICODE display name, and
+     * not against the number of bytes needed to store the ANSI string.
+     */
+    dwLength = (DWORD)wcslen(lpSvcDisplayName);
+
+    if (*lpcchBuffer > dwLength)
     {
-        dwLength = (DWORD)wcslen(lpService->lpServiceName);
         if (lpDisplayName != NULL &&
-            *lpcchBuffer > dwLength)
-        {
             WideCharToMultiByte(CP_ACP,
                                 0,
-                                lpService->lpServiceName,
-                                (int)wcslen(lpService->lpServiceName),
+                                lpSvcDisplayName,
+                                -1,
                                 lpDisplayName,
-                                dwLength + 1,
+                                (int)*lpcchBuffer,
                                 NULL,
-                                NULL);
-            return ERROR_SUCCESS;
+                                NULL) == 0)
+        {
+            /*
+             * But then, if *lpcchBuffer was greater than the number of
+             * (wide) characters of the UNICODE display name, yet smaller
+             * than the number of bytes needed due to the possible presence
+             * of DBCS characters, the *exact* number of bytes is returned
+             * (without the NULL terminator).
+             */
+            dwLength = (DWORD)WideCharToMultiByte(CP_ACP,
+                                                  0,
+                                                  lpSvcDisplayName,
+                                                  (int)dwLength,
+                                                  NULL,
+                                                  0,
+                                                  NULL,
+                                                  NULL);
+            *lpDisplayName = 0;
+            *lpcchBuffer = dwLength;
+            return ERROR_INSUFFICIENT_BUFFER;
         }
+
+        /*
+         * NOTE: On Windows, RGetServiceDisplayNameA() does not update
+         * *lpcchBuffer on success, contrary to RGetServiceDisplayNameW().
+         */
+        return ERROR_SUCCESS;
     }
     else
     {
-        dwLength = (DWORD)wcslen(lpService->lpDisplayName);
-        if (lpDisplayName != NULL &&
-            *lpcchBuffer > dwLength)
-        {
-            WideCharToMultiByte(CP_ACP,
-                                0,
-                                lpService->lpDisplayName,
-                                (int)wcslen(lpService->lpDisplayName),
-                                lpDisplayName,
-                                dwLength + 1,
-                                NULL,
-                                NULL);
-            return ERROR_SUCCESS;
-        }
+        /*
+         * NOTE: On Windows, if *lpcchBuffer is smaller than the number of
+         * (wide) characters of the UNICODE display name, only an upper
+         * estimation is returned by doubling the string length, to account
+         * for the presence of any possible DBCS characters.
+         */
+        *lpcchBuffer = dwLength * sizeof(WCHAR);
+        return ERROR_INSUFFICIENT_BUFFER;
     }
-
-    dwError = (*lpcchBuffer > dwLength) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER;
-
-    *lpcchBuffer = dwLength * 2;
-
-    return dwError;
 }
 
 
@@ -4583,10 +4575,10 @@ RGetServiceKeyNameA(
     LPSTR lpServiceName,
     LPBOUNDED_DWORD_4K lpcchBuffer)
 {
+    // PMANAGER_HANDLE hManager;
     PSERVICE lpService;
-    DWORD dwLength;
-    DWORD dwError;
     LPWSTR lpDisplayNameW;
+    DWORD dwLength;
 
     DPRINT("RGetServiceKeyNameA() called\n");
     DPRINT("hSCManager = %p\n", hSCManager);
@@ -4594,6 +4586,17 @@ RGetServiceKeyNameA(
     DPRINT("lpServiceName: %p\n", lpServiceName);
     DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
 
+#if 0
+    hManager = (PMANAGER_HANDLE)hSCManager;
+    if (hManager->Handle.Tag != MANAGER_TAG)
+    {
+        DPRINT("Invalid manager handle!\n");
+        return ERROR_INVALID_HANDLE;
+    }
+#endif
+
+    /* Get service database entry */
+
     dwLength = (DWORD)(strlen(lpDisplayName) + 1);
     lpDisplayNameW = HeapAlloc(GetProcessHeap(),
                                HEAP_ZERO_MEMORY,
@@ -4615,41 +4618,65 @@ RGetServiceKeyNameA(
     if (lpService == NULL)
     {
         DPRINT("Could not find the service!\n");
-
-        /* If the service could not be found and lpcchBuffer is 0,
-           put null in lpDisplayName and puts 1 in lpcchBuffer, verified WINXP. */
-        if (*lpcchBuffer == 0)
-        {
-            *lpcchBuffer = sizeof(CHAR);
-            if (lpServiceName != NULL)
-            {
-                *lpServiceName = 0;
-            }
-        }
-
         return ERROR_SERVICE_DOES_NOT_EXIST;
     }
 
+    /*
+     * NOTE: On Windows the comparison on *lpcchBuffer is made against
+     * the number of (wide) characters of the UNICODE service name, and
+     * not against the number of bytes needed to store the ANSI string.
+     */
     dwLength = (DWORD)wcslen(lpService->lpServiceName);
-    if (lpServiceName != NULL &&
-        *lpcchBuffer > dwLength)
+
+    if (*lpcchBuffer > dwLength)
     {
-        WideCharToMultiByte(CP_ACP,
-                            0,
-                            lpService->lpServiceName,
-                            (int)wcslen(lpService->lpServiceName),
-                            lpServiceName,
-                            dwLength + 1,
-                            NULL,
-                            NULL);
+        if (lpServiceName != NULL &&
+            WideCharToMultiByte(CP_ACP,
+                                0,
+                                lpService->lpServiceName,
+                                -1,
+                                lpServiceName,
+                                (int)*lpcchBuffer,
+                                NULL,
+                                NULL) == 0)
+        {
+            /*
+             * But then, if *lpcchBuffer was greater than the number of
+             * (wide) characters of the UNICODE service name, yet smaller
+             * than the number of bytes needed due to the possible presence
+             * of DBCS characters, the *exact* number of bytes is returned
+             * (without the NULL terminator).
+             */
+            dwLength = (DWORD)WideCharToMultiByte(CP_ACP,
+                                                  0,
+                                                  lpService->lpServiceName,
+                                                  (int)dwLength,
+                                                  NULL,
+                                                  0,
+                                                  NULL,
+                                                  NULL);
+            *lpServiceName = 0;
+            *lpcchBuffer = dwLength;
+            return ERROR_INSUFFICIENT_BUFFER;
+        }
+
+        /*
+         * NOTE: On Windows, RGetServiceKeyNameA() does not update
+         * *lpcchBuffer on success, contrary to RGetServiceKeyNameW().
+         */
         return ERROR_SUCCESS;
     }
-
-    dwError = (*lpcchBuffer > dwLength) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER;
-
-    *lpcchBuffer = dwLength * 2;
-
-    return dwError;
+    else
+    {
+        /*
+         * NOTE: On Windows, if *lpcchBuffer is smaller than the number of
+         * (wide) characters of the UNICODE service name, only an upper
+         * estimation is returned by doubling the string length, to account
+         * for the presence of any possible DBCS characters.
+         */
+        *lpcchBuffer = dwLength * sizeof(WCHAR);
+        return ERROR_INSUFFICIENT_BUFFER;
+    }
 }
 
 
index b28055a..1fe9621 100644 (file)
@@ -1693,6 +1693,11 @@ GetServiceDisplayNameW(SC_HANDLE hSCManager,
         return FALSE;
     }
 
+    /*
+     * NOTE: A size of 1 character would be enough, but tests show that
+     * Windows returns 2 characters instead, certainly due to a WCHAR/bytes
+     * mismatch in their code.
+     */
     if (!lpDisplayName || *lpcchBuffer < sizeof(WCHAR))
     {
         lpNameBuffer = szEmptyName;
@@ -1810,6 +1815,11 @@ GetServiceKeyNameW(SC_HANDLE hSCManager,
         return FALSE;
     }
 
+    /*
+     * NOTE: A size of 1 character would be enough, but tests show that
+     * Windows returns 2 characters instead, certainly due to a WCHAR/bytes
+     * mismatch in their code.
+     */
     if (!lpServiceName || *lpcchBuffer < sizeof(WCHAR))
     {
         lpNameBuffer = szEmptyName;