Windows服务编程之服务程序

Windows服务程序基本结构

当SCM(服务管理器)启动服务进程时, 该进程必须立即调用StartServiceCtrlDispatcher函数,该函数接收一个服务入口点列表,每个入口点对应该进程中的一个服务,每个入口点由其所对应的服务名称来标识;StartServiceCtrlDispatcher创建了一个命名管道与SCM进行通信,当管道建立后,等待接收到SCM发送的服务启动命令后,创建一个服务线程来调用服务入口点函数,并且实现该服务的命令循环。StartServiceCtrlDispatcher会一直等待SCM传过来的命令,只有该进程对应的所有服务都停止时,才会返回到main函数所在线程,方便进程退出前做一些清理工作。 如果一些初始化工作不超过30秒,也可以在main函数所在线程执行,例如根据不同的命令行参数执行不同的任务,但每个服务自己的初始化工作最好放在自己的服务入口点函数。

每个服务的入口点函数的第一个动作就是调用RegisterServiceCtrlHandler或者RegisterServiceCtrlHandlerEx函数,该函数接收一个称为服务控制处理的函数指针,并将该函数指针保存起来,服务必须实现该函数以便处理来自SCM的各种命令,服务控制处理函数由StartServiceCtrlDispatcher函数来调用,也就是在主线程中调用的,StartServiceCtrlDispatcher函数继续初始化该服务,包括分配内存,从注册表中读入私有的配置数据。

在服务入口点函数初始化服务的过程中,他必须调用SetServiceStatus来定期的给SCM发送状态信息,以表明该服务的启动过程正在如何运行。先看一些该函数原型

BOOL WINAPI SetServiceStatus(
  _In_ SERVICE_STATUS_HANDLE hServiceStatus,
  _In_ LPSERVICE_STATUS      lpServiceStatus
);

第一个参数为RegisterServiceCtrlHandler或者RegisterServiceCtrlHandlerEx返回的服务状态句柄,第二个参数为服务状态结构指针,该结构如下:

typedef struct _SERVICE_STATUS {
  DWORD dwServiceType;
  DWORD dwCurrentState;
  DWORD dwControlsAccepted;
  DWORD dwWin32ExitCode;
  DWORD dwServiceSpecificExitCode;
  DWORD dwCheckPoint;
  DWORD dwWaitHint;
} SERVICE_STATUS, *LPSERVICE_STATUS;
  • dwServiceType参数表示服务类型,它有如下取值:
含义
SERVICE_FILE_SYSTEM_DRIVER Windows文件系统驱动程序,系统保留
SERVICE_KERNEL_DRIVER Windows设备驱动程序,系统保留
SERVICE_WIN32_OWN_PROCESS Windows服务运行于自己的进程,独占进程资源
SERVICE_WIN32_SHARE_PROCESS Windows服务与其他服务共享一个进程,共享进程资源,优点可以节省在单独进程中运行每个一个服务的系统资源,但缺点是一旦一个服务发生错误导致服务程序退出时,其他服务也会停止工作,因此现在也不提倡使用
SERVICE_INTERACTIVE_PROCESS 必须与SERVICE_WIN32_OWN_PROCESS或者SERVICE_WIN32_SHARE_PROCESS共同使用表示可以与桌面进行交互,已经不推荐,可以通过第三方程序实现桌面交互再与服务通信的方式来实现

- dwCurrentState表示当前的服务状态,它有如下取值:

含义
SERVICE_CONTINUE_PENDING 服务处于从暂停状态恢复的过程中
SERVICE_PAUSE_PENDING 服务正在暂停过程中,但还有没完全进入暂停状态
SERVICE_PAUSED 服务已经暂停
SERVICE_RUNNING 服务正在运行
SERVICE_START_PENDING 服务在启动过程中,但还没有准备好对请求进行响应
SERVICE_STOPPED 服务已经停止

- dwControlsAccepted指定服务会接收处理那些控制码,它有如下取值

含义
SERVICE_ACCEPT_NETBINDCHANGE 该服务是一个网络组件,并且能够在服务在服务不重启的情况下,改变其所网络接收的绑定,接收SERVICE_CONTROL_NETBINDADD、SERVICE_CONTROL_NETBINDREMOVE、SERVICE_CONTROL_NETBINDENABLE、SERVICE_CONTROL_NETBINDDISABLE 的通知
SERVICE_ACCEPT_PARAMCHANGE 服务在不重启的情况下能够重新读取其配置参数,接收SERVICE_CONTROL_PARAMCHANGE 通知
SERVICE_ACCEPT_PAUSE_CONTINUE 服务支持暂停和重启,服务能够接收到SERVICE_CONTROL_PAUSE 和SERVICE_CONTROL_CONTINUE的通知
SERVICE_ACCEPT_PRESHUTDOWN 系统在关闭前,能够收到系统的SERVICE_CONTROL_PRESHUTDOWN 通知,用来处理一些关闭前的清理,xp之前不支持此控制码
SERVICE_ACCEPT_SHUTDOWN 能够接收系统退出时的SERVICE_CONTROL_SHUTDOWN 的通知,以便处理一些回收
SERVICE_ACCEPT_STOP 能够接收SERVICE_CONTROL_STOP 的通知来处理一些回收任务

