ServiceManager class
The per-service configuration object. One ServiceManager describes one Windows service the EXE knows how to host — its Name, Description, service Type, InstallStartMode, InstanceCreator, and the optional fields the SCM cares about — and exposes the methods that act on a single service: Install, Uninstall, and the ReportStatus call the service uses to inform the SCM of state transitions while running.
Note
Do not construct ServiceManager instances directly. Call Services.ConfigureNew instead — it allocates a fresh manager and registers it in the package’s internal collection so the dispatcher can find it.
See the package overview for the broader lifecycle, the two-thread split, and the elevation rules around installation.
Fields
LaunchArgs
The launch-time arguments the SCM forwarded to the service. String(). Populated by the package’s dispatcher trampoline when the SCM invokes the service-thread entry-point; not a configuration field. The service-thread ITbService.EntryPoint reads it to discover the arguments that Services.LaunchService (or the SCM, or sc.exe) passed in.
LaunchArgs(0) is the first user-supplied argument — the SCM-supplied service name that comes through as argv[0] is dropped before the array is populated, so the indexing matches the caller’s mental model.
Sub EntryPoint(ByVal ServiceManager As ServiceManager) _
Implements ITbService.EntryPoint
If Join(ServiceManager.LaunchArgs) <> "MySecretPassword" Then
ServiceManager.ReportStatus vbServiceStatusStopped, &H12345678
Exit Sub
End If
' ...steady-state work
End Sub
Properties
AutoInitializeCOM
Whether the dispatcher trampoline calls CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) on the service thread before invoking ITbService.EntryPoint. Boolean, default True.
Set to False if the service needs a different apartment model — for example, a service that creates an MTA worker pool. The service must then call CoInitializeEx itself from its EntryPoint before touching COM-aware objects.
Description
The human-readable description listed in services.msc and sc.exe query. String, no default.
The value is written to the SCM by Install via ChangeServiceConfig2W(SERVICE_CONFIG_DESCRIPTION) and is applied to a fresh service or refreshed on every re-install. Set it before calling Install; changing the field at run-time has no effect until the next install.
DependentServices
A list of service names this service depends on. Variant(), no default.
Pass an Array("OtherSvc1", "OtherSvc2"). When the SCM is asked to start the service, it auto-starts the listed dependencies first; if any dependency fails to start, the SCM aborts the start of this service. The package packs the array into the double-null-terminated string that CreateServiceW expects.
.DependentServices = Array("MSMQ", "LanmanServer")
InstallCmdLine
The command line the SCM will use when launching the service-host EXE. String, default """<App.ModulePath>""" (the running EXE path, quoted).
The default suffices only when the EXE always wants to run as a service. The conventional pattern is to override the default to add a discriminator argument so the EXE’s Sub Main can tell which mode it is in:
.InstallCmdLine = """" & App.ModulePath & """ -startService"
The matching If InStr(Command, "-startService") > 0 Then Services.RunServiceDispatcher branch in Sub Main is what makes the same EXE work both as installer / control-panel UI (when launched normally) and as service host (when launched by the SCM).
The value is captured into the SCM database at Install time. Changing it after install requires uninstalling and re-installing the service.
InstallStartMode
The SCM start mode the service is registered with. ServiceStartConstants, default tbServiceStartOnDemand.
Typical settings:
- tbServiceStartOnDemand — the service is not started automatically; user / installer / Services.LaunchService starts it on demand.
- tbServiceStartAuto — the SCM starts the service at system boot.
- tbServiceStartDisabled — the service cannot be started until its start mode is changed.
The driver-only modes (tbServiceStartBoot, tbServiceStartDriverSystem) are not meaningful for user-mode twinBASIC services.
InstanceCreator
The factory the dispatcher uses to create the ITbService instance for this service when the SCM launches it. IServiceCreator, no default.
Set this to New ServiceCreator(Of MyServiceClass) where MyServiceClass is the user’s ITbService implementation:
.InstanceCreator = New ServiceCreator(Of MyService)
RunServiceDispatcher calls InstanceCreator.CreateInstance() once per service start. InstanceCreator is read-write — the underlying private interface accepts both Let and Set assignment, so either syntax works.
If only Install / Uninstall need to run (e.g. inside a stand-alone installer), InstanceCreator can be left Nothing — the dispatcher only needs it when the SCM actually starts the service.
Name
The service’s name in the SCM database, used by services.msc and sc.exe. String, no default.
The name is what the SCM stores at HKLM\SYSTEM\CurrentControlSet\Services\<Name>; it is also the name Services.LaunchService, Services.ControlService, and Services.QueryStateOfService take as their ServiceName parameter. The same value is used for the SCM’s DisplayName — the package does not currently expose a distinct display name.
SupportsPausing
Whether the SCM is told that the service accepts SERVICE_CONTROL_PAUSE / SERVICE_CONTROL_CONTINUE notifications. Boolean, default False.
Setting this property immediately resyncs the cached SERVICE_STATUS to the SCM via SetServiceStatus, so toggling it from inside EntryPoint — once the service is past the StartPending phase — takes effect on the next SCM query. Most services that support pausing set the property to True at the top of EntryPoint and handle vbServiceControlPause / vbServiceControlContinue in ChangeState.
If the service has not yet reached the started state when SupportsPausing is set, the resync raises run-time error 5 “Can’t update the service state until the service has started”. Wait until after the first ReportStatus(vbServiceStatusRunning) call before toggling the property.
Type
The Win32 service type — controls whether the service runs in its own process, in a shared process, or is a kernel driver. ServiceTypeConstants, default tbServiceTypeOwnProcess.
Typical settings:
- tbServiceTypeOwnProcess — one service per EXE.
- tbServiceTypeShareProcess — multiple services hosted in a single EXE; the SCM keeps one process alive that serves all of them. Each ServiceManager still needs its own configuration and InstanceCreator.
The driver-only modes (tbServiceTypeSystemDriver, tbServiceTypeKernelDriver, …) are not meaningful for user-mode twinBASIC services.
Methods
Install
Registers this service in the SCM database.
Syntax: manager.Install
Opens the SCM with SC_MANAGER_CONNECT Or SC_MANAGER_CREATE_SERVICE, calls CreateServiceW with the configured fields. If a service with the same Name already exists, the method deletes it first (via OpenServiceW(SERVICE_DELETE) + DeleteService) and retries — so calling Install on a service that already exists overwrites the existing registration rather than failing. On a successful create the Description is written via ChangeServiceConfig2W(SERVICE_CONFIG_DESCRIPTION).
Important
Install writes to the SCM database, which requires administrator rights. The usual pattern is to call it once from an elevated installer, not from the application’s normal startup path. Running from within the twinBASIC IDE typically fails — the IDE is rarely elevated.
Raises run-time error 5 with a descriptive message on permission failure (“Unable to open the Service manager…“) or unrecoverable create failure (*“CreateServiceW() failed with error code
ReportStatus
Informs the SCM of the service’s current state. Called by the service from inside ITbService.EntryPoint (and from ITbService.ChangeState to acknowledge pending transitions).
Syntax: manager.ReportStatus CurrentState [, Win32ExitCode [, WaitHint ] ]
- CurrentState
- required A ServiceStatusConstants value — typically vbServiceStatusRunning, vbServiceStatusStopPending, or vbServiceStatusStopped.
- Win32ExitCode
- optional A Long exit code. Default 0 (
NO_ERROR). When reporting vbServiceStatusStopped after an error, pass either a Win32 error code or, for service-specific codes, the magic valueERROR_SERVICE_SPECIFIC_ERROR(1066) along with placing the real code in the service-specific field — but the package’s API exposes only the Win32ExitCode parameter directly. Most services pass 0 for a clean stop and a small custom code for an error stop. - WaitHint
- optional A Long giving the SCM an upper-bound milliseconds estimate of how long the current pending transition will take. Default 0. Only meaningful for pending states (vbServiceStatusStartPending, vbServiceStatusStopPending, vbServiceStatusPausePending, vbServiceStatusContinuePending) — the SCM uses it together with the auto-incremented
dwCheckPointfield to detect a stuck service.
ReportStatus fills the dwControlsAccepted field of SERVICE_STATUS automatically — Stop is always accepted except during vbServiceStatusStartPending, and Pause / Continue are accepted when SupportsPausing is True. The dwCheckPoint field auto-increments while the service is in a pending state and resets to 0 on vbServiceStatusRunning / vbServiceStatusStopped.
The package’s dispatcher trampoline reports vbServiceStatusStartPending for you immediately before calling EntryPoint; the user’s EntryPoint is responsible for the subsequent vbServiceStatusRunning and vbServiceStatusStopped transitions.
ResyncStatus
Re-applies the cached SERVICE_STATUS to the SCM via SetServiceStatus. Called automatically by ReportStatus and by the SupportsPausing setter; consumer code rarely needs to call this directly.
Syntax: manager.ResyncStatus
Raises run-time error 5 “Can’t update the service state until the service has started” if called before the service has acquired its SCM status handle (i.e. before the dispatcher trampoline has called RegisterServiceCtrlHandlerExW). Use ReportStatus from inside EntryPoint instead of ResyncStatus directly.
Uninstall
Removes this service from the SCM database.
Syntax: manager.Uninstall
Opens the SCM, opens the service with SERVICE_DELETE, calls DeleteService. The actual deletion is queued by the SCM and completes once every open handle to the service is closed — services.msc may show the service as “Marked for deletion” until the host process exits.
Important
Uninstall requires administrator rights. Raises run-time error 5 with a descriptive message if the SCM cannot be opened, the service is not installed, or DeleteService fails.
See Also
- WinServicesLib package – overview, lifecycle, two-thread split
- Services class – the predeclared coordinator that ConfigureNew comes from
- ITbService interface – what InstanceCreator must produce
- ServiceCreator(Of T) – the generic factory typically passed to InstanceCreator
- ServiceStatusConstants enum – the values ReportStatus accepts