Windows服务简单一例,捕获关机信号做些处理

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: Windows服务简单一例,捕获关机信号做些处理

Windows服务


创建在 Windows 中的可长时间运行的可执行应用程序。


这些服务可以在计算机启动时自动启动,可以暂停和重新启动而且不显示任何用户界面。它非常适合在服务器上使用,或为了不影响在同一台电脑上工作的其他用户需要长时间运行功能时使用,或者是随开机就启动后台默默干活的应用。


通过运行界面,输入services.msc可打开windows自带的服务管理界面对服务进行控制。


一个服务不管有没有被运行,都在你的硬盘里,只有当它真正被运行时,操作系统就会真正给它分配内存、CPU时间片等资源,这一次运行就对应一个“进程”。


服务管理


可以使用SC命令对服务进行管理,SC 是用于与服务控制管理器和服务进行通信的命令行程序。


如下示例:


#创建服务
sc create myservice binpath= "c:\mywin32.exe" displayname= "myService"
sc delete myservice
sc start myservice
sc stop myservice


随便写一个hello.exe以这种方式加进去行不行?


结论是可以添加进去,在服务管理界面也能看到。但是没什么意义,因为无法启动也无法控制,它也不会自动运行。


服务需要符合一定的写法。


基本写法


需要的有:


服务入口函数:


void WINAPI ServiceMain(int argc, char* argv[])


服务控制函数(暂停、关机一些命令的控制)


ServiceCtrlHandler(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)


服务状态结构体


SERVICE_STATUS m_ServiceStatus;  


注册服务控制函数


RegisterServiceCtrlHandlerEx("myService",lpHandlerProc,myContext)


设置服务状态函数


SetServiceStatus(m_ServiceStatusHandle, &m_ServiceStatus)


服务进入表,设置服务程序的入口


SERVICE_TABLE_ENTRY DispatchTable[2]


服务控制分发


StartServiceCtrlDispatcher(DispatchTable)


基本上完成以上这些,就可以创建一个简单的windows服务了,熟悉下套路还是很简单的。编译完成后生成的.exe可执行文件不能直接运行,使用上面的sc命令来创建和启动服务。


还有一些稍复杂的点用法,可以完成应用自身对服务的创建,启动和停止等管理。


比如windows上的redis服务,它通过:


./redis-server.exe  --service-install redis.windows-service.conf 就可以把自己以服务的方式运行和启动。是它的源码内部实现了服务的创建,暂停,启动等方法。如:ServiceInstall,ServiceStart,ServiceStop等。参见redis源码中的Win32_service.cpp。


源码地址:https://github.com/tporadowski/redis


简单示例


#include <Windows.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <helper.h>
#include <logging.h>
#include <redisclient.h>
using namespace std;
const int SLEEP_TIME = 5000;  // 延迟时间
BOOL bFlag = TRUE;  // 标记循环是否结束
SERVICE_STATUS_HANDLE m_ServiceStatusHandle;  // 服务状态句柄
SERVICE_STATUS m_ServiceStatus; // 服务状态结构体,保存服务程序的一些信息
// 命令行参数可加可不加
void WINAPI ServiceMain(int argc, char* argv[]);// 服务程序入口函数
//void WINAPI ServiceCtrlHandler(DWORD Opcode); // 服务控制函数(暂停、关机一些命令的控制)
DWORD WINAPI ServiceCtrlHandler(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext);  // 服务控制函数
LPVOID myContext;
void WINAPI ServiceMain(int argc, char* argv[])
{
    MEMORYSTATUS memstatus; // 内存状态结构体,存储内存的一些信息
    char str[100];  // 存储一个字符串
    size_t availmb; // 将获取到的内存大小 B --> MB转换
    // 初始化服务状态
    m_ServiceStatus.dwServiceType = SERVICE_WIN32;  // Win32类型
    m_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; // 当前状态为开始等待
    m_ServiceStatus.dwControlsAccepted =  SERVICE_ACCEPT_PAUSE_CONTINUE|SERVICE_ACCEPT_STOP;  // 接受关机与暂停两种控制
    m_ServiceStatus.dwWin32ExitCode = 0;    // 下面四个默认都为0
    m_ServiceStatus.dwServiceSpecificExitCode = 0;
    m_ServiceStatus.dwCheckPoint = 0;
    m_ServiceStatus.dwWaitHint = 0;
    //重要
    m_ServiceStatus.dwControlsAccepted |= SERVICE_ACCEPT_PRESHUTDOWN;
    //注册服务控制
    //m_ServiceStatusHandle = RegisterServiceCtrlHandler("myService", ServiceCtrlHandler);
    m_ServiceStatusHandle = RegisterServiceCtrlHandlerEx("myService",ServiceCtrlHandler,myContext);
    if (m_ServiceStatusHandle == 0) // 判断是否成功执行
    {
        LOGGING_DEBUG("RegisterServiceCtrlHandle failed");  // 错误信息写入文件
        return;
    }
    LOGGING_DEBUG("RegisterServiceCtrlHandle success");   // 成功信息写入文件
    m_ServiceStatus.dwCurrentState = SERVICE_RUNNING; // 成功运行则把状态设置为运行状态
    SetServiceStatus(m_ServiceStatusHandle, &m_ServiceStatus); // 设置状态
    bFlag = TRUE;   // 循环条件为真
    memset(str, 0, 100);  // 初始化字符串中的数据
    while (bFlag)
    {
        //todo,fixeme,服务空闲状态干什么事,这里假做记录内存信息
        GlobalMemoryStatus(&memstatus);     // 获取内存状态信息
        availmb = memstatus.dwAvailPhys / 1024 / 1024;  // 将获取的字节转化为M
        sprintf_s(str, 100, "availmb memory is %d MB ", availmb); // 格式化字符串
        LOGGING_DEBUG(str); // 写入文件中
        Sleep(SLEEP_TIME);  // 延迟五秒
    }
    LOGGING_DEBUG("service stopped"); // 结束循环后,发送一消息给文件
}
void doYourWork(){
    //ostringstream out;
    //out << "RedisClient isConnect = " << ret;
    //LOGGING_DEBUG((char*)out.str().c_str());
}
 int WriteToLog(char* str)        //自定义的写日志函数
{
  FILE* pfile;
  fopen_s(&pfile,"C:/mylog.log","a+");      //已追加的方式打开文件;fopen_s函数的安全性更高;
  if (pfile==NULL)
  {
    return -1;
  }
  fprintf_s(pfile,"%s\n",str);      //向文件中写入字符串
  fclose(pfile);          //关闭打开的文件
  return 0;
}
//注册服务控制处理
DWORD ServiceCtrlHandler(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext){
    switch( dwControl ){
        //如果是关机事件,则...
        case SERVICE_CONTROL_PRESHUTDOWN:
            LOGGING_DEBUG("myService. SERVICE_CONTROL_PRESHUTDOWN");
            doYourWork();
            //ShutdownBlockReasonCreate(hwnd, (("waiting for user response")));
            break;
        case SERVICE_CONTROL_STOP:  // 暂停控制
            bFlag = FALSE;      // 循环标志为FASE
            m_ServiceStatus.dwCurrentState = SERVICE_STOPPED;// 状态设置为停止
            LOGGING_DEBUG("myService. SERVICE_CONTROL_STOP");
            break;
        case SERVICE_CONTROL_CONTINUE:  // 继续
            LOGGING_DEBUG("myService. SERVICE_CONTROL_CONTINUE");
            break;
        case SERVICE_CONTROL_SHUTDOWN:// 关机控制
            bFlag = FALSE;      // 循环标志为FASE
            m_ServiceStatus.dwCurrentState = SERVICE_STOPPED;// 状态设置为停止
            LOGGING_DEBUG("myService. SERVICE_CONTROL_SHUTDOWN");
            break;
        default:
            ostringstream oStr;
            oStr << "lpHandlerProc dwControl=" << dwControl;
            LOGGING_DEBUG((char*)oStr.str().c_str());
            break;
    }
    // 设置服务状态
    SetServiceStatus(m_ServiceStatusHandle, &m_ServiceStatus);
    return 0;
}
int main(){
    cout<<"hello myService start"<<endl;
    LOGGING_DEBUG(" myService start...");
    //为当前调用的进程设置关闭的参数,此函数为进程设置一个相对于系统中其它进程的关闭顺序
    int ret = SetProcessShutdownParameters(0x3FF,0);
    if(ret == 0){
        LOGGING_ERROR("SetProcessShutdownParameters error");
    }
    // 定义服务进入表,设置服务程序的入口函数
    SERVICE_TABLE_ENTRY DispatchTable[2];
    DispatchTable[0].lpServiceName = "myService"; // 服务程序名称
    DispatchTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain; // 服务程序入口
    DispatchTable[1].lpServiceName = NULL;
    DispatchTable[1].lpServiceProc = NULL;
    // 开始服务控制分布
    StartServiceCtrlDispatcher(DispatchTable);
    return 0;
}


引用


Windows 服务程序(一) - 走看看


正在关闭 - Win32 apps | Microsoft Docs


关于服务 - Win32 apps | Microsoft Docs

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
4月前
|
NoSQL Redis Windows
windows服务器重装系统之后,Redis服务如何恢复?
windows服务器重装系统之后,Redis服务如何恢复?
79 6
|
2月前
|
网络安全 Windows
Windows server 2012R2系统安装远程桌面服务后无法多用户同时登录是什么原因?
【11月更文挑战第15天】本文介绍了在Windows Server 2012 R2中遇到的多用户无法同时登录远程桌面的问题及其解决方法,包括许可模式限制、组策略配置问题、远程桌面服务配置错误以及网络和防火墙问题四个方面的原因分析及对应的解决方案。
|
3月前
|
边缘计算 安全 网络安全
|
3月前
|
开发框架 .NET API
Windows Forms应用程序中集成一个ASP.NET API服务
Windows Forms应用程序中集成一个ASP.NET API服务
111 9
|
3月前
|
应用服务中间件 Apache Windows
免安装版的Tomcat注册为windows服务
免安装版的Tomcat注册为windows服务
139 3
|
3月前
|
Java 关系型数据库 MySQL
java控制Windows进程,服务管理器项目
本文介绍了如何使用Java的`Runtime`和`Process`类来控制Windows进程,包括执行命令、读取进程输出和错误流以及等待进程完成,并提供了一个简单的服务管理器项目示例。
49 1
|
4月前
|
Java 应用服务中间件 Windows
windows服务器重装系统之后,Tomcat服务如何恢复?
windows服务器重装系统之后,Tomcat服务如何恢复?
69 10
|
4月前
|
消息中间件 Java Kafka
windows服务器重装系统之后,Kafka服务如何恢复?
windows服务器重装系统之后,Kafka服务如何恢复?
39 8
|
4月前
|
安全 Windows
怎样利用 Windows XP实现网络统一关机
怎样利用 Windows XP实现网络统一关机
怎样利用 Windows XP实现网络统一关机
|
5月前
|
API Docker Windows
2024 Ollama 一站式解决在Windows系统安装、使用、定制服务与实战案例
这篇文章是一份关于Ollama工具的一站式使用指南,涵盖了在Windows系统上安装、使用和定制服务,以及实战案例。
2024 Ollama 一站式解决在Windows系统安装、使用、定制服务与实战案例