1
0
mirror of https://github.com/wine-mirror/wine synced 2024-07-08 20:06:18 +00:00

advapi32: Implement EnumServicesStatusExA/W.

This commit is contained in:
Hans Leidekker 2010-10-29 12:53:37 +02:00 committed by Alexandre Julliard
parent 27bf4c3747
commit 7135ac7641
4 changed files with 233 additions and 62 deletions

View File

@ -1558,31 +1558,125 @@ EnumServicesStatusW( SC_HANDLE hmngr, DWORD type, DWORD state, LPENUM_SERVICE_ST
* EnumServicesStatusExA [ADVAPI32.@] * EnumServicesStatusExA [ADVAPI32.@]
*/ */
BOOL WINAPI BOOL WINAPI
EnumServicesStatusExA(SC_HANDLE hSCManager, SC_ENUM_TYPE InfoLevel, DWORD dwServiceType, EnumServicesStatusExA( SC_HANDLE hmngr, SC_ENUM_TYPE level, DWORD type, DWORD state,
DWORD dwServiceState, LPBYTE lpServices, DWORD cbBufSize, LPDWORD pcbBytesNeeded, LPBYTE buffer, DWORD size, LPDWORD needed, LPDWORD returned,
LPDWORD lpServicesReturned, LPDWORD lpResumeHandle, LPCSTR pszGroupName) LPDWORD resume_handle, LPCSTR group )
{ {
FIXME("%p level=%d type=%x state=%x %p %x %p %p %p %s\n", hSCManager, InfoLevel, BOOL ret;
dwServiceType, dwServiceState, lpServices, cbBufSize, unsigned int i;
pcbBytesNeeded, lpServicesReturned, lpResumeHandle, debugstr_a(pszGroupName)); ENUM_SERVICE_STATUS_PROCESSA *services = (ENUM_SERVICE_STATUS_PROCESSA *)buffer;
if (lpServicesReturned) *lpServicesReturned = 0; ENUM_SERVICE_STATUS_PROCESSW *servicesW = NULL;
SetLastError (ERROR_ACCESS_DENIED); WCHAR *groupW = NULL;
return FALSE; 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.@] * EnumServicesStatusExW [ADVAPI32.@]
*/ */
BOOL WINAPI BOOL WINAPI
EnumServicesStatusExW(SC_HANDLE hSCManager, SC_ENUM_TYPE InfoLevel, DWORD dwServiceType, EnumServicesStatusExW( SC_HANDLE hmngr, SC_ENUM_TYPE level, DWORD type, DWORD state,
DWORD dwServiceState, LPBYTE lpServices, DWORD cbBufSize, LPDWORD pcbBytesNeeded, LPBYTE buffer, DWORD size, LPDWORD needed, LPDWORD returned,
LPDWORD lpServicesReturned, LPDWORD lpResumeHandle, LPCWSTR pszGroupName) LPDWORD resume_handle, LPCWSTR group )
{ {
FIXME("%p level=%d type=%x state=%x %p %x %p %p %p %s\n", hSCManager, InfoLevel, DWORD err, i;
dwServiceType, dwServiceState, lpServices, cbBufSize, ENUM_SERVICE_STATUS_PROCESSW *services = (ENUM_SERVICE_STATUS_PROCESSW *)buffer;
pcbBytesNeeded, lpServicesReturned, lpResumeHandle, debugstr_w(pszGroupName));
SetLastError (ERROR_ACCESS_DENIED); TRACE("%p %u 0x%x 0x%x %p %u %p %p %p %s\n", hmngr, level, type, state, buffer,
return FALSE; 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;
} }
/****************************************************************************** /******************************************************************************

View File

@ -1398,7 +1398,6 @@ static void test_enum_svc(void)
SetLastError(0xdeadbeef); SetLastError(0xdeadbeef);
ret = pEnumServicesStatusExA(NULL, 1, 0, 0, NULL, 0, NULL, NULL, NULL, NULL); ret = pEnumServicesStatusExA(NULL, 1, 0, 0, NULL, 0, NULL, NULL, NULL, NULL);
ok(!ret, "Expected failure\n"); ok(!ret, "Expected failure\n");
todo_wine
ok(GetLastError() == ERROR_INVALID_LEVEL, ok(GetLastError() == ERROR_INVALID_LEVEL,
"Expected ERROR_INVALID_LEVEL, got %d\n", GetLastError()); "Expected ERROR_INVALID_LEVEL, got %d\n", GetLastError());
@ -1406,7 +1405,6 @@ static void test_enum_svc(void)
SetLastError(0xdeadbeef); SetLastError(0xdeadbeef);
ret = pEnumServicesStatusExA(NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL); ret = pEnumServicesStatusExA(NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL);
ok(!ret, "Expected failure\n"); ok(!ret, "Expected failure\n");
todo_wine
ok(GetLastError() == ERROR_INVALID_HANDLE, ok(GetLastError() == ERROR_INVALID_HANDLE,
"Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
@ -1417,7 +1415,6 @@ static void test_enum_svc(void)
SetLastError(0xdeadbeef); SetLastError(0xdeadbeef);
ret = pEnumServicesStatusExA(scm_handle, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL); ret = pEnumServicesStatusExA(scm_handle, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL);
ok(!ret, "Expected failure\n"); ok(!ret, "Expected failure\n");
todo_wine
ok(GetLastError() == ERROR_INVALID_ADDRESS || ok(GetLastError() == ERROR_INVALID_ADDRESS ||
GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */, GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */,
"Unexpected last error %d\n", GetLastError()); "Unexpected last error %d\n", GetLastError());
@ -1429,7 +1426,6 @@ static void test_enum_svc(void)
ok(!ret, "Expected failure\n"); ok(!ret, "Expected failure\n");
ok(needed == 0xdeadbeef || broken(needed != 0xdeadbeef), /* nt4 */ ok(needed == 0xdeadbeef || broken(needed != 0xdeadbeef), /* nt4 */
"Expected no change to the needed buffer variable\n"); "Expected no change to the needed buffer variable\n");
todo_wine
ok(GetLastError() == ERROR_INVALID_ADDRESS || ok(GetLastError() == ERROR_INVALID_ADDRESS ||
GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */, GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */,
"Unexpected last error %d\n", GetLastError()); "Unexpected last error %d\n", GetLastError());
@ -1439,13 +1435,10 @@ static void test_enum_svc(void)
SetLastError(0xdeadbeef); SetLastError(0xdeadbeef);
ret = pEnumServicesStatusExA(scm_handle, 0, 0, 0, NULL, 0, NULL, &returned, NULL, NULL); ret = pEnumServicesStatusExA(scm_handle, 0, 0, 0, NULL, 0, NULL, &returned, NULL, NULL);
ok(!ret, "Expected failure\n"); ok(!ret, "Expected failure\n");
todo_wine
{
ok(returned == 0xdeadbeef, "Expected no change to the number of services variable\n"); ok(returned == 0xdeadbeef, "Expected no change to the number of services variable\n");
ok(GetLastError() == ERROR_INVALID_ADDRESS || ok(GetLastError() == ERROR_INVALID_ADDRESS ||
GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */, GetLastError() == ERROR_INVALID_PARAMETER /* NT4 */,
"Unexpected last error %d\n", GetLastError()); "Unexpected last error %d\n", GetLastError());
}
/* No valid servicetype and servicestate */ /* No valid servicetype and servicestate */
needed = 0xdeadbeef; 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); ret = pEnumServicesStatusExA(scm_handle, 0, 0, 0, NULL, 0, &needed, &returned, NULL, NULL);
ok(!ret, "Expected failure\n"); ok(!ret, "Expected failure\n");
ok(returned == 0, "Expected number of service to be set to 0, got %d\n", returned); 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 */ ok(needed == 0 || broken(needed != 0), /* nt4 */
"Expected needed buffer size to be set to 0, got %d\n", needed); "Expected needed buffer size to be set to 0, got %d\n", needed);
ok(GetLastError() == ERROR_INVALID_PARAMETER, ok(GetLastError() == ERROR_INVALID_PARAMETER,
"Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
}
/* No valid servicestate */ /* No valid servicestate */
needed = 0xdeadbeef; needed = 0xdeadbeef;
@ -1470,13 +1460,10 @@ static void test_enum_svc(void)
&needed, &returned, NULL, NULL); &needed, &returned, NULL, NULL);
ok(!ret, "Expected failure\n"); ok(!ret, "Expected failure\n");
ok(returned == 0, "Expected number of service to be set to 0, got %d\n", returned); 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 */ ok(needed == 0 || broken(needed != 0), /* nt4 */
"Expected needed buffer size to be set to 0, got %d\n", needed); "Expected needed buffer size to be set to 0, got %d\n", needed);
ok(GetLastError() == ERROR_INVALID_PARAMETER, ok(GetLastError() == ERROR_INVALID_PARAMETER,
"Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
}
/* No valid servicetype */ /* No valid servicetype */
needed = 0xdeadbeef; needed = 0xdeadbeef;
@ -1486,13 +1473,10 @@ static void test_enum_svc(void)
&needed, &returned, NULL, NULL); &needed, &returned, NULL, NULL);
ok(!ret, "Expected failure\n"); ok(!ret, "Expected failure\n");
ok(returned == 0, "Expected number of service to be set to 0, got %d\n", returned); 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 */ ok(needed == 0 || broken(needed != 0), /* nt4 */
"Expected needed buffer size to be set to 0, got %d\n", needed); "Expected needed buffer size to be set to 0, got %d\n", needed);
ok(GetLastError() == ERROR_INVALID_PARAMETER, ok(GetLastError() == ERROR_INVALID_PARAMETER,
"Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
}
/* No valid servicetype and servicestate and unknown service group */ /* No valid servicetype and servicestate and unknown service group */
needed = 0xdeadbeef; needed = 0xdeadbeef;
@ -1502,13 +1486,10 @@ static void test_enum_svc(void)
&returned, NULL, "deadbeef_group"); &returned, NULL, "deadbeef_group");
ok(!ret, "Expected failure\n"); ok(!ret, "Expected failure\n");
ok(returned == 0, "Expected number of service to be set to 0, got %d\n", returned); 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 */ ok(needed == 0 || broken(needed != 0), /* nt4 */
"Expected needed buffer size to be set to 0, got %d\n", needed); "Expected needed buffer size to be set to 0, got %d\n", needed);
ok(GetLastError() == ERROR_INVALID_PARAMETER, ok(GetLastError() == ERROR_INVALID_PARAMETER,
"Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
}
/* All parameters are correct but our access rights are wrong */ /* All parameters are correct but our access rights are wrong */
needed = 0xdeadbeef; needed = 0xdeadbeef;
@ -1517,7 +1498,6 @@ static void test_enum_svc(void)
ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL, ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL,
NULL, 0, &needed, &returned, NULL, NULL); NULL, 0, &needed, &returned, NULL, NULL);
ok(!ret, "Expected failure\n"); ok(!ret, "Expected failure\n");
todo_wine
ok(needed == 0 || broken(needed != 0), /* nt4 */ ok(needed == 0 || broken(needed != 0), /* nt4 */
"Expected needed buffer size to be set to 0, got %d\n", needed); "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); 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, ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL,
NULL, 0, &needed, &returned, NULL, "deadbeef_group"); NULL, 0, &needed, &returned, NULL, "deadbeef_group");
ok(!ret, "Expected failure\n"); ok(!ret, "Expected failure\n");
todo_wine
ok(needed == 0 || broken(needed != 0), /* nt4 */ ok(needed == 0 || broken(needed != 0), /* nt4 */
"Expected needed buffer size to be set to 0, got %d\n", needed); "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); 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"); NULL, 0, &needed, &returned, NULL, "deadbeef_group");
ok(!ret, "Expected failure\n"); ok(!ret, "Expected failure\n");
ok(returned == 0, "Expected number of service to be set to 0, got %d\n", returned); 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(needed == 0, "Expected needed buffer size to be set to 0, got %d\n", needed);
ok(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST, ok(GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST,
"Expected ERROR_SERVICE_DOES_NOT_EXIST, got %d\n", GetLastError()); "Expected ERROR_SERVICE_DOES_NOT_EXIST, got %d\n", GetLastError());
}
/* TODO: Create a test that makes sure we enumerate all services that don't /* TODO: Create a test that makes sure we enumerate all services that don't
* belong to a group. (specifying ""). * belong to a group. (specifying "").
@ -1571,12 +1547,9 @@ static void test_enum_svc(void)
NULL, 0, &needed, &returned, NULL, NULL); NULL, 0, &needed, &returned, NULL, NULL);
ok(!ret, "Expected failure\n"); ok(!ret, "Expected failure\n");
ok(returned == 0, "Expected no service returned, got %d\n", returned); 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(needed != 0xdeadbeef && needed > 0, "Expected the needed buffer size\n");
ok(GetLastError() == ERROR_MORE_DATA, ok(GetLastError() == ERROR_MORE_DATA,
"Expected ERROR_MORE_DATA, got %d\n", GetLastError()); "Expected ERROR_MORE_DATA, got %d\n", GetLastError());
}
/* Test to show we get the same needed buffer size for the W-call */ /* Test to show we get the same needed buffer size for the W-call */
neededW = 0xdeadbeef; neededW = 0xdeadbeef;
@ -1595,12 +1568,9 @@ static void test_enum_svc(void)
SetLastError(0xdeadbeef); SetLastError(0xdeadbeef);
ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL, ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL,
(BYTE*)exservices, bufsize, &needed, &returned, NULL, NULL); (BYTE*)exservices, bufsize, &needed, &returned, NULL, NULL);
todo_wine
{
ok(ret, "Expected success, got error %u\n", GetLastError()); ok(ret, "Expected success, got error %u\n", GetLastError());
ok(needed == 0, "Expected needed buffer to be 0 as we are done\n"); 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"); ok(returned == tempreturned, "Expected the same number of service from this function\n");
}
HeapFree(GetProcessHeap(), 0, exservices); HeapFree(GetProcessHeap(), 0, exservices);
/* Store the number of returned services */ /* 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, ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL,
(BYTE*)exservices, bufsize, &needed, &returned, NULL, NULL); (BYTE*)exservices, bufsize, &needed, &returned, NULL, NULL);
ok(!ret, "Expected failure\n"); ok(!ret, "Expected failure\n");
todo_wine
{
ok(needed != 0xdeadbeef && needed > 0, "Expected the needed buffer size\n"); ok(needed != 0xdeadbeef && needed > 0, "Expected the needed buffer size\n");
ok(returned < tempreturned, "Expected fewer services to be returned\n"); ok(returned < tempreturned, "Expected fewer services to be returned\n");
ok(GetLastError() == ERROR_MORE_DATA, ok(GetLastError() == ERROR_MORE_DATA,
"Expected ERROR_MORE_DATA, got %d\n", GetLastError()); "Expected ERROR_MORE_DATA, got %d\n", GetLastError());
}
/* Allocate less than the needed bytes, this time with a correct resume handle */ /* Allocate less than the needed bytes, this time with a correct resume handle */
bufsize = (tempreturned - 1) * sizeof(ENUM_SERVICE_STATUS); 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, ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL,
(BYTE*)exservices, bufsize, &needed, &returned, &resume, NULL); (BYTE*)exservices, bufsize, &needed, &returned, &resume, NULL);
ok(!ret, "Expected failure\n"); ok(!ret, "Expected failure\n");
todo_wine
{
ok(needed != 0xdeadbeef && needed > 0, "Expected the needed buffer size\n"); ok(needed != 0xdeadbeef && needed > 0, "Expected the needed buffer size\n");
ok(returned < tempreturned, "Expected fewer services to be returned\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, ok(GetLastError() == ERROR_MORE_DATA,
"Expected ERROR_MORE_DATA, got %d\n", GetLastError()); "Expected ERROR_MORE_DATA, got %d\n", GetLastError());
}
/* Fetch that last service but pass a bigger buffer size */ /* Fetch that last service but pass a bigger buffer size */
missing = tempreturned - returned; missing = tempreturned - returned;
@ -1649,11 +1613,8 @@ static void test_enum_svc(void)
SetLastError(0xdeadbeef); SetLastError(0xdeadbeef);
ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL, ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32, SERVICE_STATE_ALL,
(BYTE*)exservices, bufsize, &needed, &returned, &resume, NULL); (BYTE*)exservices, bufsize, &needed, &returned, &resume, NULL);
todo_wine
{
ok(ret, "Expected success, got error %u\n", GetLastError()); ok(ret, "Expected success, got error %u\n", GetLastError());
ok(needed == 0, "Expected needed buffer to be 0 as we are done\n"); 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(returned == missing, "Expected %u services to be returned\n", missing);
ok(resume == 0, "Expected the resume handle to be 0\n"); ok(resume == 0, "Expected the resume handle to be 0\n");
HeapFree(GetProcessHeap(), 0, exservices); HeapFree(GetProcessHeap(), 0, exservices);
@ -1693,18 +1654,19 @@ static void test_enum_svc(void)
"Something wrong in the calculation\n"); "Something wrong in the calculation\n");
/* Get all drivers and services */ /* Get all drivers and services */
pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32 | SERVICE_DRIVER, ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32 | SERVICE_DRIVER,
SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL, NULL); SERVICE_STATE_ALL, NULL, 0, &needed, &returned, NULL, NULL);
ok(!ret, "Expected failure\n");
exservices = HeapAlloc(GetProcessHeap(), 0, needed); exservices = HeapAlloc(GetProcessHeap(), 0, needed);
pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32 | SERVICE_DRIVER, ret = pEnumServicesStatusExA(scm_handle, 0, SERVICE_WIN32 | SERVICE_DRIVER,
SERVICE_STATE_ALL, (BYTE*)exservices, needed, &needed, &returned, NULL, NULL); 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 */ /* Loop through all those returned drivers and services */
for (i = 0; i < returned; i++) for (i = 0; i < returned; i++)
{ {
SERVICE_STATUS_PROCESS status = exservices[i].ServiceStatusProcess; SERVICE_STATUS_PROCESS status = exservices[i].ServiceStatusProcess;
/* lpServiceName and lpDisplayName should always be filled */ /* lpServiceName and lpDisplayName should always be filled */
ok(lstrlenA(exservices[i].lpServiceName) > 0, "Expected a service name\n"); ok(lstrlenA(exservices[i].lpServiceName) > 0, "Expected a service name\n");
ok(lstrlenA(exservices[i].lpDisplayName) > 0, "Expected a display name\n"); ok(lstrlenA(exservices[i].lpDisplayName) > 0, "Expected a display name\n");

View File

@ -352,4 +352,15 @@ typedef [switch_type(DWORD)] union
[in] DWORD cbBufSize, [in] DWORD cbBufSize,
[out] LPDWORD pcbBytesNeeded [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
);
} }

View File

@ -1212,6 +1212,110 @@ DWORD svcctl_EnumServicesStatusW(
return ERROR_SUCCESS; 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( DWORD svcctl_QueryServiceObjectSecurity(
void) void)
{ {