Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Service needs to detect if workstation is locked, and screen saver is active

Tags:

c#

.net

I'm working on a service that needs to detect user states for all user(s) logged on to a single machine. Specifically, I want to check to see whether or not the screen saver is active and whether or not their session is locked.

This code will run under a system-level service, and has no visible UI, so that may rule out several options (trapping WM messages, etc).

Aside from normal workstations, I'd like for this to work on terminal servers that have multiple users logged in to it. Due to these requirements I'm wondering if several Win32 APIs will need to be involved.

Any ideas on where to begin?

like image 344
Brian Cline Avatar asked Oct 29 '08 19:10

Brian Cline


3 Answers

As a serivce you can use the event OnSessionChange to catch all your relevant moments.

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

I'm sure it is possible to identify the user based on changeDescription.SessionId. But at the moment i don't know how...

EDIT: This should be a possibilty

    public static WindowsIdentity GetUserName(int sessionId)
    {
        foreach (Process p in Process.GetProcesses())
        {
            if(p.SessionId == sessionId) {                    
                return new WindowsIdentity(p.Handle);                          
            }                
        }
        return null;
    }

MSDN Links

  • system.serviceprocess.servicebase.onsessionchange
  • system.serviceprocess.sessionchangedescription
  • system.serviceprocess.sessionchangereason
like image 112
Michael Piendl Avatar answered Sep 23 '22 07:09

Michael Piendl


The most straightforward way would be to have a small app running in each user's session. Each instance of this app could communicate with the main instance of the service.

Windows tries pretty hard to keep logon sessions separate -- both between services and the interactive desktop, and between individual Terminal Services sessions -- so it gets very tricky to access this sort of information about a user's session unless your app is running in that session to begin with.

like image 25
Tim Robinson Avatar answered Sep 22 '22 07:09

Tim Robinson


A simpler solution would be to use Cassia, which wraps the various TS APIs, to check how long users have been idle for:

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);
    }
}
like image 31
Dan Ports Avatar answered Sep 24 '22 07:09

Dan Ports