Windows服务编程之服务程序

本文详细介绍了Windows服务程序的基本结构,包括StartServiceCtrlDispatcher的作用、服务入口点函数的实现、SetServiceStatus的状态更新,以及服务控制处理函数。此外,还探讨了Windows服务的配置方法,如通过SCM接口进行服务配置,并提供了一段基本的服务程序代码框架。

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_DRIVERWindows文件系统驱动程序,系统保留
SERVICE_KERNEL_DRIVERWindows设备驱动程序,系统保留
SERVICE_WIN32_OWN_PROCESSWindows服务运行于自己的进程,独占进程资源
SERVICE_WIN32_SHARE_PROCESSWindows服务与其他服务共享一个进程,共享进程资源,优点可以节省在单独进程中运行每个一个服务的系统资源,但缺点是一旦一个服务发生错误导致服务程序退出时,其他服务也会停止工作,因此现在也不提倡使用
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, LPVOID lpEventData, LPVOID lpContext)
{
    switch (dwControl)
    {
    case SERVICE_CONTROL_SHUTDOWN:
    case SERVICE_CONTROL_STOP:
        ReportServiceStatus(SERVICE_STOP_PENDING, 0, 0);
        SetEvent(g_hSvcStopEvent);
        break;
    case SERVICE_CONTROL_PAUSE:
        ReportServiceStatus(SERVICE_PAUSE_PENDING, 0, 0);
        g_bPausedFlag = true;
        break;
    case SERVICE_CONTROL_CONTINUE:
        ReportServiceStatus(SERVICE_CONTINUE_PENDING, 0, 0);
        g_bPausedFlag = false;
        break;
    case SERVICE_CONTROL_INTERROGATE:
        break;
    default:
        break;
    }
    return 0;
}

// 执行一些初始化过程
DWORD MyServiceInitialization(DWORD  dwArgc, LPTSTR *lpszArgv)
{
    return NO_ERROR;
}

// 服务工作执行函数
DWORD MyServiceWorker(DWORD dwArgc, LPTSTR *lpszArgv)
{
    return 0;
}

// 向服务控制管理器报告状态信息
void ReportServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
{
    static DWORD dwCheckPoint = 1;

    g_MyServiceStatus.dwCurrentState = dwCurrentState;
    g_MyServiceStatus.dwWin32ExitCode = dwWin32ExitCode;
    g_MyServiceStatus.dwWaitHint = dwWaitHint;

    if (dwCurrentState == SERVICE_START_PENDING)
        g_MyServiceStatus.dwControlsAccepted = 0;
    else 
        g_MyServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;

    if ((dwCurrentState == SERVICE_RUNNING) ||
        (dwCurrentState == SERVICE_STOPPED))
        g_MyServiceStatus.dwCheckPoint = 0;
    else 
        g_MyServiceStatus.dwCheckPoint = dwCheckPoint++;

    SetServiceStatus(g_hMyServiceStatusHandle, &g_MyServiceStatus);
}

只需要将上述程序中的MyService替换成自己的服务名称,在MyServiceInitialization函数中完成服务启动时需要的一些初始化工作,如果没有也可以不写此函数,在MyServiceWorker函数中实现自己的服务的主要工作程序。

windows服务配置程序

要修改服务的配置有两种方法,一种就是直接修改注册表,此种方法不提倡,通过SCM管理器提供的接口来配置Windows服务(本质是由SCM来修改注册表),Services.msc与命令行工具sc,都是系统提供的服务配置管理程序。因为SCM是基于RPC的程序,因此也可以配置远程电脑的服务。

Windows服务管理器提供了如下常用的接口函数供我们调用,来配置服务程序:

函数主要功能
ChangeServiceConfig修改服务的配置参数
ChangeServiceConfig2修改一些可选的服务配置参数,例如失败后操作
ControlService发送控制码到服务
ControlServiceEx发送控制码到服务
CreateService创建一个windows服务对象,并将其添加的服务管理器中
DeleteService从服务管理器中删除一个服务
EnumDependentServices枚举依赖于某个服务的所有服务
EnumServicesStatusEx枚举某个服务管理器中的所有服务的状态信息
GetServiceDisplayName通过服务名称,获取显示名称
GetServiceKeyName通过服务的显示名称,获取服务名称
OpenSCManager打开服务管理器,获取服务管理器句柄
OpenService打开一个服务,获取该服务句柄
QueryServiceConfig获取服务的配置参数
QueryServiceConfig2获取可选的服务配置参数
QueryServiceStatusEx获取当前服务状态信息
StartService启动一个服务

