前言
一、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); } }