I have a Windows service that tries to start a driver.
I get a handle to the driver and call StartService and it returns SUCCESS. But immediatelly after that, I do a QueryServiceStatusEx and dwCurrentState is 1 (SERVICE_STOPPED).
I then try to start it again (although StartService returned true the first time) and the same thing happens: StartService returns true but dwCurrentState is still 1. What's interesting is that if I put a one second sleep between the two starts, it works as expected.
The code is too big for me to post it entirely, but it looks something like this:
SERVICE_STATUS_PROCESS get_service_info(const wchar_t* szSvcName)
{
SERVICE_STATUS_PROCESS ssStatus{};
SC_HANDLE schSCManager = NULL;
SC_HANDLE schService = NULL;
DWORD dwBytesNeeded;
schSCManager = OpenSCManager(
NULL, // local computer
NULL, // servicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL != schSCManager)
{
// Get a handle to the service.
schService = OpenService(
schSCManager, // SCM database
szSvcName, // name of service
SERVICE_ALL_ACCESS); // full access
if (schService != NULL)
{
// Check the status in case the service is not stopped.
QueryServiceStatusEx(
schService, // handle to service
SC_STATUS_PROCESS_INFO, // information level
(LPBYTE)&ssStatus, // address of structure
sizeof(SERVICE_STATUS_PROCESS), // size of structure
&dwBytesNeeded); // size needed if buffer is too small
}
}
if (schService != NULL)
CloseServiceHandle(schService);
if (schSCManager != NULL)
CloseServiceHandle(schSCManager);
return ssStatus;
}
void ServiceEntryPoint()
{
if (StartStopService(name, true, 5000))
{
auto IsServiceStopped = [name]
{
SERVICE_STATUS_PROCESS status = get_service_info(name);
return status.dwCurrentState == SERVICE_STOPPED;
};
// check if the service was really started
if (IsServiceStopped())
{
//Sleep(1000);
StartStopService(name, true, 1000);
}
bool stillStopped = IsServiceStopped();
}
}
If I comment-out the Sleep(1000) line, get_service_info will return SERVICE_STATUS_PROCESS with the following values (for both calls):
DWORD dwServiceType = 1;
DWORD dwCurrentState = 1;
DWORD dwControlsAccepted = 0;
DWORD dwWin32ExitCode = 31;
DWORD dwServiceSpecificExitCode = 0;
DWORD dwCheckPoint = 0;
DWORD dwWaitHint = 0;
DWORD dwProcessId = 0;
DWORD dwServiceFlags = 0;
If I keep the Sleep(1000) line, get_service_info will return SERVICE_STATUS_PROCESS with the same values as above for the first call, but for the second call (after the sleep) it will have the following values:
DWORD dwServiceType = 1;
DWORD dwCurrentState = 4;
DWORD dwControlsAccepted = 1;
DWORD dwWin32ExitCode = 0;
DWORD dwServiceSpecificExitCode = 0;
DWORD dwCheckPoint = 0;
DWORD dwWaitHint = 0;
DWORD dwProcessId = 0;
DWORD dwServiceFlags = 0;
I have two questions:
Why does StartService return true if the service was not, in fact started? (not even pending start) I've added a __debugbreak to my driver's DriverEntry and it's not triggered the first time. It's as if I don't call StartService at all. Same behaviour for the second call, unless I add the sleep. When the sleep is present, the debugbreak is hit.
What possible explanation is there for the necessity of that sleep for this scenario to work? I've checked the Remarks section for StartService but I didn't find anything that gives any clear explanation for the this behaviour.
Note! ServiceEntryPoint is a function that is called is called at the beginning of the service that tries to start the driver (that fails to start).