Windows 服务是主要用于服务器环境而长期运行的应用程序, 这类程序不需要有用户界面或者任何模拟输出。 任何的用户消息通常都是记录在Windows 事件日志里。Windows Service可以在操作系统启动的时候开始,一直在后台运行,当有需要时也可以手动启动,我们可以通过管理工具里面的服务进行统一管理。
当系统启动完毕后,Windows服务并不需要通过登陆页面后才能启动,即使用户注销登录也不会停止,通常不和用户产生交互。
而我们启动一般的exe文件却要先登陆Windows后才能启动它,通常还有一个用户界面,命令行或者是GUI界面,通常由用户手动启动和停止。
手工注册Windows服务得用得到windows下cmd命令(管理员身份)
注册服务 [ServiceTest]:
sc create ServiceTest binpath="/path/to/exe"
启动服务:
sc start ServiceTest
停止服务、删除服务:
sc stop ServiceTest
sc delete ServiceTest
首先要初始化 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
#include
#include
#include
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);
}
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)
}
}