Golang 实现Windows服务

什么是windows服务

Windows 服务是主要用于服务器环境而长期运行的应用程序, 这类程序不需要有用户界面或者任何模拟输出。 任何的用户消息通常都是记录在Windows 事件日志里。Windows Service可以在操作系统启动的时候开始,一直在后台运行,当有需要时也可以手动启动,我们可以通过管理工具里面的服务进行统一管理。
当系统启动完毕后,Windows服务并不需要通过登陆页面后才能启动,即使用户注销登录也不会停止,通常不和用户产生交互。
而我们启动一般的exe文件却要先登陆Windows后才能启动它,通常还有一个用户界面,命令行或者是GUI界面,通常由用户手动启动和停止。

如何注册windows服务

手工注册Windows服务得用得到windows下cmd命令(管理员身份)
注册服务 [ServiceTest]:

sc create ServiceTest binpath="/path/to/exe"

启动服务:

sc start ServiceTest

停止服务、删除服务:

sc stop ServiceTest
sc delete ServiceTest

用vc++实现windows服务:

首先要初始化 SERVICE_TABLE_ENTRY 结构体数组,

SERVICE_TABLE_ENTRY serviceEntryTable[2];
serviceEntryTable[0].lpServiceName = L"ServiceTest";
serviceEntryTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceWorker;
serviceEntryTable[1].lpServiceName = NULL;
serviceEntryTable[1].lpServiceProc = NULL;

其中,ServiceTest 是要注册的服务的名称,ServiceWorker 是服务主工作函数。
通过调用 StartServiceCtrlDispatcher(serviceEntryTable),把调用进程的主线程转换为控制分派器,启动一个新线程运行分派表中的 ServiceWorker 函数。

然后,准备ServiceWorker函数,
ServiceWorker 服务程序的主运行函数,除了与普通函数执行任务之外,
它还需要完成一个工作:通过调用 RegisterServiceCtrlHandler向服务控制管理器注册控制函数。
并且调用 向 SCM(服务控制管理器)报告当前的状态。

SERVICE_STATUS serviceStatus;
serviceStatus.dwServiceType = SERVICE_WIN32;
serviceStatus.dwCurrentState = SERVICE_START_PENDING;
serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
serviceStatus.dwWin32ExitCode = 0;
serviceStatus.dwServiceSpecificExitCode = 0;
serviceStatus.dwCheckPoint = 0;
serviceStatus.dwWaitHint = 0;
serviceStatusHandle = ::RegisterServiceCtrlHandler(L"ServiceTest", ServiceCtrlHandler);

其中 ServiceCtrlHandler是控制处理函数,它接收 SCM 发来的控制命令,并且处理命令和回馈状态。

最后,准备控制处理函数 ServiceCtrlHandler
控制处理函数必须在30秒内返回,否则 SCM 会返回一个错,如果是一个比较耗时的操作,则需要另启一个线程来进行处理。

void WINAPI ServiceCtrlHandler(DWORD request)
{
 switch (request)
 {
 case SERVICE_CONTROL_STOP:
  serviceRunning = false;
  serviceStatus.dwCurrentState = SERVICE_STOPPED;
  break;
 case SERVICE_CONTROL_SHUTDOWN:
  serviceRunning = false;
  serviceStatus.dwCurrentState = SERVICE_STOPPED;
  break;
 default:
  break;
 }
 SetServiceStatus(serviceStatusHandle, &serviceStatus);
}

完整示例代码
该示例实现了一个简单的windows服务程序,
每秒钟在C盘目录下打印一个数字。

#include <string>
#include <fstream>
#include <iostream>
#include <windows.h>

using namespace std;

bool serviceRunning = false;
SERVICE_STATUS serviceStatus;
SERVICE_STATUS_HANDLE serviceStatusHandle;

void WINAPI ServiceWorker(int argc, char** argv);
void WINAPI ServiceCtrlHandler(DWORD request);


void ServiceLog(string str)
{
 fstream fout("c:/service_log.txt", ios::out | ios::app);
 if (!fout) {
  return;
 }
 fout << str << endl;
}

void WINAPI ServiceWorker(int argc, char** argv)
{
 serviceStatus.dwServiceType = SERVICE_WIN32;
 serviceStatus.dwCurrentState = SERVICE_START_PENDING;
 serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
 serviceStatus.dwWin32ExitCode = 0;
 serviceStatus.dwServiceSpecificExitCode = 0;
 serviceStatus.dwCheckPoint = 0;
 serviceStatus.dwWaitHint = 0;

 serviceStatusHandle = ::RegisterServiceCtrlHandler(L"ServiceTest", ServiceCtrlHandler);

 if (0 == serviceStatusHandle)
 {
  ServiceLog("RegisterServiceCtrlHandler failed");
  return;
 }

 ServiceLog("RegisterServiceCtrlHandler success");

 serviceStatus.dwCurrentState = SERVICE_RUNNING;
 SetServiceStatus(serviceStatusHandle, &serviceStatus);

 // 工作内容,每隔一秒钟输出一个数字
 int num = 0;
 serviceRunning = true;
 while (serviceRunning)
 {
  ServiceLog(to_string(num++));
  Sleep(1000);
 }
 ServiceLog("Service Stopped");
}

void WINAPI ServiceCtrlHandler(DWORD request)
{
 switch (request)
 {
 case SERVICE_CONTROL_STOP:
  serviceRunning = false;
  serviceStatus.dwCurrentState = SERVICE_STOPPED;
  break;

 case SERVICE_CONTROL_SHUTDOWN:
  serviceRunning = false;
  serviceStatus.dwCurrentState = SERVICE_STOPPED;
  break;

 default:
  break;
 }

 SetServiceStatus(serviceStatusHandle, &serviceStatus);
}

void main()
{
 SERVICE_TABLE_ENTRY serviceEntryTable[2];
 serviceEntryTable[0].lpServiceName = L"ServiceTest";
 serviceEntryTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceWorker;
 serviceEntryTable[1].lpServiceName = NULL;
 serviceEntryTable[1].lpServiceProc = NULL;

 StartServiceCtrlDispatcher(serviceEntryTable);
}

Golang 实现 Windows 服务

Go语言有第三方封装好的库,可以用很少的代码实现一个Windows服务,相比C++方便了很多。

 package main
	
 import (
  "log"
  "os"
  "time"
  "github.com/kardianos/service"
 )
	
 type program struct{}
	
 func (p *program) Start(s service.Service) error {
  go p.run()
  return nil
 }
	
 func (p *program) run() {
  for {
   time.Sleep(time.Second)
   log.Println("running")
  }
 }
	
 func (p *program) Stop(s service.Service) error {
  return nil
 }
	
 func init() {
  f, err := os.Create("d:/gowinservice.txt")
  if err != nil {
   log.Fatal(err)
  }
  log.SetOutput(f)
 }
	
 func main() {
  svcConfig := &service.Config{
   Name: "GoService",
   DisplayName: "GoServiceDis",
   Description: "windows service form golang",
  }
	
  prg := &program{}
  s, err := service.New(prg, svcConfig)
  if err != nil {
   log.Fatal(err)
  }
	
  if len(os.Args) > 1 {
   if os.Args[1] == "install" {
    s.Install()
    log.Println("服务安装成功")
    return
   }
	
   if os.Args[1] == "remove" {
    s.Uninstall()
    log.Println("服务卸载成功")
    return
   }
  }
	
  if err = s.Run(); err != nil {
   log.Fatal(err)
  }
 }
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
可以使用Golang的标准库中的`os`和`filepath`包来实现Windows文件监控。具体步骤如下: 1. 使用`os.Stat()`函数获取待监控文件的信息,包括文件名、路径、大小、修改时间等。 2. 使用`os.Open()`函数打开待监控文件。 3. 使用`os.File`对象的`Stat()`函数获取文件的当前信息,包括大小和修改时间。 4. 使用`os.File`对象的`Seek()`函数将文件指针移到文件末尾。 5. 使用`os.File`对象的`Read()`函数读取文件末尾的数据,如果读到数据,则表示文件有更新。 6. 如果文件有更新,则可以执行相应的操作,如打印文件更新信息等。 下面是一个简单的示例代码: ``` package main import ( "fmt" "os" "path/filepath" "time" ) func main() { filename := "test.txt" path, err := filepath.Abs(filename) if err != nil { fmt.Println(err) return } fi, err := os.Stat(path) if err != nil { fmt.Println(err) return } for { time.Sleep(time.Second) // 每秒检查一次文件更新 currFi, err := os.Stat(path) if err != nil { fmt.Println(err) continue } if currFi.Size() != fi.Size() || currFi.ModTime() != fi.ModTime() { fi = currFi fmt.Printf("File %s has been updated!\n", filename) // 可以在这里执行相应的操作 } } } ``` 在上面的示例中,程序会每秒检查一次文件是否有更新,如果文件有更新,则打印相应的信息。在实际应用中,可以根据具体需求执行相应的操作,如发送邮件、上传文件等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ango_Cango

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值