tags:

views:

495

answers:

4

What I would like to do is detect if a user is actively using a computer running Windows (2000 or later). Preferably, I would like to do this without resorting to using screensavers.

Background: We have a customer service department that sits in a hunt group and can be "Available" or not. If they are "Available" calls will be routed to their phone rather than sit in the queue. While the phone is ringing at an extension, the inbound caller hears the "ringing" sound rather than hold music. Unfortunately, we also have reps that forget to take themselves out of "Available" when they go to lunch or leave for the day.

The end result would likely be written using .NET

Any thoughts?

A: 

You could use the Windows API to add a low-level keyboard/mouse hook, then mark as away when there's been no activity for a while, then mark as available when the activity begins again.

http://www.codeproject.com/KB/cs/globalhook.aspx has something on this, but it's in C#.

MiffTheFox
That is exactly what my Google search didn't find. Thanks for the quick response!
smbarbour
Why hook when you can simply ask for the last user activity time with GetLastInputInfo()?
macbirdie
@macbirdie Because I have no experience with the Windows API whatsoever.
MiffTheFox
So Stefan's answer should be accepted since it's a lot better and simpler solution.
macbirdie
A: 

Take a look at SetWindowsHookEx and the WH_FOREGROUNDIDLE class.

Paul Alexander
+2  A: 

If users lock their workstations, or if group policy forced a screen lock after some amount of idle minutes, you could use the Windows API to subscribe to the 'Session Notification' event using WTSRegisterSessionNotification and WTSUnRegisterSessionNotification. Once you ask Windows to let your application know that the session has locked, you could use that as your indication that the user is not present.

Good reading: http://support.microsoft.com/kb/310153

Example:

private const int NOTIFY_FOR_THIS_SESSION = 0x0;
private const int WM_WTSSESSION_CHANGE = 0x2B1;
private const int WTS_CONSOLE_CONNECT = 0x1;
private const int WTS_CONSOLE_DISCONNECT = 0x2;
private const int WTS_SESSION_LOCK = 0x7;
private const int WTS_SESSION_UNLOCK = 0x8;
private const int WM_DESTROY = 0x2;
private const int WM_ACTIVATEAPP = 0x1C;

// The WTSUnRegisterSessionNotification function unregisters the specified 
// window so that the specified window receives no more session-change notifications.
[DllImport("Wtsapi32.dll")]
private static extern bool WTSUnRegisterSessionNotification(IntPtr hWnd);

// The WTSRegisterSessionNotification function registers the specified 
// window to receive session-change notifications.
[DllImport("Wtsapi32.dll")]
private static extern bool WTSRegisterSessionNotification(IntPtr hWnd, Int32 dwFlags);

// The PostQuitMessage function indicates to the system that a thread 
// has made a request to quit. The PostQuitMessage function is typically used in 
// response to a WM_DESTROY message.
[DllImport("user32.dll")]
private static extern void PostQuitMessage(Int32 nExitCode);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr PostMessage(HandleRef hwnd, int msg, int wparam, int lparam);

private void Unsubscribe() {
   if (this.Handle != IntPtr.Zero) {
      WTSUnRegisterSessionNotification(this.Handle);
   }
}

private void Subscribe() {
   if (this.Handle != IntPtr.Zero) {
      WTSRegisterSessionNotification(this.Handle, NOTIFY_FOR_THIS_SESSION);
   }
}

// override WndProc in order to catch process the Session-Notification events:
protected override void WndProc(ref Message m) {
   switch (m.Msg) {
      case WM_WTSSESSION_CHANGE:
         switch (m.WParam.ToInt32()) {
            case WTS_CONSOLE_CONNECT:
               // User-switch : Sign-on
               break;
            case WTS_CONSOLE_DISCONNECT:
               // User-switch : Sign-off
               break;
            case WTS_SESSION_LOCK:
               // Screen Lock
               break;
            case WTS_SESSION_UNLOCK:
               // Screen Unlock
               break;
            default:
               break;
         }
         break;
      default:
         break;
   }

   base.WndProc(ref m);
}
Yoopergeek
Sorry, that's C# code when the question is tagged VB.NET...
Yoopergeek
It is easy enough to here (http://www.developerfusion.com/tools/convert/csharp-to-vb/) and convert it to VB.NET. People should not be afraid to convert from one language to another. (Assuming the functionality is supported in the other language.)
beach
BTW, that is a great answer.
beach
Thanks. I used it in a little utility app I wrote for myself at some point in the past.
Yoopergeek
It is a great answer, but we are not using enforced workstation locking, and if the users are too lazy to press a button on their phone, they certainly are not going to take the time to lock the workstation. You get my upvote anyway. This is probably more useful for someone else.
smbarbour
Ahh, yes, if workstations don't automatically lock, then this doesn't really apply. You're in the same boat you're already in. As you might have surmised by this point, we have group policy objects that force screen locking after a certain amount of idle time. :) Thanks for the up-count. :)
Yoopergeek
+3  A: 

The API GetLastInputInfo() should be used for this.

Stefan