这些函数具体使用方式和参数请查询MSDN,这里不再详述。

接下来我们实现一个自己的服务配置管理工具,直接上代码:

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#include <Shlwapi.h>
#include <winsvc.h>

#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "Advapi32.lib")

// 显示提供的所有命令
void DisplayUsage(int argc, TCHAR* argv[]);
void IntallService(int argc, TCHAR* argv[]);
void StartService(int argc, TCHAR* argv[]);
void StopService(int argc, TCHAR* argv[]);
BOOL StopDependentServices(SC_HANDLE schSCManager, SC_HANDLE schService);
void RemoveService(int argc, TCHAR* argv[]);


int _tmain(int argc, TCHAR* argv[])
{
    if (argc < 2)
    {
        DisplayUsage(argc, argv);
        return 0;
    }

    TCHAR szCommand[32] = { 0 };
    StringCchCopy(szCommand, 32, argv[1]);

    if (lstrcmpi(szCommand, _T("create")))
    {
        IntallService(argc, argv);
    }
    else if (lstrcmpi(szCommand, _T("start")))
    {
        StartService(argc, argv);
    }
    else if (lstrcmpi(szCommand, _T("stop")))
    {
        StopService(argc, argv);
    }
    else if (lstrcmpi(szCommand, _T("delete")))
    {
        RemoveService(argc, argv);
    }

    return 0;
}

void DisplayUsage(int argc, TCHAR* argv[])
{

}

void IntallService(int argc, TCHAR* argv[])
{
    if (argc != 4)
    {
        _tprintf(_T("sc create [service name] [binPath= ]"));
        return;
    }

    TCHAR szServiceName[256] = { 0 };
    TCHAR szBinPath[MAX_PATH] = { 0 };
    StringCchCopy(szServiceName, 256, argv[3]);
    StringCchCopy(szBinPath, MAX_PATH, argv[4]);

    if (!PathFileExists(szBinPath))
    {
        _tprintf(_T("文件路径不存在"));
        return;
    }

    SC_HANDLE schSCManager;
    SC_HANDLE schService;

    schSCManager = OpenSCManager(
        NULL,                    // 如果是本机传入NULL,如果是远程写远端机器名
        NULL, SC_MANAGER_ALL_ACCESS);  

    if (NULL == schSCManager)
    {
        printf("OpenSCManager failed (%d)\n", GetLastError());
        return;
    }

    schService = CreateService(
        schSCManager,              // SCM句柄 
        szServiceName,             // 服务名称 
        szServiceName,             // 显示名称 
        SERVICE_ALL_ACCESS,        // 服务权限
        SERVICE_WIN32_OWN_PROCESS, // 服务类型
        SERVICE_AUTO_START,        // 服务启动类型
        SERVICE_ERROR_NORMAL,      // 服务错误控制类型
        szBinPath,                 // 服务exe所在路径
        NULL,                      // 服务所需要加载的组 
        NULL,                      // tag标识符
        NULL,                      // 依赖的服务
        NULL,                      // 服务账户,NULL表示为localSystem
        NULL);                     // 账户密码

    if (schService == NULL)
    {
        _tprintf(_T("CreateService failed (%d)\n"), GetLastError());
        CloseServiceHandle(schSCManager);
        return;
    }
    else
    {
        printf("Service installed successfully\n");
    }

    CloseServiceHandle(schService);
    CloseServiceHandle(schSCManager);
}

