|
|
@@ -35,11 +35,20 @@ const (
|
|
|
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 @@ type Status struct {
|
|
|
// 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 @@ type Handler interface {
|
|
|
|
|
|
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 @@ func init() {
|
|
|
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 @@ func (s *service) run() {
|
|
|
var outch chan ChangeRequest
|
|
|
inch := s.c
|
|
|
var cmd Cmd
|
|
|
+ var evtype uint32
|
|
|
+ var evdata uintptr
|
|
|
loop:
|
|
|
for {
|
|
|
select {
|
|
|
@@ -219,7 +236,9 @@ loop:
|
|
|
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 @@ func Run(name string, handler Handler) error {
|
|
|
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 @@ func Run(name string, handler Handler) error {
|
|
|
e.errno = sysErrNewThreadInCallback
|
|
|
}
|
|
|
s.c <- e
|
|
|
+ // Always return NO_ERROR (0) for now.
|
|
|
return 0
|
|
|
}
|
|
|
|
|
|
@@ -301,7 +321,7 @@ func Run(name string, handler Handler) error {
|
|
|
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 @@ func Run(name string, handler Handler) error {
|
|
|
}
|
|
|
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)
|
|
|
+}
|