advapi32: Partially implement NotifyServiceStatusChangeW.
This commit is contained in:
parent
48f0f16311
commit
afa965a152
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue