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)
  }
 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章