ITbService interface
The contract every service class in a WinServicesLib project implements. Three subs, each invoked at a specific point in the service’s lifecycle:
- EntryPoint — runs the service’s actual work.
- StartupFailed — invoked when the SCM handshake fails before EntryPoint can run.
- ChangeState — invoked when the SCM delivers a control code (Stop, Pause, Continue, …).
The package’s ServiceCreator(Of T) factory creates one instance per service start; the dispatcher trampoline holds the instance for the lifetime of the service and routes the three lifecycle subs to it.
[COMCreatable(False)]
Class MyService
Implements ITbService
Public IsStopping As Boolean
Sub EntryPoint(ByVal ServiceManager As ServiceManager) _
Implements ITbService.EntryPoint
ServiceManager.ReportStatus vbServiceStatusRunning
Do Until IsStopping
' ...do work, then yield with WaitForSingleObject / Sleep / etc.
Loop
ServiceManager.ReportStatus vbServiceStatusStopped
End Sub
Sub ChangeState(ByVal ServiceManager As ServiceManager, _
ByVal dwControl As ServiceControlCodeConstants, _
ByVal dwEventType As Long, _
ByVal lpEventData As LongPtr) _
Implements ITbService.ChangeState
Select Case dwControl
Case vbServiceControlStop, vbServiceControlShutdown
ServiceManager.ReportStatus vbServiceStatusStopPending
IsStopping = True
End Select
End Sub
Sub StartupFailed(ByVal ServiceManager As ServiceManager) _
Implements ITbService.StartupFailed
' …optional failure-reporting hook
End Sub
End Class
Important
EntryPoint runs on the service thread. ChangeState runs on the dispatcher thread (the EXE’s main thread). The two methods execute concurrently and must coordinate through shared Public flags on the class — see The two-thread split on the package overview.
Methods
ChangeState
Invoked by the SCM dispatcher thread when a control code is delivered to the service.
Syntax: service.ChangeState ServiceManager, dwControl, dwEventType, lpEventData
- ServiceManager
- The ServiceManager for this service — the same instance passed to EntryPoint. Use it to call ReportStatus acknowledging the pending transition.
- dwControl
- A ServiceControlCodeConstants value identifying the control. Standard codes the SCM may deliver include vbServiceControlStop, vbServiceControlShutdown, vbServiceControlPause, vbServiceControlContinue, vbServiceControlInterrogate, and the event-bearing codes (vbServiceControlSessionChange, vbServiceControlPowerEvent, vbServiceControlDeviceEvent, vbServiceControlHardwareProfileChange). User-defined codes in the range 128–255 can also be delivered through Services.ControlService.
- dwEventType
- A Long carrying the event-type sub-code for the codes that have one. 0 otherwise. See Microsoft’s
HandlerExdocumentation for the per-code interpretation. - lpEventData
- A LongPtr to an event-specific data structure for the codes that have one.
vbNullPtrotherwise.
The typical pattern is a Select Case dwControl that handles the codes the service cares about and ignores the rest. The minimum a service needs to handle is Stop:
Select Case dwControl
Case vbServiceControlStop, vbServiceControlShutdown
ServiceManager.ReportStatus vbServiceStatusStopPending
IsStopping = True ' signal the service thread
End Select
ChangeState does not stop EntryPoint — it only delivers the SCM’s request. The user’s code is responsible for the actual shutdown logic, typically by setting a shared Public flag the service thread polls (IsStopping) or by calling a signal method on a blocking primitive that EntryPoint owns (NamedPipeServer.ManualMessageLoopLeave, SetEvent on a Win32 event handle, …).
The method runs on a different thread than EntryPoint; see The two-thread split for the coordination rules.
EntryPoint
The service’s main routine. Invoked by the package’s dispatcher trampoline on the SCM-spawned service thread once the SCM handshake has completed and the trampoline has reported vbServiceStatusStartPending.
Syntax: service.EntryPoint ServiceManager
- ServiceManager
- The ServiceManager for this service. Carries the configuration that was set during
Sub Mainplus the runtime LaunchArgs the SCM passed in. Use it to call ReportStatus on every state transition.
The body of EntryPoint is the service’s actual work. The minimum responsibilities:
- Optionally validate startup conditions (typically by inspecting LaunchArgs). Failure paths should call
ServiceManager.ReportStatus vbServiceStatusStopped, <ExitCode>andExit Sub. - Call
ServiceManager.ReportStatus vbServiceStatusRunningonce steady-state is reached. - Run the service’s long-running loop. The loop typically blocks on something (a
WaitForSingleObjecton a manual-reset event, aNamedPipeServer.ManualMessageLoopEnter, a custom message loop, …) and breaks out when ChangeState signals shutdown through a shared flag. - Call
ServiceManager.ReportStatus vbServiceStatusStoppedbefore returning.
After the EntryPoint sub returns, the service thread exits and the SCM marks the service as stopped.
Important
EntryPoint runs on the service thread, not the dispatcher thread. The two threads execute concurrently for the lifetime of the service. Use shared Public flags on the implementing class (IsStopping, IsPaused, …) to coordinate state changes triggered from ChangeState.
StartupFailed
Invoked when the SCM handshake fails before EntryPoint can run.
Syntax: service.StartupFailed ServiceManager
- ServiceManager
- The ServiceManager for this service.
This sub fires when RegisterServiceCtrlHandlerExW returns a zero handle — typically because the service was launched outside the SCM context, or the SCM’s RegisterServiceCtrlHandlerExW rejected the registration. The service has no SCM status handle in this state, so ServiceManager.ReportStatus cannot be called from inside StartupFailed — calling it raises run-time error 5.
The typical implementation is a logging-only hook so the failure is recorded somewhere a developer can find it later:
Sub StartupFailed(ByVal ServiceManager As ServiceManager) _
Implements ITbService.StartupFailed
LogFailure service_startup_failed, status_changed, CurrentComponentName
End Sub
If you have no useful failure-reporting hook to add, an empty implementation is fine — the SCM has already given up at this point and no recovery is possible.
See Also
- WinServicesLib package – overview, lifecycle, the two-thread split
- ServiceManager class – the per-service object passed into every method
- ServiceCreator(Of T) class – the factory that creates an ITbService instance for each service start
- ServiceControlCodeConstants enum – the values ChangeState dispatches on
- ServiceStatusConstants enum – the values EntryPoint reports through ServiceManager.ReportStatus