[笔记]Windows系统编程《九》Windows服务编程(一)

简介: [笔记]Windows系统编程《九》Windows服务编程

前言

一、Windows服务的概念和管理

1.1 管理windows服务

打开Windows服务管理器窗口

ctrl+R + 命令:

services.ms

sc.exe管理Windows服务

sc <server>[ command] [service name] <option1><option2> ...

1.2 服务控制器

服务控制器(Service Control Manager,SCM)是Windows NT家族操作系统中的一个系统进程,它可以启动、停止Windows服务,并与 Windows服务交流。

服务控制器对应的可执行文件是%SystemRoot%\services.exe,它以 Windows控制台程序的形式运行。在系统启动时,由Wininit进程加载。

SCM 的主函数是SvcCtrlMain(),它将加载被配置为自动启动的服务列表。

Windows服务的信息保存在注册表的如下位置中:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services

二、Windows服务编程

在Visual C++程序中,可以对Windows服务进行控制,例如:

  • 创建服务
  • 启动服务
  • 停止服务
  • 查询服务的状态
  • 枚举服务列表等

2.1 与SCM建立连接

要在程序中访问Windows服务,首先需要与服务控制器( SCM)建立连接。

OpenSCManager

SC_HANDLE OpenSCManager(
  [in, optional] LPCSTR lpMachineName,
  [in, optional] LPCSTR lpDatabaseName,
  [in]           DWORD  dwDesiredAccess
);

lpMachineName:目标计算机的名称。如果指针是NULL或空字符串,函数连接到本地计算机上的服务控制管理器。

lpDatabaseName:服务控制管理器数据库的名称。这个参数应该设置为SERVICES_ACTIVE_DATABASE。如果是NULL,

SERVICES_ACTIVE_DATABASE数据库默认打开。

dwDesiredAccess:对服务的访问控制管理。

相关权限查看访问

2.2 创建服务

CreateService

调用CreateService()函数可以创建一个服务对象,并将其添加到数据库中,函数原型如下:

SC_HANDLE CreateService(
  [in]            SC_HANDLE hSCManager,
  [in]            LPCSTR    lpServiceName,
  [in, optional]  LPCSTR    lpDisplayName,
  [in]            DWORD     dwDesiredAccess,
  [in]            DWORD     dwServiceType,
  [in]            DWORD     dwStartType,
  [in]            DWORD     dwErrorControl,
  [in, optional]  LPCSTR    lpBinaryPathName,
  [in, optional]  LPCSTR    lpLoadOrderGroup,
  [out, optional] LPDWORD   lpdwTagId,
  [in, optional]  LPCSTR    lpDependencies,
  [in, optional]  LPCSTR    lpServiceStartName,
  [in, optional]  LPCSTR    lpPassword
);

2.3 打开服务/关闭服务

在对服务进行操作之前 需要调用OpenService函数打开指定服务对象,并返回服务句柄。

SC_HANDLE OpenService(
  [in] SC_HANDLE hSCManager,
  [in] LPCSTR    lpServiceName,
  [in] DWORD     dwDesiredAccess
);

hSCManager:调用OpenSCManager()函数返回的服务控制器句柄。

lpServiceName:要打开的服务名

dwDesiredAccess:对服务的访问权限。

返回值:

返回码 描述
ERROR_ACCESS_DENIED 没有对该访问服务的权限
ERROR_INVALID_HANDLE 指定句柄无效
ERROR_INVALID_NAME 指定服务名无效
ERROR_SERVICE_DOES_NOT_EXIST 指定的服务不存在

关闭服务

BOOL CloseServiceHandle(
  [in] SC_HANDLE hSCObject
);

hSCObject:关闭的服务句柄。

2.4 枚举服务列表

枚举服务控制器数据库中服务列表。

BOOL EnumServicesStatusEx(
  [in]                SC_HANDLE    hSCManager,
  [in]                SC_ENUM_TYPE InfoLevel,
  [in]                DWORD        dwServiceType,
  [in]                DWORD        dwServiceState,
  [out, optional]     LPBYTE       lpServices,
  [in]                DWORD        cbBufSize,
  [out]               LPDWORD      pcbBytesNeeded,
  [out]               LPDWORD      lpServicesReturned,
  [in, out, optional] LPDWORD      lpResumeHandle,
  [in, optional]      LPCSTR       pszGroupName
);

hSCManager:调用OpenSCManager()函数返回的服务控制器句柄。

InfoLevel:指定返回得服务属性。

dwServiceType:指定美剧服务的类型。

dwServiceStatus:指定枚举服务的状态。

lpServices:用于接收服务信息的缓冲区。

cbBufferSize:lpService指定缓冲区大小。

pcbBytesNeeded:如果缓冲区太小,则指定剩余服务记录的数量。

实例:枚举系统服务并打印

#include <Windows.h>
#include <stdio.h>
int main(int argc, char* argv[])
{
  LONG lRet = 0;
  BOOL bRet = FALSE;
  SC_HANDLE hSCM = NULL;              // 服务数据库句柄
  char* pBuf = NULL;                  // 缓冲区指针
  DWORD dwBufSize = 0;                // 传入的缓冲长度
  DWORD dwBufNeed = 0;                // 需要的缓冲长度
  DWORD dwNumberOfService = 0;        // 返回的服务个数
  ENUM_SERVICE_STATUS_PROCESS* pServiceInfo = NULL;   // 服务信息
  // 建立了一个到服务控制管理器的连接,并打开指定的数据库
  hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT);
  if (NULL == hSCM)
  {
    printf("OpenSCManager error.\n");
    return -1;
  }
  // 获取需要的缓冲区大小
  if (EnumServicesStatusEx(hSCM, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL,
    NULL, dwBufSize, &dwBufNeed, &dwNumberOfService, NULL, NULL)) 
  {
    printf("get buffer failed! \n");
    return -1;
  }
  // 多设置存放1个服务信息的长度
  dwBufSize = dwBufNeed + sizeof(ENUM_SERVICE_STATUS_PROCESS);
  pBuf = (char*)malloc(dwBufSize);
  if (NULL == pBuf)
  {
    printf("malloc error.\n");
    return -1;
  }
  memset(pBuf, 0, dwBufSize);
  // 获取服务信息
  bRet = EnumServicesStatusEx(hSCM, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL,
    (LPBYTE)pBuf, dwBufSize, &dwBufNeed, &dwNumberOfService, NULL, NULL);
  if (bRet == FALSE)
  {
    printf("EnumServicesStatusEx error.\n");
    ::CloseServiceHandle(hSCM);
    free(pBuf);
    return -1;
  }
  // 关闭打开的服务句柄
  bRet = ::CloseServiceHandle(hSCM);
  if (bRet == FALSE)
  {
    printf("CloseServiceHandle error.\n");
  }
  printf("Service Num:%d\n", dwNumberOfService);
  pServiceInfo = (LPENUM_SERVICE_STATUS_PROCESS)pBuf;
  // 打印取得的服务信息
  for (unsigned int i = 0; i < dwNumberOfService; i++)
  {
    printf("----------%d----------\n", i);
    printf("DisplayName \t\t : %s \n", pServiceInfo[i].lpDisplayName);
    printf("ServiceName \t\t : %s \n", pServiceInfo[i].lpServiceName);
    printf("ServiceType \t\t : %d \n", pServiceInfo[i].ServiceStatusProcess.dwServiceType);
    printf("CurrentState \t\t : %d \n", pServiceInfo[i].ServiceStatusProcess.dwCurrentState);
    printf("ControlsAccepted \t : %d \n", pServiceInfo[i].ServiceStatusProcess.dwControlsAccepted);
    printf("Win32ExitCode \t\t : %d \n", pServiceInfo[i].ServiceStatusProcess.dwWin32ExitCode);
    printf("ServiceSpecificExitCode  : %d \n", pServiceInfo[i].ServiceStatusProcess.dwServiceSpecificExitCode);
    printf("CheckPoint \t\t : %d \n", pServiceInfo[i].ServiceStatusProcess.dwCheckPoint);
    printf("WaitHint \t\t : %d \n", pServiceInfo[i].ServiceStatusProcess.dwWaitHint);
    printf("Process Id \t\t : %d \n", pServiceInfo[i].ServiceStatusProcess.dwProcessId);
    printf("ServiceFlags \t\t : %d \n", pServiceInfo[i].ServiceStatusProcess.dwServiceFlags);
  }
  free(pBuf);
  system("PAUSE");
  return 0;
}

2.5 启动服务

2.6 停止服务

2.7 查询服务的状态

2.8 修改服务的配置参数

2.9 完整创建Windows服务

https://docs.microsoft.com/en-us/windows/win32/services/svc-cpp

