My Training Period: xx hours. Before you begin, read someinstruction here.
|
The programming abilities for this session:
Note: For Multithreaded program examples, you have to set your project toMultithread project type.
Sending Control Requests to a Service
The following example uses the ControlService() function to send a control value to a running service. Different control values require different levels of access to the service object. For example, a service object handle must have SERVICE_STOP access to send the SERVICE_CONTROL_STOP control code. When ControlService() returns, a SERVICE_STATUS structure contains the latest status information for the service.
// ForWinXp #define _WIN32_WINNT 0x0501 #include <windows.h> #include <tchar.h> #include <stdio.h>
// This example try to stop a service by sending // a control code. It ONLY for services that don't // have dependencies...else it will fail // For services that have dependencies, use the previous example... // Parameters: // argc - the number of command-line arguments // argv[ ] - an array of command-line arguments
int main(int argc,char *argv[ ]) { SC_HANDLE schSCManager, schService; LPCTSTR lpszServiceName; SERVICE_STATUS ssStatus; DWORD fdwAccess; // Try sending stop control code... // Notifies a service that it should stop... DWORD fdwControl = SERVICE_CONTROL_STOP; |
// If no command line argument supplied...
if (argc < 2)
{
printf("usage: %s <ServiceName>\n", argv[0]);
printf("Try again.\n");
return 1;
}
lpszServiceName = argv[1];
// Open a handle to the SC Manager database...
schSCManager = OpenSCManager(
NULL, // local machine
NULL, // SERVICES_ACTIVE_DATABASE database is opened by default
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL == schSCManager)
printf("OpenSCManager(), Open a handle to the SC Manager database failed, error: %d.\n", GetLastError());
else
printf("OpenSCManager(), Open a handle to the SC Manager database looks OK.\n");
// The required service object access depends on the control...
switch (fdwControl)
{
case SERVICE_CONTROL_STOP:
fdwAccess = SERVICE_STOP;
break;
case SERVICE_CONTROL_PAUSE:
case SERVICE_CONTROL_CONTINUE:
fdwAccess = SERVICE_PAUSE_CONTINUE;
break;
case SERVICE_CONTROL_INTERROGATE:
fdwAccess = SERVICE_INTERROGATE;
break;
default:
fdwAccess = SERVICE_INTERROGATE;
}
// Open a handle to the service.
schService = OpenService(
schSCManager, // SCManager database
lpszServiceName, // name of service
fdwAccess); // specify the access right
if (schService == NULL)
printf("OpenService(), open a handle to a service with appropriate access failed, error: %d", GetLastError());
else
printf("OpenService(), open a handle to a service with appropriate access looks OK.\n");
// Send a control value to the service...
if (!ControlService(
schService, // handle to service
fdwControl, // control value to send, here is SERVICE_CONTROL_STOP
&ssStatus)) // address of status info
printf("ControlService(), sending control value to stop a service failed, error: %d\n", GetLastError());
else
printf("ControlService(), sending control value to a stop service looks OK.\n");
// Print the service status.
printf("\nStatus of %s: \n", lpszServiceName);
printf(" Service Type: 0x%x\n", ssStatus.dwServiceType);
printf(" Current State: 0x%x\n", ssStatus.dwCurrentState);
printf(" Controls Accepted: 0x%x\n", ssStatus.dwControlsAccepted);
printf(" Exit Code: %d\n", ssStatus.dwWin32ExitCode);
printf(" Service Specific Exit Code: %d\n", ssStatus.dwServiceSpecificExitCode);
printf(" Check Point: %d\n", ssStatus.dwCheckPoint);
printf(" Wait Hint: %d\n", ssStatus.dwWaitHint);
return 0;
}
Running the program at the command prompt, the following are sample outputs.
F:\myproject\myservices\Release>myservices dhcp
OpenSCManager(), Open a handle to the SC Manager database looks OK.
OpenService(), open a handle to a service with appropriate access looks OK.
ControlService(), sending control value to a stop service looks OK.
Status of dhcp:
Service Type: 0x20
Current State: 0x3
Controls Accepted: 0x5
Exit Code: 0
Service Specific Exit Code: 0
Check Point: 1
Wait Hint: 25000
F:\myproject\myservices\Release>myservices iisadmin
OpenSCManager(), Open a handle to the SC Manager database looks OK.
OpenService(), open a handle to a service with appropriate access looks OK.
ControlService(), sending control value to stop a service failed, error: 1051
Status of iisadmin:
Service Type: 0x20
Current State: 0x4
Controls Accepted: 0x7
Exit Code: 0
Service Specific Exit Code: 0
Check Point: 0
Wait Hint: 0
F:\myproject\myservices\Release>myservices w3svc
OpenSCManager(), Open a handle to the SC Manager database looks OK.
OpenService(), open a handle to a service with appropriate access looks OK.
ControlService(), sending control value to a stop service looks OK.
Status of w3svc:
Service Type: 0x20
Current State: 0x3
Controls Accepted: 0x7
Exit Code: 0
Service Specific Exit Code: 0
Check Point: 0
Wait Hint: 0
F:\myproject\myservices\Release>
By assuming the dhcp,w3svc and IISADMIN services are running, from the output, services that do not have dependencies such asdhcp and w3svc have been stopped but the IISADMIN (depend on thew3svc service) failed because its dependency on w3svc that should be stopped first. You can verify those services through the Servicescontrol panel. Error 1051 says that “A stop control has been sent to a service that other running services are dependent on”, (ERROR_DEPENDENT_SERVICES_RUNNING).
An application can create or modify the DACL associated with a Service object in order to control access. To retrieve the DACL associated with a service object, use the QueryServiceObjectSecurity() function. To set the DACL, use the SetServiceObjectSecurity() function. Any changes made to the SECURITY_DESCRIPTOR associated with the service object are persistent until the service is removed from the system.
The following sample code creates and sets a new DACL for the service specified on the command line. The sample code merges one Access Control Entry (ACE) to the existing DACL for the service. The new ACE grants the Guest account start, stop, delete, and READ_CONTROL access to the specified service. Access to the service can be modified by theAccessPermissions parameter passed to the BuildExplicitAccessWithName() function. Note that for critical and security aware services, this program example not a good one to be executed :o)
// ForWinXp
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <aclapi.h>
#include <stdio.h>
#include <tchar.h>
// Simple user defined error messages...
void DisplayError(DWORD dwError, LPTSTR pszAPI)
{
LPVOID lpvMessageBuffer;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, dwError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpvMessageBuffer, 0, NULL);
// Display the string.
_tprintf(TEXT("ERROR: API = %s.\n"), pszAPI);
_tprintf(TEXT(" error code = %u.\n"), dwError);
_tprintf(TEXT(" message = %s.\n"), (LPTSTR)lpvMessageBuffer);
// Free the buffer allocated by the system.
LocalFree(lpvMessageBuffer);
ExitProcess(dwError);
}
// Using _tmain(), TCHAR - A WCHAR if UNICODE/wide character is defined, a CHAR otherwise.
int _tmain(int argc, TCHAR *argv[ ])
{
BOOL bDaclPresent = FALSE;
BOOL bDaclDefaulted = FALSE;
DWORD dwError = 0;
DWORD dwSize = 0;
EXPLICIT_ACCESS ea;
PACL pacl = NULL;
PACL pNewAcl = NULL;
PSECURITY_DESCRIPTOR psd;
SC_HANDLE schManager = NULL;
SC_HANDLE schService = NULL;
SECURITY_DESCRIPTOR sd;
if (argc != 2)
{
_tprintf(TEXT("Usage: %s [service name].\n"), argv[0]);
_tprintf(TEXT("Please try again.\n"));
return 1;
}
// Obtain a handle to the Service Controller.
schManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if (schManager == NULL)
DisplayError(GetLastError(), TEXT("OpenSCManager()"));
else
_tprintf(TEXT("OpenSCManager(), handle to the service controller obtained successfully.\n"));
// Obtain a handle to the service.
schService = OpenService(schManager, argv[1], READ_CONTROL | WRITE_DAC);
if (schService == NULL)
DisplayError(GetLastError(), TEXT("OpenService()"));
else
_tprintf(TEXT("OpenSCManager(), handle to the service obtained successfully.\n"));
// Get the current security descriptor.
if (!QueryServiceObjectSecurity(schService, DACL_SECURITY_INFORMATION, &psd, 0, &dwSize))
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
psd = (PSECURITY_DESCRIPTOR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
if (psd == NULL)
{
DisplayError(0, TEXT("HeapAlloc()"));
// note HeapAlloc() does not support GetLastError()
}
else
_tprintf(TEXT("HeapAlloc() looks OK.\n"));
if (!QueryServiceObjectSecurity(schService, DACL_SECURITY_INFORMATION, psd, dwSize, &dwSize))
DisplayError(GetLastError(), TEXT("QueryServiceObjectSecurity()"));
else
_tprintf(TEXT("QueryServiceObjectSecurity() looks OK.\n"));
}
else
DisplayError(GetLastError(), TEXT("QueryServiceObjectSecurity()"));
}
// Get the DACL... if (!GetSecurityDescriptorDacl(psd, &bDaclPresent, &pacl, &bDaclDefaulted)) DisplayError(GetLastError(), TEXT("GetSecurityDescriptorDacl()")); else _tprintf(TEXT("GetSecurityDescriptorDacl() looks OK.\n"));
// Build the ACE. BuildExplicitAccessWithName(&ea, TEXT("GUEST"), SERVICE_START | SERVICE_STOP | READ_CONTROL | DELETE, SET_ACCESS, NO_INHERITANCE);
dwError = SetEntriesInAcl(1, &ea, pacl, &pNewAcl);
if (dwError != ERROR_SUCCESS) DisplayError(dwError, TEXT("SetEntriesInAcl()")); else _tprintf(TEXT("SetEntriesInAcl() looks OK.\n")); |
// Initialize a NEW Security Descriptor.
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
DisplayError(GetLastError(), TEXT("InitializeSecurityDescriptor()"));
else
_tprintf(TEXT("SetEntriesInAcl() looks OK.\n"));
// Set the new DACL in the Security Descriptor.
if (!SetSecurityDescriptorDacl(&sd, TRUE, pNewAcl, FALSE))
DisplayError(GetLastError(), TEXT("SetSecurityDescriptorDacl()"));
else
_tprintf(TEXT("SetSecurityDescriptorDacl() looks OK.\n"));
// Set the new DACL for the service object.
if (!SetServiceObjectSecurity(schService, DACL_SECURITY_INFORMATION, &sd))
DisplayError(GetLastError(), TEXT("SetServiceObjectSecurity()"));
else
_tprintf(TEXT("SetServiceObjectSecurity() looks OK.\n"));
// Close the handles.
if (!CloseServiceHandle(schManager))
DisplayError(GetLastError(), TEXT("CloseServiceHandle() schManager"));
else
_tprintf(TEXT("CloseServiceHandle() schManager looks OK.\n"));
if (!CloseServiceHandle(schService))
DisplayError(GetLastError(), TEXT("CloseServiceHandle() schService"));
else
_tprintf(TEXT("CloseServiceHandle() schService looks OK.\n"));
// Free buffers.
LocalFree((HLOCAL)pNewAcl);
HeapFree(GetProcessHeap(), 0, (LPVOID)psd);
return 0;
}

We test our program by changing the spooler service’s Discretionary Access List (DACL) by adding guest user with start, stop,delete, and READ_CONTROL access right. Tenouk failed to verify this…:o) Using the program property page didn’t show the sign! May need to enumerate…
--------------------------------------------------End examples---------------------------------------------
The following are functions and structures used with services.
The following functions are used or implemented by services.
Function | Description |
Handler() | An application-defined callback function used with the RegisterServiceCtrlHandler() function. |
HandlerEx() | An application-defined callback function used with the RegisterServiceCtrlHandlerEx() function. |
RegisterServiceCtrlHandler() | Register a function to handle service control requests for an application. |
RegisterServiceCtrlHandlerEx() | Register a function to handle service control requests for an application. |
ServiceMain() | An application-defined function that serves as the starting point for a service. |
SetServiceStatus() | Updates the service control manager's status information for the calling service. |
StartServiceCtrlDispatcher() | Connects the main thread of a service process to the service control manager. |
Table 1 | |
The following functions are used by programs that control or configure services.
Function | Description |
ChangeServiceConfig() | Changes the configuration parameters of a service. |
ChangeServiceConfig2() | Changes the optional configuration parameters of a service. |
CloseServiceHandle() | Closes the specified handle to a service control manager object or a service object. |
ControlService() | Sends a control code to a service. |
CreateService() | Creates a service object and adds it to the specified service control manager database. |
DeleteService() | Marks the specified service for deletion from the service control manager database. |
EnumDependentServices() | Retrieves the name and status of each service that depends on the specified service. |
EnumServicesStatus() | Enumerates services in the specified service control manager database. |
EnumServicesStatusEx() | Enumerates services in the specified service control manager database based on the specified information level. |
GetServiceDisplayName() | Retrieves the display name of the specified service. |
GetServiceKeyName() | Retrieves the service name of the specified service. |
LockServiceDatabase() | Requests ownership of the service control manager database lock. |
NotifyBootConfigStatus() | Reports the boot status to the service control manager. |
OpenSCManager() | Establishes a connection to the service control manager on the specified computer and opens the specified service control manager database. |
OpenService() | Opens an existing service. |
QueryServiceConfig() | Retrieves the configuration parameters of the specified service. |
QueryServiceConfig2() | Retrieves the optional configuration parameters of the specified service. |
QueryServiceLockStatus() | Retrieves the lock status of the specified service control manager database. |
QueryServiceObjectSecurity() | Retrieves a copy of the security descriptor associated with a service object. |
QueryServiceStatus() | Retrieves the current status of the specified service. |
QueryServiceStatusEx() | Retrieves the current status of the specified service based on the specified information level. |
SetServiceBits() | Registers a service type with the service control manager and the Server service. |
SetServiceObjectSecurity() | Sets the security descriptor of a service object. |
StartService() | Starts a service. |
UnlockServiceDatabase() | Unlocks a service control manager database by releasing the specified lock. |
Table 2 | |
Applications can use the following functions for manipulating window station objects.
Function | Description |
CloseWindowStation() | Closes a specified window station. |
CreateWindowStation() | Creates a new window station. |
EnumWindowStations() | Enumerates the window stations in the system by repeatedly calling an application-defined EnumWindowStationProc() callback function. |
GetProcessWindowStation() | Retrieves a handle to the window station assigned to the calling process. |
GetUserObjectInformation() | Gets information about a window station or desktop object. |
GetUserObjectSecurity() | Gets security information for a window station or desktop object. |
OpenWindowStation() | Opens a handle to an existing window station. |
SetProcessWindowStation() | Assigns a specified window station to the calling process. |
SetUserObjectInformation() | Sets information about a window station or desktop object. |
SetUserObjectSecurity() | Sets security information for a window station or desktop object. |
Table 3 | |
Applications can use the following functions for manipulating desktop objects.
Function | Description |
CloseDesktop() | Closes a specified desktop. |
CreateDesktop() | Creates a new desktop on a specified window station. |
EnumDesktops() | Enumerates the desktops on a specified window station by repeatedly calling an application-definedEnumDesktopProc() callback function. |
EnumDesktopWindows() | Enumerates the windows on a specified desktop by repeatedly calling an application-defined EnumWindowsProc() callback function. |
GetThreadDesktop() | Returns a handle to the desktop assigned to the calling thread. |
GetUserObjectInformation() | Gets information about a window station or desktop object. |
GetUserObjectSecurity() | Gets security information for a window station or desktop object. |
OpenDesktop() | Opens a handle to an existing desktop. |
OpenInputDesktop() | Opens a handle to the desktop that receives user input. |
SetThreadDesktop() | Assigns a specified desktop to the calling thread. |
SetUserObjectInformation() | Sets information about a window station or desktop object. |
SetUserObjectSecurity() | Sets security information for a window station or desktop object. |
Table 4 | |
The following structures are used with services:
-------------End Windows Services Examples-------------
Further reading and digging:
Structure, enum, union and typedef story can be foundC/C++ struct, enum, union & typedef.
For Multi bytes, Unicode characters and Localization please refer to Locale, wide characters & Unicode (Story) and Windows users & groups programming tutorials (Implementation).
Windows data types areWindows data types.
Microsoft Visual C++, online MSDN.
ReactOS - Windows binary compatible OS - C/C++ source code repository, Doxygen.
Check the best selling C / C++ and Windows books at Amazon.com.