advapi32: Implement EnumServicesStatusA/W.

Hans Leidekker hans at codeweavers.com
Wed Oct 27 05:03:28 CDT 2010


---
 dlls/advapi32/service.c       |  107 ++++++++++++++++++++++++-----
 dlls/advapi32/tests/service.c |  153 ++++++++++++++++++++++++++++------------
 include/wine/svcctl.idl       |   16 ++++-
 programs/services/rpc.c       |  105 ++++++++++++++++++++++++++--
 4 files changed, 308 insertions(+), 73 deletions(-)

diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c
index cce2af4..6d96795 100644
--- a/dlls/advapi32/service.c
+++ b/dlls/advapi32/service.c
@@ -1455,32 +1455,103 @@ BOOL WINAPI QueryServiceConfig2W(SC_HANDLE hService, DWORD dwLevel, LPBYTE buffe
  * EnumServicesStatusA [ADVAPI32.@]
  */
 BOOL WINAPI
-EnumServicesStatusA( SC_HANDLE hSCManager, DWORD dwServiceType,
-                     DWORD dwServiceState, LPENUM_SERVICE_STATUSA lpServices,
-                     DWORD cbBufSize, LPDWORD pcbBytesNeeded,
-                     LPDWORD lpServicesReturned, LPDWORD lpResumeHandle )
+EnumServicesStatusA( SC_HANDLE hmngr, DWORD type, DWORD state, LPENUM_SERVICE_STATUSA
+                     services, DWORD size, LPDWORD needed, LPDWORD returned,
+                     LPDWORD resume_handle )
 {
-    FIXME("%p type=%x state=%x %p %x %p %p %p\n", hSCManager,
-          dwServiceType, dwServiceState, lpServices, cbBufSize,
-          pcbBytesNeeded, lpServicesReturned,  lpResumeHandle);
-    SetLastError (ERROR_ACCESS_DENIED);
-    return FALSE;
+    BOOL ret;
+    unsigned int i;
+    ENUM_SERVICE_STATUSW *servicesW = NULL;
+    DWORD sz, n;
+    char *p;
+
+    TRACE("%p 0x%x 0x%x %p %u %p %p %p\n", hmngr, type, state, services, size, needed,
+          returned, resume_handle);
+
+    if (size && !(servicesW = HeapAlloc( GetProcessHeap(), 0, 2 * size )))
+    {
+        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+        return FALSE;
+    }
+
+    ret = EnumServicesStatusW( hmngr, type, state, servicesW, 2 * size, needed, returned, resume_handle );
+    if (!ret) goto done;
+
+    p = (char *)services + *returned * sizeof(ENUM_SERVICE_STATUSA);
+    n = size - (p - (char *)services);
+    ret = FALSE;
+    for (i = 0; i < *returned; i++)
+    {
+        sz = WideCharToMultiByte( CP_ACP, 0, servicesW[i].lpServiceName, -1, p, n, NULL, NULL );
+        if (!sz) goto done;
+        services[i].lpServiceName = p;
+        p += sz;
+        n -= sz;
+        if (servicesW[i].lpDisplayName)
+        {
+            sz = WideCharToMultiByte( CP_ACP, 0, servicesW[i].lpDisplayName, -1, p, n, NULL, NULL );
+            if (!sz) goto done;
+            services[i].lpDisplayName = p;
+            p += sz;
+            n -= sz;
+        }
+        services[i].ServiceStatus = servicesW[i].ServiceStatus;
+    }
+
+    ret = TRUE;
+
+done:
+    HeapFree( GetProcessHeap(), 0, servicesW );
+    return ret;
 }
 
 /******************************************************************************
  * EnumServicesStatusW [ADVAPI32.@]
  */
 BOOL WINAPI
-EnumServicesStatusW( SC_HANDLE hSCManager, DWORD dwServiceType,
-                     DWORD dwServiceState, LPENUM_SERVICE_STATUSW lpServices,
-                     DWORD cbBufSize, LPDWORD pcbBytesNeeded,
-                     LPDWORD lpServicesReturned, LPDWORD lpResumeHandle )
+EnumServicesStatusW( SC_HANDLE hmngr, DWORD type, DWORD state, LPENUM_SERVICE_STATUSW
+                     services, DWORD size, LPDWORD needed, LPDWORD returned,
+                     LPDWORD resume_handle )
 {
-    FIXME("%p type=%x state=%x %p %x %p %p %p\n", hSCManager,
-          dwServiceType, dwServiceState, lpServices, cbBufSize,
-          pcbBytesNeeded, lpServicesReturned,  lpResumeHandle);
-    SetLastError (ERROR_ACCESS_DENIED);
-    return FALSE;
+    DWORD err, i;
+
+    TRACE("%p 0x%x 0x%x %p %u %p %p %p\n", hmngr, type, state, services, size, needed,
+          returned, resume_handle);
+
+    if (resume_handle)
+        FIXME("resume handle not supported\n");
+
+    if (!hmngr)
+    {
+        SetLastError( ERROR_INVALID_HANDLE );
+        return FALSE;
+    }
+
+    __TRY
+    {
+        err = svcctl_EnumServicesStatusW( hmngr, type, state, (BYTE *)services, size, needed, returned );
+    }
+    __EXCEPT(rpc_filter)
+    {
+        err = map_exception_code( GetExceptionCode() );
+    }
+    __ENDTRY
+
+    if (err != ERROR_SUCCESS)
+    {
+        SetLastError( err );
+        return FALSE;
+    }
+
+    for (i = 0; i < *returned; i++)
+    {
+        /* convert buffer offsets into pointers */
+        services[i].lpServiceName = (WCHAR *)((char *)services + (DWORD_PTR)services[i].lpServiceName);
+        if (services[i].lpDisplayName)
+            services[i].lpDisplayName = (WCHAR *)((char *)services + (DWORD_PTR)services[i].lpDisplayName);
+    }
+
+    return TRUE;
 }
 
 /******************************************************************************
diff --git a/dlls/advapi32/tests/service.c b/dlls/advapi32/tests/service.c
index eba1884..a171b31 100644
--- a/dlls/advapi32/tests/service.c
+++ b/dlls/advapi32/tests/service.c
@@ -1038,6 +1038,7 @@ static void test_enum_svc(void)
     DWORD tempneeded, tempreturned, missing;
     DWORD servicecountactive, servicecountinactive;
     ENUM_SERVICE_STATUS *services;
+    ENUM_SERVICE_STATUSW *servicesW;
     ENUM_SERVICE_STATUS_PROCESS *exservices;
     UINT i;
 
@@ -1045,7 +1046,12 @@ static void test_enum_svc(void)
     SetLastError(0xdeadbeef);
     ret = EnumServicesStatusA(NULL, 1, 0, NULL, 0, NULL, NULL, NULL);
     ok(!ret, "Expected failure\n");
-    todo_wine
+    ok(GetLastError() == ERROR_INVALID_HANDLE,
+       "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = EnumServicesStatusW(NULL, 1, 0, NULL, 0, NULL, NULL, NULL);
+    ok(!ret, "Expected failure\n");
     ok(GetLastError() == ERROR_INVALID_HANDLE,
        "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
 
@@ -1056,7 +1062,13 @@ static void test_enum_svc(void)
     SetLastError(0xdeadbeef);
     ret = EnumServicesStatusA(scm_handle, 1, 0, NULL, 0, NULL, NULL, NULL);
     ok(!ret, "Expected failure\n");
-    todo_wine
+    ok(GetLastError() == ERROR_INVALID_ADDRESS ||
+       GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */,
+       "Unexpected last error %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = EnumServicesStatusW(scm_handle, 1, 0, NULL, 0, NULL, NULL, NULL);
+    ok(!ret, "Expected failure\n");
     ok(GetLastError() == ERROR_INVALID_ADDRESS ||
        GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */,
        "Unexpected last error %d\n", GetLastError());
@@ -1067,7 +1079,15 @@ static void test_enum_svc(void)
     ret = EnumServicesStatusA(scm_handle, 0, 0, NULL, 0, NULL, &returned, NULL);
     ok(!ret, "Expected failure\n");
     ok(returned == 0xdeadbeef, "Expected no change to the number of services variable\n");
-    todo_wine
+    ok(GetLastError() == ERROR_INVALID_ADDRESS ||
+       GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */,
+       "Unexpected last error %d\n", GetLastError());
+
+    returned = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = EnumServicesStatusW(scm_handle, 0, 0, NULL, 0, NULL, &returned, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(returned == 0xdeadbeef, "Expected no change to the number of services variable\n");
     ok(GetLastError() == ERROR_INVALID_ADDRESS ||
        GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */,
        "Unexpected last error %d\n", GetLastError());
@@ -1079,7 +1099,16 @@ static void test_enum_svc(void)
     ok(!ret, "Expected failure\n");
     ok(needed == 0xdeadbeef || broken(needed != 0xdeadbeef), /* nt4 */
        "Expected no change to the needed buffer variable\n");
-    todo_wine
+    ok(GetLastError() == ERROR_INVALID_ADDRESS ||
+       GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */,
+       "Unexpected last error %d\n", GetLastError());
+
+    needed = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = EnumServicesStatusW(scm_handle, 0, 0, NULL, 0, &needed, NULL, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(needed == 0xdeadbeef || broken(needed != 0xdeadbeef), /* nt4 */
+       "Expected no change to the needed buffer variable\n");
     ok(GetLastError() == ERROR_INVALID_ADDRESS ||
        GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */,
        "Unexpected last error %d\n", GetLastError());
@@ -1090,59 +1119,93 @@ static void test_enum_svc(void)
     SetLastError(0xdeadbeef);
     ret = EnumServicesStatusA(scm_handle, 0, 0, NULL, 0, &needed, &returned, NULL);
     ok(!ret, "Expected failure\n");
-    todo_wine
-    {
     ok(needed == 0 || broken(needed != 0), /* nt4 */
        "Expected needed buffer size to be set to 0, got %d\n", needed);
     ok(returned == 0, "Expected number of services to be set to 0, got %d\n", returned);
     ok(GetLastError() == ERROR_INVALID_PARAMETER,
        "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
-    }
 
-    /* No valid servicetype and servicestate */
+    needed = 0xdeadbeef;
+    returned = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = EnumServicesStatusW(scm_handle, 0, 0, NULL, 0, &needed, &returned, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(needed == 0 || broken(needed != 0), /* nt4 */
+       "Expected needed buffer size to be set to 0, got %d\n", needed);
+    ok(returned == 0 || broken(returned != 0), /* nt4 */
+       "Expected number of services to be set to 0, got %d\n", returned);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    /* No valid servicestate */
     needed = 0xdeadbeef;
     returned = 0xdeadbeef;
     SetLastError(0xdeadbeef);
     ret = EnumServicesStatusA(scm_handle, SERVICE_WIN32, 0, NULL, 0, &needed, &returned, NULL);
     ok(!ret, "Expected failure\n");
-    todo_wine
-    {
     ok(needed == 0 || broken(needed != 0), /* nt4 */
        "Expected needed buffer size to be set to 0, got %d\n", needed);
     ok(returned == 0, "Expected number of services to be set to 0, got %d\n", returned);
     ok(GetLastError() == ERROR_INVALID_PARAMETER,
        "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
-    }
 
-    /* No valid servicetype and servicestate */
     needed = 0xdeadbeef;
     returned = 0xdeadbeef;
     SetLastError(0xdeadbeef);
-    ret = EnumServicesStatusA(scm_handle, 0, SERVICE_STATE_ALL, NULL, 0,
-                              &needed, &returned, NULL);
+    ret = EnumServicesStatusW(scm_handle, SERVICE_WIN32, 0, NULL, 0, &needed, &returned, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(needed == 0 || broken(needed != 0), /* nt4 */
+       "Expected needed buffer size to be set to 0, got %d\n", needed);
+    ok(returned == 0 || broken(returned != 0), /* nt4 */
+       "Expected number of services to be set to 0, got %d\n", returned);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
+
+    /* No valid servicetype */
+    needed = 0xdeadbeef;
+    returned = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = EnumServicesStatusA(scm_handle, 0, SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL);
     ok(!ret, "Expected failure\n");
-    todo_wine
-    {
     ok(needed == 0 || broken(needed != 0), /* nt4 */
        "Expected needed buffer size to be set to 0, got %d\n", needed);
     ok(returned == 0, "Expected number of services to be set to 0, got %d\n", returned);
     ok(GetLastError() == ERROR_INVALID_PARAMETER,
        "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
-    }
+
+    needed = 0xdeadbeef;
+    returned = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = EnumServicesStatusW(scm_handle, 0, SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(needed == 0 || broken(needed != 0), /* nt4 */
+       "Expected needed buffer size to be set to 0, got %d\n", needed);
+    ok(returned == 0 || broken(returned != 0), /* nt4 */
+       "Expected number of services to be set to 0, got %d\n", returned);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
 
     /* All parameters are correct but our access rights are wrong */
     needed = 0xdeadbeef;
     returned = 0xdeadbeef;
     SetLastError(0xdeadbeef);
-    ret = EnumServicesStatusA(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0,
-                              &needed, &returned, NULL);
+    ret = EnumServicesStatusA(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL);
     ok(!ret, "Expected failure\n");
-    todo_wine
-    {
     ok(needed == 0 || broken(needed != 0), /* nt4 */
        "Expected needed buffer size to be set to 0, got %d\n", needed);
     ok(returned == 0, "Expected number of services to be set to 0, got %d\n", returned);
-    }
+    ok(GetLastError() == ERROR_ACCESS_DENIED,
+       "Expected ERROR_ACCESS_DENIED, got %d\n", GetLastError());
+
+    needed = 0xdeadbeef;
+    returned = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = EnumServicesStatusW(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(needed == 0 || broken(needed != 0), /* nt4 */
+       "Expected needed buffer size to be set to 0, got %d\n", needed);
+    ok(returned == 0 || broken(returned != 0), /* nt4 */
+       "Expected number of services to be set to 0, got %d\n", returned);
     ok(GetLastError() == ERROR_ACCESS_DENIED,
        "Expected ERROR_ACCESS_DENIED, got %d\n", GetLastError());
 
@@ -1154,22 +1217,24 @@ static void test_enum_svc(void)
     needed = 0xdeadbeef;
     returned = 0xdeadbeef;
     SetLastError(0xdeadbeef);
-    ret = EnumServicesStatusA(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0,
-                              &needed, &returned, NULL);
+    ret = EnumServicesStatusA(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL);
     ok(!ret, "Expected failure\n");
-    todo_wine
-    {
     ok(needed != 0xdeadbeef && needed > 0, "Expected the needed buffer size for this one service\n");
     ok(returned == 0, "Expected no service returned, got %d\n", returned);
     ok(GetLastError() == ERROR_MORE_DATA,
        "Expected ERROR_MORE_DATA, got %d\n", GetLastError());
-    }
 
     /* Test to show we get the same needed buffer size for the W-call */
     neededW = 0xdeadbeef;
-    ret = EnumServicesStatusW(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0,
-                              &neededW, &returnedW, NULL);
+    returnedW = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = EnumServicesStatusW(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &neededW, &returnedW, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(neededW != 0xdeadbeef && neededW > 0, "Expected the needed buffer size for this one service\n");
     ok(neededW == needed, "Expected needed buffersize to be the same for A- and W-calls\n");
+    ok(returnedW == 0, "Expected no service returned, got %d\n", returnedW);
+    ok(GetLastError() == ERROR_MORE_DATA,
+       "Expected ERROR_MORE_DATA, got %d\n", GetLastError());
 
     /* Store the needed bytes */
     tempneeded = needed;
@@ -1182,17 +1247,26 @@ static void test_enum_svc(void)
     SetLastError(0xdeadbeef);
     ret = EnumServicesStatusA(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL,
                               services, bufsize, &needed, &returned, NULL);
-    todo_wine
-    {
     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 != 0xdeadbeef && returned > 0, "Expected some returned services\n");
-    }
     HeapFree(GetProcessHeap(), 0, services);
 
     /* Store the number of returned services */
     tempreturned = returned;
 
+    servicesW = HeapAlloc(GetProcessHeap(), 0, neededW);
+    bufsize = neededW;
+    neededW = 0xdeadbeef;
+    returnedW = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = EnumServicesStatusW(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL,
+                              servicesW, bufsize, &neededW, &returnedW, NULL);
+    ok(ret, "Expected success, got error %u\n", GetLastError());
+    ok(neededW == 0, "Expected needed buffer to be 0 as we are done\n");
+    ok(returnedW != 0xdeadbeef && returnedW > 0, "Expected some returned services\n");
+    HeapFree(GetProcessHeap(), 0, servicesW);
+
     /* Allocate less than the needed bytes and don't specify a resume handle */
     services = HeapAlloc(GetProcessHeap(), 0, tempneeded);
     bufsize = (tempreturned - 1) * sizeof(ENUM_SERVICE_STATUS);
@@ -1202,13 +1276,10 @@ static void test_enum_svc(void)
     ret = EnumServicesStatusA(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL,
                               services, bufsize, &needed, &returned, NULL);
     ok(!ret, "Expected failure\n");
-    todo_wine
-    {
     ok(needed != 0xdeadbeef && needed > 0, "Expected the needed buffer size for this one service\n");
     ok(returned < tempreturned, "Expected fewer services to be returned\n");
     ok(GetLastError() == ERROR_MORE_DATA,
        "Expected ERROR_MORE_DATA, got %d\n", GetLastError());
-    }
 
     /* Allocate less than the needed bytes, this time with a correct resume handle */
     bufsize = (tempreturned - 1) * sizeof(ENUM_SERVICE_STATUS);
@@ -1219,14 +1290,11 @@ static void test_enum_svc(void)
     ret = EnumServicesStatusA(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL,
                               services, bufsize, &needed, &returned, &resume);
     ok(!ret, "Expected failure\n");
-    todo_wine
-    {
     ok(needed != 0xdeadbeef && needed > 0, "Expected the needed buffer size for this one service\n");
     ok(returned < tempreturned, "Expected fewer services to be returned\n");
-    ok(resume, "Expected a resume handle\n");
+    todo_wine ok(resume, "Expected a resume handle\n");
     ok(GetLastError() == ERROR_MORE_DATA,
        "Expected ERROR_MORE_DATA, got %d\n", GetLastError());
-    }
 
     /* Fetch the missing services but pass a bigger buffer size */
     missing = tempreturned - returned;
@@ -1236,12 +1304,9 @@ static void test_enum_svc(void)
     SetLastError(0xdeadbeef);
     ret = EnumServicesStatusA(scm_handle, SERVICE_WIN32, SERVICE_STATE_ALL,
                               services, bufsize, &needed, &returned, &resume);
-    todo_wine
-    {
     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);
-    }
     ok(resume == 0, "Expected the resume handle to be 0\n");
     HeapFree(GetProcessHeap(), 0, services);
 
@@ -1286,7 +1351,6 @@ static void test_enum_svc(void)
     HeapFree(GetProcessHeap(), 0, services);
 
     /* Check if total is the same as active and inactive win32 services */
-    todo_wine
     ok(returned == (servicecountactive + servicecountinactive),
        "Something wrong in the calculation\n");
 
@@ -1323,11 +1387,8 @@ static void test_enum_svc(void)
     }
     HeapFree(GetProcessHeap(), 0, services);
 
-    todo_wine
-    {
     ok(servicecountactive == 0, "Active services mismatch %u\n", servicecountactive);
     ok(servicecountinactive == 0, "Inactive services mismatch %u\n", servicecountinactive);
-    }
 
     CloseServiceHandle(scm_handle);
 
diff --git a/include/wine/svcctl.idl b/include/wine/svcctl.idl
index 0414dc4..8d6a092 100644
--- a/include/wine/svcctl.idl
+++ b/include/wine/svcctl.idl
@@ -122,6 +122,12 @@ typedef struct _SERVICE_FAILURE_ACTIONSW {
 #define SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO 6
 #define SERVICE_CONFIG_PRESHUTDOWN_INFO         7
 
+typedef struct _ENUM_SERVICE_STATUSW {
+    LPWSTR          lpServiceName;
+    LPWSTR          lpDisplayName;
+    SERVICE_STATUS  ServiceStatus;
+} ENUM_SERVICE_STATUSW, *LPENUM_SERVICE_STATUSW;
+
 cpp_quote("#endif")
 
 typedef [switch_type(DWORD)] union
@@ -220,7 +226,15 @@ typedef [switch_type(DWORD)] union
     DWORD svcctl_EnumDependentServicesW(/* FIXME */);
 
     /* Not compatible with Windows function 0x0e */
-    DWORD svcctl_EnumServicesStatusW(/* FIXME */);
+    DWORD svcctl_EnumServicesStatusW(
+        [in] SC_RPC_HANDLE hmngr,
+        [in] DWORD type,
+        [in] DWORD state,
+        [out,size_is(size)] BYTE *buffer,
+        [in] DWORD size,
+        [out] LPDWORD needed,
+        [out] LPDWORD returned
+    );
 
     /* Compatible with Windows function 0x0f */
     DWORD svcctl_OpenSCManagerW(
diff --git a/programs/services/rpc.c b/programs/services/rpc.c
index 41630be..104aa8a 100644
--- a/programs/services/rpc.c
+++ b/programs/services/rpc.c
@@ -1115,6 +1115,103 @@ DWORD svcctl_UnlockServiceDatabase(
     return ERROR_SUCCESS;
 }
 
+static BOOL map_state(DWORD state, DWORD mask)
+{
+    switch (state)
+    {
+    case SERVICE_START_PENDING:
+    case SERVICE_STOP_PENDING:
+    case SERVICE_RUNNING:
+    case SERVICE_CONTINUE_PENDING:
+    case SERVICE_PAUSE_PENDING:
+    case SERVICE_PAUSED:
+        if (SERVICE_ACTIVE & mask) return TRUE;
+        break;
+    case SERVICE_STOPPED:
+        if (SERVICE_INACTIVE & mask) return TRUE;
+        break;
+    default:
+        WINE_ERR("unknown state %u\n", state);
+        break;
+    }
+    return FALSE;
+}
+
+DWORD svcctl_EnumServicesStatusW(
+    SC_RPC_HANDLE hmngr,
+    DWORD type,
+    DWORD state,
+    BYTE *buffer,
+    DWORD size,
+    LPDWORD needed,
+    LPDWORD returned)
+{
+    DWORD err, sz, total_size, num_services;
+    DWORD_PTR offset;
+    struct sc_manager_handle *manager;
+    struct service_entry *service;
+    ENUM_SERVICE_STATUSW *s;
+
+    WINE_TRACE("(%p, 0x%x, 0x%x, %p, %u, %p, %p)\n", hmngr, type, state, buffer, size, needed, returned);
+
+    if (!type || !state)
+        return ERROR_INVALID_PARAMETER;
+
+    if ((err = validate_scm_handle(hmngr, SC_MANAGER_ENUMERATE_SERVICE, &manager)) != ERROR_SUCCESS)
+        return err;
+
+    scmdatabase_lock_exclusive(manager->db);
+
+    total_size = num_services = 0;
+    LIST_FOR_EACH_ENTRY(service, &manager->db->services, struct service_entry, entry)
+    {
+        if ((service->status.dwServiceType & type) && map_state(service->status.dwCurrentState, state))
+        {
+            total_size += sizeof(ENUM_SERVICE_STATUSW);
+            total_size += (strlenW(service->name) + 1) * sizeof(WCHAR);
+            if (service->config.lpDisplayName)
+            {
+                total_size += (strlenW(service->config.lpDisplayName) + 1) * sizeof(WCHAR);
+            }
+            num_services++;
+        }
+    }
+    *returned = 0;
+    *needed = total_size;
+    if (total_size > size)
+    {
+        scmdatabase_unlock(manager->db);
+        return ERROR_MORE_DATA;
+    }
+    s = (ENUM_SERVICE_STATUSW *)buffer;
+    offset = num_services * sizeof(ENUM_SERVICE_STATUSW);
+    LIST_FOR_EACH_ENTRY(service, &manager->db->services, struct service_entry, entry)
+    {
+        if ((service->status.dwServiceType & type) && map_state(service->status.dwCurrentState, state))
+        {
+            sz = (strlenW(service->name) + 1) * sizeof(WCHAR);
+            memcpy(buffer + offset, service->name, sz);
+            s->lpServiceName = (WCHAR *)offset; /* store a buffer offset instead of a pointer */
+            offset += sz;
+
+            if (!service->config.lpDisplayName) s->lpDisplayName = NULL;
+            else
+            {
+                sz = (strlenW(service->config.lpDisplayName) + 1) * sizeof(WCHAR);
+                memcpy(buffer + offset, service->config.lpDisplayName, sz);
+                s->lpDisplayName = (WCHAR *)offset;
+                offset += sz;
+            }
+            memcpy(&s->ServiceStatus, &service->status, sizeof(SERVICE_STATUS));
+            s++;
+        }
+    }
+    *returned = num_services;
+    *needed = 0;
+    scmdatabase_unlock(manager->db);
+    return ERROR_SUCCESS;
+}
+
 DWORD svcctl_QueryServiceObjectSecurity(
     void)
 {
@@ -1159,14 +1256,6 @@ DWORD svcctl_EnumDependentServicesW(
     return ERROR_CALL_NOT_IMPLEMENTED;
 }
 
-DWORD svcctl_EnumServicesStatusW(
-    void)
-{
-    WINE_FIXME("\n");
-    return ERROR_CALL_NOT_IMPLEMENTED;
-}
-
-
 DWORD svcctl_QueryServiceLockStatusW(
     void)
 {
-- 
1.7.1






More information about the wine-patches mailing list