#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
#pragma comment(lib, "advapi32.lib")
#define SVCNAME TEXT("SvcName")
SERVICE_STATUS          gSvcStatus;
SERVICE_STATUS_HANDLE   gSvcStatusHandle;
HANDLE                  ghSvcStopEvent = NULL;
VOID SvcInstall(void);
VOID WINAPI SvcCtrlHandler(DWORD);
VOID WINAPI SvcMain(DWORD, LPTSTR*);
VOID ReportSvcStatus(DWORD, DWORD, DWORD);
VOID SvcInit(DWORD, LPTSTR*);
VOID SvcReportEvent(LPCTSTR);
#define SVC_ERROR -11
//int __cdecl _tmain(int argc, TCHAR* argv[])
//{
//    // If command-line parameter is "install", install the service. 
//    // Otherwise, the service is probably being started by the SCM.
//
//    if (lstrcmpi(argv[1], TEXT("install")) == 0)
//    {
//        SvcInstall();
//        return 0;
//    }
//    TCHAR svcname[] = SVCNAME;
//    // TO_DO: Add any additional services for the process to this table.
//    SERVICE_TABLE_ENTRY DispatchTable[] =
//    {
//        { svcname, (LPSERVICE_MAIN_FUNCTION)SvcMain },
//        { NULL, NULL }
//    };
//
//    // This call returns when the service has stopped. 
//    // The process should simply terminate when the call returns.
//
//    if (!StartServiceCtrlDispatcher(DispatchTable))
//    {
//        SvcReportEvent(TEXT("StartServiceCtrlDispatcher"));
//    }
//    return 0;
//}
VOID SvcInstall()
{
    SC_HANDLE schSCManager;
    SC_HANDLE schService;
    TCHAR szPath[MAX_PATH];
    if (!GetModuleFileName(NULL, szPath, MAX_PATH))
    {
        printf("Cannot install service (%d)\n", GetLastError());
        return;
    }
    // Get a handle to the SCM database. 
    schSCManager = OpenSCManager(
        NULL,                    // local computer
        NULL,                    // ServicesActive database 
        SC_MANAGER_ALL_ACCESS);  // full access rights 
    if (NULL == schSCManager)
    {
        printf("OpenSCManager failed (%d)\n", GetLastError());
        return;
    }
    // Create the service
    schService = CreateService(
        schSCManager,              // SCM database 
        SVCNAME,                   // name of service 
        SVCNAME,                   // service name to display 
        SERVICE_ALL_ACCESS,        // desired access 
        SERVICE_WIN32_OWN_PROCESS, // service type 
        SERVICE_DEMAND_START,      // start type 
        SERVICE_ERROR_NORMAL,      // error control type 
        szPath,                    // path to service's binary 
        NULL,                      // no load ordering group 
        NULL,                      // no tag identifier 
        NULL,                      // no dependencies 
        NULL,                      // LocalSystem account 
        NULL);                     // no password 
    if (schService == NULL)
    {
        printf("CreateService failed (%d)\n", GetLastError());
        CloseServiceHandle(schSCManager);
        return;
    }
    else printf("Service installed successfully\n");
    CloseServiceHandle(schService);
    CloseServiceHandle(schSCManager);
}
VOID WINAPI SvcMain(DWORD dwArgc, LPTSTR* lpszArgv)
{
    // Register the handler function for the service
    gSvcStatusHandle = RegisterServiceCtrlHandler(
        SVCNAME,
        SvcCtrlHandler);
    if (!gSvcStatusHandle)
    {
        SvcReportEvent(TEXT("RegisterServiceCtrlHandler"));
        return;
    }
    // These SERVICE_STATUS members remain as set here
    gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    gSvcStatus.dwServiceSpecificExitCode = 0;
    // Report initial status to the SCM
    ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
    // Perform service-specific initialization and work.
    SvcInit(dwArgc, lpszArgv);
}
VOID SvcInit(DWORD dwArgc, LPTSTR* lpszArgv)
{
    // TO_DO: Declare and set any required variables.
    //   Be sure to periodically call ReportSvcStatus() with 
    //   SERVICE_START_PENDING. If initialization fails, call
    //   ReportSvcStatus with SERVICE_STOPPED.
    // Create an event. The control handler function, SvcCtrlHandler,
    // signals this event when it receives the stop control code.
    ghSvcStopEvent = CreateEvent(
        NULL,    // default security attributes
        TRUE,    // manual reset event
        FALSE,   // not signaled
        NULL);   // no name
    if (ghSvcStopEvent == NULL)
    {
        ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
        return;
    }
    // Report running status when initialization is complete.
    ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
    // TO_DO: Perform work until service stops.
    while (1)
    {
        // Check whether to stop the service.
        WaitForSingleObject(ghSvcStopEvent, INFINITE);
        ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
        return;
    }
}
VOID ReportSvcStatus(DWORD dwCurrentState,
    DWORD dwWin32ExitCode,
    DWORD dwWaitHint)
{
    static DWORD dwCheckPoint = 1;
    // Fill in the SERVICE_STATUS structure.
    gSvcStatus.dwCurrentState = dwCurrentState;
    gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
    gSvcStatus.dwWaitHint = dwWaitHint;
    if (dwCurrentState == SERVICE_START_PENDING)
        gSvcStatus.dwControlsAccepted = 0;
    else gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    if ((dwCurrentState == SERVICE_RUNNING) ||
        (dwCurrentState == SERVICE_STOPPED))
        gSvcStatus.dwCheckPoint = 0;
    else gSvcStatus.dwCheckPoint = dwCheckPoint++;
    // Report the status of the service to the SCM.
    SetServiceStatus(gSvcStatusHandle, &gSvcStatus);
}
VOID WINAPI SvcCtrlHandler(DWORD dwCtrl)
{
    // Handle the requested control code. 
    switch (dwCtrl)
    {
    case SERVICE_CONTROL_STOP:
        ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
        // Signal the service to stop.
        SetEvent(ghSvcStopEvent);
        ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
        return;
    case SERVICE_CONTROL_INTERROGATE:
        break;
    default:
        break;
    }
}
VOID SvcReportEvent(LPCTSTR szFunction)
{
    HANDLE hEventSource;
    LPCTSTR lpszStrings[2];
    TCHAR Buffer[80];
    hEventSource = RegisterEventSource(NULL, SVCNAME);
    if (NULL != hEventSource)
    {
        StringCchPrintf(Buffer, 80, TEXT("%s failed with %d"), szFunction, GetLastError());
        lpszStrings[0] = SVCNAME;
        lpszStrings[1] = Buffer;
        ReportEvent(hEventSource,        // event log handle
            EVENTLOG_ERROR_TYPE, // event type
            0,                   // event category
            SVC_ERROR,           // event identifier
            NULL,                // no security identifier
            2,                   // size of lpszStrings array
            0,                   // no binary data
            lpszStrings,         // array of strings
            NULL);               // no binary data
        DeregisterEventSource(hEventSource);
    }
}

