如何检测我的应用程序是作为服务运行还是在交互式会话中运行?

时间:2010-04-19 15:51:09

标签: c++ windows-services

我正在编写一个能够作为服务或独立运行的应用程序,但我想检测应用程序是作为服务执行还是作为普通用户会话执行。

7 个答案:

答案 0 :(得分:10)

如果这是一个C ++应用程序,在启动代码中的某个地方,您必须调用StartServiceCtrlDispatcher。如果失败且GetLastError()返回ERROR_FAILED_SERVICE_CONTROLLER_CONNECT,则该应用尚未作为服务启动。

答案 1 :(得分:7)

另一种选择是使用System.Environment.UserInteractive http://msdn.microsoft.com/en-us/library/system.environment.userinteractive.aspx

更新:为了弥补发布C ++主题的.NET答案,我提供了一个基于.NET实现的C实现。

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;
}

答案 2 :(得分:6)

我认为您可以在交互组中查询流程令牌以获取成员资格。

来自http://support.microsoft.com/kb/243330

  

SID:S-1-5-4

     

名称:互动

     

描述:包含以交互方式登录的所有用户的组。成员资格由操作系统控制。

使用TokenGroups调用GetTokenInformation以获取与运行该进程的帐户关联的组,然后迭代查找Interactive sid的sid。

我在http://marc.info/?l=openssl-dev&m=104401851331452&w=2

找到了一大堆代码

答案 3 :(得分:3)

我认为您可以根据服务使用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;

答案 4 :(得分:2)

以上所有方法都不可靠。会话ID不一定是0(至少在之前的Windows版本中不是这样),如果“如果服务在LocalSystem帐户中运行并且正在与桌面交互”,则Window Station仅 WinSta0 。 有关详细信息,请参阅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,No Interactive Group成员资格......

答案 5 :(得分:1)

有一种简单的方法可以检测应用程序是否作为服务启动。当您使用CreateService创建服务时,请传入 lpBinaryPathName 参数一些其他参数,例如 -s ,这表示您的应用程序是作为服务启动的。然后在应用程序中,您可以检查此参数。它在调试时也可能有所帮助,因为您可以在不实际作为服务运行的情况下测试您的服务功能。如果StartServiceCtrlDispatcher ERROR_FAILED_SERVICE_CONTROLLER_CONNECT 而失败,您可以设置一个标志,指示程序正在作为模拟服务模式的控制台应用程序运行,因此您可以使用此标志跳过与服务相关的API调用。

答案 6 :(得分:-1)

普通用户会话中的进程始终有一个名为 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
}