Andrew Eikum : advapi32: Partially implement NotifyServiceStatusChangeW.

Alexandre Julliard julliard at wine.codeweavers.com
Thu Feb 26 09:17:00 CST 2015


Module: wine
Branch: master
Commit: afa965a152672dcd24e0a9cb4483871a778ee2df
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=afa965a152672dcd24e0a9cb4483871a778ee2df

Author: Andrew Eikum <aeikum at codeweavers.com>
Date:   Wed Feb 25 14:29:12 2015 -0600

advapi32: Partially implement NotifyServiceStatusChangeW.

---

 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;




More information about the wine-cvs mailing list