相关文章
|
11天前
|
缓存 开发工具 git
windows系统通过git上传代码
windows系统通过git上传代码
|
12天前
|
运维 监控 关系型数据库
运维实战:Windows服务挂掉了怎么办,通过Bat脚本实现自动重启
本文介绍了如何使用Bat脚本自动监控并重启Windows服务器上的挂掉服务,例如MySQL,以避免在假期等情况下需要紧急处理问题。首先,创建一个Bat脚本,设定每小时检查一次服务状态,如果服务停止则自动重启。脚本内容包括检查服务是否运行并根据状态执行相应操作。同时,脚本中包含了确保以管理员权限运行的代码。 脚本需设置为ANSI编码以防止乱码。推荐将Bat脚本封装为Windows服务以保证稳定运行,提供了使用NSSM工具、Windows服务程序和开源的Java工具winsw将批处理脚本转化为服务的方法。这些方法可以确保服务在后台可靠运行,即使在服务意外停止时也能自动恢复。
|
4天前
|
编解码 安全 网络安全
RealVNC的 VNC server在windows7系统下无法正确运行
在Windows 7上运行旧版VNC Server(如4.1.2)可能存在兼容性问题,但可通过调整配置解决。步骤包括:安装VNC Server,设置兼容性模式(选择Windows XP SP3),启动VNC Server,配置VNC连接参数。若遇到问题,检查防火墙设置,确保系统更新,并考虑升级到新版VNC Server以提高性能和兼容性。
|
11天前
|
Windows
windows系统bat批处理 打开乱码
windows系统bat批处理 打开乱码
|
11天前
|
Windows
windows系统bat批处理 打开设备管理器
windows系统bat批处理 打开设备管理器
|
11天前
|
关系型数据库 MySQL 数据安全/隐私保护
windows系统bat批处理 mysql 脚本启动关闭
windows系统bat批处理 mysql 脚本启动关闭
|
11天前
|
Java Windows
windows系统bat批处理 一键配置java jdk环境变量
windows系统bat批处理 一键配置java jdk环境变量
|
10天前
|
Windows
windows系统bat批处理 windows 关机,重启,锁定,休眠,注销
windows系统bat批处理 windows 关机,重启,锁定,休眠,注销
|
10天前
|
数据安全/隐私保护 Windows
windows系统bat批处理 笔记本开wifi 笔记本查看wifi密码
windows系统bat批处理 笔记本开wifi 笔记本查看wifi密码
|
11天前
|
Windows
windows系统bat批处理 网络设置大全 设置静态、动态IP地址
windows系统bat批处理 网络设置大全 设置静态、动态IP地址