Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-cw
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-cw
Commits
1f88b90b
Commit
1f88b90b
authored
Jan 23, 2018
by
Andrew Eikum
Committed by
Alexandre Julliard
Jan 23, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
advapi32: Implement NotifyServiceStatusChange.
Signed-off-by:
Andrew Eikum
<
aeikum@codeweavers.com
>
Signed-off-by:
Alexandre Julliard
<
julliard@winehq.org
>
parent
73e8d0e6
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
232 additions
and
53 deletions
+232
-53
service.c
dlls/advapi32/service.c
+126
-20
service.c
dlls/advapi32/tests/service.c
+106
-33
No files found.
dlls/advapi32/service.c
View file @
1f88b90b
...
...
@@ -48,6 +48,7 @@
#include "advapi32_misc.h"
#include "wine/exception.h"
#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL
(
service
);
...
...
@@ -83,6 +84,18 @@ typedef struct dispatcher_data_t
HANDLE
pipe
;
}
dispatcher_data
;
typedef
struct
notify_data_t
{
SC_HANDLE
service
;
SC_RPC_NOTIFY_PARAMS
params
;
SERVICE_NOTIFY_STATUS_CHANGE_PARAMS_2
cparams
;
SC_NOTIFY_RPC_HANDLE
notify_handle
;
SERVICE_NOTIFYW
*
notify_buffer
;
HANDLE
calling_thread
,
ready_evt
;
struct
list
entry
;
}
notify_data
;
static
struct
list
notify_list
=
LIST_INIT
(
notify_list
);
static
CRITICAL_SECTION
service_cs
;
static
CRITICAL_SECTION_DEBUG
service_cs_debug
=
{
...
...
@@ -2596,37 +2609,130 @@ BOOL WINAPI EnumDependentServicesW( SC_HANDLE hService, DWORD dwServiceState,
return
TRUE
;
}
static
DWORD
WINAPI
notify_thread
(
void
*
user
)
{
DWORD
err
;
notify_data
*
data
=
user
;
SC_RPC_NOTIFY_PARAMS_LIST
*
list
;
SERVICE_NOTIFY_STATUS_CHANGE_PARAMS_2
*
cparams
;
BOOL
dummy
;
__TRY
{
/* GetNotifyResults blocks until there is an event */
err
=
svcctl_GetNotifyResults
(
data
->
notify_handle
,
&
list
);
}
__EXCEPT
(
rpc_filter
)
{
err
=
map_exception_code
(
GetExceptionCode
());
}
__ENDTRY
EnterCriticalSection
(
&
service_cs
);
list_remove
(
&
data
->
entry
);
LeaveCriticalSection
(
&
service_cs
);
if
(
err
==
ERROR_SUCCESS
&&
list
)
{
cparams
=
list
->
NotifyParamsArray
[
0
].
u
.
params
;
data
->
notify_buffer
->
dwNotificationStatus
=
cparams
->
dwNotificationStatus
;
memcpy
(
&
data
->
notify_buffer
->
ServiceStatus
,
&
cparams
->
ServiceStatus
,
sizeof
(
SERVICE_STATUS_PROCESS
));
data
->
notify_buffer
->
dwNotificationTriggered
=
cparams
->
dwNotificationTriggered
;
data
->
notify_buffer
->
pszServiceNames
=
NULL
;
QueueUserAPC
((
PAPCFUNC
)
data
->
notify_buffer
->
pfnNotifyCallback
,
data
->
calling_thread
,
(
ULONG_PTR
)
data
->
notify_buffer
);
HeapFree
(
GetProcessHeap
(),
0
,
list
);
}
else
WARN
(
"GetNotifyResults server call failed: %u
\n
"
,
err
);
__TRY
{
err
=
svcctl_CloseNotifyHandle
(
&
data
->
notify_handle
,
&
dummy
);
}
__EXCEPT
(
rpc_filter
)
{
err
=
map_exception_code
(
GetExceptionCode
());
}
__ENDTRY
if
(
err
!=
ERROR_SUCCESS
)
WARN
(
"CloseNotifyHandle server call failed: %u
\n
"
,
err
);
CloseHandle
(
data
->
calling_thread
);
HeapFree
(
GetProcessHeap
(),
0
,
data
);
return
0
;
}
/******************************************************************************
* NotifyServiceStatusChangeW [ADVAPI32.@]
*/
DWORD
WINAPI
NotifyServiceStatusChangeW
(
SC_HANDLE
hService
,
DWORD
dwNotifyMask
,
SERVICE_NOTIFYW
*
pNotifyBuffer
)
{
DWORD
dummy
;
BOOL
ret
;
SERVICE_STATUS_PROCESS
st
;
static
int
once
;
DWORD
err
;
BOOL
b_dummy
=
FALSE
;
GUID
g_dummy
=
{
0
};
notify_data
*
data
;
TRACE
(
"%p 0x%x %p
\n
"
,
hService
,
dwNotifyMask
,
pNotifyBuffer
);
if
(
!
once
++
)
FIXME
(
"%p 0x%x %p - semi-stub
\n
"
,
hService
,
dwNotifyMask
,
pNotifyBuffer
);
data
=
HeapAlloc
(
GetProcessHeap
(),
HEAP_ZERO_MEMORY
,
sizeof
(
*
data
));
if
(
!
data
)
return
ERROR_NOT_ENOUGH_MEMORY
;
ret
=
QueryServiceStatusEx
(
hService
,
SC_STATUS_PROCESS_INFO
,
(
void
*
)
&
st
,
sizeof
(
st
),
&
dummy
);
if
(
ret
)
data
->
service
=
hService
;
data
->
notify_buffer
=
pNotifyBuffer
;
if
(
!
DuplicateHandle
(
GetCurrentProcess
(),
GetCurrentThread
(),
GetCurrentProcess
(),
&
data
->
calling_thread
,
0
,
FALSE
,
DUPLICATE_SAME_ACCESS
))
{
/* 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
);
}
ERR
(
"DuplicateHandle failed: %u
\n
"
,
GetLastError
());
HeapFree
(
GetProcessHeap
(),
0
,
data
);
return
ERROR_NOT_ENOUGH_MEMORY
;
}
data
->
params
.
dwInfoLevel
=
2
;
data
->
params
.
u
.
params
=
&
data
->
cparams
;
data
->
cparams
.
dwNotifyMask
=
dwNotifyMask
;
EnterCriticalSection
(
&
service_cs
);
__TRY
{
err
=
svcctl_NotifyServiceStatusChange
(
hService
,
data
->
params
,
&
g_dummy
,
&
g_dummy
,
&
b_dummy
,
&
data
->
notify_handle
);
}
__EXCEPT
(
rpc_filter
)
{
err
=
map_exception_code
(
GetExceptionCode
());
}
__ENDTRY
/* TODO: If the service is not currently in a matching state, we should
* tell `services` to monitor it. */
if
(
err
!=
ERROR_SUCCESS
)
{
WARN
(
"NotifyServiceStatusChange server call failed: %u
\n
"
,
err
);
LeaveCriticalSection
(
&
service_cs
);
CloseHandle
(
data
->
calling_thread
);
CloseHandle
(
data
->
ready_evt
);
HeapFree
(
GetProcessHeap
(),
0
,
data
);
return
err
;
}
CloseHandle
(
CreateThread
(
NULL
,
0
,
&
notify_thread
,
data
,
0
,
NULL
));
list_add_tail
(
&
notify_list
,
&
data
->
entry
);
LeaveCriticalSection
(
&
service_cs
);
return
ERROR_SUCCESS
;
}
dlls/advapi32/tests/service.c
View file @
1f88b90b
...
...
@@ -2263,73 +2263,146 @@ static DWORD try_start_stop(SC_HANDLE svc_handle, const char* name, DWORD is_nt4
return
le1
;
}
#define PHASE_STOPPED 1
#define PHASE_RUNNING 2
struct
notify_data
{
SERVICE_NOTIFYW
notify
;
SC_HANDLE
svc
;
BOOL
was_called
;
DWORD
phase
;
};
static
void
CALLBACK
cb_stopped
(
void
*
user
)
static
void
CALLBACK
notify_cb
(
void
*
user
)
{
struct
notify_data
*
data
=
user
;
BOOL
br
;
switch
(
data
->
phase
)
{
case
PHASE_STOPPED
:
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
);
break
;
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
);
case
PHASE_RUNNING
:
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
);
break
;
}
br
=
StartServiceA
(
data
->
svc
,
0
,
NULL
);
ok
(
br
,
"StartService failed: %u
\n
"
,
GetLastError
());
data
->
was_called
=
TRUE
;
}
static
void
CALLBACK
cb_running
(
void
*
user
)
static
void
test_servicenotify
(
SC_HANDLE
scm_handle
,
const
char
*
servicename
)
{
struct
notify_data
*
data
=
user
;
DWORD
dr
,
dr2
;
struct
notify_data
data
;
struct
notify_data
data2
;
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
;
HANDLE
svc
,
svc2
;
if
(
!
pNotifyServiceStatusChangeW
){
win_skip
(
"No NotifyServiceStatusChangeW
\n
"
);
return
;
}
svc
=
OpenServiceA
(
scm_handle
,
servicename
,
GENERIC_ALL
);
svc2
=
OpenServiceA
(
scm_handle
,
servicename
,
GENERIC_ALL
);
ok
(
svc
!=
NULL
&&
svc2
!=
NULL
,
"Failed to open service
\n
"
);
if
(
!
svc
||
!
svc2
)
return
;
/* receive stopped notification, then start service */
memset
(
&
data
.
notify
,
0
,
sizeof
(
data
.
notify
));
data
.
notify
.
dwVersion
=
SERVICE_NOTIFY_STATUS_CHANGE
;
data
.
notify
.
pfnNotifyCallback
=
&
cb_stopped
;
data
.
notify
.
pfnNotifyCallback
=
&
notify_cb
;
data
.
notify
.
pContext
=
&
data
;
data
.
svc
=
svc
;
data
.
phase
=
PHASE_STOPPED
;
data
.
was_called
=
FALSE
;
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
"
);
ok
(
dr
==
WAIT_IO_COMPLETION
,
"Got wrong SleepEx result: %u
\n
"
,
dr
);
ok
(
data
.
was_called
==
TRUE
,
"APC wasn't called
\n
"
);
br
=
StartServiceA
(
svc
,
0
,
NULL
);
ok
(
br
,
"StartService failed: %u
\n
"
,
GetLastError
());
data
.
notify
.
pfnNotifyCallback
=
&
cb_running
;
/* receive running notification */
data
.
phase
=
PHASE_RUNNING
;
data
.
was_called
=
FALSE
;
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
"
);
ok
(
dr
==
WAIT_IO_COMPLETION
,
"Got wrong SleepEx result: %u
\n
"
,
dr
);
ok
(
data
.
was_called
==
TRUE
,
"APC wasn't called
\n
"
);
/* cannot register two notifications */
data
.
phase
=
PHASE_STOPPED
;
data
.
was_called
=
FALSE
;
dr
=
pNotifyServiceStatusChangeW
(
svc
,
SERVICE_NOTIFY_STOPPED
|
SERVICE_NOTIFY_RUNNING
,
&
data
.
notify
);
ok
(
dr
==
ERROR_SUCCESS
,
"NotifyServiceStatusChangeW failed: %u
\n
"
,
dr
);
memset
(
&
data2
.
notify
,
0
,
sizeof
(
data2
.
notify
));
data2
.
notify
.
dwVersion
=
SERVICE_NOTIFY_STATUS_CHANGE
;
data2
.
notify
.
pfnNotifyCallback
=
&
notify_cb
;
data2
.
notify
.
pContext
=
&
data2
;
dr
=
pNotifyServiceStatusChangeW
(
svc
,
SERVICE_NOTIFY_STOPPED
|
SERVICE_NOTIFY_RUNNING
,
&
data2
.
notify
);
ok
(
dr
==
ERROR_SUCCESS
||
/* win8+ */
dr
==
ERROR_ALREADY_REGISTERED
,
"NotifyServiceStatusChangeW gave wrong result: %u
\n
"
,
dr
);
/* should receive no notification because status has not changed.
* on win8+, SleepEx quits early but the callback is still not invoked. */
dr2
=
SleepEx
(
100
,
TRUE
);
ok
((
dr
==
ERROR_SUCCESS
&&
dr2
==
WAIT_IO_COMPLETION
)
||
/* win8+ */
(
dr
==
ERROR_ALREADY_REGISTERED
&&
dr2
==
0
),
"Got wrong SleepEx result: %u
\n
"
,
dr
);
ok
(
data
.
was_called
==
FALSE
,
"APC should not have been called
\n
"
);
/* stop service and receive notifiction */
br
=
ControlService
(
svc
,
SERVICE_CONTROL_STOP
,
&
status
);
ok
(
br
,
"ControlService failed: %u
\n
"
,
GetLastError
());
dr
=
SleepEx
(
100
,
TRUE
);
ok
(
dr
==
WAIT_IO_COMPLETION
,
"Got wrong SleepEx result: %u
\n
"
,
dr
);
ok
(
data
.
was_called
==
TRUE
,
"APC wasn't called
\n
"
);
/* test cancelation: create notify on svc that will block until service
* start; close svc; start service on svc2; verify that notification does
* not happen */
data
.
phase
=
PHASE_RUNNING
;
data
.
was_called
=
FALSE
;
dr
=
pNotifyServiceStatusChangeW
(
svc
,
SERVICE_NOTIFY_STOPPED
|
SERVICE_NOTIFY_RUNNING
,
&
data
.
notify
);
ok
(
dr
==
ERROR_SUCCESS
,
"NotifyServiceStatusChangeW failed: %u
\n
"
,
dr
);
CloseServiceHandle
(
svc
);
br
=
StartServiceA
(
svc2
,
0
,
NULL
);
ok
(
br
,
"StartService failed: %u
\n
"
,
GetLastError
());
dr
=
SleepEx
(
100
,
TRUE
);
ok
(
dr
==
0
,
"Got wrong SleepEx result: %u
\n
"
,
dr
);
ok
(
data
.
was_called
==
FALSE
,
"APC should not have been called
\n
"
);
br
=
ControlService
(
svc2
,
SERVICE_CONTROL_STOP
,
&
status
);
ok
(
br
,
"ControlService failed: %u
\n
"
,
GetLastError
());
CloseServiceHandle
(
svc2
);
}
static
void
test_start_stop
(
void
)
...
...
@@ -2409,7 +2482,7 @@ static void test_start_stop(void)
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
(
s
vc_handl
e
);
test_servicenotify
(
s
cm_handle
,
servicenam
e
);
cleanup:
if
(
svc_handle
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment