advapi32: Partially implement NotifyServiceStatusChangeW.
This commit is contained in:
parent
48f0f16311
commit
afa965a152
|
@ -496,7 +496,7 @@
|
||||||
@ stdcall NotifyChangeEventLog (long long)
|
@ stdcall NotifyChangeEventLog (long long)
|
||||||
# @ stub NotifyServiceStatusChange
|
# @ stub NotifyServiceStatusChange
|
||||||
# @ stub NotifyServiceStatusChangeA
|
# @ stub NotifyServiceStatusChangeA
|
||||||
# @ stub NotifyServiceStatusChangeW
|
@ stdcall NotifyServiceStatusChangeW(ptr long ptr)
|
||||||
@ stdcall ObjectCloseAuditAlarmA(str ptr long)
|
@ stdcall ObjectCloseAuditAlarmA(str ptr long)
|
||||||
@ stdcall ObjectCloseAuditAlarmW(wstr ptr long)
|
@ stdcall ObjectCloseAuditAlarmW(wstr ptr long)
|
||||||
# @ stub ObjectDeleteAuditAlarmA
|
# @ stub ObjectDeleteAuditAlarmA
|
||||||
|
|
|
@ -2335,3 +2335,37 @@ BOOL WINAPI EnumDependentServicesW( SC_HANDLE hService, DWORD dwServiceState,
|
||||||
*lpServicesReturned = 0;
|
*lpServicesReturned = 0;
|
||||||
return TRUE;
|
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);
|
DWORD, LPDWORD);
|
||||||
static BOOL (WINAPI *pQueryServiceObjectSecurity)(SC_HANDLE, SECURITY_INFORMATION,
|
static BOOL (WINAPI *pQueryServiceObjectSecurity)(SC_HANDLE, SECURITY_INFORMATION,
|
||||||
PSECURITY_DESCRIPTOR, DWORD, LPDWORD);
|
PSECURITY_DESCRIPTOR, DWORD, LPDWORD);
|
||||||
|
static DWORD (WINAPI *pNotifyServiceStatusChangeW)(SC_HANDLE,DWORD,SERVICE_NOTIFYW*);
|
||||||
|
|
||||||
static void init_function_pointers(void)
|
static void init_function_pointers(void)
|
||||||
{
|
{
|
||||||
|
@ -63,6 +64,7 @@ static void init_function_pointers(void)
|
||||||
pQueryServiceConfig2W= (void*)GetProcAddress(hadvapi32, "QueryServiceConfig2W");
|
pQueryServiceConfig2W= (void*)GetProcAddress(hadvapi32, "QueryServiceConfig2W");
|
||||||
pQueryServiceStatusEx= (void*)GetProcAddress(hadvapi32, "QueryServiceStatusEx");
|
pQueryServiceStatusEx= (void*)GetProcAddress(hadvapi32, "QueryServiceStatusEx");
|
||||||
pQueryServiceObjectSecurity = (void*)GetProcAddress(hadvapi32, "QueryServiceObjectSecurity");
|
pQueryServiceObjectSecurity = (void*)GetProcAddress(hadvapi32, "QueryServiceObjectSecurity");
|
||||||
|
pNotifyServiceStatusChangeW = (void*)GetProcAddress(hadvapi32, "NotifyServiceStatusChangeW");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_open_scm(void)
|
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;
|
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)
|
static void test_start_stop(void)
|
||||||
{
|
{
|
||||||
BOOL ret;
|
BOOL ret;
|
||||||
|
@ -2267,6 +2338,13 @@ static void test_start_stop(void)
|
||||||
le = try_start_stop(svc_handle, displayname, is_nt4);
|
le = try_start_stop(svc_handle, displayname, is_nt4);
|
||||||
ok(le == ERROR_SERVICE_REQUEST_TIMEOUT, "%d != ERROR_SERVICE_REQUEST_TIMEOUT\n", le);
|
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:
|
cleanup:
|
||||||
if (svc_handle)
|
if (svc_handle)
|
||||||
{
|
{
|
||||||
|
@ -2370,6 +2448,57 @@ static void test_refcount(void)
|
||||||
CloseServiceHandle(scm_handle);
|
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)
|
START_TEST(service)
|
||||||
{
|
{
|
||||||
SC_HANDLE scm_handle;
|
SC_HANDLE scm_handle;
|
||||||
|
@ -2380,6 +2509,8 @@ START_TEST(service)
|
||||||
GetFullPathNameA(myARGV[0], sizeof(selfname), selfname, NULL);
|
GetFullPathNameA(myARGV[0], sizeof(selfname), selfname, NULL);
|
||||||
if (myARGC >= 3)
|
if (myARGC >= 3)
|
||||||
{
|
{
|
||||||
|
if (strcmp(myARGV[2], "serve") == 0)
|
||||||
|
run_service();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -159,6 +159,43 @@ typedef struct _SERVICE_STATUS_PROCESS
|
||||||
DWORD dwServiceFlags;
|
DWORD dwServiceFlags;
|
||||||
} SERVICE_STATUS_PROCESS, *LPSERVICE_STATUS_PROCESS;
|
} 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 {
|
typedef enum _SC_STATUS_TYPE {
|
||||||
SC_STATUS_PROCESS_INFO = 0
|
SC_STATUS_PROCESS_INFO = 0
|
||||||
} SC_STATUS_TYPE;
|
} SC_STATUS_TYPE;
|
||||||
|
|
Loading…
Reference in New Issue