Windows StartService возвращает true, но служба фактически остановлена - PullRequest
0 голосов
/ 07 июня 2019

У меня есть служба 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, если служба не была запущена?(даже не в ожидании запуска) Я добавил __debugbreak в DriverEntry моего драйвера, и он не срабатывает в первый раз.Как будто я вообще не вызываю StartService.То же поведение для второго вызова, если я не добавлю режим сна.При наличии сна происходит отладка.

  2. Какое возможное объяснение необходимости этого сна для работы этого сценария?Я проверил раздел «Примечания» для StartService, но не нашел ничего, что давало бы четкое объяснение этому поведению.

Примечание!ServiceEntryPoint - это вызываемая функция, вызываемая в начале службы, которая пытается запустить драйвер (который не запускается).

Ответы [ 2 ]

1 голос
/ 07 июня 2019

StartService() возвращение true не означает, что служба фактически была запущена, только то, что запрос был успешно передан SCM, и были предприняты шаги для запуска службы. Служба несет ответственность за то, чтобы сообщить о своем фактическом статусе обратно в СКМ. Если StartService() возвращает true, вам нужно вызывать QueryServiceStatus/Ex() в цикле, пока служба не перестанет находиться в состоянии «ожидания» или пока не истечет время ожидания сообщения CheckPoint. Это ясно объяснено в StartService документации :

Когда служба запускается, диспетчер управления службами (SCM) запускает процесс службы, если это необходимо. Если указанная служба совместно использует процесс с другими службами, требуемый процесс может уже существовать. Функция StartService не ожидает первого обновления статуса новой службы , поскольку это может занять некоторое время. Вместо этого он возвращает, когда SCM получает уведомление от диспетчера управления службами о том, что поток ServiceMain для этой службы был успешно создан.

SCM устанавливает следующие значения состояния по умолчанию перед возвратом из StartService:

  • Текущее состояние службы установлено на SERVICE_START_PENDING.
  • Принятые элементы управления установлены на ноль (ноль).
  • Значение CheckPoint установлено на ноль.
  • Время ожидания равно 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);
}
0 голосов
/ 07 июня 2019

Одна возможная последовательность событий, которая может объяснить, что вы видите:

  1. Вы звоните StartService
  2. Ваш сервис запускается
  3. Ваш сервис быстро падает
  4. Вы вызываете QueryServiceStatusEx, который возвращает SERVICE_STOPPED
  5. Windows перезапускает службу (из-за Настройки восстановления )
  6. Ваша служба запускается нормально (на этот раз не падает)

Добавление Sleep () задерживает проверку до тех пор, пока сервис не запустится нормально во второй раз.

Проверьте Системную область журналов событий , чтобы увидеть, если этопроисходит.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...