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?
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
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.
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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With