Help us understand the problem. What is going on with this article?

Go で Windows Service を作成する

lxn / win の PR をマージしたやつに、Windows Service 関連の API を追加したので、Golang で Windows のサービスプログラムの開発がまぁまぁ簡単にできるようになった。

後の人のために以下に載せておきます。

main.go
package main

import (
    "os"
    "syscall"
    "unsafe"

    "winut"
    "github.com/blono/win" // Fork of lxn/win see https://qiita.com/manymanyuni/items/c41f5bf0fd141e299336
)

func registerService() {
    scManager := win.OpenSCManager(nil, nil, win.SC_MANAGER_CREATE_SERVICE|win.SC_MANAGER_LOCK)
    if scManager == 0 {
        return
    }

    service := win.CreateService(scManager, "my-service", "my-service",
        win.SERVICE_ALL_ACCESS, win.SERVICE_WIN32_OWN_PROCESS, win.SERVICE_AUTO_START,
        win.SERVICE_ERROR_NORMAL, winut.Getwd()+"\\my-service.exe", nil, nil, nil, nil, nil)
    if service == 0 {
        return
    }
    defer win.CloseServiceHandle(service)

    desc := win.SERVICE_DESCRIPTION{
        LpDescription: winut.NewUTF16("サービスの説明"),
    }
    win.LockServiceDatabase(scManager)
    win.ChangeServiceConfig2(service, win.SERVICE_CONFIG_DESCRIPTION, uintptr(unsafe.Pointer(&desc)))
    win.UnlockServiceDatabase(scManager)
    win.StartService(service, 0, nil)
}

func unregisterService() {
    scManager := win.OpenSCManager(nil, nil, win.SC_MANAGER_CONNECT)
    if scManager == 0 {
        return
    }

    service := win.OpenService(scManager, "my-service", win.SERVICE_STOP|win.SERVICE_QUERY_STATUS|win.DELETE)
    if service == 0 {
        return
    }
    defer win.CloseServiceHandle(service)

    var ss win.SERVICE_STATUS

    win.QueryServiceStatus(service, &ss)
    if ss.DwCurrentState == win.SERVICE_RUNNING {
        win.ControlService(service, win.SERVICE_CONTROL_STOP, &ss)
    }

    win.DeleteService(service)
}

var serviceStatus win.SERVICE_STATUS
var serviceHandle win.HANDLE
var events []win.HANDLE

func serviceHandler(control, eventType uint32, eventData, context uintptr) uintptr {
    switch control {
    case win.SERVICE_CONTROL_STOP:
        serviceStatus.DwCurrentState = win.SERVICE_STOP_PENDING
        serviceStatus.DwCheckPoint = 0
        serviceStatus.DwWaitHint = 1000

        win.SetServiceStatus(serviceHandle, &serviceStatus)
        win.SetEvent(events[0])
    case win.SERVICE_CONTROL_INTERROGATE:
        win.SetServiceStatus(serviceHandle, &serviceStatus)
    }

    return uintptr(uint32(0))
}

func serviceMain(numServicesArgs uint32, serviceArgVectors *uint16) uintptr {
    events = make([]win.HANDLE, 1)
    events[0] = win.CreateEvent(nil, win.FALSE, win.FALSE, nil)
    defer win.CloseHandle(events[0])

    serviceHandle = win.RegisterServiceCtrlHandlerEx("my-service", syscall.NewCallback(serviceHandler), uintptr(unsafe.Pointer(nil)))

    serviceStatus.DwServiceType = win.SERVICE_WIN32_OWN_PROCESS
    serviceStatus.DwCurrentState = win.SERVICE_RUNNING
    serviceStatus.DwControlsAccepted = win.SERVICE_ACCEPT_STOP
    serviceStatus.DwWin32ExitCode = 0
    serviceStatus.DwServiceSpecificExitCode = 0
    serviceStatus.DwCheckPoint = 0
    serviceStatus.DwWaitHint = 0

    win.SetServiceStatus(serviceHandle, &serviceStatus)
    win.WaitForMultipleObjects(uint32(len(events)), &events[0], win.TRUE, win.INFINITE)

    serviceStatus.DwCurrentState = win.SERVICE_STOPPED
    serviceStatus.DwCheckPoint = 0
    serviceStatus.DwWaitHint = 0

    win.SetServiceStatus(serviceHandle, &serviceStatus)

    return uintptr(unsafe.Pointer(nil))
}

func main() {
    if len(os.Args) >= 2 {
        if os.Args[1] == "/register" || os.Args[1] == "-register" {
            registerService()
        } else if os.Args[1] == "/unregister" || os.Args[1] == "-unregister" {
            unregisterService()
        }
    } else {
        services := []win.SERVICE_TABLE_ENTRY{
            {
                LpServiceName: winut.NewUTF16("my-service"),
                LpServiceProc: syscall.NewCallback(serviceMain),
            },
            {
                LpServiceName: nil,
                LpServiceProc: uintptr(unsafe.Pointer(nil)),
            },
        }

        win.StartServiceCtrlDispatcher(&services[0])
    }
}
winut.go
// package, import は省略

func NewUTF16(s string) *uint16 {
    result, _ := syscall.UTF16PtrFromString(s)

    return result
}

func Getwd() string {
    dir, _ := os.Getwd()

    return dir
}

というか、普通にあったんですねこういうの。
https://godoc.org/golang.org/x/sys/windows/svc/example

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした