Как определить, что мое приложение работает как служба или в интерактивном сеансе? - PullRequest
15 голосов
/ 19 апреля 2010

Я пишу приложение, которое может работать как служба или автономно, но я хочу определить, было ли приложение выполнено как служба или в обычном сеансе пользователя.

Ответы [ 7 ]

10 голосов
/ 11 декабря 2012

Если это приложение C ++, где-то в вашем коде запуска вы должны вызвать StartServiceCtrlDispatcher . Если происходит сбой и GetLastError() возвращает ERROR_FAILED_SERVICE_CONTROLLER_CONNECT, приложение не было запущено в качестве службы.

7 голосов
/ 04 мая 2010

Другой вариант - использовать System.Environment.UserInteractive. http://msdn.microsoft.com/en-us/library/system.environment.userinteractive.aspx

Обновление : Чтобы компенсировать публикацию ответа .NET на тему C ++, я предоставляю реализацию C на основе реализации .NET.

BOOL IsUserInteractive()
{
   BOOL bIsUserInteractive = TRUE;

   HWINSTA hWinStation = GetProcessWindowStation();
   if (hWinStation != NULL)
   {     
     USEROBJECTFLAGS uof = {0};     
     if (GetUserObjectInformation(hWinStation, UOI_FLAGS, &uof, sizeof(USEROBJECTFLAGS), NULL) && ((uof.dwFlags & WSF_VISIBLE) == 0))
     {
       bIsUserInteractive = FALSE;
     }     
   }
   return bIsUserInteractive;
}
6 голосов
/ 19 апреля 2010

Я думаю, вы можете запросить токен процесса для членства в Интерактивной группе.

От http://support.microsoft.com/kb/243330:

SID: S-1-5-4

Имя: Интерактивное

Описание: Группа, в которую входят все пользователи, вошедшие в систему в интерактивном режиме.Членство контролируется операционной системой.

Вызвать GetTokenInformation с TokenGroups, чтобы получить группы, связанные с учетной записью, под которой выполняется процесс, затем выполнить итерацию по сторонам в поисках интерактивного sid.

Я нашел хороший кусок кода в http://marc.info/?l=openssl-dev&m=104401851331452&w=2

3 голосов
/ 19 апреля 2010

Я думаю, что вы можете основывать свое обнаружение на том факте, что службы работают с SessionID 0, а учетные записи пользователей имеют другие значения (например, 1).

 bServiceMode = false;
 SessionID=-1;
 Size=0;
 hToken = NULL;
 (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
     GetLastError();

 if (!GetTokenInformation(hToken, TokenSessionId, &SessionID, sizeof(SessionID), &Size) || !Size)
     return FALSE;
 if(SessionID==0)
    bServiceMode = true;
2 голосов
/ 28 февраля 2012

Все вышеперечисленные методы ненадежны. Идентификатор сеанса не обязательно равен 0 (по крайней мере, в предыдущих версиях Windows), Window Station имеет значение только WinSta0 , если «если служба работает в учетной записи LocalSystem и взаимодействует с рабочим столом». Подробнее см. KB171890 .

Один из способов определения того, выполняется ли процесс как служба, заключается в следующем:

Обратите внимание: С помощью этого метода будут обнаружены только службы, установленные в базе данных служб, но не дочерние процессы, запущенные процессом службы, которые не зарегистрированы в базе данных. В этом случае это также не будет системная служба. * 1.

bool IsRunningAsService(unsigned int Pid) {
    bool Result = false;
    SC_HANDLE hScm = OpenSCManager(
        0,
        SERVICES_ACTIVE_DATABASE,
        SC_MANAGER_ENUMERATE_SERVICE
    );
    if (hScm == 0) {
        return Result;
    }
    DWORD ServicesBufferRequired = 0;
    DWORD ResumeHandle = 0;

    DWORD ServicesBufferSize = 0;
    DWORD ServicesCount = 0;
    ENUM_SERVICE_STATUS_PROCESS* ServicesBuffer = 0;

    EnumServicesStatusEx(hScm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, 
    SERVICE_ACTIVE, 0, 0, &ServicesBufferRequired, &ServicesCount, &ResumeHandle, 0);
    // Todo: Error handling (GetLastError() results are currently bogus?)
    ServicesBuffer = (ENUM_SERVICE_STATUS_PROCESS*) new 
    char[ServicesBufferRequired];
    ServicesBufferSize = ServicesBufferRequired;
    EnumServicesStatusEx(hScm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, 
    SERVICE_ACTIVE, (LPBYTE) ServicesBuffer, ServicesBufferSize, 
    &ServicesBufferRequired, &ServicesCount, &ResumeHandle, 0);

    ENUM_SERVICE_STATUS_PROCESS* ServicesBufferPtr  = ServicesBuffer;
    while (ServicesCount--) {
        if (ServicesBufferPtr->ServiceStatusProcess.dwProcessId == Pid) {
            Result = true;
            break;
        }
        ServicesBufferPtr++;
    }
    delete [] ServicesBuffer;

    CloseServiceHandle(hScm);
    return Result;
}

Обратите внимание, что приведенный выше код должен содержать дополнительную обработку ошибок, особенно он должен вызываться в цикле, пока EnumServicesStatusEx не вернет ненулевое значение. Но, к сожалению, как я выяснил, GetLastError() всегда возвращает 1 (ERROR_INVALID_FUNCTION) даже если буфер правильно заполнен данными.

* 1: Тестирование, если процесс был запущен службой: в этом случае вы можете использовать комбинацию других решений. Можно проверить, есть ли у процесса родительский (дедушкин ...) процесс, зарегистрированный как сервис. Вы можете использовать CreateToolhelp32Snapshot API для этой цели. Однако, если родительский процесс уже убит, все становится сложнее. Я уверен, что есть какие-либо недокументированные настройки, которые могут определить, работает ли процесс как служба, кроме обычных подозреваемых, таких как SessionId = 0, WindowStation = 0, WSF_VISIBLE, Нет членства в интерактивной группе ...

1 голос
/ 28 августа 2016

Существует простой способ определить, запущено ли приложение как служба.Когда вы создаете сервис с помощью CreateService , передайте в параметр lpBinaryPathName некоторый дополнительный аргумент, скажем -s , который будет указывать, что ваше приложение запускается как сервис.Затем в приложении вы можете проверить этот аргумент.Это также может помочь при отладке, потому что вы можете протестировать функциональность вашего сервиса, фактически не работая как сервис.Если StartServiceCtrlDispatcher завершается неудачно с ERROR_FAILED_SERVICE_CONTROLLER_CONNECT , вы можете установить флаг, указывающий, что программа работает как консольное приложение, моделирующее режим службы, поэтому вы можете пропустить вызовы API, связанные со службой, используя этот флаг.

0 голосов
/ 04 мая 2010

Процесс в обычном сеансе пользователя всегда имеет оконную станцию ​​с именем WinSta0 .

wchar_t buffer[256] = {0};
DWORD length = 0;
GetUserObjectInformation(GetProcessWindowStation(), UOI_NAME, buffer, 256, &length);
if (!lstricmp(buffer, "WinSta0")) {
  // normal user session
} else {
  // service session
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...