windows/svc: rewrite in Go

The old service management code was written in assembly and communicated
over Windows events, which resulted in non-obvious control flow.
NewCallback makes it possible to rewrite all of this in vanilla Go. This
also enables the service test on the Go builders, as modifying system
services shouldn't be an issue there.

Change-Id: I8003b57d11d4469f762058c648a4b7733530eeb8
Reviewed-on: https://go-review.googlesource.com/c/sys/+/330010
Trust: Jason A. Donenfeld <Jason@zx2c4.com>
Trust: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Jason A. Donenfeld <Jason@zx2c4.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/windows/service.go b/windows/service.go
index b269850..1a22b3e 100644
--- a/windows/service.go
+++ b/windows/service.go
@@ -235,3 +235,4 @@
 //sys	NotifyServiceStatusChange(service Handle, notifyMask uint32, notifier *SERVICE_NOTIFY) (ret error) = advapi32.NotifyServiceStatusChangeW
 //sys	SubscribeServiceChangeNotifications(service Handle, eventType uint32, callback uintptr, callbackCtx uintptr, subscription *uintptr) (ret error) = sechost.SubscribeServiceChangeNotifications?
 //sys	UnsubscribeServiceChangeNotifications(subscription uintptr) = sechost.UnsubscribeServiceChangeNotifications?
