简体   繁体   English

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

[英]How do I detect that my application is running as service or in an interactive session?

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

If this is a C++ application, somewhere in your startup code you have to call StartServiceCtrlDispatcher . 如果这是一个C ++应用程序,在启动代码中的某个地方,您必须调用StartServiceCtrlDispatcher If it fails and GetLastError() returns ERROR_FAILED_SERVICE_CONTROLLER_CONNECT , the app has not been started as a service. 如果失败并且GetLastError()返回ERROR_FAILED_SERVICE_CONTROLLER_CONNECT ,则该应用程序尚未作为服务启动。

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

Update : To make up for posting a .NET answer to a C++ topic, I provide a C implementation based on the .NET implementation. 更新 :为了弥补发布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;
}

I think you can query the process token for membership in the Interactive group. 我认为您可以在Interactive组中查询进程令牌以获取成员资格。

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

SID: S-1-5-4 SID:S-1-5-4

Name: Interactive 名称:互动

Description: A group that includes all users that have logged on interactively. 描述:包含以交互方式登录的所有用户的组。 Membership is controlled by the operating system. 成员资格由操作系统控制。

Call GetTokenInformation with TokenGroups to get the groups associated with the account under which the process is running, then iterate over the sids looking for the Interactive sid. 使用TokenGroups调用GetTokenInformation以获取与运行该进程的帐户关联的组,然后遍历查找Interactive sid的sid。

I found a nice chunk of code at http://marc.info/?l=openssl-dev&m=104401851331452&w=2 我在http://marc.info/?l=openssl-dev&m=104401851331452&w=2找到了一大堆代码

I think you can base your detection on the fact that services are running with SessionID 0 and user accounts do have other values (like 1). 我认为您可以根据服务使用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;

All of the above methods are unreliable. 所有上述方法都不可靠。 Session Id is not necessarily 0 (at least not in previous Windows versions), Window Station is only WinSta0 if "If the service is running in the LocalSystem account and is interacting with the desktop". 会话ID不一定是0(至少在以前的Windows版本中没有),如果“如果服务在LocalSystem帐户中运行并且正在与桌面交互”,则Window Station仅为WinSta0 See KB171890 for more details. 有关详细信息,请参阅KB171890

One method for detecting if a process is running as service is following: 检测进程是否作为服务运行的一种方法如下:

Please note: Only services installed in services database will be detected with this method, but not child processes started by a service process that are not registered in the database. 请注意:使用此方法只能检测服务数据库中安装的服务,但不会检测未在数据库中注册的服务进程启动的子进程。 In this case, it would not also be a system service. 在这种情况下,它也不是一个系统服务。 *1. * 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;
}

Please note, the code above should contain additional error handling, especially it should be called in a loop until EnumServicesStatusEx returns nonzero. 请注意,上面的代码应该包含额外的错误处理,特别是它应该在循环中调用,直到EnumServicesStatusEx返回非零。 But unfortunetaly as I found out, GetLastError() always returns 1 (ERROR_INVALID_FUNCTION) even if the buffer is correctly filled with data. 但不幸的是,我发现,即使缓冲区正确填充数据, GetLastError()始终返回1(ERROR_INVALID_FUNCTION)。

*1: Testing if a process was started by a service: In this case you could use a combination of other solutions. * 1:测试服务是否启动了流程:在这种情况下,您可以使用其他解决方案的组合。 One could test, if the process has a parent (grandparent...) process that is a registered as a service. 如果进程具有作为服务注册的父(祖父母......)进程,则可以测试。 You could use CreateToolhelp32Snapshot API for this purpose. 您可以使用CreateToolhelp32Snapshot API来实现此目的。 However if the parent process is already killed, things getting difficult. 但是,如果父进程已经被杀死,事情就变得困难了。 I'm sure there are any undocumented settings which can determine whether a process is running as a service apart from the usual suspects like SessionId = 0, WindowStation = 0, WSF_VISIBLE, No Interactive Group membership... 我确信有任何未记录的设置可以确定进程是否作为服务运行,除了常见的嫌疑人,如SessionId = 0,WindowStation = 0,WSF_VISIBLE,No Interactive Group成员资格......

There is a simple way to detect whether the application is started as a service. 有一种简单的方法可以检测应用程序是否作为服务启动。 When you create a service with CreateService , pass in lpBinaryPathName parameter some additional argument, say -s which would indicate that your application is started as a service. 使用CreateService创建服务时,请在lpBinaryPathName参数中传入一些其他参数,例如-s ,这表示您的应用程序是作为服务启动的。 Then in the application you can check for this argument. 然后在应用程序中,您可以检查此参数。 It can also possibly help when debugging, because you can test your service functionality without actually running as a service. 它在调试时也可能有所帮助,因为您可以在不实际作为服务运行的情况下测试您的服务功能。 If StartServiceCtrlDispatcher fails with ERROR_FAILED_SERVICE_CONTROLLER_CONNECT , you can set a flag indicating the program is running as a console application simulating a service mode, so you can skip service related API calls using this flag. 如果StartServiceCtrlDispatcherERROR_FAILED_SERVICE_CONTROLLER_CONNECT而失败,您可以设置一个标志,指示程序正在作为模拟服务模式的控制台应用程序运行,因此您可以使用此标志跳过与服务相关的API调用。

Process in normal user session always has a window station called WinSta0 . 普通用户会话中的进程始终有一个名为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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM