VB.NET Get services running in svchost

June 10, 2010 — 1 Comment

I wrote this code primarily to be able to get the windows services running in each svchost.exe process, however it can be used against any process to see which services (if any) are running within it. It makes use of the EnumServicesStatusEx, OpenSCManager, and CloseServiceHandle Windows APIs. If you are not a developer and just want to see which services are in an svchost.exe process, see this post

When I originally set out to try and find out which services were running in a given instance of svchost, I came up with the following plan:

Get the command line parameters for the svchost instance to see which service group it has been told to start. Then look in the relevant part of the registry for the list of services that are in this service group, that in theory would tell me which services were running in that instance of svchost… only that is not correct because some services that run within svchost can be marked as requiring their own process (ie they cannot share a process with other services). This means that they get run in another instance of svchost which has been started with the same command line parameters as the svchost that is running the rest of the services from that group (the ones that do not require their own process).

So I changed my plan to a method that feels less ‘smart’ but actually works much better as it does not require admin rights (a side effect of reading the command line arguments from another process – see this post for more info and an example of how to do it). What I do now is get the process ID for the svchost instance I am interested in and then get a list of all services running on the machine with their process IDs by using EnumServicesStatusEx – then its simply a case of comparing the PID of the svchost process to that of each of the running services. I’ve tested it on Windows 7 x64 and Windows XP x86 and the code posted here worked fine on both of them.

As usual, I’ve made a .NET class that might help anyone wanting to do this same thing avoid having to figure out or work with the APIs directly, so here’s an example of how you could use my class (named NativeService) to show the services running in every svchost process. Yes you could improve this code but its just a brief example of how to use the class:

Dim AllServices As List(Of NativeService) = Nothing
‘Get a list of all active services
AllServices = NativeService.GetServices()

‘Loop through each running instance of svchost.exe
For Each CurrentSvchost As Process In Process.GetProcessesByName(“svchost”)
Dim ServiceListString As String = “svchost with process ID “ & _
CurrentSvchost.Id & ” contains:” & vbNewLine
‘Loop through all active services
For Each Service As NativeService In AllServices
‘Check to see if the process ID of this service matches the proces ID
‘of this svchost.exe process
If Service.ProcessID = CurrentSvchost.Id Then
‘If the PIDs match, add this service’s name to our string
ServiceListString &= Service.DisplayName & vbNewLine
End If
Next
‘Show the list of all services running in this svchost.exe process
MessageBox.Show(ServiceListString)
Next

and here is the definition of my NativeService class that you can just copy and paste into your own project (or borrow bits from if you would rather make your own class :))

First the API definitions:

Public Const SC_ENUM_PROCESS_INFO As UInteger = 0
Public Const SC_MANAGER_ENUMERATE_SERVICE As UInteger = 4
Public Const SERVICE_WIN32 As UInteger = &H30
Public Const SERVICE_DRIVER As UInteger = &HB
Public Const SERVICE_ACTIVE As UInteger = 1

<StructLayoutAttribute(LayoutKind.Sequential)> _
Public Structure ENUM_SERVICE_STATUS_PROCESS
<MarshalAsAttribute(UnmanagedType.LPTStr)> _
Public lpServiceName As String
<MarshalAsAttribute(UnmanagedType.LPTStr)> _
Public lpDisplayName As String
Public ServiceStatusProcess As SERVICE_STATUS_PROCESS
End Structure

<StructLayoutAttribute(LayoutKind.Sequential)> _
Public Structure SERVICE_STATUS_PROCESS
Public dwServiceType As UInteger
Public dwCurrentState As UInteger
Public dwControlsAccepted As UInteger
Public dwWin32ExitCode As UInteger
Public dwServiceSpecificExitCode As UInteger
Public dwCheckPoint As UInteger
Public dwWaitHint As UInteger
Public dwProcessId As UInteger
Public dwServiceFlags As UInteger
End Structure

<DllImportAttribute(“advapi32.dll”, EntryPoint:=“OpenSCManagerW”)> _
Public Shared Function OpenSCManager(<InAttribute(), MarshalAsAttribute(UnmanagedType.LPWStr)> ByVal lpMachineName As String, <InAttribute(), MarshalAsAttribute(UnmanagedType.LPWStr)> ByVal lpDatabaseName As String, ByVal dwDesiredAccess As UInteger) As System.IntPtr
End Function

<DllImportAttribute(“advapi32.dll”, EntryPoint:=“CloseServiceHandle”)> _
Public Shared Function CloseServiceHandle(<InAttribute()> ByVal hSCObject As IntPtr) As <MarshalAsAttribute(UnmanagedType.Bool)> Boolean
End Function

<DllImportAttribute(“advapi32.dll”, EntryPoint:=“EnumServicesStatusExW”)> _
Public Shared Function EnumServicesStatusEx(<InAttribute()> ByVal hSCManager As IntPtr, _
ByVal InfoLevel As UInteger, ByVal dwServiceType As UInteger, _
ByVal dwServiceState As UInteger, _
ByVal lpServices As System.IntPtr, _
ByVal cbBufSize As UInteger, _
<OutAttribute()> ByRef pcbBytesNeeded As UInteger, _
<OutAttribute()> ByRef lpServicesReturned As UInteger, _
ByVal lpResumeHandle As System.IntPtr, <InAttribute(), _
MarshalAsAttribute(UnmanagedType.LPWStr)> ByVal pszGroupName As String) As <MarshalAsAttribute(UnmanagedType.Bool)> Boolean
End Function

and here’s my NativeService class itself, with plenty of comments:

Public Class NativeService

”’ <summary>
”’ Returns a list of all Windows services that are currently running
”’ </summary>
Public Shared Function GetServices() As List(Of NativeService)
‘Get a handle to the service control manager
Dim ScmPtr As IntPtr = OpenSCManager(Nothing, Nothing, SC_MANAGER_ENUMERATE_SERVICE)
If ScmPtr = IntPtr.Zero Then
Throw New ApplicationException(“Unable to connect to service manager. The last error that was reported was: “ & New System.ComponentModel.Win32Exception().Message)
End If
Dim BytesNeeded As UInteger = 0
‘Find out how many bytes we will need to store the list of services
EnumServicesStatusEx(ScmPtr, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_ACTIVE, IntPtr.Zero, 0, BytesNeeded, 0, Nothing, Nothing)
‘Allocate some space for the number of bytes we will need and get the memory address pointer
Dim ServicesPtr As IntPtr = Marshal.AllocHGlobal(CInt(BytesNeeded))
Dim ServicesFound As UInteger = 0
‘Get all of the active services, which will now be held at the memory address that we allocated
Dim Result As Boolean = EnumServicesStatusEx(ScmPtr, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_ACTIVE, ServicesPtr, BytesNeeded, 0, ServicesFound, Nothing, Nothing)
If Not Result Then
Throw New System.ComponentModel.Win32Exception
End If
If ServicesFound = 0 Then
Throw New ApplicationException(“No services were found”)
End If
‘This will hold our list of services
Dim AllServices As New List(Of NativeService)
‘Get the base address that the array of service information structures is held at
‘This will be used to keep track of which structure we are reading
‘Int64 used so that this works on 64 bit OS as well
Dim CurrentAddress As Int64 = ServicesPtr.ToInt64
‘Get the size of a single service information structure so we know how much to
‘advance our address number by each time we read a structure
Dim ServiceStructureSize As Integer = Marshal.SizeOf(GetType(ENUM_SERVICE_STATUS_PROCESS))
For i As Integer = 0 To CInt(ServicesFound – 1)
‘Get a service information structure from the bytes at the memory address we are currently on
Dim CurrentService As ENUM_SERVICE_STATUS_PROCESS = CType(Marshal.PtrToStructure(New IntPtr(CurrentAddress), GetType(ENUM_SERVICE_STATUS_PROCESS)), ENUM_SERVICE_STATUS_PROCESS)
AllServices.Add(New NativeService(CurrentService.lpServiceName, CurrentService.lpDisplayName, CInt(CurrentService.ServiceStatusProcess.dwProcessId)))
‘Advance our current address by the size of a single service information structure so
‘that next time we iterate through this loop we read the next structure in the array
CurrentAddress += ServiceStructureSize
Next
‘Clean up and return the list to the caller
Marshal.FreeHGlobal(ServicesPtr)
CloseServiceHandle(ScmPtr)
Return AllServices
End Function

Private _Name As String
”’ <summary>
”’ The service name
”’ </summary>
Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property

Private _DisplayName As String
”’ <summary>
”’ The display name of the service
”’ </summary>
Public Property DisplayName() As String
Get
Return _DisplayName
End Get
Set(ByVal value As String)
_DisplayName = value
End Set
End Property

Private _ProcessID As Integer
”’ <summary>
”’ The ID of the process that this service is running in
”’ </summary>
Public Property ProcessID() As Integer
Get
Return _ProcessID
End Get
Set(ByVal value As Integer)
_ProcessID = value
End Set
End Property

”’ <summary>
”’ Creates a new instance of the NativeService class
”’ </summary>
”’ <param name=”ServiceName”>The name of the service</param>
”’ <param name=”FriendlyName”>The display name of the service</param>
”’ <param name=”ID”>The process ID of the service</param>
”’ <remarks></remarks>
Public Sub New(ByVal ServiceName As String, ByVal FriendlyName As String, ByVal ID As Integer)
_Name = ServiceName
_DisplayName = FriendlyName
_ProcessID = ID
End Sub

End Class

Enjoy!

Chris

One response to VB.NET Get services running in svchost

  1. 

    Just to add more info for users of Visual Studio 2010 (VS2010):

    1. Start a new Windows Forms Application

    2. Add a button to the form to execute the code.

    3. Double-click the button’s click event to create “Private Sub Button1_Click” subroutine in Form1.

    4. At the top of Form1 add the line, “Imports System.Runtime.InteropServices”. (Otherwise it won’t recognize some of these commands.)

    5. Copy and paste the first section of code above into the “Button1_Click” subroutine of Form1. (Starts with “Dim AllServices As List…”)

    6. Copy and paste the second and third sections of code above anywhere else inside Form1’s class. (Just not inside the “Button1_Click” section.)

    7. Run the application and click the button to run the code.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s