Managing Windows Services from a Remote System

A simple command line utility for managing Windows services from a remote system

.

Environment: VC++6.0, Windows NT 4.0 and Windows 2000

Introduction

Most of us use the Services Manager tool to start and stop Windows Services. To install and uninstall, we need to run a Service program as a normal process with specific command-line parameters. For example, if we have a service executable called as "MyService.exe", we can install this as a service by executing this with a command line parameter "-Service". To uninstall, we need to pass "-UnregServer" as the parameter.

The following code provides a simple utility for Windows Services Management from custom applications. Using this code, you can Install, Uninstall, Start, Stop, and Query the status of a service running on the local or a remote system, provided the user has administrative privileges for that system.

int main(int argc, char* argv[])
{
  if(argc < 3)
  {
    printf("USAGE:  ServiceInstaller [install / uninstall _
        / start / stop] [Service Name]");
    printf(" [Computer's Network Name](optional)\n");
    return 0;
  }

  char *pSysName = NULL;
  char *pSvcPath = NULL;

  if(argc == 4)
  {
    pSysName = new char[50];      //if remote system
    strcpy(pSysName, argv[3]);

    ImpersonateUser();
  }

    //access service control manager
    SC_HANDLE hSCM = ::OpenSCManager(pSysName,
                       SERVICES_ACTIVE_DATABASE,
                       SC_MANAGER_ALL_ACCESS);

    if (hSCM == 0)
    {
      printf("ERROR: UNABLE TO OPEN SERVICE MANAGER\n");
      PrintError(GetLastError());
      return;
    }

  if(!strcmp(argv[1],"install"))
  {
    //to install we need the path for the executable
    printf("Please enter path of the Executable\n"); 
    pSvcPath = new char[MAX_PATH];
    scanf("%s",pSvcPath);

// Computer's network name, service name, and path of 
// the executable
    Install(hSCM, argv[2], path);
    delete[] path;
  }
  else if(!strcmp(argv[1],"uninstall"))
  {
    Uninstall(hSCM, argv[2]);
  }
  else if(!strcmp(argv[1],"start"))
  {
    StartSvc(hSCM, argv[2]);
  }
  else if(!strcmp(argv[1],"stop"))
  {
    StopSvc(hSCM, argv[2]);
  }
  else if(!strcmp(argv[1],"status"))
  {
    QuerySvc(hSCM, argv[2]);
  }
  else
  {
    printf("WARNING: Bad command\n");
  }

::CloseServiceHandle(hSCM);

  delete[] pName;

return 0;
}

The following is a helper function, which is used to impersonate the logged-in user in case of a remote system access.

void ImpersonateUser()
{
  //prepare to access remote system

  DWORD id = GetCurrentProcessId();
  HANDLE hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);

  HANDLE t;

  BOOL b = OpenProcessToken( hp,
                             TOKEN_QUERY | TOKEN_DUPLICATE ,
                             &t);

  if(!ImpersonateLoggedOnUser(t))
  {
    PrintError(GetLastError());
    return;
  }
}

In case of a Windows error, this function helps decode the error code to an user-understandable error string.

void PrintError(DWORD code)
{
     LPTSTR serr;

     if(::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
      FORMAT_MESSAGE_FROM_SYSTEM,
      NULL,
      code,
      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
      (LPTSTR)&serr,
      0,
      NULL) == 0)
  {
    printf("Unknown Error\n");
  }
    else
  {
    printf("Error : %s\n", serr);
    LocalFree(serr);
  }
}

It is a simple command-line application, which calls different functions based on the first command-line parameter. The second parameter is the service name, and the third parameter is the optional network name of the computer you are trying to access. If you are providing the remote system name, you will have to have the administrative privileges for that system.

Installing a Service

Here we are creating the service using Windows' CreateService function. We are providing the user with a "manual start" option; the other option we could have provided was SERVICE_AUTO_START, which will get the service started automatically by the SCM at the system startup.

void Install(SC_HANDLE hSCM, char *szSvcName,char *szFilePath)
{
  // Service installation

  SC_HANDLE hService = ::CreateService(
  hSCM, szSvcName, szSvcName,
  SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
  // by default we are providing the user with 
  // manual-start option,
  SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE,
  szFilePath, NULL, NULL, NULL, NULL, NULL);

  if (hService == NULL)
  {
    printf("ERROR: COULDN'T CREATE SERVICE\n");
    return;
  }

  ::CloseServiceHandle(hService);
}