void StartService(int argc, TCHAR* argv[])
{
    if (argc != 3)
    {
        _tprintf(_T(" sc start [service name]"));
        return;
    }

    TCHAR szServiceName[256] = { 0 };
    StringCchCopy(szServiceName, 256, argv[3]);

    SC_HANDLE schSCManager;
    SC_HANDLE schService;
    schSCManager = OpenSCManager(NULL, NULL,  SC_MANAGER_ALL_ACCESS);
    if (NULL == schSCManager)
    {
        _tprintf(_T("OpenSCManager failed (%d)\n"), GetLastError());
        return;
    }

    schService = OpenService(
        schSCManager,        
        szServiceName,            
        SERVICE_ALL_ACCESS);  

    if (schService == NULL)
    {
        _tprintf(_T("OpenService failed (%d)\n"), GetLastError());
        CloseServiceHandle(schSCManager);
        return;
    }

    SERVICE_STATUS_PROCESS ssStatus;
    DWORD dwBytesNeeded;
    // 查看服务是否已经启动
    if (!QueryServiceStatusEx(
        schService,                     // 服务句柄 
        SC_STATUS_PROCESS_INFO,         // 要获取的信息级别,不同的级别返回的信息详细程度不同
        (LPBYTE)&ssStatus,              // 服务进程状态结构体
        sizeof(SERVICE_STATUS_PROCESS), // 服务进程状态结构体大小
        &dwBytesNeeded))                // 还需要的额外地址空间
    {
        _tprintf(_T("QueryServiceStatusEx failed (%d)\n"), GetLastError());
        CloseServiceHandle(schService);
        CloseServiceHandle(schSCManager);
        return;
    }

    // 如果服务已经启动,直接返回
    if (ssStatus.dwCurrentState != SERVICE_STOPPED && 
        ssStatus.dwCurrentState != SERVICE_STOP_PENDING)
    {
        _tprintf(_T("Cannot start the service because it is already running\n"));
        CloseServiceHandle(schService);
        CloseServiceHandle(schSCManager);
        return;
    }

    DWORD dwOldCheckPoint;
    DWORD dwStartTickCount;
    DWORD dwWaitTime;
    // 保存原始的CheckPoint
    dwStartTickCount = GetTickCount();
    dwOldCheckPoint = ssStatus.dwCheckPoint;

    // 如果服务正在关闭,则等待关闭后再启动
    while (ssStatus.dwCurrentState == SERVICE_STOP_PENDING)
    {
        dwWaitTime = ssStatus.dwWaitHint / 10;
        if (dwWaitTime < 1000)
            dwWaitTime = 1000;
        else if (dwWaitTime > 10000)
            dwWaitTime = 10000;

        Sleep(dwWaitTime);

        // 检查服务是否关闭
        if (!QueryServiceStatusEx(
            schService,                     
            SC_STATUS_PROCESS_INFO,        
            (LPBYTE)&ssStatus,             
            sizeof(SERVICE_STATUS_PROCESS), 
            &dwBytesNeeded))             
        {
            _tprintf(_T("QueryServiceStatusEx failed (%d)\n"), GetLastError());
            CloseServiceHandle(schService);
            CloseServiceHandle(schSCManager);
            return;
        }

        if (ssStatus.dwCheckPoint > dwOldCheckPoint)
        {
            dwStartTickCount = GetTickCount();
            dwOldCheckPoint = ssStatus.dwCheckPoint;
        }
        else
        {
            if (GetTickCount() - dwStartTickCount > ssStatus.dwWaitHint)
            {
                _tprintf(_T("Timeout waiting for service to stop\n"));
                CloseServiceHandle(schService);
                CloseServiceHandle(schSCManager);
                return;
            }
        }
    }

    // 尝试启动服务
    if (!StartService(
        schService,  // 服务句柄
        0,           // 参数个数 
        NULL))       // 参数
    {
        _tprintf(_T("StartService failed (%d)\n"), GetLastError());
        CloseServiceHandle(schService);
        CloseServiceHandle(schSCManager);
        return;
    }
    else
    {
        _tprintf(_T("Service start pending...\n"));
    }

    // 检查服务状态直到到服务启动为止 
    if (!QueryServiceStatusEx(
        schService,                    
        SC_STATUS_PROCESS_INFO,         
        (LPBYTE)&ssStatus,             
        sizeof(SERVICE_STATUS_PROCESS), 
        &dwBytesNeeded))              
    {
        _tprintf(_T("QueryServiceStatusEx failed (%d)\n"), GetLastError());
        CloseServiceHandle(schService);
        CloseServiceHandle(schSCManager);
        return;
    }

    dwStartTickCount = GetTickCount();
    dwOldCheckPoint = ssStatus.dwCheckPoint;
    while (ssStatus.dwCurrentState == SERVICE_START_PENDING)
    {
        dwWaitTime = ssStatus.dwWaitHint / 10;
        if (dwWaitTime < 1000)
            dwWaitTime = 1000;
        else if (dwWaitTime > 10000)
            dwWaitTime = 10000;

        Sleep(dwWaitTime);

        if (!QueryServiceStatusEx(
            schService,             
            SC_STATUS_PROCESS_INFO, 
            (LPBYTE)&ssStatus,             
            sizeof(SERVICE_STATUS_PROCESS), 
            &dwBytesNeeded))              
        {
            _tprintf(_T("QueryServiceStatusEx failed (%d)\n"), GetLastError());
            break;
        }

        if (ssStatus.dwCheckPoint > dwOldCheckPoint)
        {
            dwStartTickCount = GetTickCount();
            dwOldCheckPoint = ssStatus.dwCheckPoint;
        }
        else
        {
            if (GetTickCount() - dwStartTickCount > ssStatus.dwWaitHint)
            {
                break;
            }
        }
    }

    // 检查服务是否启动
    if (ssStatus.dwCurrentState == SERVICE_RUNNING)
    {
        _tprintf(_T("Service started successfully.\n"));
    }
    else
    {
        _tprintf(_T("Service not started. \n"));
        _tprintf(_T("  Current State: %d\n"), ssStatus.dwCurrentState);
        _tprintf(_T("  Exit Code: %d\n"), ssStatus.dwWin32ExitCode);
        _tprintf(_T("  Check Point: %d\n"), ssStatus.dwCheckPoint);
        _tprintf(_T("  Wait Hint: %d\n"), ssStatus.dwWaitHint);
    }

    CloseServiceHandle(schService);
    CloseServiceHandle(schSCManager);
}