+//sys	RegisterServiceCtrlHandlerEx(serviceName *uint16, handlerProc uintptr, context uintptr) (handle Handle, err error) = advapi32.RegisterServiceCtrlHandlerExW
diff --git a/windows/svc/event.go b/windows/svc/event.go
deleted file mode 100644
index 0508e22..0000000
--- a/windows/svc/event.go
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build windows
-
-package svc
-
-import (
-	"errors"
-
-	"golang.org/x/sys/windows"
-)
-
-// event represents auto-reset, initially non-signaled Windows event.
-// It is used to communicate between go and asm parts of this package.
-type event struct {
-	h windows.Handle
-}
-
-func newEvent() (*event, error) {
-	h, err := windows.CreateEvent(nil, 0, 0, nil)
-	if err != nil {
-		return nil, err
-	}
-	return &event{h: h}, nil
-}
-
-func (e *event) Close() error {
-	return windows.CloseHandle(e.h)
-}
-
-func (e *event) Set() error {
-	return windows.SetEvent(e.h)
-}
-
-func (e *event) Wait() error {
-	s, err := windows.WaitForSingleObject(e.h, windows.INFINITE)
-	switch s {
-	case windows.WAIT_OBJECT_0:
-		break
-	case windows.WAIT_FAILED:
-		return err
-	default:
-		return errors.New("unexpected result from WaitForSingleObject")
-	}
-	return nil
-}
diff --git a/windows/svc/go12.c b/windows/svc/go12.c
deleted file mode 100644
index 6f1be1f..0000000
--- a/windows/svc/go12.c
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build windows
-// +build !go1.3
-
-// copied from pkg/runtime
-typedef	unsigned int	uint32;
-typedef	unsigned long long int	uint64;
-#ifdef _64BIT
-typedef	uint64		uintptr;
-#else
-typedef	uint32		uintptr;
-#endif
-
-// from sys_386.s or sys_amd64.s
-void ·servicemain(void);
-
-void
-·getServiceMain(uintptr *r)
-{
-	*r = (uintptr)·servicemain;
-}
diff --git a/windows/svc/go12.go b/windows/svc/go12.go
deleted file mode 100644
index cd8b913..0000000
--- a/windows/svc/go12.go
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build windows
-// +build !go1.3
-
-package svc
-
-// from go12.c
-func getServiceMain(r *uintptr)
diff --git a/windows/svc/go13.go b/windows/svc/go13.go
deleted file mode 100644
index 9d7f3ce..0000000
--- a/windows/svc/go13.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build windows
-// +build go1.3
-
-package svc
-
-import "unsafe"
-
-const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const
-
-// Should be a built-in for unsafe.Pointer?
-func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
-	return unsafe.Pointer(uintptr(p) + x)
-}
-
-// funcPC returns the entry PC of the function f.
-// It assumes that f is a func value. Otherwise the behavior is undefined.
-func funcPC(f interface{}) uintptr {
-	return **(**uintptr)(add(unsafe.Pointer(&f), ptrSize))
-}
-
-// from sys_386.s and sys_amd64.s
-func servicectlhandler(ctl uint32) uintptr
-func servicemain(argc uint32, argv **uint16)
-
-func getServiceMain(r *uintptr) {
-	*r = funcPC(servicemain)
-}
diff --git a/windows/svc/service.go b/windows/svc/service.go
index 3748528..46c73a5 100644
--- a/windows/svc/service.go
+++ b/windows/svc/service.go
@@ -10,8 +10,7 @@
 
 import (
 	"errors"
-	"runtime"
-	"syscall"
+	"sync"
 	"unsafe"
 
 	"golang.org/x/sys/internal/unsafeheader"
@@ -91,7 +90,6 @@
 
 // Handler is the interface that must be implemented to build Windows service.
 type Handler interface {
-
 	// Execute will be called by the package code at the start of
 	// the service, and the service will exit once Execute completes.
 	// Inside Execute you must read service change requests from r and
@@ -106,28 +104,6 @@
 	Execute(args []string, r <-chan ChangeRequest, s chan<- Status) (svcSpecificEC bool, exitCode uint32)
 }
 
-var (
-	// These are used by asm code.
-	goWaitsH                       uintptr
-	cWaitsH                        uintptr
-	ssHandle                       uintptr
-	sName                          *uint16
-	sArgc                          uintptr
-	sArgv                          **uint16
-	ctlHandlerExProc               uintptr
-	cSetEvent                      uintptr
-	cWaitForSingleObject           uintptr
-	cRegisterServiceCtrlHandlerExW uintptr
-)
-
-func init() {
-	k := windows.NewLazySystemDLL("kernel32.dll")
-	cSetEvent = k.NewProc("SetEvent").Addr()
-	cWaitForSingleObject = k.NewProc("WaitForSingleObject").Addr()
-	a := windows.NewLazySystemDLL("advapi32.dll")
-	cRegisterServiceCtrlHandlerExW = a.NewProc("RegisterServiceCtrlHandlerExW").Addr()
-}
-
 type ctlEvent struct {
 	cmd       Cmd
 	eventType uint32
@@ -140,36 +116,10 @@
 type service struct {
 	name    string
 	h       windows.Handle
-	cWaits  *event
-	goWaits *event
 	c       chan ctlEvent
 	handler Handler
 }
 
-func newService(name string, handler Handler) (*service, error) {
-	var s service
-	var err error
-	s.name = name
-	s.c = make(chan ctlEvent)
-	s.handler = handler
-	s.cWaits, err = newEvent()
-	if err != nil {
-		return nil, err
-	}
-	s.goWaits, err = newEvent()
-	if err != nil {
-		s.cWaits.Close()
-		return nil, err
-	}
-	return &s, nil
-}
-
-func (s *service) close() error {
-	s.cWaits.Close()
-	s.goWaits.Close()
-	return nil
-}
-
 type exitCode struct {
 	isSvcSpecific bool
 	errno         uint32
@@ -224,23 +174,43 @@
 	return windows.SetServiceStatus(s.h, &t)
 }
 
-const (
-	sysErrSetServiceStatusFailed = uint32(syscall.APPLICATION_ERROR) + iota
-	sysErrNewThreadInCallback
+var (
+	initCallbacks       sync.Once
+	ctlHandlerCallback  uintptr
+	serviceMainCallback uintptr
 )
 
-func (s *service) run() {
-	s.goWaits.Wait()
-	s.h = windows.Handle(ssHandle)
+func ctlHandler(ctl, evtype, evdata, context uintptr) uintptr {
+	s := (*service)(unsafe.Pointer(context))
+	e := ctlEvent{cmd: Cmd(ctl), eventType: uint32(evtype), eventData: evdata, context: 123456} // Set context to 123456 to test issue #25660.
+	s.c <- e
+	return 0
+}
 
-	var argv []*uint16
-	hdr := (*unsafeheader.Slice)(unsafe.Pointer(&argv))
-	hdr.Data = unsafe.Pointer(sArgv)
-	hdr.Len = int(sArgc)
-	hdr.Cap = int(sArgc)
+var theService service // This is, unfortunately, a global, which means only one service per process.
 
-	args := make([]string, len(argv))
-	for i, a := range argv {
+// serviceMain is the entry point called by the service manager, registered earlier by
+// the call to StartServiceCtrlDispatcher.
+func serviceMain(argc uint32, argv **uint16) uintptr {
+	handle, err := windows.RegisterServiceCtrlHandlerEx(windows.StringToUTF16Ptr(theService.name), ctlHandlerCallback, uintptr(unsafe.Pointer(&theService)))
+	if sysErr, ok := err.(windows.Errno); ok {
+		return uintptr(sysErr)
+	} else if err != nil {
+		return uintptr(windows.ERROR_UNKNOWN_EXCEPTION)
+	}
+	theService.h = handle
+	defer func() {
+		theService.h = 0
+		windows.CloseHandle(handle)
+	}()
+	var args16 []*uint16
+	hdr := (*unsafeheader.Slice)(unsafe.Pointer(&args16))
+	hdr.Data = unsafe.Pointer(argv)
+	hdr.Len = int(argc)
+	hdr.Cap = int(argc)
+
+	args := make([]string, len(args16))
+	for i, a := range args16 {
 		args[i] = windows.UTF16PtrToString(a)
 	}
 
@@ -249,7 +219,7 @@
 	exitFromHandler := make(chan exitCode)
 
 	go func() {
-		ss, errno := s.handler.Execute(args, cmdsToHandler, changesFromHandler)
+		ss, errno := theService.handler.Execute(args, cmdsToHandler, changesFromHandler)
 		exitFromHandler <- exitCode{ss, errno}
 	}()
 
@@ -258,7 +228,7 @@
 		CurrentStatus: Status{State: Stopped},
 	}
 	var outch chan ChangeRequest
-	inch := s.c
+	inch := theService.c
 loop:
 	for {
 		select {
@@ -274,14 +244,13 @@
 			outcr.EventData = r.eventData
 			outcr.Context = r.context
 		case outch <- outcr:
-			inch = s.c
+			inch = theService.c
 			outch = nil
 		case c := <-changesFromHandler:
-			err := s.updateStatus(&c, &ec)
+			err := theService.updateStatus(&c, &ec)
 			if err != nil {
-				// best suitable error number
-				ec.errno = sysErrSetServiceStatusFailed
-				if err2, ok := err.(syscall.Errno); ok {
+				ec.errno = uint32(windows.ERROR_EXCEPTION_IN_SERVICE)
+				if err2, ok := err.(windows.Errno); ok {
 					ec.errno = uint32(err2)
 				}
 				break loop
@@ -292,87 +261,30 @@
 		}
 	}
 
-	s.updateStatus(&Status{State: Stopped}, &ec)
-	s.cWaits.Set()
-}
+	theService.updateStatus(&Status{State: Stopped}, &ec)
 
-func newCallback(fn interface{}) (cb uintptr, err error) {
-	defer func() {
-		r := recover()
-		if r == nil {
-			return
-		}
-		cb = 0
-		switch v := r.(type) {
-		case string:
-			err = errors.New(v)
-		case error:
-			err = v
-		default:
-			err = errors.New("unexpected panic in syscall.NewCallback")
-		}
-	}()
-	return syscall.NewCallback(fn), nil
+	return windows.NO_ERROR
 }
 
-// BUG(brainman): There is no mechanism to run multiple services
-// inside one single executable. Perhaps, it can be overcome by
-// using RegisterServiceCtrlHandlerEx Windows api.
-
 // Run executes service name by calling appropriate handler function.
 func Run(name string, handler Handler) error {
-	runtime.LockOSThread()
-
-	tid := windows.GetCurrentThreadId()
-
-	s, err := newService(name, handler)
-	if err != nil {
-		return err
-	}
-
-	ctlHandler := func(ctl, evtype, evdata, context uintptr) uintptr {
-		e := ctlEvent{cmd: Cmd(ctl), eventType: uint32(evtype), eventData: evdata, context: context}
-		// 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
-		// check here to verify, otherwise things will go bad
-		// quickly, if ignored.
-		i := windows.GetCurrentThreadId()
-		if i != tid {
-			e.errno = sysErrNewThreadInCallback
-		}
-		s.c <- e
-		// Always return NO_ERROR (0) for now.
-		return windows.NO_ERROR
-	}
-
-	var svcmain uintptr
-	getServiceMain(&svcmain)
+	initCallbacks.Do(func() {
+		ctlHandlerCallback = windows.NewCallback(ctlHandler)
+		serviceMainCallback = windows.NewCallback(serviceMain)
+	})
+	theService.name = name
+	theService.handler = handler
+	theService.c = make(chan ctlEvent)
 	t := []windows.SERVICE_TABLE_ENTRY{
-		{ServiceName: syscall.StringToUTF16Ptr(s.name), ServiceProc: svcmain},
+		{ServiceName: windows.StringToUTF16Ptr(theService.name), ServiceProc: serviceMainCallback},
 		{ServiceName: nil, ServiceProc: 0},
 	}
-
-	goWaitsH = uintptr(s.goWaits.h)
-	cWaitsH = uintptr(s.cWaits.h)
-	sName = t[0].ServiceName
-	ctlHandlerExProc, err = newCallback(ctlHandler)
-	if err != nil {
-		return err
-	}
-
-	go s.run()
-
-	err = windows.StartServiceCtrlDispatcher(&t[0])
-	if err != nil {
-		return err
-	}
-	return nil
+	return windows.StartServiceCtrlDispatcher(&t[0])
 }
 
 // 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)
+	return theService.h
 }
diff --git a/windows/svc/svc_test.go b/windows/svc/svc_test.go
index e8684dd..f7833ad 100644
--- a/windows/svc/svc_test.go
+++ b/windows/svc/svc_test.go
@@ -77,7 +77,7 @@
 }
 
 func TestExample(t *testing.T) {
-	if testing.Short() {
+	if testing.Short() && os.Getenv("GO_BUILDER_NAME") != "" {
 		t.Skip("skipping test in short mode - it modifies system services")
 	}
 
diff --git a/windows/svc/sys_windows_386.s b/windows/svc/sys_windows_386.s
deleted file mode 100644
index 1ed9141..0000000
--- a/windows/svc/sys_windows_386.s
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// func servicemain(argc uint32, argv **uint16)
-TEXT ·servicemain(SB),7,$0
-	MOVL	argc+0(FP), AX
-	MOVL	AX, ·sArgc(SB)
-	MOVL	argv+4(FP), AX
-	MOVL	AX, ·sArgv(SB)
-
-	PUSHL	BP
-	PUSHL	BX
-	PUSHL	SI
-	PUSHL	DI
-
-	SUBL	$12, SP
-
-	MOVL	·sName(SB), AX
-	MOVL	AX, (SP)
-	MOVL	$·servicectlhandler(SB), AX
-	MOVL	AX, 4(SP)
-	// Set context to 123456 to test issue #25660.
-	MOVL	$123456, 8(SP)
-	MOVL	·cRegisterServiceCtrlHandlerExW(SB), AX
-	MOVL	SP, BP
-	CALL	AX
-	MOVL	BP, SP
-	CMPL	AX, $0
-	JE	exit
-	MOVL	AX, ·ssHandle(SB)
-
-	MOVL	·goWaitsH(SB), AX
-	MOVL	AX, (SP)
-	MOVL	·cSetEvent(SB), AX
-	MOVL	SP, BP
-	CALL	AX
-	MOVL	BP, SP
-
-	MOVL	·cWaitsH(SB), AX
-	MOVL	AX, (SP)
-	MOVL	$-1, AX
-	MOVL	AX, 4(SP)
-	MOVL	·cWaitForSingleObject(SB), AX
-	MOVL	SP, BP
-	CALL	AX
-	MOVL	BP, SP
-
-exit:
-	ADDL	$12, SP
-
-	POPL	DI
-	POPL	SI
-	POPL	BX
-	POPL	BP
-
-	MOVL	0(SP), CX
-	ADDL	$12, SP
-	JMP	CX
-
-// I do not know why, but this seems to be the only way to call
-// ctlHandlerProc on Windows 7.
-
-// func servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
-TEXT ·servicectlhandler(SB),7,$0
-	MOVL	·ctlHandlerExProc(SB), CX
-	JMP	CX
diff --git a/windows/svc/sys_windows_amd64.s b/windows/svc/sys_windows_amd64.s
deleted file mode 100644
index 1e5ef92..0000000
--- a/windows/svc/sys_windows_amd64.s
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// func servicemain(argc uint32, argv **uint16)
-TEXT ·servicemain(SB),7,$0
-	MOVQ	SP, AX
-	ANDQ	$~15, SP	// alignment as per Windows requirement
-	SUBQ	$48, SP		// room for SP and 4 args as per Windows requirement
-				// plus one extra word to keep stack 16 bytes aligned
-	MOVQ	AX, 32(SP)
-
-	MOVL	CX, ·sArgc(SB)
-	MOVQ	DX, ·sArgv(SB)
-
-	MOVQ	·sName(SB), CX
-	MOVQ	$·servicectlhandler(SB), DX
-	// BUG(pastarmovj): Figure out a way to pass in context in R8.
-	// Set context to 123456 to test issue #25660.
-	MOVQ	$123456, R8
-	MOVQ	·cRegisterServiceCtrlHandlerExW(SB), AX
-	CALL	AX
-	CMPQ	AX, $0
-	JE	exit
-	MOVQ	AX, ·ssHandle(SB)
-
-	MOVQ	·goWaitsH(SB), CX
-	MOVQ	·cSetEvent(SB), AX
-	CALL	AX
-
-	MOVQ	·cWaitsH(SB), CX
-	MOVQ	$4294967295, DX
-	MOVQ	·cWaitForSingleObject(SB), AX
-	CALL	AX
-
-exit:
-	MOVQ	32(SP), SP
-	RET
-
-// I do not know why, but this seems to be the only way to call
-// ctlHandlerProc on Windows 7.
-
-// func ·servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr {
-TEXT ·servicectlhandler(SB),7,$0
-	MOVQ	·ctlHandlerExProc(SB), AX
-	JMP	AX
diff --git a/windows/svc/sys_windows_arm.s b/windows/svc/sys_windows_arm.s
deleted file mode 100644
index 360b86e..0000000
--- a/windows/svc/sys_windows_arm.s
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#include "textflag.h"
-
-// func servicemain(argc uint32, argv **uint16)
-TEXT ·servicemain(SB),NOSPLIT|NOFRAME,$0
-	MOVM.DB.W [R4, R14], (R13)	// push {r4, lr}
-	MOVW	R13, R4
-	BIC	$0x7, R13		// alignment for ABI
-
-	MOVW	R0, ·sArgc(SB)
-	MOVW	R1, ·sArgv(SB)
-
-	MOVW	·sName(SB), R0
-	MOVW	·ctlHandlerExProc(SB), R1
-	MOVW	$0, R2
-	MOVW	·cRegisterServiceCtrlHandlerExW(SB), R3
-	BL	(R3)
-	CMP	$0, R0
-	BEQ	exit
-	MOVW	R0, ·ssHandle(SB)
-
-	MOVW	·goWaitsH(SB), R0
-	MOVW	·cSetEvent(SB), R1
-	BL	(R1)
-
-	MOVW	·cWaitsH(SB), R0
-	MOVW	$-1, R1
-	MOVW	·cWaitForSingleObject(SB), R2
-	BL	(R2)
-
-exit:
-	MOVW	R4, R13			// free extra stack space
-	MOVM.IA.W (R13), [R4, R15]	// pop {r4, pc}
diff --git a/windows/svc/sys_windows_arm64.s b/windows/svc/sys_windows_arm64.s
deleted file mode 100644
index 3ca540e..0000000
--- a/windows/svc/sys_windows_arm64.s
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#include "textflag.h"
-
-// func servicemain(argc uint32, argv **uint16)
-TEXT ·servicemain(SB),NOSPLIT|NOFRAME,$0
-	MOVD	R0, ·sArgc(SB)
-	MOVD	R1, ·sArgv(SB)
-
-	MOVD	·sName(SB), R0
-	MOVD	·ctlHandlerExProc(SB), R1
-	MOVD	$0, R2
-	MOVD	·cRegisterServiceCtrlHandlerExW(SB), R3
-	BL	(R3)
-	CMP	$0, R0
-	BEQ	exit
-	MOVD	R0, ·ssHandle(SB)
-
-	MOVD	·goWaitsH(SB), R0
-	MOVD	·cSetEvent(SB), R1
-	BL	(R1)
-
-	MOVD	·cWaitsH(SB), R0
-	MOVD	$-1, R1
-	MOVD	·cWaitForSingleObject(SB), R2
-	BL	(R2)
-
-exit:
-	RET
diff --git a/windows/zsyscall_windows.go b/windows/zsyscall_windows.go
index 4ea788e..1636013 100644
--- a/windows/zsyscall_windows.go
+++ b/windows/zsyscall_windows.go
@@ -124,6 +124,7 @@
 	procRegQueryInfoKeyW                                     = modadvapi32.NewProc("RegQueryInfoKeyW")
 	procRegQueryValueExW                                     = modadvapi32.NewProc("RegQueryValueExW")
 	procRegisterEventSourceW                                 = modadvapi32.NewProc("RegisterEventSourceW")
+	procRegisterServiceCtrlHandlerExW                        = modadvapi32.NewProc("RegisterServiceCtrlHandlerExW")
 	procReportEventW                                         = modadvapi32.NewProc("ReportEventW")
 	procRevertToSelf                                         = modadvapi32.NewProc("RevertToSelf")
 	procSetEntriesInAclW                                     = modadvapi32.NewProc("SetEntriesInAclW")
@@ -1055,6 +1056,15 @@
 	return
 }
 
+func RegisterServiceCtrlHandlerEx(serviceName *uint16, handlerProc uintptr, context uintptr) (handle Handle, err error) {
+	r0, _, e1 := syscall.Syscall(procRegisterServiceCtrlHandlerExW.Addr(), 3, uintptr(unsafe.Pointer(serviceName)), uintptr(handlerProc), uintptr(context))
+	handle = Handle(r0)
+	if handle == 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
 func ReportEvent(log Handle, etype uint16, category uint16, eventId uint32, usrSId uintptr, numStrings uint16, dataSize uint32, strings **uint16, rawData *byte) (err error) {
 	r1, _, e1 := syscall.Syscall9(procReportEventW.Addr(), 9, uintptr(log), uintptr(etype), uintptr(category), uintptr(eventId), uintptr(usrSId), uintptr(numStrings), uintptr(dataSize), uintptr(unsafe.Pointer(strings)), uintptr(unsafe.Pointer(rawData)))
 	if r1 == 0 {