Maxbad`Blog

golang 创建windows服务

2022-09-16 · 4 min read
goland
package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/kardianos/service"
)

var logger service.Logger

type program struct {
	isExit chan struct{}
}

func (p *program) runApp() {
	ticker := time.NewTicker(time.Second * 3)
	for {
		select {
		case <-ticker.C:
			logger.Info("服务运行中......")
		case <-p.isExit:
			logger.Info("服务即将停止!")
			ticker.Stop()
			return
		}
	}
}

func (p *program) Start(s service.Service) error {
	if service.Interactive() {
		logger.Info("程序开始!")
	} else {
		logger.Info("服务开始!")
	}
	p.isExit = make(chan struct{})
	go p.runApp()
	return nil
}

func (p *program) Stop(s service.Service) error {
	logger.Info("服务停止!")
	close(p.isExit)
	return nil
}

func init() {
	file, _ := exec.LookPath(os.Args[0])
	path, _ := filepath.Abs(file)
	index := strings.LastIndex(path, string(os.PathSeparator))
	runPath := path[:index]
	os.Chdir(runPath)
}

func main() {
	options := make(service.KeyValue)
	options["OnFailure"] = "restart" // 失败后重启服务
	options["OnFailureDelayDuration"] = "3s"

	svcConfig := &service.Config{
		Name:        "go_service_demo",
		DisplayName: "go service demo",
		Description: "go windows服务演示",
		Arguments:   []string{"service"},
		Option:      options,
	}

	prg := &program{}
	s, err := service.New(prg, svcConfig)
	if err != nil {
		fmt.Printf("service new failed! %s\r\n", err.Error())
		return
	}

	errs := make(chan error, 5)
	logger, err = s.Logger(errs)
	if err != nil {
		fmt.Printf("service Logger failed! %s\r\n", err.Error())
		return
	}

	if len(os.Args) > 1 && os.Args[1] == "service" {
		s.Run()
	} else {
		inputNum := 0
		isFirst := true
		for {
			if !isFirst {
				fmt.Println("请按回车键继续...")
				var str string
				fmt.Scanln(&str)
				cmd := exec.Command("cmd.exe", "/c", "cls")
				cmd.Stdout = os.Stdout
				cmd.Run()
			}
			isFirst = false

			serviceStatus, _ := s.Status()
			if serviceStatus == service.StatusUnknown {
				inputNum = showMenu(svcConfig, "未安装", "安装服务", "直接运行(非服务)", "退出程序")
				switch inputNum {
				case 1:
					if err = s.Install(); err == nil {
						fmt.Println("安装服务成功!")
					} else {
						fmt.Println("安装服务失败!", err)
					}
				case 2:
					s.Run()
					return
				case 3:
					return
				}

			} else if serviceStatus == service.StatusRunning {
				inputNum = showMenu(svcConfig, "正在运行", "停止服务", "退出程序")
				switch inputNum {
				case 1:
					if err = s.Stop(); err == nil {
						fmt.Println("停止服务成功!")
					} else {
						fmt.Println("停止服务失败!", err)
					}
				case 2:
					return
				}
			} else if serviceStatus == service.StatusStopped {
				inputNum = showMenu(svcConfig, "未启动", "启动服务", "删除服务", "退出程序")
				switch inputNum {
				case 1:
					if err = s.Start(); err == nil {
						fmt.Println("启动服务成功!")
					} else {
						fmt.Println("启动服务失败!", err)
					}
				case 2:
					if err = s.Uninstall(); err == nil {
						fmt.Println("删除服务成功!")
					} else {
						fmt.Println("删除服务失败!", err)
					}
				case 3:
					return
				}
			} else {
				return
			}

		}
	}

}

func showMenu(srvName *service.Config, srvStatus string, tips ...string) (inputNum int) {

	fmt.Printf("=======================================\r\n\r\n")
	fmt.Printf("\t服务名称:%s\r\n", srvName.Name)
	fmt.Printf("\t服务介绍:%s\r\n", srvName.Description)
	fmt.Printf("\t服务状态:%s\r\n", srvStatus)
	fmt.Println("")
	index := 1
	for _, v := range tips {
		fmt.Printf("\t%d.%s\r\n", index, v)
		index++
	}
	fmt.Printf("\r\n=======================================\r\n")
	fmt.Print("请输入:")
	fmt.Scanln(&inputNum)
	return
}