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:

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:

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 value ERROR_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 dwCheckPoint field 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