Uninstalling a Service

To uninstall a service, we need to do three things. First, get a handle on the service with SERVICE_STOP and DELETE access rights, call the ControlService function with the SERVICE_CONTROL_STOP flag that sends a quit message to the service, and then call the DeleteService method to remove the service from the list of available services.

void Uninstall(SC_HANDLE hSCM, char *szSvcName)
{
    //get a handle to the service
    SC_HANDLE hService = ::OpenService(hSCM, 
                                       szSvcName,
                                       SERVICE_STOP | DELETE);

    if (hService == NULL)
    {
        printf("ERROR: COULDN'T OPEN SERVICE\n");
        return;
    }

    // let us first stop the service
    SERVICE_STATUS status;
    ::ControlService(hService, SERVICE_CONTROL_STOP, &status);

    //Print the status of the service
    PrintStatus(status);

   // now uninstall the service
    BOOL bDelete = ::DeleteService(hService);
    ::CloseServiceHandle(hService);

    if (!bDelete)
        printf("WARNING: SERVICE COULD NOT BE DELETED\n");
} 

Now let us take a look at the PrintStatus method. This method prints the current state of the service given the SERVICE_STATUS structure.

void PrintStatus(SERVICE_STATUS sc)
{
  switch(sc.dwCurrentState)
  {
    case SERVICE_STOPPED : 
         printf("The service is not running.");
         break;
    case SERVICE_START_PENDING : 
         printf("The service is starting.");
         break;
    case SERVICE_STOP_PENDING:   
         printf("The service is stopping. ");
         break;
    case SERVICE_RUNNING:
         printf("The service is running. ");
         break;
    case SERVICE_CONTINUE_PENDING:
         printf("The service continue is pending.");
         break;
    case SERVICE_PAUSE_PENDING:
         printf("The service pause is pending. ");
         break;
    case SERVICE_PAUSED:
         printf("The service is paused.");
         break;
    default: 
         printf("Status Unknown"); 
         break;
  }
}

Starting a Service

To start a service, you need to get a handle to the service providing the SERVICE_START flag to the OpenService function, and call the StartService function. This function takes a service handle, the number of arguments as an integer, and a string-list. After starting the service, we shall query the status of the service.

void StartSvc(SC_HANDLE hSCM, char *szSvcName)
{
    SC_HANDLE hService = ::OpenService(hSCM, 
                                       szSvcName,
                                       SERVICE_START);

    if (hService == NULL)
    {
        printf("ERROR: COULDN'T ACCESS SERVICE\n");
        return;
    }

    //no arguments
    if(::StartService(hService, 0, NULL)==0)
    {
    PrintError(GetLastError());
     }

    ::CloseServiceHandle(hService);

     QuerySvc(szNetworkName, szSvcName);
}

Querying the Current Status of the Service

When we open the Windows Service Manager, we get to know the status of each service. Similarly, we have to have a way to query the status of the service programmatically. The code listed below provides this utility. After we get the Service handle, we will call QueryService function to get the service status.

void QuerySvc(SC_HANDLE hSCM,, char *szSvcName)
{
    //get service handle
    SC_HANDLE hService = ::OpenService(hSCM, szSvcName,
        SERVICE_QUERY_STATUS);

    if (hService == NULL)
    {
        printf("ERROR: COULDN'T OPEN SERVICE\n");
        return;
    }

  SERVICE_STATUS status;

    //query status
  if(!QueryServiceStatus(hService,&status))
  {
    ::CloseServiceHandle(hSCM);
    PrintError(GetLastError());
  }

  PrintStatus(status);

    ::CloseServiceHandle(hService);
}

Stopping the Service

To stop a service, we call the ControlService with the SERVICE_CONTROL_STOP flag.

void StopSvc(SC_HANDLE hSCM,, char *szSvcName)
{
    SC_HANDLE hService = ::OpenService(hSCM, 
                                       szSvcName,
                                       SERVICE_STOP);

    if (hService == NULL)
    {
        printf("ERROR: COULDN'T OPEN SERVICE\n");
        return;
    }

    SERVICE_STATUS status;
    if(!::ControlService(hService, 
                         SERVICE_CONTROL_STOP,
                         &status))
    printf("ERROR: COULDN'T STOP SERVICE\n");


    ::CloseServiceHandle(hService);

    QuerySvc(szNetworkName, szSvcName);
}

Downloads

Download source - 6 Kb