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 MANAGERn"); PrintError(GetLastError()); return; } if(!strcmp(argv[1],"install")) { //to install we need the path for the executable printf("Please enter path of the Executablen"); 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 commandn"); } ::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 Errorn"); } else { printf("Error : %sn", 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 SERVICEn"); 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 SERVICEn"); 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 DELETEDn"); }
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 SERVICEn");
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 SERVICEn"); 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 SERVICEn"); return; } SERVICE_STATUS status; if(!::ControlService(hService, SERVICE_CONTROL_STOP, &status)) printf("ERROR: COULDN'T STOP SERVICEn"); ::CloseServiceHandle(hService); QuerySvc(szNetworkName, szSvcName); }