advapi32: Partially implement NotifyServiceStatusChangeW.

This commit is contained in:
Andrew Eikum 2015-02-25 14:29:12 -06:00 committed by Alexandre Julliard
parent 48f0f16311
commit afa965a152
4 changed files with 203 additions and 1 deletions

View File

@ -496,7 +496,7 @@
@ stdcall NotifyChangeEventLog (long long)
# @ stub NotifyServiceStatusChange
# @ stub NotifyServiceStatusChangeA
# @ stub NotifyServiceStatusChangeW
@ stdcall NotifyServiceStatusChangeW(ptr long ptr)
@ stdcall ObjectCloseAuditAlarmA(str ptr long)
@ stdcall ObjectCloseAuditAlarmW(wstr ptr long)
# @ stub ObjectDeleteAuditAlarmA

View File

@ -2335,3 +2335,37 @@ BOOL WINAPI EnumDependentServicesW( SC_HANDLE hService, DWORD dwServiceState,
*lpServicesReturned = 0;
return TRUE;
}
/******************************************************************************
* NotifyServiceStatusChangeW [ADVAPI32.@]
*/
DWORD WINAPI NotifyServiceStatusChangeW(SC_HANDLE hService, DWORD dwNotifyMask,
SERVICE_NOTIFYW *pNotifyBuffer)
{
DWORD dummy;
BOOL ret;
SERVICE_STATUS_PROCESS st;
FIXME("%p 0x%x %p - semi-stub\n", hService, dwNotifyMask, pNotifyBuffer);
ret = QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (void*)&st, sizeof(st), &dummy);
if (ret)
{
/* dwNotifyMask is a set of bitflags in same order as SERVICE_ statuses */
if (dwNotifyMask & (1 << (st.dwCurrentState - SERVICE_STOPPED)))
{
pNotifyBuffer->dwNotificationStatus = ERROR_SUCCESS;
memcpy(&pNotifyBuffer->ServiceStatus, &st, sizeof(pNotifyBuffer->ServiceStatus));
pNotifyBuffer->dwNotificationTriggered = 1 << (st.dwCurrentState - SERVICE_STOPPED);
pNotifyBuffer->pszServiceNames = NULL;
TRACE("Queueing notification: 0x%x\n", pNotifyBuffer->dwNotificationTriggered);
QueueUserAPC((PAPCFUNC)pNotifyBuffer->pfnNotifyCallback,
GetCurrentThread(), (ULONG_PTR)pNotifyBuffer);
}
}
/* TODO: If the service is not currently in a matching state, we should
* tell `services` to monitor it. */
return ERROR_SUCCESS;
}

View File

@ -50,6 +50,7 @@ static BOOL (WINAPI *pQueryServiceStatusEx)(SC_HANDLE, SC_STATUS_TYPE, LPBYTE,
DWORD, LPDWORD);
static BOOL (WINAPI *pQueryServiceObjectSecurity)(SC_HANDLE, SECURITY_INFORMATION,
PSECURITY_DESCRIPTOR, DWORD, LPDWORD);
static DWORD (WINAPI *pNotifyServiceStatusChangeW)(SC_HANDLE,DWORD,SERVICE_NOTIFYW*);
static void init_function_pointers(void)
{
@ -63,6 +64,7 @@ static void init_function_pointers(void)
pQueryServiceConfig2W= (void*)GetProcAddress(hadvapi32, "QueryServiceConfig2W");
pQueryServiceStatusEx= (void*)GetProcAddress(hadvapi32, "QueryServiceStatusEx");
pQueryServiceObjectSecurity = (void*)GetProcAddress(hadvapi32, "QueryServiceObjectSecurity");
pNotifyServiceStatusChangeW = (void*)GetProcAddress(hadvapi32, "NotifyServiceStatusChangeW");
}
static void test_open_scm(void)
@ -2195,6 +2197,75 @@ static DWORD try_start_stop(SC_HANDLE svc_handle, const char* name, DWORD is_nt4
return le1;
}
struct notify_data {
SERVICE_NOTIFYW notify;
SC_HANDLE svc;
};
void CALLBACK cb_stopped(void *user)
{
struct notify_data *data = user;
BOOL br;
ok(data->notify.dwNotificationStatus == ERROR_SUCCESS,
"Got wrong notification status: %u\n", data->notify.dwNotificationStatus);
ok(data->notify.ServiceStatus.dwCurrentState == SERVICE_STOPPED,
"Got wrong service state: 0x%x\n", data->notify.ServiceStatus.dwCurrentState);
ok(data->notify.dwNotificationTriggered == SERVICE_NOTIFY_STOPPED,
"Got wrong notification triggered: 0x%x\n", data->notify.dwNotificationTriggered);
br = StartServiceA(data->svc, 0, NULL);
ok(br, "StartService failed: %u\n", GetLastError());
}
void CALLBACK cb_running(void *user)
{
struct notify_data *data = user;
BOOL br;
SERVICE_STATUS status;
ok(data->notify.dwNotificationStatus == ERROR_SUCCESS,
"Got wrong notification status: %u\n", data->notify.dwNotificationStatus);
ok(data->notify.ServiceStatus.dwCurrentState == SERVICE_RUNNING,
"Got wrong service state: 0x%x\n", data->notify.ServiceStatus.dwCurrentState);
ok(data->notify.dwNotificationTriggered == SERVICE_NOTIFY_RUNNING,
"Got wrong notification triggered: 0x%x\n", data->notify.dwNotificationTriggered);
br = ControlService(data->svc, SERVICE_CONTROL_STOP, &status);
ok(br, "ControlService failed: %u\n", GetLastError());
}
static void test_servicenotify(SC_HANDLE svc)
{
DWORD dr;
struct notify_data data;
if(!pNotifyServiceStatusChangeW){
win_skip("No NotifyServiceStatusChangeW\n");
return;
}
memset(&data.notify, 0, sizeof(data.notify));
data.notify.dwVersion = SERVICE_NOTIFY_STATUS_CHANGE;
data.notify.pfnNotifyCallback = &cb_stopped;
data.notify.pContext = &data;
data.svc = svc;
dr = pNotifyServiceStatusChangeW(svc, SERVICE_NOTIFY_STOPPED | SERVICE_NOTIFY_RUNNING, &data.notify);
ok(dr == ERROR_SUCCESS, "NotifyServiceStatusChangeW failed: %u\n", dr);
dr = SleepEx(100, TRUE);
ok(dr == WAIT_IO_COMPLETION, "APC wasn't called\n");
data.notify.pfnNotifyCallback = &cb_running;
dr = pNotifyServiceStatusChangeW(svc, SERVICE_NOTIFY_STOPPED | SERVICE_NOTIFY_RUNNING, &data.notify);
ok(dr == ERROR_SUCCESS, "NotifyServiceStatusChangeW failed: %u\n", dr);
dr = SleepEx(100, TRUE);
ok(dr == WAIT_IO_COMPLETION, "APC wasn't called\n");
}
static void test_start_stop(void)
{
BOOL ret;
@ -2267,6 +2338,13 @@ static void test_start_stop(void)
le = try_start_stop(svc_handle, displayname, is_nt4);
ok(le == ERROR_SERVICE_REQUEST_TIMEOUT, "%d != ERROR_SERVICE_REQUEST_TIMEOUT\n", le);
/* create a real service and test notifications */
sprintf(cmd, "%s service serve", selfname);
displayname = "Winetest Service";
ret = ChangeServiceConfigA(svc_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, cmd, NULL, NULL, NULL, NULL, NULL, displayname);
ok(ret, "ChangeServiceConfig() failed le=%u\n", GetLastError());
test_servicenotify(svc_handle);
cleanup:
if (svc_handle)
{
@ -2370,6 +2448,57 @@ static void test_refcount(void)
CloseServiceHandle(scm_handle);
}
static DWORD WINAPI ctrl_handler(DWORD ctl, DWORD type, void *data, void *user)
{
HANDLE evt = user;
switch(ctl){
case SERVICE_CONTROL_STOP:
SetEvent(evt);
break;
case SERVICE_CONTROL_INTERROGATE:
return NO_ERROR;
}
return ERROR_CALL_NOT_IMPLEMENTED;
}
static void WINAPI service_main(DWORD argc, char **argv)
{
SERVICE_STATUS_HANDLE st_handle;
SERVICE_STATUS st;
HANDLE evt = CreateEventW(0, FALSE, FALSE, 0);
st_handle = RegisterServiceCtrlHandlerExA("", &ctrl_handler, evt);
st.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
st.dwServiceSpecificExitCode = 0;
st.dwCurrentState = SERVICE_RUNNING;
st.dwWin32ExitCode = NO_ERROR;
st.dwWaitHint = 0;
st.dwControlsAccepted = SERVICE_ACCEPT_STOP;
st.dwCheckPoint = 0;
SetServiceStatus(st_handle, &st);
WaitForSingleObject(evt, 5000);
st.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(st_handle, &st);
}
static void run_service(void)
{
char empty[] = {0};
SERVICE_TABLE_ENTRYA table[] = {
{empty, &service_main },
{0, 0}
};
StartServiceCtrlDispatcherA(table);
}
START_TEST(service)
{
SC_HANDLE scm_handle;
@ -2380,6 +2509,8 @@ START_TEST(service)
GetFullPathNameA(myARGV[0], sizeof(selfname), selfname, NULL);
if (myARGC >= 3)
{
if (strcmp(myARGV[2], "serve") == 0)
run_service();
return;
}

View File

@ -159,6 +159,43 @@ typedef struct _SERVICE_STATUS_PROCESS
DWORD dwServiceFlags;
} SERVICE_STATUS_PROCESS, *LPSERVICE_STATUS_PROCESS;
#define SERVICE_NOTIFY_STATUS_CHANGE 2
#define SERVICE_NOTIFY_STOPPED 0x1
#define SERVICE_NOTIFY_START_PENDING 0x2
#define SERVICE_NOTIFY_STOP_PENDING 0x4
#define SERVICE_NOTIFY_RUNNING 0x8
#define SERVICE_NOTIFY_CONTINUE_PENDING 0x10
#define SERVICE_NOTIFY_PAUSE_PENDING 0x20
#define SERVICE_NOTIFY_PAUSED 0x40
#define SERVICE_NOTIFY_CREATED 0x80
#define SERVICE_NOTIFY_DELETED 0x100
#define SERVICE_NOTIFY_DELETE_PENDING 0x200
typedef void (CALLBACK *PFN_SC_NOTIFY_CALLBACK)(void *);
typedef struct _SERVICE_NOTIFY_2A {
DWORD dwVersion;
PFN_SC_NOTIFY_CALLBACK pfnNotifyCallback;
void *pContext;
DWORD dwNotificationStatus;
SERVICE_STATUS_PROCESS ServiceStatus;
DWORD dwNotificationTriggered;
char *pszServiceNames;
} SERVICE_NOTIFY_2A, SERVICE_NOTIFYA;
typedef struct _SERVICE_NOTIFY_2W {
DWORD dwVersion;
PFN_SC_NOTIFY_CALLBACK pfnNotifyCallback;
void *pContext;
DWORD dwNotificationStatus;
SERVICE_STATUS_PROCESS ServiceStatus;
DWORD dwNotificationTriggered;
WCHAR *pszServiceNames;
} SERVICE_NOTIFY_2W, SERVICE_NOTIFYW;
DWORD WINAPI NotifyServiceStatusChangeW(SC_HANDLE,DWORD,SERVICE_NOTIFYW*);
typedef enum _SC_STATUS_TYPE {
SC_STATUS_PROCESS_INFO = 0
} SC_STATUS_TYPE;