我正在编写一个能够作为服务或独立运行的应用程序,但我想检测应用程序是作为服务执行还是作为普通用户会话执行。
答案 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。
找到了一大堆代码答案 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
}