- dwWin32ExitCode逻辑服务正常退出时的退出吗,一般为0(NoError)
- dwServiceSpecificExitCode只有在dwWin32ExitCode设置为ERROR_SERVICE_SPECIFIC_ERROR才有效,可用于服务启动或停止时错误码
- dwCheckPoint,在服务启动,停止,暂停,继续,过程中不断自增来标识一个计数
- dwWaitHint,标识两次调用SetServiceStatus所消耗的时间,这两次调用要么是dwCheckPoint的递增值,要么是dwCurrentState的更改,这个时间超时了,服务管理器可以认为是一种服务发生了错误。

服务控制处理函数(在RegisterServiceCtrlHandler或者RegisterServiceCtrlHandlerEx注册的函数,),用来处理接收来自于SCM的通知,这里以RegisterServiceCtrlHandlerEx注册的函数为例,原型如下:

DWORD WINAPI HandlerEx(
  _In_ DWORD  dwControl,
  _In_ DWORD  dwEventType,
  _In_ LPVOID lpEventData,
  _In_ LPVOID lpContext
);

RegisterServiceCtrlHandler注册函数没有lpContext函数,其中dwControl表示接收到的来自于SCM的通知,除了一些常用通知,还能接收哪些通知与上面SERVICE_STATUS中的dwControlsAccepted有关。其中0-127由系统占用,用户还可以通过RegisterDeviceNotification 注册自己的控制码通知。

下面是一个windows服务程序的基本结构代码,基本满足大多数服务。

#include <windows.h>
#include <tchar.h>
#include <strsafe.h>

#define MyServiceName TEXT("MyService")

bool                    g_bPausedFlag = false;
HANDLE                  g_hSvcStopEvent = NULL;
SERVICE_STATUS          g_MyServiceStatus;
SERVICE_STATUS_HANDLE   g_hMyServiceStatusHandle;

void __stdcall MyServiceMain(DWORD  dwArgc, LPTSTR *lpszArgv);
DWORD __stdcall MyServiceHandlerEx(DWORD  dwControl, DWORD  dwEventType, LPVOID lpEventData, LPVOID lpContext);
void ReportServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);
DWORD MyServiceInitialization(DWORD  dwArgc, LPTSTR *lpszArgv);
DWORD MyServiceWorker(DWORD dwArgc, LPTSTR *lpszArgv);

int _tmain(int argc, TCHAR* argv[])
{
    SERVICE_TABLE_ENTRY dispatchTable[] = { 
        {MyServiceName, (LPSERVICE_MAIN_FUNCTION)MyServiceMain},
        {
  NULL, NULL} };

    if (!StartServiceCtrlDispatcher(dispatchTable))
    {
        // cleaning worker
    }

    return 0;
}

void __stdcall MyServiceMain(DWORD  dwArgc, LPTSTR *lpszArgv)
{
    g_hSvcStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    g_hMyServiceStatusHandle = RegisterServiceCtrlHandlerEx(MyServiceName, (LPHANDLER_FUNCTION_EX)MyServiceHandlerEx, NULL);
    if (g_hMyServiceStatusHandle == NULL)
        return;

    g_MyServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    g_MyServiceStatus.dwServiceSpecificExitCode = 0;
    ReportServiceStatus(SERVICE_START_PENDING, 0, 0);

    DWORD dwRet = MyServiceInitialization(dwArgc, lpszArgv);
    if (dwRet != NO_ERROR)
        ReportServiceStatus(SERVICE_STOPPED, dwRet, 0);
    ReportServiceStatus(SERVICE_RUNNING, 0, 0);

    MyServiceWorker(dwArgc, lpszArgv);

    while (true)
    {
        WaitForSingleObject(g_hSvcStopEvent, INFINITE);
        ReportServiceStatus(SERVICE_STOPPED, 0, 0);
        return;
    }
}

DWORD __stdcall MyServiceHandlerEx(DWORD  dwControl, DWORD  dwEventType,
  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值