[PATCH 5/5] advapi32: Reimplement EnumServicesStatusW() on top of EnumServicesStatusExW().

Zebediah Figura z.figura12 at gmail.com
Mon Apr 27 22:35:34 CDT 2020


Signed-off-by: Zebediah Figura <zfigura at codeweavers.com>
---
This introduces a test failure, which is fundamentally due to a missing
implementation of the "resume_handle" parameter.  The test previously passed
because we returned 0 services if the buffer was too short, but as the ANSI
tests show, that behaviour is incorrect—we should return as many services as we
have room for.  This patch corrects that behaviour.

 dlls/advapi32/service.c       | 124 +++++++++++++++-------------------
 dlls/advapi32/tests/service.c |   2 +-
 2 files changed, 54 insertions(+), 72 deletions(-)

diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c
index 4d32b44eefd..eb195d84d05 100644
--- a/dlls/advapi32/service.c
+++ b/dlls/advapi32/service.c
@@ -1749,109 +1749,91 @@ done:
  * EnumServicesStatusW [ADVAPI32.@]
  */
 BOOL WINAPI
-EnumServicesStatusW( SC_HANDLE hmngr, DWORD type, DWORD state, LPENUM_SERVICE_STATUSW
-                     services, DWORD size, LPDWORD needed, LPDWORD returned,
-                     LPDWORD resume_handle )
+EnumServicesStatusW( SC_HANDLE manager, DWORD type, DWORD state, ENUM_SERVICE_STATUSW *status,
+                     DWORD size, DWORD *ret_size, DWORD *ret_count, DWORD *resume_handle )
 {
-    DWORD err, i, offset, buflen, count, total_size = 0;
-    struct enum_service_status *entry;
-    const WCHAR *str;
-    BYTE *buf;
+    ENUM_SERVICE_STATUS_PROCESSW *status_ex;
+    DWORD alloc_size, count, i;
+    WCHAR *p;
 
-    TRACE("%p 0x%x 0x%x %p %u %p %p %p\n", hmngr, type, state, services, size, needed,
-          returned, resume_handle);
+    TRACE("%p 0x%x 0x%x %p %u %p %p %p\n", manager, type, state, status, size,
+          ret_size, ret_count, resume_handle);
 
-    if (!hmngr)
+    if (!manager)
     {
         SetLastError( ERROR_INVALID_HANDLE );
         return FALSE;
     }
-    if (!needed || !returned)
+
+    if (!ret_size || !ret_count)
     {
-        SetLastError( ERROR_INVALID_ADDRESS );
+        SetLastError( ERROR_INVALID_PARAMETER );
         return FALSE;
     }
 
-    /* make sure we pass a valid pointer */
-    buflen = max( size, sizeof(*services) );
-    if (!(buf = heap_alloc( buflen )))
+    *ret_size = 0;
+    *ret_count = 0;
+    if (!EnumServicesStatusExW( manager, SC_ENUM_PROCESS_INFO, type, state,
+                                NULL, 0, &alloc_size, &count, resume_handle, NULL )
+            && GetLastError() != ERROR_MORE_DATA)
+        return FALSE;
+
+    if (!(status_ex = heap_alloc( alloc_size )))
     {
         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
         return FALSE;
     }
 
-    __TRY
+    if (!EnumServicesStatusExW( manager, SC_ENUM_PROCESS_INFO, type, state, (BYTE *)status_ex,
+                                alloc_size, &alloc_size, &count, resume_handle, NULL )
+            && GetLastError() != ERROR_MORE_DATA)
     {
-        err = svcctl_EnumServicesStatusW( hmngr, type, state, buf, buflen, needed, &count, resume_handle );
-    }
-    __EXCEPT(rpc_filter)
-    {
-        err = map_exception_code( GetExceptionCode() );
+        heap_free( status_ex );
+        return FALSE;
     }
-    __ENDTRY
 
-    *returned = 0;
-    if (err != ERROR_SUCCESS)
+    for (i = 0; i < count; i++)
     {
-        /* double the needed size to fit the potentially larger ENUM_SERVICE_STATUSW */
-        if (err == ERROR_MORE_DATA) *needed *= 2;
-        heap_free( buf );
-        SetLastError( err );
-        return FALSE;
+        *ret_size += sizeof(ENUM_SERVICE_STATUSW);
+        *ret_size += (strlenW( status_ex[i].lpServiceName ) + 1) * sizeof(WCHAR);
+        if (status_ex[i].lpDisplayName)
+            *ret_size += (strlenW( status_ex[i].lpDisplayName ) + 1) * sizeof(WCHAR);
+
+        if (*ret_size <= size)
+            ++*ret_count;
     }
 
-    entry = (struct enum_service_status *)buf;
-    for (i = 0; i < count; i++)
+    p = (WCHAR *)(status + *ret_count);
+    for (i = 0; i < *ret_count; i++)
     {
-        total_size += sizeof(*services);
-        if (entry->service_name)
-        {
-            str = (const WCHAR *)(buf + entry->service_name);
-            total_size += (strlenW( str ) + 1) * sizeof(WCHAR);
-        }
-        if (entry->display_name)
+        strcpyW( p, status_ex[i].lpServiceName );
+        status[i].lpServiceName = (WCHAR *)p;
+        p += strlenW( p ) + 1;
+        if (status_ex[i].lpDisplayName)
         {
-            str = (const WCHAR *)(buf + entry->display_name);
-            total_size += (strlenW( str ) + 1) * sizeof(WCHAR);
+            strcpyW( p, status_ex[i].lpDisplayName );
+            status[i].lpDisplayName = (WCHAR *)p;
+            p += strlenW( p ) + 1;
         }
-        entry++;
+        else status[i].lpDisplayName = NULL;
+
+        status[i].ServiceStatus.dwServiceType             = status_ex[i].ServiceStatusProcess.dwServiceType;
+        status[i].ServiceStatus.dwCurrentState            = status_ex[i].ServiceStatusProcess.dwCurrentState;
+        status[i].ServiceStatus.dwControlsAccepted        = status_ex[i].ServiceStatusProcess.dwControlsAccepted;
+        status[i].ServiceStatus.dwWin32ExitCode           = status_ex[i].ServiceStatusProcess.dwWin32ExitCode;
+        status[i].ServiceStatus.dwServiceSpecificExitCode = status_ex[i].ServiceStatusProcess.dwServiceSpecificExitCode;
+        status[i].ServiceStatus.dwCheckPoint              = status_ex[i].ServiceStatusProcess.dwCheckPoint;
+        status[i].ServiceStatus.dwWaitHint                = status_ex[i].ServiceStatusProcess.dwWaitHint;
     }
 
-    if (total_size > size)
+    heap_free( status_ex );
+    if (*ret_size > size)
     {
-        heap_free( buf );
-        *needed = total_size;
         SetLastError( ERROR_MORE_DATA );
         return FALSE;
     }
 
-    offset = count * sizeof(*services);
-    entry = (struct enum_service_status *)buf;
-    for (i = 0; i < count; i++)
-    {
-        DWORD str_size;
-        str = (const WCHAR *)(buf + entry->service_name);
-        str_size = (strlenW( str ) + 1) * sizeof(WCHAR);
-        services[i].lpServiceName = (WCHAR *)((char *)services + offset);
-        memcpy( services[i].lpServiceName, str, str_size );
-        offset += str_size;
-
-        if (!entry->display_name) services[i].lpDisplayName = NULL;
-        else
-        {
-            str = (const WCHAR *)(buf + entry->display_name);
-            str_size = (strlenW( str ) + 1) * sizeof(WCHAR);
-            services[i].lpDisplayName = (WCHAR *)((char *)services + offset);
-            memcpy( services[i].lpDisplayName, str, str_size );
-            offset += str_size;
-        }
-        services[i].ServiceStatus = entry->service_status;
-        entry++;
-    }
-
-    heap_free( buf );
-    *needed = 0;
-    *returned = count;
+    *ret_size = 0;
     return TRUE;
 }
 
diff --git a/dlls/advapi32/tests/service.c b/dlls/advapi32/tests/service.c
index 4bf9f4c34b3..7048e5a09ab 100644
--- a/dlls/advapi32/tests/service.c
+++ b/dlls/advapi32/tests/service.c
@@ -1339,7 +1339,7 @@ static void test_enum_svc(void)
                               services, bufsize, &needed, &returned, &resume);
     ok(ret, "Expected success, got error %u\n", GetLastError());
     ok(needed == 0, "Expected needed buffer to be 0 as we are done\n");
-    ok(returned == missing, "Expected %u services to be returned\n", missing);
+    todo_wine ok(returned == missing, "Expected %u services to be returned\n", missing);
     ok(resume == 0, "Expected the resume handle to be 0\n");
     HeapFree(GetProcessHeap(), 0, services);
 
-- 
2.26.2




More information about the wine-devel mailing list