我有一个Windows服务试图启动驱动程序。
我获得了驱动程序的句柄并调用StartService,它返回SUCCESS。 但是在那之后,我立即执行QueryServiceStatusEx,并且dwCurrentState为1(SERVICE_STOPPED)。
然后我尝试再次启动它(尽管StartService第一次返回true),并且发生了相同的事情:StartService返回true,但dwCurrentState仍然为1。 有趣的是,如果我在两次开始之间放置一秒钟的睡眠,它将按预期工作。
对于我来说,代码太大了,无法完全发布,但是看起来像这样:
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();
}
}
如果我注释掉Sleep(1000)行,则get_service_info将返回带有以下值的SERVICE_STATUS_PROCESS(对于两个调用):
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;
如果我保留Sleep(1000)行,则get_service_info将为第一个调用返回具有与上述相同的值的SERVICE_STATUS_PROCESS,但是对于第二个调用(在休眠之后),它将具有以下值:
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;
我有两个问题:
如果服务未启动,实际上启动了,为什么StartService返回true? (甚至没有等待启动) 我在驱动程序的DriverEntry中添加了__debugbreak,它不是第一次触发。好像我根本不调用StartService。 除非我添加睡眠,否则第二个呼叫的行为相同。当出现睡眠时,将触发调试中断。
对于这种情况下,必须睡眠,有什么可能的解释? 我已经检查了StartService的“备注”部分,但没有找到任何可以对此行为进行清楚解释的内容。
注意! ServiceEntryPoint是一个在尝试启动驱动程序(无法启动)的服务开始时调用的函数。
答案 0 :(得分:1)
StartService()
返回true并不意味着该服务实际上已经启动,仅是该请求已成功发送给SCM,并且已采取步骤开始启动该服务。服务负责将其实际状态报告给SCM。如果StartService()
返回true,则需要循环调用QueryServiceStatus/Ex()
,直到服务不再处于“挂起”状态或报告的CheckPoint
超时为止。 StartService
documentation中对此有清楚的解释:
启动服务时,如有必要,服务控制管理器(SCM)会生成服务进程。如果指定的服务与其他服务共享一个进程,则所需的进程可能已经存在。 StartService函数不会等待新服务的首次状态更新,因为这可能需要一段时间。相反,当SCM从服务控制调度程序收到有关成功创建此服务的ServiceMain线程的通知时,它将返回。
在从StartService返回之前,SCM设置以下默认状态值:
- 该服务的当前状态设置为SERVICE_START_PENDING。
- 接受的控件设置为无(零)。
- CheckPoint值设置为零。
- WaitHint时间设置为2秒。
调用过程可以通过定期调用QueryServiceStatus函数来查询新服务的状态来确定新服务是否已完成其初始化。
Microsoft甚至提供了完整的示例:
//
// Purpose:
// Starts the service if possible.
//
// Parameters:
// None
//
// Return value:
// None
//
VOID __stdcall DoStartSvc()
{
SERVICE_STATUS_PROCESS ssStatus;
DWORD dwOldCheckPoint;
DWORD dwStartTickCount;
DWORD dwWaitTime;
DWORD dwBytesNeeded;
// Get a handle to the SCM database.
schSCManager = OpenSCManager(
NULL, // local computer
NULL, // servicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL == schSCManager)
{
printf("OpenSCManager failed (%d)\n", GetLastError());
return;
}
// Get a handle to the service.
schService = OpenService(
schSCManager, // SCM database
szSvcName, // name of service
SERVICE_ALL_ACCESS); // full access
if (schService == NULL)
{
printf("OpenService failed (%d)\n", GetLastError());
CloseServiceHandle(schSCManager);
return;
}
// Check the status in case the service is not stopped.
if (!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
{
printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return;
}
// Check if the service is already running. It would be possible
// to stop the service here, but for simplicity this example just returns.
if(ssStatus.dwCurrentState != SERVICE_STOPPED && ssStatus.dwCurrentState != SERVICE_STOP_PENDING)
{
printf("Cannot start the service because it is already running\n");
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return;
}
// Save the tick count and initial checkpoint.
dwStartTickCount = GetTickCount();
dwOldCheckPoint = ssStatus.dwCheckPoint;
// Wait for the service to stop before attempting to start it.
while (ssStatus.dwCurrentState == SERVICE_STOP_PENDING)
{
// Do not wait longer than the wait hint. A good interval is
// one-tenth of the wait hint but not less than 1 second
// and not more than 10 seconds.
dwWaitTime = ssStatus.dwWaitHint / 10;
if( dwWaitTime < 1000 )
dwWaitTime = 1000;
else if ( dwWaitTime > 10000 )
dwWaitTime = 10000;
Sleep( dwWaitTime );
// Check the status until the service is no longer stop pending.
if (!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
{
printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return;
}
if ( ssStatus.dwCheckPoint > dwOldCheckPoint )
{
// Continue to wait and check.
dwStartTickCount = GetTickCount();
dwOldCheckPoint = ssStatus.dwCheckPoint;
}
else
{
if(GetTickCount()-dwStartTickCount > ssStatus.dwWaitHint)
{
printf("Timeout waiting for service to stop\n");
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return;
}
}
}
// Attempt to start the service.
if (!StartService(
schService, // handle to service
0, // number of arguments
NULL) ) // no arguments
{
printf("StartService failed (%d)\n", GetLastError());
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return;
}
else printf("Service start pending...\n");
// Check the status until the service is no longer start pending.
if (!QueryServiceStatusEx(
schService, // handle to service
SC_STATUS_PROCESS_INFO, // info level
(LPBYTE) &ssStatus, // address of structure
sizeof(SERVICE_STATUS_PROCESS), // size of structure
&dwBytesNeeded ) ) // if buffer too small
{
printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return;
}
// Save the tick count and initial checkpoint.
dwStartTickCount = GetTickCount();
dwOldCheckPoint = ssStatus.dwCheckPoint;
while (ssStatus.dwCurrentState == SERVICE_START_PENDING)
{
// Do not wait longer than the wait hint. A good interval is
// one-tenth the wait hint, but no less than 1 second and no
// more than 10 seconds.
dwWaitTime = ssStatus.dwWaitHint / 10;
if( dwWaitTime < 1000 )
dwWaitTime = 1000;
else if ( dwWaitTime > 10000 )
dwWaitTime = 10000;
Sleep( dwWaitTime );
// Check the status again.
if (!QueryServiceStatusEx(
schService, // handle to service
SC_STATUS_PROCESS_INFO, // info level
(LPBYTE) &ssStatus, // address of structure
sizeof(SERVICE_STATUS_PROCESS), // size of structure
&dwBytesNeeded ) ) // if buffer too small
{
printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
break;
}
if ( ssStatus.dwCheckPoint > dwOldCheckPoint )
{
// Continue to wait and check.
dwStartTickCount = GetTickCount();
dwOldCheckPoint = ssStatus.dwCheckPoint;
}
else
{
if(GetTickCount()-dwStartTickCount > ssStatus.dwWaitHint)
{
// No progress made within the wait hint.
break;
}
}
}
// Determine whether the service is running.
if (ssStatus.dwCurrentState == SERVICE_RUNNING)
{
printf("Service started successfully.\n");
}
else
{
printf("Service not started. \n");
printf(" Current State: %d\n", ssStatus.dwCurrentState);
printf(" Exit Code: %d\n", ssStatus.dwWin32ExitCode);
printf(" Check Point: %d\n", ssStatus.dwCheckPoint);
printf(" Wait Hint: %d\n", ssStatus.dwWaitHint);
}
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
}
答案 1 :(得分:0)
一个可能解释您所见内容的事件序列:
添加Sleep()会将检查延迟到服务第二次正常启动之后。
检查System area of the Event logs,看看是否正在发生这种情况。