windows: use RegisterServiceCtrlHandlerEx to allow for advanced notifications
This allows services written with this library to register for more
advanced notifications like SERVICE_CONTROL_DEVICEEVENT for example.
Also the code now exposes the service status handle through a new
api call, because the handle is needed to register for such
notifications and can not be obtained by any other means.
Change-Id: I80796e1dd9d94ec9d6c236d0413b17288c67fe1f
Reviewed-on: https://go-review.googlesource.com/42812
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Run-TryBot: Alex Brainman <alex.brainman@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/windows/svc/debug/service.go b/windows/svc/debug/service.go
index d5ab94b..123df98 100644
--- a/windows/svc/debug/service.go
+++ b/windows/svc/debug/service.go
@@ -31,7 +31,7 @@
for {
select {
case <-sig:
- cmds <- svc.ChangeRequest{svc.Stop, status}
+ cmds <- svc.ChangeRequest{svc.Stop, 0, 0, status}
case status = <-changes:
}
}
diff --git a/windows/svc/service.go b/windows/svc/service.go
index 9864f7a..1ea4a88 100644
--- a/windows/svc/service.go
+++ b/windows/svc/service.go
@@ -35,11 +35,20 @@
type Cmd uint32
const (
- Stop = Cmd(windows.SERVICE_CONTROL_STOP)
- Pause = Cmd(windows.SERVICE_CONTROL_PAUSE)
- Continue = Cmd(windows.SERVICE_CONTROL_CONTINUE)
- Interrogate = Cmd(windows.SERVICE_CONTROL_INTERROGATE)
- Shutdown = Cmd(windows.SERVICE_CONTROL_SHUTDOWN)
+ Stop = Cmd(windows.SERVICE_CONTROL_STOP)
+ Pause = Cmd(windows.SERVICE_CONTROL_PAUSE)
+ Continue = Cmd(windows.SERVICE_CONTROL_CONTINUE)
+ Interrogate = Cmd(windows.SERVICE_CONTROL_INTERROGATE)
+ Shutdown = Cmd(windows.SERVICE_CONTROL_SHUTDOWN)
+ ParamChange = Cmd(windows.SERVICE_CONTROL_PARAMCHANGE)
+ NetBindAdd = Cmd(windows.SERVICE_CONTROL_NETBINDADD)
+ NetBindRemove = Cmd(windows.SERVICE_CONTROL_NETBINDREMOVE)
+ NetBindEnable = Cmd(windows.SERVICE_CONTROL_NETBINDENABLE)
+ NetBindDisable = Cmd(windows.SERVICE_CONTROL_NETBINDDISABLE)
+ DeviceEvent = Cmd(windows.SERVICE_CONTROL_DEVICEEVENT)
+ HardwareProfileChange = Cmd(windows.SERVICE_CONTROL_HARDWAREPROFILECHANGE)
+ PowerEvent = Cmd(windows.SERVICE_CONTROL_POWEREVENT)
+ SessionChange = Cmd(windows.SERVICE_CONTROL_SESSIONCHANGE)
)
// Accepted is used to describe commands accepted by the service.
@@ -63,6 +72,8 @@
// ChangeRequest is sent to the service Handler to request service status change.
type ChangeRequest struct {
Cmd Cmd
+ EventType uint32
+ EventData uintptr
CurrentStatus Status
}
@@ -85,16 +96,16 @@
var (
// These are used by asm code.
- goWaitsH uintptr
- cWaitsH uintptr
- ssHandle uintptr
- sName *uint16
- sArgc uintptr
- sArgv **uint16
- ctlHandlerProc uintptr
- cSetEvent uintptr
- cWaitForSingleObject uintptr
- cRegisterServiceCtrlHandlerW uintptr
+ goWaitsH uintptr
+ cWaitsH uintptr
+ ssHandle uintptr
+ sName *uint16
+ sArgc uintptr
+ sArgv **uint16
+ ctlHandlerExProc uintptr
+ cSetEvent uintptr
+ cWaitForSingleObject uintptr
+ cRegisterServiceCtrlHandlerExW uintptr
)
func init() {
@@ -102,12 +113,16 @@
cSetEvent = k.MustFindProc("SetEvent").Addr()
cWaitForSingleObject = k.MustFindProc("WaitForSingleObject").Addr()
a := syscall.MustLoadDLL("advapi32.dll")
- cRegisterServiceCtrlHandlerW = a.MustFindProc("RegisterServiceCtrlHandlerW").Addr()
+ cRegisterServiceCtrlHandlerExW = a.MustFindProc("RegisterServiceCtrlHandlerExW").Addr()
}
+// The HandlerEx prototype also has a context pointer but since we don't use
+// it at start-up time we don't have to pass it over either.
type ctlEvent struct {
- cmd Cmd
- errno uint32
+ cmd Cmd
+ eventType uint32
+ eventData uintptr
+ errno uint32
}
// service provides access to windows service api.
@@ -208,6 +223,8 @@
var outch chan ChangeRequest
inch := s.c
var cmd Cmd
+ var evtype uint32
+ var evdata uintptr
loop:
for {
select {
@@ -219,7 +236,9 @@
inch = nil
outch = cmdsToHandler
cmd = r.cmd
- case outch <- ChangeRequest{cmd, status}:
+ evtype = r.eventType
+ evdata = r.eventData
+ case outch <- ChangeRequest{cmd, evtype, evdata, status}:
inch = s.c
outch = nil
case c := <-changesFromHandler:
@@ -276,8 +295,8 @@
return err
}
- ctlHandler := func(ctl uint32) uintptr {
- e := ctlEvent{cmd: Cmd(ctl)}
+ ctlHandler := func(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
+ e := ctlEvent{cmd: Cmd(ctl), eventType: evtype, eventData: evdata}
// We assume that this callback function is running on
// the same thread as Run. Nowhere in MS documentation
// I could find statement to guarantee that. So putting
@@ -288,6 +307,7 @@
e.errno = sysErrNewThreadInCallback
}
s.c <- e
+ // Always return NO_ERROR (0) for now.
return 0
}
@@ -301,7 +321,7 @@
goWaitsH = uintptr(s.goWaits.h)
cWaitsH = uintptr(s.cWaits.h)
sName = t[0].ServiceName
- ctlHandlerProc, err = newCallback(ctlHandler)
+ ctlHandlerExProc, err = newCallback(ctlHandler)
if err != nil {
return err
}
@@ -314,3 +334,10 @@
}
return nil
}
+
+// StatusHandle returns service status handle. It is safe to call this function
+// from inside the Handler.Execute because then it is guaranteed to be set.
+// This code will have to change once multiple services are possible per process.
+func StatusHandle() windows.Handle {
+ return windows.Handle(ssHandle)
+}
diff --git a/windows/svc/sys_386.s b/windows/svc/sys_386.s
index 5e11bfa..2c82a9d 100644
--- a/windows/svc/sys_386.s
+++ b/windows/svc/sys_386.s
@@ -22,7 +22,8 @@
MOVL AX, (SP)
MOVL $·servicectlhandler(SB), AX
MOVL AX, 4(SP)
- MOVL ·cRegisterServiceCtrlHandlerW(SB), AX
+ MOVL $0, 8(SP)
+ MOVL ·cRegisterServiceCtrlHandlerExW(SB), AX
MOVL SP, BP
CALL AX
MOVL BP, SP
@@ -61,7 +62,7 @@
// I do not know why, but this seems to be the only way to call
// ctlHandlerProc on Windows 7.
-// func servicectlhandler(ctl uint32) uintptr
+// func servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
TEXT ·servicectlhandler(SB),7,$0
- MOVL ·ctlHandlerProc(SB), CX
+ MOVL ·ctlHandlerExProc(SB), CX
JMP CX
diff --git a/windows/svc/sys_amd64.s b/windows/svc/sys_amd64.s
index 87dbec8..06b4259 100644
--- a/windows/svc/sys_amd64.s
+++ b/windows/svc/sys_amd64.s
@@ -13,7 +13,8 @@
MOVQ ·sName(SB), CX
MOVQ $·servicectlhandler(SB), DX
- MOVQ ·cRegisterServiceCtrlHandlerW(SB), AX
+ // BUG(pastarmovj): Figure out a way to pass in context in R8.
+ MOVQ ·cRegisterServiceCtrlHandlerExW(SB), AX
CALL AX
CMPQ AX, $0
JE exit
@@ -35,7 +36,7 @@
// I do not know why, but this seems to be the only way to call
// ctlHandlerProc on Windows 7.
-// func servicectlhandler(ctl uint32) uintptr
+// func ·servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
TEXT ·servicectlhandler(SB),7,$0
- MOVQ ·ctlHandlerProc(SB), AX
+ MOVQ ·ctlHandlerExProc(SB), AX
JMP AX