ServiceState class

A read-only snapshot of an installed service’s current state as reported by the SCM. Typically obtained via Services.QueryStateOfService; can also be constructed directly with New ServiceState(ServiceName).

Dim state As ServiceState
Set state = Services.QueryStateOfService("MyService")

Debug.Print state.CurrentStateText, "PID " & state.ProcessId

The snapshot is taken once at construction time and never refreshed. To monitor a service over time, call Services.QueryStateOfService again at each sampling interval — typically from a low-frequency Timer.

Methods

New

Initializes a ServiceState instance by querying the SCM for the named service’s current status.

Syntax: New ServiceState(ServiceName)

ServiceName
required A String naming the service as registered in the SCM database (the same value passed to Services.QueryStateOfService or stored in ServiceManager.Name).

Opens the SCM with SC_MANAGER_CONNECT, opens the named service with SERVICE_QUERY_STATUS, and calls QueryServiceStatusEx(SC_STATUS_PROCESS_INFO, ...) to populate the internal SERVICE_STATUS_PROCESS buffer. All service and SCM handles are closed before the constructor returns.

Three failure modes raise run-time error 5 with a descriptive message:

  • "Unable to open the Service manager (OpenSCManagerW failed). Check permissions." — the calling process lacks sufficient rights to open the SCM.
  • "Service '<name>' is not installed on this system" — no service with the given name exists in the SCM database.
  • "Unable to query the service state"QueryServiceStatusEx failed after the service handle was opened.

Wrap the constructor in On Error Resume Next when the caller needs to distinguish “service is running” from “service is not installed”:

Private Function GetStateText(ByVal serviceName As String) As String
    On Error Resume Next
    Dim state As ServiceState
    Set state = New ServiceState(serviceName)
    If Err.Number = 0 Then
        GetStateText = state.CurrentStateText
    Else
        GetStateText = "not installed"
    End If
End Function

Properties

CheckPoint

The SCM-reported dwCheckPoint value. Long.

Services in a Pending state (StartPending, StopPending, PausePending, ContinuePending) report a periodically-incrementing CheckPoint so the SCM can tell a slow-but-progressing transition from a hung service. The package’s ServiceManager.ReportStatus auto-increments the field while the service is in a pending state and resets it to 0 on Running or Stopped.

ControlsAccepted

A bitmask of SERVICE_ACCEPT_* flags indicating which control codes the service has told the SCM it accepts. Long.

Note

Although the underlying SCM field is a flag bitmask, the property is typed plain Long in this release rather than as a typed enum. The bit values follow the Win32 documented constants — SERVICE_ACCEPT_STOP (1), SERVICE_ACCEPT_PAUSE_CONTINUE (2), SERVICE_ACCEPT_SHUTDOWN (4), SERVICE_ACCEPT_PARAMCHANGE (8), SERVICE_ACCEPT_NETBINDCHANGE (16), SERVICE_ACCEPT_HARDWAREPROFILECHANGE (32), SERVICE_ACCEPT_POWEREVENT (64), SERVICE_ACCEPT_SESSIONCHANGE (128), SERVICE_ACCEPT_PRESHUTDOWN (256), and so on.

CurrentState

The SCM-reported dwCurrentState value. Long.

Note

The property is typed plain Long in this release rather than as ServiceStatusConstants. The numeric values do match the enum (e.g. 4 is vbServiceStatusRunning), so a cast such as CType(state.CurrentState, ServiceStatusConstants) recovers typed access if needed. For display purposes CurrentStateText is usually more convenient.

CurrentStateText

A human-readable rendering of CurrentState. String.

The mapping:

Any unrecognised state value is rendered as UNKNOWN STATE (<n>).

ExitCode

The Win32 exit code the service reported when it last stopped. Long. Read-only.

Syntax: state.ExitCode

state
required An object expression that evaluates to a ServiceState instance, obtained from Services.QueryStateOfService.

Returns the dwWin32ExitCode field from the SERVICE_STATUS_PROCESS structure the SCM fills in. For a service that stopped normally this is 0 (NO_ERROR). For a service that stopped due to an error, this is either a standard Win32 error code or the sentinel value ERROR_SERVICE_SPECIFIC_ERROR (1066) — in which case the real vendor-defined code is in ServiceSpecificExitCode.

While the service is in any state other than Stopped the SCM keeps this field at 0. The value becomes meaningful only after the service thread exits and the SCM records the terminal status.

Flags

The SCM-reported dwServiceFlags value. Long.

Only one bit is currently documented — SERVICE_RUNS_IN_SYSTEM_PROCESS (1), set when the service is hosted inside the system process (services.exe).

ProcessId

The OS process ID hosting the service, or 0 if the service is not running. Long.

Syntax: object.ProcessId

object
required An object expression that evaluates to a ServiceState instance, obtained from Services.QueryStateOfService.

The value is the Win32 process identifier (dwProcessId) as reported by QueryServiceStatusEx. When the service is in the Stopped state the SCM reports 0, as there is no live process. For a service in Running state the value matches the PID shown in Task Manager or returned by tasklist /svc.

Use ProcessId as a quick “is the service alive?” check in preference to string-comparing CurrentStateText:

Dim state As ServiceState
Set state = Services.QueryStateOfService("MyService")
If state.ProcessId <> 0 Then
    Debug.Print "Service is alive; PID " & state.ProcessId
Else
    Debug.Print "Service is not running"
End If

ServiceSpecificExitCode

The SCM-reported dwServiceSpecificExitCode value. Long.

Meaningful only when ExitCode equals ERROR_SERVICE_SPECIFIC_ERROR (1066); otherwise the field is 0 and should be ignored. Services that report custom error codes through ServiceManager.ReportStatus populate this field through the package’s machinery.

Type

The SCM-reported service type. ServiceTypeConstants. Read-only.

Syntax: state.Type

state
required An object expression that evaluates to a ServiceState instance, obtained from Services.QueryStateOfService.

Returns the ServiceTypeConstants value the SCM has on file for the service. For twinBASIC services this is typically one of:

The value is read directly from the dwServiceType field of the SERVICE_STATUS_PROCESS structure returned by QueryServiceStatusEx. It reflects whatever was registered in the SCM at install time via ServiceManager.Type.

WaitHint

The estimated upper bound, in milliseconds, of the time the current pending state transition will take. Long. Read-only.

Syntax: object.WaitHint

object
required An object expression that evaluates to a ServiceState instance, obtained from Services.QueryStateOfService.

Returns the dwWaitHint field from the SERVICE_STATUS_PROCESS structure the SCM fills in. The value is the estimate the service last reported through SetServiceStatus when it entered the current pending state. It is only meaningful while the service is in a Pending state (StartPending, StopPending, PausePending, ContinuePending). For services in Running or Stopped states the field is 0 or the last value set before the transition completed and should not be interpreted.

The SCM uses CheckPoint and WaitHint together to decide whether a pending service is making progress. If the CheckPoint value has not increased within the WaitHint interval, the SCM considers the service hung and may report an error. A service that expects a long pending phase should either set WaitHint large enough to cover the whole transition, or increment CheckPoint at regular sub-intervals to signal continued progress.

Example

This example reads the wait hint and check point for a service currently in a pending state and prints a progress summary.

Dim state As ServiceState
Set state = Services.QueryStateOfService("MyService")

If state.CurrentStateText = "STARTING" Or state.CurrentStateText = "STOPPING" Then
    Debug.Print "Pending -- checkpoint: " & state.CheckPoint & _
                ", max wait: " & state.WaitHint & " ms"
End If

See Also