Windows StartService返回true,但服务实际上已停止

时间:2019-06-07 08:37:05

标签: winapi windows-services

我有一个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;

我有两个问题:

  1. 如果服务未启动,实际上启动了,为什么StartService返回true? (甚至没有等待启动) 我在驱动程序的DriverEntry中添加了__debugbreak,它不是第一次触发。好像我根本不调用StartService。 除非我添加睡眠,否则第二个呼叫的行为相同。当出现睡眠时,将触发调试中断。

  2. 对于这种情况下,必须睡眠,有什么可能的解释? 我已经检查了StartService的“备注”部分,但没有找到任何可以对此行为进行清楚解释的内容。

注意! ServiceEntryPoint是一个在尝试启动驱动程序(无法启动)的服务开始时调用的函数。

2 个答案:

答案 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甚至提供了完整的示例:

Starting a Service

//
// 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)

一个可能解释您所见内容的事件序列:

  1. 您致电StartService
  2. 您的服务开始
  3. 您的服务迅速崩溃
  4. 您调用QueryServiceStatusEx,它返回SERVICE_STOPPED
  5. Windows重新启动服务(由于Recovery settings
  6. 您的服务正常启动(这次不会崩溃)

添加Sleep()会将检查延迟到服务第二次正常启动之后。

检查System area of the Event logs,看看是否正在发生这种情况。

相关问题