Install/Uninstall/Control Windows Services Remotely Using VB.Net

Windows services have a few basic properties: They can be configured to run under a specific user. They can have parent or child services; a parent service is a service that your service is dependant on and a child service is a service that depends on your service. This comes into play when you start/stop services. For a clean start, you need to make sure the parent services of your service are started.

For a clean stop, make sure the child services of your service are stopped.

Now, start with the code: To control services in VB.Net, you need to import the service process namespace; therefore, your first line is:

Imports System.ServiceProcess

First, just try to start a service without checking for any dependenciers on parent services.

Dim objWinServ As New ServiceController
objWinServ.ServiceName = sServiceName
objWinServ.MachineName = MachineName
StartService(objWinServ)

Private Sub StartService(ByVal Service As ServiceController)
   If Service.Status = ServiceControllerStatus.Stopped Then
      Try
         Service.Start()
         Service.WaitForStatus(ServiceControllerStatus.Running, _
            System.TimeSpan.FromSeconds(20))
      Catch ex As TimeoutException
         Status = "Could not Start " & Service.DisplayName & _
                  " - TimeOut expired"
      Catch e As Exception
         Status = "Could not Start " & Service.DisplayName & _
                  " - " & e.Message
      End Try

   End If

End Sub

The code is fairly straightforward:

  1. Create a new service controller object called objWinServ.
  2. Assign the servcie name and machine name (because you're calling it remotely).
  3. Supply the objWinServ object to your start service routine.
  4. The routine first checks the status of the service to see if it is stopped. You can further check to see whether it's paused, running, and so forth.
  5. Then, it tries to start the service. I've put in a max wait of 20 seconds for the service to start. If it does not, it throws an timeout exception. I've put in a general exception clause as well.

Now, to make the code more robust, two things you may want to check for are:

  1. Parent Services that need to start before your service
  2. Whether the service actually exists on the server.

To check for parent services that need to be started, you use this recursive sub:

Private Sub CheckForParentServices _
   (ByVal Service As ServiceController)
      Dim objParentService As ServiceController
      For Each objParentService In Service.ServicesDependedOn
         CheckForParentServices(objParentService)
      Next
      Call StartService(Service)
End Sub

Pass objWinServ to the sub and let it do its magic!

To check whether or not the servcie exists, use this function that will return a boolean value depending on whether or not the service exists:

Public Function CheckforService(ByVal ServerName As String, _
   ByVal ServiceName As String) As Boolean
      Dim Exist As Boolean = False
      Dim objWinServ As New ServiceController
      Dim ServiceStatus As ServiceControllerStatus
      objWinServ.ServiceName = ServiceName
      objWinServ.MachineName = ServerName

      Try
         ServiceStatus = objWinServ.Status
         Exist = True
      Catch ex As Exception
      Finally
         objWinServ = Nothing
      End Try
      Return Exist
End Function

Now, look into stopping the service. First, the basic function:

Private Sub StopService(ByVal Service As ServiceController)
   Dim status As String
   If Service.Status = ServiceControllerStatus.Running Then
      Try
         Service.Stop()
         Service.WaitForStatus(ServiceControllerStatus.Stopped, _
                               System.TimeSpan.FromSeconds(20))
      Catch ex As TimeoutException
         status = "Could not Stop " & Service.DisplayName & _
                  "Service - TimeOut expired"
      Catch e As Exception
         status = "Could not Stop " & Service.DisplayName & _
                 "Service - " & e.Message
      End Try
   End If

End Sub

Pass objWinServ, with the same kind of exception handling and wait times.

You can use the same function as earlier to check whether or not the service exists, but when it comes to stopping a service, you need to check if the service has any dependant child servcies that also need to be stopped. The Sub for that is:

Private Sub CheckForChildServices(ByVal Service As ServiceController, _
                                  ByVal NextService As String)

   Dim objChildService As ServiceController

   For Each objChildService In Service.DependentServices
      CheckForChildServices(objChildService, NextService)
   Next

   If NextService = "Stop" Then
      Call StopService(Service)
   Else
      Call ContinueService(Service)
   End If

End Sub

I've added a second attribute, 'NextService', for this sub. This is to make the child service check sub more reusable. You need to check for child services if you want to change the status to continue as well. So, you add an attribute that can take values of stop or continue.

Here's the sub for continue service. You can write a similar one for pause as well.

Private Sub ContinueService(ByVal Service As ServiceController)
   Dim status As String
   If Service.Status = ServiceControllerStatus.Paused Then
      Try
         Service.Continue()
         Service.WaitForStatus(ServiceControllerStatus.Running, _
                               System.TimeSpan.FromSeconds(20))
      Catch ex As TimeoutException
         status = "Could not change status from Paused To _
                   Continue for " & Service.DisplayName " _
                  " - TimeOut expired"
      Catch e As Exception
         status = "Could not change status from Paused To _
                   Continue for " & Service.DisplayName & " - " _
                   & e.Message
      End Try
   End If

   If Service.Status = ServiceControllerStatus.Stopped Then
         Call StopService(Service)
   End If

End Sub

Install/Uninstall/Control Windows Services Remotely Using VB.Net

Here's a general function that calls each of these status subs:

Public Function ControlServices(ByVal sServiceName As String, _
   ByVal sTask As String, ByVal MachineName As String)
   Status = ""
   Dim objWinServ As New ServiceController
   objWinServ.ServiceName = sServiceName
   objWinServ.MachineName = MachineName

      Select Case sTask

      Case "Reset"
         If objWinServ.Status = ServiceControllerStatus.Running Then
            'Service is currently running, so you will have to
            'stop it before starting it
            'First stop all it's child services, then stop the
            'service itself
            Call CheckForChildServices(objWinServ, "Stop")
         End If

         If objWinServ.Status = ServiceControllerStatus.Stopped Then
            'This is satisfied if the service was running and then
            'was stopped by code or if it was already stopped.
            'Service is already stopped, so just start it...
            'so first start all it's parent services
            Call CheckForParentServices(objWinServ)
               If Status = "" Then Status = "Successfully Started"
            Else
               Status = Status '"Could not Start " & _
                  objWinServ.DisplayName
            End If
         End If

      Case "Start"
         If objWinServ.Status = ServiceControllerStatus.Stopped Then
            'This is satisfied if the service was running and then
            'was stopped by code or if it was already stopped
               Call CheckForParentServices(objWinServ)
               If Status = "" Then
                  Status = "Could not Start " & _
                           objWinServ.DisplayName
               Else
                  Status = "Successfully Started"
               End If

         End If

      Case "Stop"
         If objWinServ.Status = ServiceControllerStatus.Running Then
               Call CheckForChildServices(objWinServ, "Stop")

               Status = "Successfully Stopped"

         ElseIf objWinServ.Status = ServiceControllerStatus.Stopped Then
               Status = "Successfully Stopped"
         Else
               Status = "Could not Stop " & objWinServ.DisplayName
         End If
   End Select
         Return Status
End Function

That's pretty much everything you would need to control services remotely. If you need just a status return, you could write something like this:

Public Function GetServiceStatus(ByVal sServername As String, _
   ByVal sServiceName As String) As String

   Dim ServiceStatus As New ServiceController
   ServiceStatus.ServiceName = sServiceName
   ServiceStatus.MachineName = sServername

   Try
      If ServiceStatus.Status = ServiceControllerStatus.Running Then
         Return "Running"
      ElseIf ServiceStatus.Status = _
         ServiceControllerStatus.Stopped Then
         Return "Stopped"
      Else
         Return "Intermidiate"
      End If
   Catch ex As Exception
      Return "Stopped"
   Finally
      ServiceStatus = Nothing
   End Try

End Function

Now, move into install and uninstall. The first step declare these as global:

Private Declare Ansi Function WritePrivateProfileString Lib _
   "KERNEL32.DLL" Alias "WritePrivateProfileStringA" _
   (ByVal lpSectionName As String, ByVal lpKeyName As String, _
    ByVal lpKeyValue As String, ByVal lpFileName As String) As Integer
   Private Declare Ansi Function GetPrivateProfileString Lib _
      "KERNEL32.DLL" Alias "GetPrivateProfileStringA" _
      (ByVal lpSectionName As String, ByVal lpKeyName As String, _
       ByVal lpDefault As String, _
       ByVal lpReturnedString As StringBuilder, _
       ByVal nSize As Integer, _
       ByVal lpFileName As String) As Integer

To install a service, you need to pass it the user name and credentials under which the service will run, and the path to the exe called by the service:

Private Sub InstallService(ByVal sServiceName As String)
   Try
      Console.WriteLine("Running Install Service...")

      Dim sUserID As String = sDomainName & "\" & sUserID
      Dim sPassword As String = sPassword

      If IsServiceInstalled(sServiceName) Then _
         UninstallService(sServiceName)

      ' INSTALL
      Dim hSCM As IntPtr = OpenSCManager(Nothing, Nothing, _
         ServiceControlManagerEnum.AllAccess)
      If hSCM.ToInt32 = 0 Then
         Throw New Exception("Could not install service. [1]")
      Else
         Dim iServiceType As Integer = ServiceTypeEnum.Win32OwnProcess
         
      Dim hService As IntPtr = CreateService(hSCM, sServiceName, _
         sServiceName, ServiceControlManagerEnum.AllAccess, _
            iServiceType, ServiceTypeEnum.AutoStart, _
            ServiceTypeEnum.ErrorNormal, sPath, Nothing, Nothing, _
            Nothing, sUserID, sPassword)
      If hService.ToInt32 = 0 Then
         Throw New Exception("Could not install service. [2]")
      Else
         CloseServiceHandle(hService)
      End If

         CloseServiceHandle(hSCM)
         Call ControlServices(sServiceName, "Start")
      End If
   Catch ex As Exception
      Console.WriteLine(ex.Message)
      Console.ReadLine()
   End Try
End Sub

Install/Uninstall/Control Windows Services Remotely Using VB.Net

There's a check to see whether or not the service exists:

Private Function IsServiceInstalled(ByVal sServiceName As String) _
   As Boolean
   Dim bResult As Boolean = False

   Dim oServiceArray() As ServiceProcess.ServiceController
   oServiceArray = ServiceProcess.ServiceController.GetServices

   For Each oServiceController _
      As ServiceProcess.ServiceController In oServiceArray
      If oServiceController.ServiceName.Trim.ToUpper = _
         sServiceName.Trim.ToUpper Then
         Dim i As New ServiceControllerPermissionAttribute _
            (Security.Permissions.SecurityAction.Demand)
         Dim d As New ServiceControllerPermission
         Try
            If d.Any Then
               d.ToString()
            End If
         Catch
         End Try
         bResult = True
         Exit For
      End If
   Next

   Return bResult
End Function

The uninstall procedure operates along the same lines:

Private Sub UninstallService(ByVal sServiceName As String)
   Console.WriteLine("Running Uninstall Service...")

   If IsServiceInstalled(sServiceName) Then
      ' STOP SERVICE
      Call ControlServices(sServiceName, "Stop")
   Else
      Exit Sub
   End If

   Dim hSCM As IntPtr = OpenSCManager(Nothing, Nothing, _
      ServiceControlManagerEnum.AllAccess)
   If hSCM.ToInt32 = 0 Then
      Throw New Exception("Could not delete service. [1]")
   Else
      Dim hService As IntPtr = OpenService(hSCM, sServiceName, _
         ServiceAccessTypeEnum.AllAccess)
      If hService.ToInt32 = 0 Then
         ' TODO: FAILED
      Else
         If Not DeleteService(hService) Then
            Throw New Exception("Could not delete service. [2]")
         End If

         CloseServiceHandle(hService)
      End If

      CloseServiceHandle(hSCM)
   End If
End Sub

To change the logon user, the cleanest method I've found to uninstall the service and then reinstall it with the new credentials.

I've included the ,sln file for a console app that has all these functions defined already. I'm only calling the uninstall function, but you can easily add, start, stop, and so on to the main function.



About the Author

Aneeza Haleem

4+ years experience with VB.Net and SQL

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • It's not unusual for a company to use a variety of formal and informal file-sharing methods. Many methods are fraught with significant operational, financial, and legal risks in addition to other potentially negative business consequences. Strategic managed file transfer (MFT) software and services help connect the business "dots" between the various ad hoc and more systematic ways that files are moved. Read this white paper to learn how the right MFT solution, can bring order, structure, and, importantly, …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds