Introduction
This article shows how to interact, control and configure Windows services from a native application written in C++. The purpose of the article is to present some of the Windows API for interacting with services and build reusable wrapping components in C++ along the way. However, this will not be a comprehensive walk through all the Windows service APIs.
For a complete Windows API reference, see
Service Functions
.
General Procedure
In order to interact with an existing service, you must follow these general steps:
Call
OpenSCManager
to establish a connection to the service control manager on a specified computer and open the specified service control manager database. With this call, you must specify the
desire access
. To enumerate services, you must specify
SC_MANAGER_ENUMERATE_SERVICE
. To open services, query, change statuses, etc., you need
SC_MANAGER_CONNECT
. For additional operations such as creating services or locking the service database, you need to specify other access right codes. However, in this case, the process must run elevated with administrator privileges, otherwise the call will fail.
Call
OpenService
to open an existing service.
Make calls to functions that query, control or configure the service (such as
QueryServiceStatusEx
to get the service status,
QueryServiceConfig
to get the service configuration data,
ChangeServiceConfig
to change the service configuration data, or
EnumDependentServices
to enumerate dependent services).
Call
CloseServiceHandle
to close the handles for the service control manager (the handle returned by
OpenSCManager
) and the service (the handle returned by
OpenService
).
Examples using the C++ API
The attached source code contains types and functions written in C++ that wrap the C Windows API, making it easier to use in a C++ application. These components will enable us to write code like the following.
Example for enumerating all services on the local computer:
auto
services = ServiceEnumerator::EnumerateServices();
for
(
auto
const
& s : services)
std::wcout
<
<
"
Name: "
<
<
s.ServiceName
<
<
std::endl
<
<
"
Display: "
<
<
s.DisplayName
<
<
std::endl
<
<
"
Status: "
<
<
ServiceStatusToString
(
static_cast
<
ServiceStatus
>
(s.Status.dwCurrentState))
<
<
std::endl
<
<
"
--------------------------"
<
<
std::endl
;
Example for opening a service on the local computer, reading and updating its configuration, reading and changing its status:
auto
service = ServiceController{ L
"
LanmanWorkstation"
};
auto
print_status = [&service]() {
std::wcout
<
<
"
Status:
"
<
<
ServiceStatusToString(service.GetStatus())
<
<
std::endl
;
auto
print_config = [](ServiceConfig
const
config) {
std::wcout
<
<
"
---------------------"
<
<
std::endl
;
std::wcout
<
<
"
Start name: "
<
<
config.GetStartName()
<
<
std::endl
;
std::wcout
<
<
"
Display name: "
<
<
config.GetDisplayName()
<
<
std::endl
;
std::wcout
<
<
"
Description: "
<
<
config.GetDescription()
<
<
std::endl
;
std::wcout
<
<
"
Type:
"
<
<
ServiceTypeToString(config.GetType())
<
<
std::endl
;
std::wcout
<
<
"
Start type:
"
<
<
ServiceStartTypeToString(config.GetStartType())
<
<
std::endl
;
std::wcout
<
<
"
Error control:
"
<
<
ServiceErrorControlToString(config.GetErrorControl())
<
<
std::endl
;
std::wcout
<
<
"
Binary path: "
<
<
config.GetBinaryPathName()
<
<
std::endl
;
std::wcout
<
<
"
Load ordering group: "
<
<
config.GetLoadOrderingGroup()
<
<
std::endl
;
std::wcout
<
<
"
Tag ID: "
<
<
config.GetTagId()
<
<
std::endl
;
std::wcout
<
<
"
Dependencies: "
;
for
(
auto
const
& d : config.GetDependencies())
std::wcout
<
<
d
<
<
"
, "
;
std::wcout
<
<
std::endl
;
std::wcout
<
<
"
---------------------"
<
<
std::endl
;