[PATCH 3/3] advapi32: Partially implement NotifyServiceStatusChangeW
Andrew Eikum
aeikum at codeweavers.com
Wed Feb 25 14:29:12 CST 2015
---
dlls/advapi32/advapi32.spec | 2 +-
dlls/advapi32/service.c | 34 +++++++++++
dlls/advapi32/tests/service.c | 131 ++++++++++++++++++++++++++++++++++++++++++
include/winsvc.h | 37 ++++++++++++
4 files changed, 203 insertions(+), 1 deletion(-)
diff --git a/dlls/advapi32/advapi32.spec b/dlls/advapi32/advapi32.spec
index 322360d..e7b7cf7 100644
--- a/dlls/advapi32/advapi32.spec
+++ b/dlls/advapi32/advapi32.spec
@@ -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
diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c
index ae002c9..4657bd9 100644
--- a/dlls/advapi32/service.c
+++ b/dlls/advapi32/service.c
@@ -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;
+}
diff --git a/dlls/advapi32/tests/service.c b/dlls/advapi32/tests/service.c
index 0fc8e3a..f87508b 100644
--- a/dlls/advapi32/tests/service.c
+++ b/dlls/advapi32/tests/service.c
@@ -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;
}
diff --git a/include/winsvc.h b/include/winsvc.h
index c1af509..7d768f5 100644
--- a/include/winsvc.h
+++ b/include/winsvc.h
@@ -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;
--
2.3.0
More information about the wine-patches
mailing list