ServiceManager 类
每服务配置对象。一个 ServiceManager 描述EXE知道如何托管的一个Windows服务——其 Name、Description、服务 Type、InstallStartMode、InstanceCreator 以及SCM关心的可选字段——并暴露作用于单个服务的方法:Install、Uninstall 以及服务运行时用于通知SCM状态转换的 ReportStatus 调用。
INFO
不要直接构造 ServiceManager 实例。改为调用 Services.ConfigureNew——它分配一个新的管理器并将其注册到包的内部集合中,以便调度器可以找到它。
With Services.ConfigureNew
.Name = "MyService"
.Description = "An example twinBASIC service"
.Type = tbServiceTypeOwnProcess
.InstallStartMode = tbServiceStartOnDemand
.InstallCmdLine = """" & App.ModulePath & """ -startService"
.InstanceCreator = New ServiceCreator(Of MyService)
End With参见包概述了解更广泛的生命周期、双线程分离以及围绕安装的提升权限规则。
字段
LaunchArgs
SCM转发给服务的启动时参数。String()。由包的调度器跳板在SCM调用服务线程入口点时填充;不是配置字段。服务线程的 ITbService.EntryPoint 读取它以发现 Services.LaunchService(或SCM,或 sc.exe)传入的参数。
LaunchArgs(0) 是第一个用户提供的参数——SCM供应的作为 argv[0] 的服务名称在填充数组之前已被丢弃,因此索引与调用者的心智模型匹配。
Sub EntryPoint(ByVal ServiceManager As ServiceManager) _
Implements ITbService.EntryPoint
If Join(ServiceManager.LaunchArgs) <> "MySecretPassword" Then
ServiceManager.ReportStatus vbServiceStatusStopped, &H12345678
Exit Sub
End If
' ...稳态工作
End Sub属性
AutoInitializeCOM
调度器跳板是否在调用 ITbService.EntryPoint 之前在服务线程上调用 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)。Boolean,默认 True。
如果服务需要不同的单元模型,设置为 False——例如创建MTA工作池的服务。服务必须随后从其 EntryPoint 中自己调用 CoInitializeEx,然后才能接触COM感知的对象。
Description
在 services.msc 和 sc.exe query 中列出的人类可读描述。String,无默认值。
该值由 Install 通过 ChangeServiceConfig2W(SERVICE_CONFIG_DESCRIPTION) 写入SCM,会应用于新服务或在每次重新安装时刷新。该字段必须在调用 Install 之前赋值;运行时更改直到下次安装才会生效。
DependentServices
此服务依赖的服务名称列表。Variant(),无默认值。
传入 Array("OtherSvc1", "OtherSvc2")。当SCM被要求启动该服务时,它首先自动启动列出的依赖项;如果任何依赖项启动失败,SCM会中止此服务的启动。包将数组打包为 CreateServiceW 期望的双空终止字符串。
.DependentServices = Array("MSMQ", "LanmanServer")InstallCmdLine
SCM启动服务宿主EXE时将使用的命令行。String,默认 """<App.ModulePath>"""(正在运行的EXE路径,带引号)。
默认值仅在EXE始终作为服务运行时足够。常规模式是覆盖默认值以添加鉴别参数,以便EXE的 Sub Main 可以辨别它处于哪种模式:
.InstallCmdLine = """" & App.ModulePath & """ -startService"Sub Main 中匹配的 If InStr(Command, "-startService") > 0 Then Services.RunServiceDispatcher 分支是使同一EXE既可作为安装程序/控制面板UI(正常启动时)又可作为服务宿主(由SCM启动时)工作的原因。
该值在 Install 时被捕获到SCM数据库中。安装后更改需要卸载并重新安装服务。
InstallStartMode
服务注册的SCM启动模式。ServiceStartConstants,默认 tbServiceStartOnDemand。
典型设置:
- tbServiceStartOnDemand——服务不自动启动;用户/安装程序/Services.LaunchService 按需启动。
- tbServiceStartAuto——SCM在系统启动时启动服务。
- tbServiceStartDisabled——服务无法启动,直到其启动模式被更改。
仅驱动程序模式(tbServiceStartBoot、tbServiceStartDriverSystem)对用户模式twinBASIC服务没有意义。
InstanceCreator
调度器在SCM启动服务时用于创建 ITbService 实例的工厂。IServiceCreator,无默认值。
赋值为 New ServiceCreator(Of MyServiceClass),其中 MyServiceClass 是用户的 ITbService 实现:
.InstanceCreator = New ServiceCreator(Of MyService)RunServiceDispatcher 每次服务启动调用一次 InstanceCreator.CreateInstance()。InstanceCreator 是可读写的——底层私有接口同时接受 Let 和 Set 赋值,因此两种语法都可以。
如果只需要运行 Install / Uninstall(例如在独立安装程序内),InstanceCreator 可以保留为 Nothing——调度器仅在SCM实际启动服务时才需要它。
Name
服务在SCM数据库中的名称,由 services.msc 和 sc.exe 使用。String,无默认值。
名称是SCM存储在 HKLM\SYSTEM\CurrentControlSet\Services\<Name> 的内容;它也是 Services.LaunchService、Services.ControlService 和 Services.QueryStateOfService 作为 ServiceName 参数接受的名称。相同的值用于SCM的 DisplayName——包目前不暴露独立的显示名称。
SupportsPausing
是否告知SCM服务接受 SERVICE_CONTROL_PAUSE / SERVICE_CONTROL_CONTINUE 通知。Boolean,默认 False。
设置此属性后立即通过 SetServiceStatus 将缓存的 SERVICE_STATUS 重新同步到SCM,因此从 EntryPoint 内部切换——一旦服务过了 StartPending 阶段——在下次SCM查询时生效。大多数支持暂停的服务在 EntryPoint 顶部将属性设置为 True,并在 ChangeState 中处理 vbServiceControlPause / vbServiceControlContinue。
如果设置 SupportsPausing 时服务尚未达到已启动状态,重新同步会引发运行时错误5 "Can't update the service state until the service has started"。等到第一次 ReportStatus(vbServiceStatusRunning) 调用之后再切换属性。
Type
Win32服务类型——控制服务是否在自己的进程、共享进程中运行,或者是内核驱动程序。ServiceTypeConstants,默认 tbServiceTypeOwnProcess。
典型设置:
- tbServiceTypeOwnProcess——一个EXE一个服务。
- tbServiceTypeShareProcess——多个服务托管在单个EXE中;SCM保持一个进程存活为所有服务服务。每个 ServiceManager 仍需要自己的配置和 InstanceCreator。
仅驱动程序模式(tbServiceTypeSystemDriver、tbServiceTypeKernelDriver、…)对用户模式twinBASIC服务没有意义。
方法
Install
在SCM数据库中注册此服务。
语法:manager.Install
以 SC_MANAGER_CONNECT Or SC_MANAGER_CREATE_SERVICE 打开SCM,用已配置字段调用 CreateServiceW。如果具有相同 Name 的服务已经存在,方法会先删除它(通过 OpenServiceW(SERVICE_DELETE) + DeleteService)然后重试——因此对已存在的服务调用 Install 会覆盖现有注册而非失败。创建成功后,Description 通过 ChangeServiceConfig2W(SERVICE_CONFIG_DESCRIPTION) 写入。
WARNING
Install 写入SCM数据库,需要管理员权限。通常的做法是从提升的安装程序中调用一次,而不是从应用程序的正常启动路径中调用。从twinBASIC IDE内运行通常会失败——IDE很少以提升权限运行。
权限失败时引发运行时错误5,附带描述性消息("Unable to open the Service manager...")或不可恢复的创建失败("CreateServiceW() failed with error code <N>")。
ReportStatus
通知SCM服务的当前状态。由服务从 ITbService.EntryPoint 内部调用(以及从 ITbService.ChangeState 调用以确认待处理的转换)。
语法:manager.ReportStatus CurrentState [, Win32ExitCode [, WaitHint ] ]
- CurrentState
- 必需 ServiceStatusConstants 值——通常为 vbServiceStatusRunning、vbServiceStatusStopPending 或 vbServiceStatusStopped。
- Win32ExitCode
- 可选 Long 退出代码。默认 0(
NO_ERROR)。当因错误报告 vbServiceStatusStopped 时,传入Win32错误代码,或者对于服务特定代码使用魔术值ERROR_SERVICE_SPECIFIC_ERROR(1066)并将实际代码放在服务特定字段中——但包的API只直接暴露 Win32ExitCode 参数。大多数服务为干净停止传入 0,为错误停止传入小的自定义代码。 - WaitHint
- 可选 给SCM当前待处理转换预计所需的毫秒数上限的 Long。默认 0。仅对待处理状态有意义(vbServiceStatusStartPending、vbServiceStatusStopPending、vbServiceStatusPausePending、vbServiceStatusContinuePending)——SCM将其与自动递增的
dwCheckPoint字段一起使用来检测卡住的服务。
ReportStatus 自动填充 SERVICE_STATUS 的 dwControlsAccepted 字段——Stop 在 vbServiceStatusStartPending 期间以外总是被接受,Pause / Continue 在 SupportsPausing 为 True 时被接受。dwCheckPoint 字段在服务处于待处理状态时自动递增,在 vbServiceStatusRunning / vbServiceStatusStopped 时重置为 0。
包的调度器跳板在调用 EntryPoint 之前立即报告 vbServiceStatusStartPending;用户的 EntryPoint 负责后续的 vbServiceStatusRunning 和 vbServiceStatusStopped 转换。
ResyncStatus
通过 SetServiceStatus 将缓存的 SERVICE_STATUS 重新应用到SCM。由 ReportStatus 和 SupportsPausing 设置器自动调用;消费者代码很少需要直接调用此方法。
语法:manager.ResyncStatus
如果在服务获取其SCM状态句柄之前调用(即在调度器跳板调用 RegisterServiceCtrlHandlerExW 之前),引发运行时错误5 "Can't update the service state until the service has started"。从 EntryPoint 内部,ReportStatus 是正确的调用,而非直接调用 ResyncStatus。
Uninstall
从SCM数据库中移除此服务。
语法:manager.Uninstall
打开SCM,以 SERVICE_DELETE 打开服务,调用 DeleteService。实际删除由SCM排队并在每个打开的服务句柄关闭后完成——services.msc 可能将服务显示为 "Marked for deletion" 直到宿主进程退出。
WARNING
Uninstall 需要管理员权限。如果无法打开SCM、服务未安装或 DeleteService 失败,引发运行时错误5,附带描述性消息。
另见
- WinServicesLib 包 -- 概述、生命周期、双线程分离
- Services 类 -- ConfigureNew 来源的预声明协调器
- ITbService 接口 -- InstanceCreator 必须产生的内容
- ServiceCreator(Of T) -- 通常传递给 InstanceCreator 的通用工厂
- ServiceStatusConstants 枚举 -- ReportStatus 接受的值