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


Comments

  • corrected the source..

    Posted by maqubex on 07/12/2012 11:32am

    one doubt.. i had rectified the erors in the source as its some server errors.. D anyway one doubt regarding the code. am running this program with an admin privileged account. but i could nt install or start programs like calc ,cmd etc

    Reply
  • How to connect a remote system?

    Posted by pco96 on 03/02/2004 08:55pm

    I can't understand that you say "provided the user has administrative privileges for that system". BOOL b = OpenProcessToken( hp,TOKEN_QUERY | TOKEN_DUPLICATE , &t); whar is the "t"?

    Reply
  • GetServiceKeyName

    Posted by Legacy on 04/11/2003 12:00am

    Originally posted by: Ameer

    I was looking on the web for code like this. I happened to try something similar, but I kept running to problems. Basically, I would get an error from OpenService() saying that the specified service was not installed. This was totally frustrating because I could start services on my local machine using "NET START" but not my program. Well, I found out why...I thought I would share this info with others. The problem is that Windows has two names for the services, the display name and the service name. Unfortunately, you can't put the display name into OpenService(). You have to get the service name using GetServiceKeyName() then pass it into OpenService. Some services' display name and service name are the same.

    Cheers.

    Reply
  • Terrific!!

    Posted by Legacy on 03/06/2003 12:00am

    Originally posted by: sellenoff

    Thanks this was exactly what I wanted!! Very straightforward, and easy to follow! Helped me incorporate simple service start/stop functionality into my own app! Much appreciated!

    Reply
  • Windows XP ?

    Posted by Legacy on 10/18/2002 12:00am

    Originally posted by: richard

    When trying to connect to a Windows XP machine, OpenSCManager keeps replying 'acces is denied'.

    I understand what does happen but don't know what to do to solve the problem (either connect to XP with different user rights or change something locally in Windows XP so that remote computers can access the Service Manaer Database).

    richard

    Reply
  • Can you run an exe from inside a service?

    Posted by Legacy on 08/22/2002 12:00am

    Originally posted by: Ideshini

    Hi
    
    

    Do you know if a service application can execute an EXE?

    I have a service app that has been working well except when I added a DLL to it that sometimes calls an EXE. The service app now crashes.

    Would appreciate any advice you might have on this.

    Kind regards,
    Ideshini.

    Reply
  • How to set the "Log On" Identity of a Service Programmatically

    Posted by Legacy on 08/20/2002 12:00am

    Originally posted by: Tman

    Any ideas on how to set the service's "Log On" identity programmatically? When the service is installed the identity defaults to "local system". It would be great to be able to set this using your techniques.

    Reply
  • Can you explain ImpersonateLoggedOnUser?!

    Posted by Legacy on 08/19/2002 12:00am

    Originally posted by: Alexi Jordanov

    Hi Sathya,
    Can you explain ImpersonateLoggedOnUser?!

    I look at MSDN but like previous times I did not find any clear explanation of this question.

    ...........
    The ImpersonateLoggedOnUser function lets the calling thread impersonate the security context of a logged-on user. The user is represented by a token handle.

    BOOL ImpersonateLoggedOnUser(
    HANDLE hToken // handle to a token that represents a logged-on
    // user
    );
    ...........

    MicroSoft love to write in way to confuse everybody :((
    I don't understand their policy.

    Regards, Alex

    Reply
  • Managing Windows Services from a Remote System

    Posted by Legacy on 08/18/2002 12:00am

    Originally posted by: WCarmazzi

    Hi
    I am a begianer at this and would like to know do I have to go into DOS to in still this or at windows?
    I would like to learn all I can to programing and would love all the help I can get ti do this.
    Thank you
    WCarmazzi

    Reply
  • Compilation errors

    Posted by Legacy on 08/02/2002 12:00am

    Originally posted by: Barry Cunney

    Hi Sathya
    I received the following errors when trying to compile your source file

    Compiling...
    ServiceInstaller.cpp
    d:\c++\serviceadmin\serviceinstaller.cpp(51) : error C2059: syntax error : ','
    d:\c++\serviceadmin\serviceinstaller.cpp(54) : error C2065: 'szSvcName' : undeclared identifier
    d:\c++\serviceadmin\serviceinstaller.cpp(142) : error C2065: 'szNetworkName' : undeclared identifier
    d:\c++\serviceadmin\serviceinstaller.cpp(146) : error C2059: syntax error : ','
    d:\c++\serviceadmin\serviceinstaller.cpp(185) : error C2065: 'ImpersonateUser' : undeclared identifier
    d:\c++\serviceadmin\serviceinstaller.cpp(195) : warning C4508: 'main' : function should return a value; 'void' return type assumed
    d:\c++\serviceadmin\serviceinstaller.cpp(206) : error C2065: 'path' : undeclared identifier
    d:\c++\serviceadmin\serviceinstaller.cpp(207) : error C2541: delete : cannot delete objects that are not pointers
    d:\c++\serviceadmin\serviceinstaller.cpp(219) : error C2660: 'StopSvc' : function does not take 2 parameters
    d:\c++\serviceadmin\serviceinstaller.cpp(223) : error C2660: 'QuerySvc' : function does not take 2 parameters
    d:\c++\serviceadmin\serviceinstaller.cpp(232) : error C2065: 'pName' : undeclared identifier
    d:\c++\serviceadmin\serviceinstaller.cpp(232) : error C2541: delete : cannot delete objects that are not pointers
    d:\c++\serviceadmin\serviceinstaller.cpp(234) : error C2562: 'main' : 'void' function returning a value
    d:\c++\serviceadmin\serviceinstaller.cpp(168) : see declaration of 'main'

    Reply
  • Loading, Please Wait ...

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

Top White Papers and Webcasts

  • Savvy enterprises are discovering that the cloud holds the power to transform IT processes and support business objectives. IT departments can use the cloud to redefine the continuum of development and operations—a process that is becoming known as DevOps. Download the Executive Brief DevOps: Why IT Operations Managers Should Care About the Cloud—prepared by Frost & Sullivan and sponsored by IBM—to learn how IBM SmartCloud Application services provide a robust platform that streamlines …

  • Live Event Date: August 14, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Data protection has long been considered "overhead" by many organizations in the past, many chalking it up to an insurance policy or an extended warranty you may never use. The realities of today make data protection a must-have, as we live in a data driven society. The digital assets we create, share, and collaborate with others on must be managed and protected for many purposes. Check out this upcoming eSeminar and join eVault Chief Technology …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds