From 7135ac76412fabbe918108af857d775adb6cb440 Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Fri, 29 Oct 2010 12:53:37 +0200 Subject: [PATCH] advapi32: Implement EnumServicesStatusExA/W. --- dlls/advapi32/service.c | 128 +++++++++++++++++++++++++++++----- dlls/advapi32/tests/service.c | 52 ++------------ include/wine/svcctl.idl | 11 +++ programs/services/rpc.c | 104 +++++++++++++++++++++++++++ 4 files changed, 233 insertions(+), 62 deletions(-) diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c index 6d967953bef..5d09d18c8f3 100644 --- a/dlls/advapi32/service.c +++ b/dlls/advapi32/service.c @@ -1558,31 +1558,125 @@ EnumServicesStatusW( SC_HANDLE hmngr, DWORD type, DWORD state, LPENUM_SERVICE_ST * EnumServicesStatusExA [ADVAPI32.@] */ BOOL WINAPI -EnumServicesStatusExA(SC_HANDLE hSCManager, SC_ENUM_TYPE InfoLevel, DWORD dwServiceType, - DWORD dwServiceState, LPBYTE lpServices, DWORD cbBufSize, LPDWORD pcbBytesNeeded, - LPDWORD lpServicesReturned, LPDWORD lpResumeHandle, LPCSTR pszGroupName) +EnumServicesStatusExA( SC_HANDLE hmngr, SC_ENUM_TYPE level, DWORD type, DWORD state, + LPBYTE buffer, DWORD size, LPDWORD needed, LPDWORD returned, + LPDWORD resume_handle, LPCSTR group ) { - FIXME("%p level=%d type=%x state=%x %p %x %p %p %p %s\n", hSCManager, InfoLevel, - dwServiceType, dwServiceState, lpServices, cbBufSize, - pcbBytesNeeded, lpServicesReturned, lpResumeHandle, debugstr_a(pszGroupName)); - if (lpServicesReturned) *lpServicesReturned = 0; - SetLastError (ERROR_ACCESS_DENIED); - return FALSE; + BOOL ret; + unsigned int i; + ENUM_SERVICE_STATUS_PROCESSA *services = (ENUM_SERVICE_STATUS_PROCESSA *)buffer; + ENUM_SERVICE_STATUS_PROCESSW *servicesW = NULL; + WCHAR *groupW = NULL; + DWORD sz, n; + char *p; + + TRACE("%p %u 0x%x 0x%x %p %u %p %p %p %s\n", hmngr, level, type, state, buffer, + size, needed, returned, resume_handle, debugstr_a(group)); + + if (size && !(servicesW = HeapAlloc( GetProcessHeap(), 0, 2 * size ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + if (group) + { + int len = MultiByteToWideChar( CP_ACP, 0, group, -1, NULL, 0 ); + if (!(groupW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + HeapFree( GetProcessHeap(), 0, servicesW ); + return FALSE; + } + MultiByteToWideChar( CP_ACP, 0, group, -1, groupW, len * sizeof(WCHAR) ); + } + + ret = EnumServicesStatusExW( hmngr, level, type, state, (BYTE *)servicesW, 2 * size, + needed, returned, resume_handle, groupW ); + if (!ret) goto done; + + p = (char *)services + *returned * sizeof(ENUM_SERVICE_STATUS_PROCESSA); + 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].ServiceStatusProcess = servicesW[i].ServiceStatusProcess; + } + + ret = TRUE; + +done: + HeapFree( GetProcessHeap(), 0, servicesW ); + HeapFree( GetProcessHeap(), 0, groupW ); + return ret; } /****************************************************************************** * EnumServicesStatusExW [ADVAPI32.@] */ BOOL WINAPI -EnumServicesStatusExW(SC_HANDLE hSCManager, SC_ENUM_TYPE InfoLevel, DWORD dwServiceType, - DWORD dwServiceState, LPBYTE lpServices, DWORD cbBufSize, LPDWORD pcbBytesNeeded, - LPDWORD lpServicesReturned, LPDWORD lpResumeHandle, LPCWSTR pszGroupName) +EnumServicesStatusExW( SC_HANDLE hmngr, SC_ENUM_TYPE level, DWORD type, DWORD state, + LPBYTE buffer, DWORD size, LPDWORD needed, LPDWORD returned, + LPDWORD resume_handle, LPCWSTR group ) { - FIXME("%p level=%d type=%x state=%x %p %x %p %p %p %s\n", hSCManager, InfoLevel, - dwServiceType, dwServiceState, lpServices, cbBufSize, - pcbBytesNeeded, lpServicesReturned, lpResumeHandle, debugstr_w(pszGroupName)); - SetLastError (ERROR_ACCESS_DENIED); - return FALSE; + DWORD err, i; + ENUM_SERVICE_STATUS_PROCESSW *services = (ENUM_SERVICE_STATUS_PROCESSW *)buffer; + + TRACE("%p %u 0x%x 0x%x %p %u %p %p %p %s\n", hmngr, level, type, state, buffer, + size, needed, returned, resume_handle, debugstr_w(group)); + + if (resume_handle) + FIXME("resume handle not supported\n"); + + if (level != SC_ENUM_PROCESS_INFO) + { + SetLastError( ERROR_INVALID_LEVEL ); + return FALSE; + } + if (!hmngr) + { + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; + } + + __TRY + { + err = svcctl_EnumServicesStatusExW( hmngr, type, state, buffer, size, needed, + returned, group ); + } + __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 a171b31a5a2..5a4fabdd892 100644 --- a/dlls/advapi32/tests/service.c +++ b/dlls/advapi32/tests/service.c @@ -1398,7 +1398,6 @@ static void test_enum_svc(void) SetLastError(0xdeadbeef); ret = pEnumServicesStatusExA(NULL, 1, 0, 0, NULL, 0, NULL, NULL, NULL, NULL); ok(!ret, "Expected failure\n"); - todo_wine ok(GetLastError() == ERROR_INVALID_LEVEL, "Expected ERROR_INVALID_LEVEL, got %d\n", GetLastError()); @@ -1406,7 +1405,6 @@ static void test_enum_svc(void) SetLastError(0xdeadbeef); ret = pEnumServicesStatusExA(NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL); ok(!ret, "Expected failure\n"); - todo_wine ok(GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); @@ -1417,7 +1415,6 @@ static void test_enum_svc(void) SetLastError(0xdeadbeef); ret = pEnumServicesStatusExA(scm_handle, 0, 0, 0, NULL, 0, NULL, 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()); @@ -1429,7 +1426,6 @@ 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()); @@ -1439,13 +1435,10 @@ static void test_enum_svc(void) SetLastError(0xdeadbeef); ret = pEnumServicesStatusExA(scm_handle, 0, 0, 0, NULL, 0, NULL, &returned, NULL, NULL); ok(!ret, "Expected failure\n"); - todo_wine - { 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()); - } /* No valid servicetype and servicestate */ needed = 0xdeadbeef; @@ -1454,13 +1447,10 @@ static void test_enum_svc(void) ret = pEnumServicesStatusExA(scm_handle, 0, 0, 0, NULL, 0, &needed, &returned, NULL, NULL); ok(!ret, "Expected failure\n"); ok(returned == 0, "Expected number of service to be set to 0, got %d\n", returned); - todo_wine - { ok(needed == 0 || broken(needed != 0), /* nt4 */ "Expected needed buffer size to be set to 0, got %d\n", needed); ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); - } /* No valid servicestate */ needed = 0xdeadbeef; @@ -1470,13 +1460,10 @@ static void test_enum_svc(void) &needed, &returned, NULL, NULL); ok(!ret, "Expected failure\n"); ok(returned == 0, "Expected number of service to be set to 0, got %d\n", returned); - todo_wine - { ok(needed == 0 || broken(needed != 0), /* nt4 */ "Expected needed buffer size to be set to 0, got %d\n", needed); ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); - } /* No valid servicetype */ needed = 0xdeadbeef; @@ -1486,13 +1473,10 @@ static void test_enum_svc(void) &needed, &returned, NULL, NULL); ok(!ret, "Expected failure\n"); ok(returned == 0, "Expected number of service to be set to 0, got %d\n", returned); - todo_wine - { ok(needed == 0 || broken(needed != 0), /* nt4 */ "Expected needed buffer size to be set to 0, got %d\n", needed); ok(GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); - } /* No valid servicetype and servicestate and unknown service group */ needed = 0xdeadbeef; @@ -1502,13 +1486,10 @@ static void test_enum_svc(void) &returned, NULL, "deadbeef_group"); ok(!ret, "Expected failure\n"); ok(returned == 0, "Expected number of service to be set to 0, got %d\n", returned); - todo_wine - { ok(needed == 0 || broken(needed != 0), /* nt4 */ "Expected needed buffer size to be set to 0, got %d\n", needed); 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; @@ -1517,7 +1498,6 @@ static void test_enum_svc(void) ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL, 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 service to be set to 0, got %d\n", returned); @@ -1533,7 +1513,6 @@ static void test_enum_svc(void) ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL, "deadbeef_group"); 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 service to be set to 0, got %d\n", returned); @@ -1552,12 +1531,9 @@ static void test_enum_svc(void) NULL, 0, &needed, &returned, NULL, "deadbeef_group"); ok(!ret, "Expected failure\n"); ok(returned == 0, "Expected number of service to be set to 0, got %d\n", returned); - todo_wine - { ok(needed == 0, "Expected needed buffer size to be set to 0, got %d\n", needed); ok(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST, "Expected ERROR_SERVICE_DOES_NOT_EXIST, got %d\n", GetLastError()); - } /* TODO: Create a test that makes sure we enumerate all services that don't * belong to a group. (specifying ""). @@ -1571,12 +1547,9 @@ static void test_enum_svc(void) NULL, 0, &needed, &returned, NULL, NULL); ok(!ret, "Expected failure\n"); ok(returned == 0, "Expected no service returned, got %d\n", returned); - todo_wine - { ok(needed != 0xdeadbeef && needed > 0, "Expected the needed buffer size\n"); 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; @@ -1595,12 +1568,9 @@ static void test_enum_svc(void) SetLastError(0xdeadbeef); ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL, (BYTE*)exservices, bufsize, &needed, &returned, NULL, 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 == tempreturned, "Expected the same number of service from this function\n"); - } HeapFree(GetProcessHeap(), 0, exservices); /* Store the number of returned services */ @@ -1615,13 +1585,10 @@ static void test_enum_svc(void) ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL, (BYTE*)exservices, bufsize, &needed, &returned, NULL, NULL); ok(!ret, "Expected failure\n"); - todo_wine - { ok(needed != 0xdeadbeef && needed > 0, "Expected the needed buffer size\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); @@ -1632,14 +1599,11 @@ static void test_enum_svc(void) ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL, (BYTE*)exservices, bufsize, &needed, &returned, &resume, NULL); ok(!ret, "Expected failure\n"); - todo_wine - { ok(needed != 0xdeadbeef && needed > 0, "Expected the needed buffer size\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 that last service but pass a bigger buffer size */ missing = tempreturned - returned; @@ -1649,11 +1613,8 @@ static void test_enum_svc(void) SetLastError(0xdeadbeef); ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL, (BYTE*)exservices, bufsize, &needed, &returned, &resume, 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 == missing, "Expected %u services to be returned\n", missing); ok(resume == 0, "Expected the resume handle to be 0\n"); HeapFree(GetProcessHeap(), 0, exservices); @@ -1693,18 +1654,19 @@ static void test_enum_svc(void) "Something wrong in the calculation\n"); /* Get all drivers and services */ - pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32 | SERVICE_DRIVER, - SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL, NULL); + ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32 | SERVICE_DRIVER, + SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL, NULL); + ok(!ret, "Expected failure\n"); exservices = HeapAlloc(GetProcessHeap(), 0, needed); - pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32 | SERVICE_DRIVER, - SERVICE_STATE_ALL, (BYTE*)exservices, needed, &needed, &returned, NULL, NULL); + ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32 | SERVICE_DRIVER, + SERVICE_STATE_ALL, (BYTE*)exservices, needed, &needed, &returned, NULL, NULL); + ok(ret, "Expected success %u\n", GetLastError()); /* Loop through all those returned drivers and services */ for (i = 0; i < returned; i++) { SERVICE_STATUS_PROCESS status = exservices[i].ServiceStatusProcess; - /* lpServiceName and lpDisplayName should always be filled */ ok(lstrlenA(exservices[i].lpServiceName) > 0, "Expected a service name\n"); ok(lstrlenA(exservices[i].lpDisplayName) > 0, "Expected a display name\n"); diff --git a/include/wine/svcctl.idl b/include/wine/svcctl.idl index 8d6a092b785..895ee2eeede 100644 --- a/include/wine/svcctl.idl +++ b/include/wine/svcctl.idl @@ -352,4 +352,15 @@ typedef [switch_type(DWORD)] union [in] DWORD cbBufSize, [out] LPDWORD pcbBytesNeeded ); + + DWORD svcctl_EnumServicesStatusExW( + [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, + [in,unique] LPCWSTR group + ); } diff --git a/programs/services/rpc.c b/programs/services/rpc.c index 104aa8aa8b9..82dc8c65cb4 100644 --- a/programs/services/rpc.c +++ b/programs/services/rpc.c @@ -1212,6 +1212,110 @@ DWORD svcctl_EnumServicesStatusW( return ERROR_SUCCESS; } +struct service_entry *find_service_by_group(struct scmdatabase *db, const WCHAR *group) +{ + struct service_entry *service; + LIST_FOR_EACH_ENTRY(service, &db->services, struct service_entry, entry) + { + if (service->config.lpLoadOrderGroup && !strcmpiW(group, service->config.lpLoadOrderGroup)) + return service; + } + return NULL; +} + +static BOOL match_group(const WCHAR *g1, const WCHAR *g2) +{ + if (!g2) return TRUE; + if (!g2[0] && (!g1 || !g1[0])) return TRUE; + if (g1 && !strcmpW(g1, g2)) return TRUE; + return FALSE; +} + +DWORD svcctl_EnumServicesStatusExW( + SC_RPC_HANDLE hmngr, + DWORD type, + DWORD state, + BYTE *buffer, + DWORD size, + LPDWORD needed, + LPDWORD returned, + LPCWSTR group) +{ + DWORD err, sz, total_size, num_services; + DWORD_PTR offset; + struct sc_manager_handle *manager; + struct service_entry *service; + ENUM_SERVICE_STATUS_PROCESSW *s; + + WINE_TRACE("(%p, 0x%x, 0x%x, %p, %u, %p, %p, %s)\n", hmngr, type, state, buffer, size, + needed, returned, wine_dbgstr_w(group)); + + 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); + + if (group && !find_service_by_group(manager->db, group)) + { + scmdatabase_unlock(manager->db); + return ERROR_SERVICE_DOES_NOT_EXIST; + } + + 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) + && match_group(service->config.lpLoadOrderGroup, group)) + { + total_size += sizeof(ENUM_SERVICE_STATUS_PROCESSW); + 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_STATUS_PROCESSW *)buffer; + offset = num_services * sizeof(ENUM_SERVICE_STATUS_PROCESSW); + LIST_FOR_EACH_ENTRY(service, &manager->db->services, struct service_entry, entry) + { + if ((service->status.dwServiceType & type) && map_state(service->status.dwCurrentState, state) + && match_group(service->config.lpLoadOrderGroup, group)) + { + 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; + } + s->ServiceStatusProcess = service->status; + s++; + } + } + *returned = num_services; + *needed = 0; + scmdatabase_unlock(manager->db); + return ERROR_SUCCESS; +} + DWORD svcctl_QueryServiceObjectSecurity( void) {