服务需要检测工作站是否被锁定,以及屏幕保护程序是否激活。

9
我正在开发一项服务,需要检测单台计算机上所有已登录用户的状态。具体来说,我想要检查屏幕保护程序是否激活以及他们的会话是否被锁定。
这段代码将在系统级服务下运行,没有可见的UI,因此可能排除了几个选项(捕获WM消息等)。
除了普通工作站外,我希望它能够在有多个用户登录的终端服务器上运行。由于这些要求,我想知道是否需要涉及多个Win32 API。
有什么建议从哪里开始吗?
3个回答

20
作为一种服务,您可以使用事件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链接


GetUserName代码对我没用(抛出了异常)。但是,我在这里找到了所需的代码:http://msmvps.com/blogs/siva/archive/2006/10/02/Getting-Windows-Process-Owner-Name.aspx - Kramii

4
最直接的方式是在每个用户的会话中运行一个小应用程序。每个应用程序实例可以与服务的主要实例进行通信。
Windows非常努力地保持登录会话的分离 - 在服务和交互式桌面之间以及在单个终端服务会话之间 - 因此,如果您的应用程序一开始就在该会话中运行,那么访问关于用户会话的这种信息会变得非常棘手。

谢谢,我会尝试一下。也许一个简单的套接字是实现这个的最好方式。 - Brian Cline

2

一种更简单的解决方案是使用Cassia,它封装了各种TS API,以检查用户闲置时间的长短:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接