[笔记]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);
    }
}

相关文章
|
1月前
|
安全 Windows
永久关闭 Windows 11 系统更新
永久关闭 Windows 11 系统更新
123 0
|
19天前
|
存储 负载均衡 Java
如何配置Windows主机MPIO多路径访问存储系统
Windows主机多路径(MPIO)是一种技术,用于在客户端计算机上配置多个路径到存储设备,以提高数据访问的可靠性和性能。本文以Windows2012 R2版本为例介绍如何在客户端主机和存储系统配置多路径访问。
65 13
如何配置Windows主机MPIO多路径访问存储系统
|
9天前
|
Windows Python
如何反向读取Windows系统日志EVTX文件?
以下是如何反向读取Windows系统日志EVTX文件
21 2
|
1月前
|
监控 Ubuntu Linux
视频监控笔记(五):Ubuntu和windows时区同步问题-your clock is behind
这篇文章介绍了如何在Ubuntu和Windows系统中通过设置相同的时区并使用ntp服务来解决时间同步问题。
64 4
视频监控笔记(五):Ubuntu和windows时区同步问题-your clock is behind
|
1月前
|
边缘计算 安全 网络安全
|
1月前
|
开发框架 .NET API
Windows Forms应用程序中集成一个ASP.NET API服务
Windows Forms应用程序中集成一个ASP.NET API服务
95 9
|
1月前
|
Windows
.NET 隐藏/自定义windows系统光标
【10月更文挑战第20天】在.NET中,可以使用`Cursor`类来控制光标。要隐藏光标,可将光标设置为`Cursors.None`。此外,还可以通过从文件或资源加载自定义光标来更改光标的样式。例如,在表单加载时设置`this.Cursor = Cursors.None`隐藏光标,或使用`Cursor.FromFile`方法加载自定义光标文件,也可以将光标文件添加到项目资源中并通过资源管理器加载。这些方法适用于整个表单或特定控件。
|
1月前
|
Apache 数据中心 Windows
将网站迁移到阿里云Windows系统云服务器,访问该站点提示连接被拒绝,如何处理?
将网站迁移到阿里云Windows系统云服务器,访问该站点提示连接被拒绝,如何处理?
|
1月前
|
域名解析 缓存 网络协议
Windows系统云服务器自定义域名解析导致网站无法访问怎么解决?
Windows系统云服务器自定义域名解析导致网站无法访问怎么解决?
|
1月前
|
Windows
安装Windows XP系统
安装Windows XP系统

热门文章

最新文章

下一篇
无影云桌面