【发布时间】:2009-04-09 21:01:13
【问题描述】:
我正在开发一项服务,该服务需要检测登录到单台计算机的所有用户的用户状态。具体来说,我想检查一下屏幕保护程序是否处于活动状态以及他们的会话是否被锁定。
此代码将在系统级服务下运行,并且没有可见的 UI,因此可能会排除几个选项(捕获 WM 消息等)。
除了普通的工作站,我希望它可以在有多个用户登录的终端服务器上工作。由于这些要求,我想知道是否需要涉及多个 Win32 API。
关于从哪里开始有什么想法吗?
【问题讨论】:
我正在开发一项服务,该服务需要检测登录到单台计算机的所有用户的用户状态。具体来说,我想检查一下屏幕保护程序是否处于活动状态以及他们的会话是否被锁定。
此代码将在系统级服务下运行,并且没有可见的 UI,因此可能会排除几个选项(捕获 WM 消息等)。
除了普通的工作站,我希望它可以在有多个用户登录的终端服务器上工作。由于这些要求,我想知道是否需要涉及多个 Win32 API。
关于从哪里开始有什么想法吗?
【问题讨论】:
作为一项服务,您可以使用事件 OnSessionChange 来捕捉所有相关时刻。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceProcess;
using System.Diagnostics;
namespace MyCode
{
class MyService : ServiceBase
{
public MyService()
{
this.CanHandleSessionChangeEvent = true;
}
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
switch (changeDescription.Reason)
{
case SessionChangeReason.SessionLogon:
Debug.WriteLine(changeDescription.SessionId + " logon");
break;
case SessionChangeReason.SessionLogoff:
Debug.WriteLine(changeDescription.SessionId + " logoff");
break;
case SessionChangeReason.SessionLock:
Debug.WriteLine(changeDescription.SessionId + " lock");
break;
case SessionChangeReason.SessionUnlock:
Debug.WriteLine(changeDescription.SessionId + " unlock");
break;
}
base.OnSessionChange(changeDescription);
}
}
}
我确信可以根据 changeDescription.SessionId 识别用户。但目前我不知道如何...
编辑:这应该是一种可能性
public static WindowsIdentity GetUserName(int sessionId)
{
foreach (Process p in Process.GetProcesses())
{
if(p.SessionId == sessionId) {
return new WindowsIdentity(p.Handle);
}
}
return null;
}
MSDN 链接
【讨论】:
最直接的方法是在每个用户的会话中运行一个小应用程序。此应用的每个实例都可以与服务的主实例进行通信。
Windows 非常努力地将登录会话分开——在服务和交互式桌面之间,以及在各个终端服务会话之间——因此访问有关用户会话的此类信息变得非常棘手,除非您的应用正在运行在该会话开始。
【讨论】:
更简单的解决方案是使用封装了各种 TS API 的 Cassia 来检查用户空闲了多长时间:
using Cassia;
foreach (ITerminalServicesSession session in new TerminalServicesManager().GetSessions())
{
if ((session.CurrentTime - session.LastInputTime > TimeSpan.FromMinutes(10)) &&
(!string.IsNullOrEmpty(session.UserName)))
{
Console.WriteLine("Session {0} (User {1}) is idle", session.SessionId, session.UserName);
}
}
【讨论】: