Как определить, что мое приложение работает как служба или в интерактивном сеансе?

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

Ответы

Ответ 1

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

Из http://support.microsoft.com/kb/243330:

SID: S-1-5-4

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

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

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

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

Ответ 2

Если это приложение на С++, где-то в вашем стартовом коде вам нужно вызвать StartServiceCtrlDispatcher. Если это не удается, приложение не запускается как служба.

Ответ 3

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

Обновить. Чтобы компенсировать отправку ответа .NET на тему С++, я предоставляю реализацию 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;
}

Ответ 4

Я думаю, вы можете основывать свое обнаружение на том, что службы работают с 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;

Ответ 5

Все вышеперечисленные методы ненадежны. Идентификатор сеанса не обязательно 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: Тестирование, если процесс был запущен службой: в этом случае вы могли бы использовать комбинацию других решений. Можно проверить, если процесс имеет родительский (grandparent...) процесс, который является зарегистрированным в качестве службы. Для этой цели вы можете использовать API CreateToolhelp32Snapshot. Однако, если родительский процесс уже убит, все становится трудно. Я уверен, что есть недокументированные настройки, которые могут определять, работает ли процесс как служба, кроме обычных подозреваемых, таких как SessionId = 0, WindowStation = 0, WSF_VISIBLE, No Interactive Group Members...

Ответ 6

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

Ответ 7

Процесс в обычном пользовательском сеансе всегда имеет оконную станцию ​​с именем 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
}