Windows服务程序编写

Windows服务程序编写

作者: yym439 时间: 2021-08-24

一、 Windows 服务程序

Windows 服务被设计用于需要在后台运行的应用程序以及实现没有用户交互的任务,编写服务程序需要5个步骤:

1. 主函数,创建分派表并启动控制分派机

//包含头文件
#include < windows.h >
#include < stdio.h >

//服务名称
#define SERVICE_NAME "MemoryStatus"
#define SLEEP_TIME 5000
#define LOGFILE "C:\\MyServices\\memstatus.txt"

//全局变量定义
SERVICE_STATUS        g_ServiceStatus = {0};
SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
void ServiceMain(int argc, char** argv);
void ControlHandler(DWORD request);
int InitService();

//写入文件
int WriteToLog(char* str)
{
    FILE* log;
    log = fopen(LOGFILE, "a+");
    if (log == NULL)
    return -1;
    fprintf(log, "%s\n", str);
    fclose(log);
    return 0;
}

int main()
{
    //创建“分派表”
    SERVICE_TABLE_ENTRY serviceTable[] = { 
        { SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
        { NULL, NULL}
    };

    // 启动“控制分派机”线程,运行ServiceMain函数
    if(StartServiceCtrlDispatcher(ServiceTable) == FALSE)
    {
        return -1;
    }
    return 0;
}

  • SERVICE_TABLE_ENTRY分派表结构:
    • lpServiceName:指向表示服务名称字符串的指针;当定义了多个服务时,那么这个域必须指定(一个程序可能包含若干个服务);
    • lpServiceProc: 指向服务主函数的指针(服务入口点);
  • 服务控制管理器(SCM):是一个管理系统所有服务的进程。当 SCM 启动某个服务时,它等待某个进程的主线程来调用 StartServiceCtrlDispatcher 函数。将分派表传递给 StartServiceCtrlDispatcher。这将把调用进程的主线程转换为控制分派器。该分派器启动一个新线程,该线程运行分派表中每个服务的 ServiceMain 函数(本文例子中只有一个服务)分派机还监视程序中所有服务的执行情况。然后分派机将控制请求从 SCM 传给服务。
  • 如果 StartServiceCtrlDispatcher 函数30秒没有被调用,便会报错,为了避免这种情况,我们必须在 ServiceMain 函数中(参见本文例子)或在非主函数的单独线程中初始化服务分派表。
  • 分派表中所有的服务执行完之后(例如,用户通过“服务”控制面板程序停止它们),或者发生错误时。StartServiceCtrlDispatcher 调用返回。然后主进程终止。

2. ServiceMain 函数

  • 服务入口点,它运行在一个单独的线程当中,这个线程是由控制分派器创建的
VOID WINAPI ServiceMain (DWORD argc, LPTSTR *argv)
{
    DWORD Status = E_FAIL;
    OutputDebugString(_T("My Sample Service: ServiceMain: Entry"));



    //1.注册控制处理器,它指示控制分派器调用 ServiceCtrlHandler 函数处理 SCM 控制请求
    g_StatusHandle = RegisterServiceCtrlHandler (SERVICE_NAME, ServiceCtrlHandler);

    if (g_StatusHandle == NULL) 
    {
        OutputDebugString(_T("My Sample Service: ServiceMain: RegisterServiceCtrlHandler returned error"));
        goto EXIT;
    }

    // 2.初始化服务特征和当前状态
    ZeroMemory (&g_ServiceStatus, sizeof (g_ServiceStatus));
    g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; //服务类型,创建 Win32 服务。赋值 SERVICE_WIN32
    g_ServiceStatus.dwControlsAccepted = 0;     //这个域通知 SCM 服务接受哪个域
    g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;     //服务的当前状态
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwServiceSpecificExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 0; //初始化某个服务进程时要30秒以上。本文例子服务的初始化过程很短,所以值为 0。
    //3. 向 SCM 报告服务的状态
    if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE) 
    {
        OutputDebugString(_T("My Sample Service: ServiceMain: SetServiceStatus returned error"));
    }

    /* 
     * Perform tasks neccesary to start the service here
     */
    OutputDebugString(_T("My Sample Service: ServiceMain: Performing Service Start Operations"));

    // Create stop event to wait on later.
    g_ServiceStopEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
    if (g_ServiceStopEvent == NULL) 
    {
        OutputDebugString(_T("My Sample Service: ServiceMain: CreateEvent(g_ServiceStopEvent) returned error"));

        g_ServiceStatus.dwControlsAccepted = 0;
        g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        g_ServiceStatus.dwWin32ExitCode = GetLastError();
        g_ServiceStatus.dwCheckPoint = 1;

        if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
	    {
		    OutputDebugString(_T("My Sample Service: ServiceMain: SetServiceStatus returned error"));
	    }
        goto EXIT; 
    }    

    // Tell the service controller we are started
    g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 0;

    if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
    {
	    OutputDebugString(_T("My Sample Service: ServiceMain: SetServiceStatus returned error"));
    }

    // 3.启动将执行服务的主要任务的线程
    HANDLE hThread = CreateThread (NULL, 0, ServiceWorkerThread, NULL, 0, NULL);

    OutputDebugString(_T("My Sample Service: ServiceMain: Waiting for Worker Thread to complete"));

    // Wait until our worker thread exits effectively signaling that the service needs to stop
    WaitForSingleObject (hThread, INFINITE);
    
    OutputDebugString(_T("My Sample Service: ServiceMain: Worker Thread Stop Event signaled"));
    
    
    /* 
     * Perform any cleanup tasks
     */
    OutputDebugString(_T("My Sample Service: ServiceMain: Performing Cleanup Operations"));

    CloseHandle (g_ServiceStopEvent);

    g_ServiceStatus.dwControlsAccepted = 0;
    g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 3;

    if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
    {
	    OutputDebugString(_T("My Sample Service: ServiceMain: SetServiceStatus returned error"));
    }
    
    EXIT:
    OutputDebugString(_T("My Sample Service: ServiceMain: Exit"));

    return;
}

3. ServiceCtrlHandler处理控制请求

VOID WINAPI ServiceCtrlHandler (DWORD CtrlCode)
{
    OutputDebugString(_T("My Sample Service: ServiceCtrlHandler: Entry"));

    switch (CtrlCode) 
	{
     case SERVICE_CONTROL_STOP :

        OutputDebugString(_T("My Sample Service: ServiceCtrlHandler: SERVICE_CONTROL_STOP Request"));

        if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
           break;

        /* 
         * Perform tasks neccesary to stop the service here 
         */
        
        g_ServiceStatus.dwControlsAccepted = 0;
        g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
        g_ServiceStatus.dwWin32ExitCode = 0;
        g_ServiceStatus.dwCheckPoint = 4;

        if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
		{
			OutputDebugString(_T("My Sample Service: ServiceCtrlHandler: SetServiceStatus returned error"));
		}

        // This will signal the worker thread to start shutting down
        SetEvent (g_ServiceStopEvent);

        break;

     default:
         break;
    }

    OutputDebugString(_T("My Sample Service: ServiceCtrlHandler: Exit"));
}

4. 安装和配置服务

//创建服务命令,注意 binpath= 和路径之间的那个空格
sc create MemoryStatus binpath= c:\MyServices\MemoryStatus.exe

//启动服务
sc start myService

//移除服务
sc delete MemoryStatus

5. 测试服务

任务管理器-服务-启动-停止

博客

github博客