Windows——关于service

15 篇文章 0 订阅

作者:小 琛
欢迎转载,请标明出处

本博文针对Windows系统的service进行相关分享,注意内容为:作用、底层管理、开发使用API(根据开发基本流程)、其它注意事项

Windows service

Windows对service的管理机制

相关API

Windows下的大部分函数,都有A版本和W版本,这里使用W版本(Unicode)作为例子

CloseServiceHandle

这里我将关闭句柄的函数放到了第一位,通常来说不合理,但我希望以此强调一点:对于service的任何操作,一定要在你的相关操作结束后,将句柄关闭,否则你可能会遇到各种奇怪的错误,并且很难排查,详细内容可以看标题“使用规范”

BOOL CloseServiceHandle(
  [in] SC_HANDLE hSCObject
);
  1. 参数
  • [in] hSCObject
    服务控制管理器对象或要关闭的服务对象的句柄。服务控制管理器对象的句柄由 OpenSCManager函数返回,服务对象的句柄由 OpenService或 CreateService函数返回。
  1. 返回值
    如果函数成功,则返回值非零。
    如果函数失败,则返回值为零。要获取扩展的错误信息,请调用 GetLastError。

OpenSCManager

建立与指定计算机上的服务控制管理器的连接并打开指定的服务控制管理器数据库。 无论你是要对现有的service进行操作还是新创建service,都必须拿到可以操控本系统服务控制管理库的句柄。

SC_HANDLE OpenSCManagerW(
  [in, optional] LPCWSTR lpMachineName,
  [in, optional] LPCWSTR lpDatabaseName,
  [in]           DWORD   dwDesiredAccess
);
  1. 参数
  • lpMachineName
    目标计算机的名称,如果设置为NULL,则默认为本计算机
  • lpDatabaseName
    服务控制管理器数据库的名称。此参数应设置为 SERVICES_ACTIVE_DATABASE。实际中,通常设置为NULL,系统默认帮我们设置为SERVICES_ACTIVE_DATABASE
  • dwDesiredAccess
    对服务控制管理器的访问权限,尽可能缩小权限范围。详细内容,看这里
  1. 返回值
    如果函数成功,则返回值是指定服务控制管理器数据库的句柄。
    如果函数失败,则返回值为 NULL。要获取扩展的错误信息,请调用 GetLastError。
  2. 注意事项
  • 调用失败通常是权限问题,如果当前用户在连接到另一台计算机上的服务时没有适当的访问权限,则 OpenSCManager函数调用将失败。
  • 只有具有管理员权限的进程才能打开可由 CreateService函数使用的数据库句柄。
  • 返回的句柄仅对调用 OpenSCManager函数的进程有效,且CloseServiceHandle函数可以关闭该句柄,这点是非常关键的,因此,对于服务尽可能做到:用时再拿句柄,用完即使释放,否则就会造成一些难以排查的错误

CreateService

创建服务对象并将其添加到指定的服务控制管理器数据库。

SC_HANDLE CreateServiceW(
  [in]            SC_HANDLE hSCManager,
  [in]            LPCWSTR   lpServiceName,
  [in, optional]  LPCWSTR   lpDisplayName,
  [in]            DWORD     dwDesiredAccess,
  [in]            DWORD     dwServiceType,
  [in]            DWORD     dwStartType,
  [in]            DWORD     dwErrorControl,
  [in, optional]  LPCWSTR   lpBinaryPathName,
  [in, optional]  LPCWSTR   lpLoadOrderGroup,
  [out, optional] LPDWORD   lpdwTagId,
  [in, optional]  LPCWSTR   lpDependencies,
  [in, optional]  LPCWSTR   lpServiceStartName,
  [in, optional]  LPCWSTR   lpPassword
);
  1. 参数
  • [in] hSCManager
    服务控制管理器数据库的句柄。此句柄由 OpenSCManager函数返回,并且必须具有SC_MANAGER_CREATE_SERVICE访问权限。

  • [in] lpServiceName
    要安装的服务的名称。最大字符串长度为 256 个字符。服务控制管理器数据库保留字符的大小写,但服务名称比较始终不区分大小写。正斜杠 (/) 和反斜杠 () 不是有效的服务名称字符。

  • [in, optional] lpDisplayName
    用户界面程序用来标识服务的显示名称。此字符串的最大长度为 256 个字符。该名称在服务控制管理器中保留大小写。显示名称比较始终不区分大小写。

  • [in] dwDesiredAccess
    对服务的访问权限,更多内容看这里

  • [in] dwServiceType
    服务类型。此参数可以是以下值之一。

意义
SERVICE_ADAPTER预订的
SERVICE_FILE_SYSTEM_DRIVER文件系统驱动服务
SERVICE_KERNEL_DRIVER核心驱动服务
SERVICE_RECOGNIZER_DRIVER预订的
SERVICE_WIN32_OWN_PROCESS在自己的进程中运行的服务
SERVICE_WIN32_SHARE_PROCESS与一个或多个其他服务共享进程的服务
  • [in] dwStartType
    服务启动选项。此参数可以是以下值之一。
意义
SERVICE_AUTO_START预订的
SERVICE_BOOT_START由系统加载程序启动的设备驱动程序
SERVICE_DEMAND_START当进程调用StartService函数 时由服务控制管理器启动的服务
SERVICE_RECOGNIZER_DRIVER预订的
SERVICE_DISABLED无法启动的服务
SERVICE_SYSTEM_START由IoInitSystem函数启动的设备驱动程序
  • [in] dwErrorControl
    如果此服务无法启动,则错误的严重性和所采取的操作。此参数可以是以下值之一:
意义
SERVICE_ERROR_CRITICAL如果可能,启动程序会在事件日志中记录错误。
SERVICE_ERROR_IGNORE启动程序忽略错误并继续启动操作
SERVICE_ERROR_NORMAL启动程序在事件日志中记录错误但继续启动操作
SERVICE_ERROR_SEVERE启动程序在事件日志中记录错误
  • [in, optional] lpBinaryPathName
    服务二进制文件的完全限定路径。如果路径包含空格,则必须用引号引起来,以便正确解释。例如,“d:\my share\myservice.exe”应指定为“d:\my share\myservice.exe”。

  • [in, optional] lpLoadOrderGroup
    此服务所属的负载排序组的名称。一帮设置为NULL

  • [out, optional] lpdwTagId
    指向变量的指针,该变量接收在lpLoadOrderGroup参数中指定的组中唯一的标记值。一般设置为NULL

  • [in, optional] lpDependencies
    指向系统必须在此服务之前启动的服务或加载排序组的空分隔名称的双空终止数组的指针。如果服务没有依赖项,则指定 NULL

  • [in, optional] lpServiceStartName
    服务应在其下运行的帐户的名称。

  • [in, optional] lpPassword
    lpServiceStartName参数指定的帐户名的密码。
    如果 lpServiceStartName参数指定的帐户名称是托管服务帐户的名称或虚拟帐户名称,则lpPassword参数必须为 NULL。
    驱动程序服务将忽略密码。

  1. 返回值
    如果函数成功,则返回值是服务的句柄。
    如果函数失败,则返回值为 NULL。如果需要具体的错误信息,请调用 GetLastError。

关于错误,以下内容可以参考

错误码解析
ERROR_ACCESS_DENIEDSCM 数据库的句柄没有SC_MANAGER_CREATE_SERVICE访问权限
ERROR_CIRCULAR_DEPENDENCY指定了循环服务依赖项
ERROR_DUPLICATE_SERVICE_NAME显示名称已作为服务名称或另一个显示名称存在于服务控制管理器数据库中
ERROR_INVALID_HANDLE指定的服务控制管理器数据库的句柄无效
ERROR_INVALID_NAME指定的服务名称无效
ERROR_INVALID_PARAMETER指定的参数无效
ERROR_INVALID_SERVICE_ACCOUNT指定的服务名称无效
ERROR_INVALID_NAME指定的用户帐户名称不存在
ERROR_SERVICE_EXISTS此数据库中已存在指定的服务
ERROR_SERVICE_MARKED_FOR_DELETE此数据库中已存在指定的服务,并已标记为删除

最后一种错误,是一个非常恶心的内容,我在使用规范中将详细讲述,并帮助大家尽可能的避免

OpenService

打开现有服务。通常情况下,当我们想获取某个服务的句柄时,会使用该API,但切记,使用后调用CloseServiceHandle进行关闭

SC_HANDLE OpenServiceW(
  [in] SC_HANDLE hSCManager,
  [in] LPCWSTR   lpServiceName,
  [in] DWORD     dwDesiredAccess
);
  1. 参数
  • [in] hSCManager
    服务控制管理器数据库的句柄。OpenSCManager函数返回此句柄。

  • [in] lpServiceName
    要打开的服务的名称。和创建服务对象时由CreateService函数的lpServiceName参数指定的名称对应
    名称比较始终不区分大小写。正斜杠 (/) 和反斜杠 () 是无效的服务名称字符。

  • [in] dwDesiredAccess
    对服务的访问权限,具体看这里

  1. 返回值
    如果函数成功,则返回值是服务的句柄。
    如果函数失败,则返回值为 NULL。要获取扩展的错误信息,请调用 GetLastError。

常见的错误,看这里

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

注意:返回的句柄仅对调用 OpenService的进程有效,因此仍要遵循,随时用,随时取,同时在使用结束后,调用 CloseServiceHandle函数来关闭。

StartService

启动现有的服务

BOOL StartServiceW(
  [in]           SC_HANDLE hService,
  [in]           DWORD     dwNumServiceArgs,
  [in, optional] LPCWSTR   *lpServiceArgVectors
);
  1. 参数
  • [in] hService
    服务的句柄。此句柄由 OpenService或 CreateService函数返回,它必须具有 SERVICE_START 访问权限。

  • [in] dwNumServiceArgs
    lpServiceArgVectors数组中的字符串数。如果lpServiceArgVectors为 NULL,则此参数可以为零。

  • [in, optional] lpServiceArgVectors
    要作为参数传递给服务的ServiceMain函数的以 null 结尾的字符串。如果没有参数,则此参数可以为 NULL。

  1. 返回值
    如果函数成功,则返回值非零。
    如果函数失败,则返回值为零。要获取扩展的错误信息,调用 GetLastError。

以下为一些你可能需要的错误处理情况:

返回码描述
ERROR_ACCESS_DENIED该句柄没有 SERVICE_START 访问权限。
ERROR_INVALID_HANDLE句柄无效
ERROR_SERVICE_ALREADY_RUNNING该句柄的另一个实例已经在运行
ERROR_SERVICE_DISABLED该服务已被禁用
ERROR_SERVICE_NO_THREAD无法为服务创建进程
ERROR_INVALID_HANDLE句柄无效
ERROR_SERVICE_REQUEST_TIMEOUT该服务的进程已启动,但它没有调用 StartServiceCtrlDispatcher,或者调用 StartServiceCtrlDispatcher 的线程 可能在控制处理程序函数中被阻塞。
ERROR_SERVICE_DEPENDENCY_DELETED该服务依赖于不存在或已标记为删除的服务
ERROR_SERVICE_MARKED_FOR_DELETE该服务已经被删除

最后两种情况,非常恶心,一定要在使用中避免让你的程序出现,具体内容我在使用规范中进行了总结。

使用规范

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值