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);
}

Comments
corrected the source..
Posted by maqubex on 07/12/2012 11:32amone 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
ReplyHow to connect a remote system?
Posted by pco96 on 03/02/2004 08:55pmI 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"?
ReplyGetServiceKeyName
Posted by Legacy on 04/11/2003 12:00amOriginally 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.
ReplyTerrific!!
Posted by Legacy on 03/06/2003 12:00amOriginally 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!
ReplyWindows XP ?
Posted by Legacy on 10/18/2002 12:00amOriginally 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
ReplyCan you run an exe from inside a service?
Posted by Legacy on 08/22/2002 12:00amOriginally posted by: Ideshini
ReplyHow to set the "Log On" Identity of a Service Programmatically
Posted by Legacy on 08/20/2002 12:00amOriginally 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.
ReplyCan you explain ImpersonateLoggedOnUser?!
Posted by Legacy on 08/19/2002 12:00amOriginally 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
ReplyManaging Windows Services from a Remote System
Posted by Legacy on 08/18/2002 12:00amOriginally posted by: WCarmazzi
Hi
ReplyI 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
Compilation errors
Posted by Legacy on 08/02/2002 12:00amOriginally posted by: Barry Cunney
Hi Sathya
I received the following errors when trying to compile your source file
Compiling...
ReplyServiceInstaller.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'
Loading, Please Wait ...