void StopService(int argc, TCHAR* argv[])
{
    if (argc != 3)
    {
        _tprintf(_T(" sc stop [service name]"));
        return;
    }

    TCHAR szServiceName[256] = { 0 };
    StringCchCopy(szServiceName, 256, argv[3]);

    SC_HANDLE schSCManager;
    SC_HANDLE schService;
    schSCManager = OpenSCManager(
        NULL,                    
        NULL,                    
        SC_MANAGER_ALL_ACCESS);  

    if (NULL == schSCManager)
    {
        printf("OpenSCManager failed (%d)\n", GetLastError());
        return;
    }

    schService = OpenService(
        schSCManager,         
        szServiceName,            
        SERVICE_STOP |
        SERVICE_QUERY_STATUS |
        SERVICE_ENUMERATE_DEPENDENTS);

    if (schService == NULL)
    {
        _tprintf(_T("OpenService failed (%d)\n"), GetLastError());
        CloseServiceHandle(schSCManager);
        return;
    }

    SERVICE_STATUS_PROCESS ssp;
    DWORD dwBytesNeeded;
    // 检查服务是否已经在关闭状态
    if (!QueryServiceStatusEx(
        schService,
        SC_STATUS_PROCESS_INFO,
        (LPBYTE)&ssp,
        sizeof(SERVICE_STATUS_PROCESS),
        &dwBytesNeeded))
    {
        printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
        goto stop_cleanup;
    }

    if (ssp.dwCurrentState == SERVICE_STOPPED)
    {
        _tprintf(_T("Service is already stopped.\n"));
        goto stop_cleanup;
    }

    DWORD dwStartTime = GetTickCount();
    DWORD dwTimeout = 30000;
    DWORD dwWaitTime;
    // 如果服务正在关闭,等待其完全关闭
    while (ssp.dwCurrentState == SERVICE_STOP_PENDING)
    {
        _tprintf(_T("Service stop pending...\n"));
        dwWaitTime = ssp.dwWaitHint / 10;

        if (dwWaitTime < 1000)
            dwWaitTime = 1000;
        else if (dwWaitTime > 10000)
            dwWaitTime = 10000;

        Sleep(dwWaitTime);

        if (!QueryServiceStatusEx(
            schService,
            SC_STATUS_PROCESS_INFO,
            (LPBYTE)&ssp,
            sizeof(SERVICE_STATUS_PROCESS),
            &dwBytesNeeded))
        {
            _tprintf(_T("QueryServiceStatusEx failed (%d)\n"), GetLastError());
            goto stop_cleanup;
        }

        if (ssp.dwCurrentState == SERVICE_STOPPED)
        {
            _tprintf(_T("Service stopped successfully.\n"));
            goto stop_cleanup;
        }

        if (GetTickCount() - dwStartTime > dwTimeout)
        {
            _tprintf(_T("Service stop timed out.\n"));
            goto stop_cleanup;
        }
    }

    // 如果服务正在运行过程中,需要先关闭依赖该服务的服务
    StopDependentServices(schSCManager, schService);

    // 发送一个服务退出通知给服务程序
    if (!ControlService(
        schService,
        SERVICE_CONTROL_STOP,
        (LPSERVICE_STATUS)&ssp))
    {
        _tprintf(_T("ControlService failed (%d)\n"), GetLastError());
        goto stop_cleanup;
    }

    // 等待服务关闭
    while (ssp.dwCurrentState != SERVICE_STOPPED)
    {
        Sleep(ssp.dwWaitHint);
        if (!QueryServiceStatusEx(
            schService,
            SC_STATUS_PROCESS_INFO,
            (LPBYTE)&ssp,
            sizeof(SERVICE_STATUS_PROCESS),
            &dwBytesNeeded))
        {
            _tprintf(_T("QueryServiceStatusEx failed (%d)\n"), GetLastError());
            goto stop_cleanup;
        }

        if (ssp.dwCurrentState == SERVICE_STOPPED)
            break;

        if (GetTickCount() - dwStartTime > dwTimeout)
        {
            _tprintf(_T("Wait timed out\n"));
            goto stop_cleanup;
        }
    }
    _tprintf(_T("Service stopped successfully\n"));

stop_cleanup:
    CloseServiceHandle(schService);
    CloseServiceHandle(schSCManager);
}

BOOL StopDependentServices(SC_HANDLE schSCManager, SC_HANDLE schService)
{
    DWORD i;
    DWORD dwBytesNeeded;
    DWORD dwCount;

    LPENUM_SERVICE_STATUS   lpDependencies = NULL;
    ENUM_SERVICE_STATUS     ess;
    SC_HANDLE               hDepService;
    SERVICE_STATUS_PROCESS  ssp;

    DWORD dwStartTime = GetTickCount();
    DWORD dwTimeout = 30000; 

    if (EnumDependentServices(schService, SERVICE_ACTIVE,
        lpDependencies, 0, &dwBytesNeeded, &dwCount))
    {
        return TRUE;
    }
    else
    {
        if (GetLastError() != ERROR_MORE_DATA)
            return FALSE; 

        // Allocate a buffer for the dependencies.
        lpDependencies = (LPENUM_SERVICE_STATUS)HeapAlloc(
            GetProcessHeap(), HEAP_ZERO_MEMORY, dwBytesNeeded);

        if (!lpDependencies)
            return FALSE;

        __try {
            // Enumerate the dependencies.
            if (!EnumDependentServices(schService, SERVICE_ACTIVE,
                lpDependencies, dwBytesNeeded, &dwBytesNeeded,
                &dwCount))
                return FALSE;

            for (i = 0; i < dwCount; i++)
            {
                ess = *(lpDependencies + i);
                // Open the service.
                hDepService = OpenService(schSCManager,
                    ess.lpServiceName,
                    SERVICE_STOP | SERVICE_QUERY_STATUS);

                if (!hDepService)
                    return FALSE;

                __try {
                    // Send a stop code.
                    if (!ControlService(hDepService,
                        SERVICE_CONTROL_STOP,
                        (LPSERVICE_STATUS)&ssp))
                        return FALSE;

                    // Wait for the service to stop.
                    while (ssp.dwCurrentState != SERVICE_STOPPED)
                    {
                        Sleep(ssp.dwWaitHint);
                        if (!QueryServiceStatusEx(
                            hDepService,
                            SC_STATUS_PROCESS_INFO,
                            (LPBYTE)&ssp,
                            sizeof(SERVICE_STATUS_PROCESS),
                            &dwBytesNeeded))
                            return FALSE;

                        if (ssp.dwCurrentState == SERVICE_STOPPED)
                            break;

                        if (GetTickCount() - dwStartTime > dwTimeout)
                            return FALSE;
                    }
                }
                __finally
                {
                    CloseServiceHandle(hDepService);
                }
            }
        }
        __finally
        {
            HeapFree(GetProcessHeap(), 0, lpDependencies);
        }
    }
    return TRUE;
}


void RemoveService(int argc, TCHAR* argv[])
{
    if (argc != 3)
    {
        _tprintf(_T("sc delete [service name]"));
        return;
    }

    TCHAR szServiceName[256] = { 0 };
    StringCchCopy(szServiceName, 256, argv[3]);

    SC_HANDLE schSCManager;
    SC_HANDLE schService;
    schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);  

    if (NULL == schSCManager)
    {
        _tprintf(_T("OpenSCManager failed (%d)\n"), GetLastError());
        return;
    }

    // 打开服务获取服务句柄
    schService = OpenService(schSCManager, szServiceName, DELETE);
    if (schService == NULL)
    {
        _tprintf(_T("OpenService failed (%d)\n"), GetLastError());
        CloseServiceHandle(schSCManager);
        return;
    }

    // 删除服务
    if (!DeleteService(schService))
    {
        _tprintf(_T("DeleteService failed (%d)\n"), GetLastError());
    }
    else 
        _tprintf(_T("Service deleted successfully\n"));

    CloseServiceHandle(schService);
    CloseServiceHandle(schSCManager);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值