views:

3283

answers:

9

I'm writing up some productivity/metrics tools for myself to help monitor my focus throughout the day. Recently, I've noticed that I tend to get off track more than usual and feel the need to get up and go for walks/drinks/etc and I'm concerned that I'm "wasting" too much time.

Since I always lock my computer when I go anywhere, and I unlock it as soon as I return (even if I'm just reading at my desk, etc), I was wondering how I can determine, in code, how long the machine is locked.

I'm writing this in C# if that helps, but I'm open to other ideas.


I like the windows service idea (and have accepted it) for simplicity and cleanliness, but unfortunately I don't think it will work for me in this particular case. I wanted to run this on my workstation at work rather than home (or in addition to home, I suppose), but it's locked down pretty hard courtesy of the DoD. That's part of the reason I'm rolling my own, actually.

I'll write it up anyway and see if it works. Thanks everyone!

+4  A: 

The solution is to use the OpenDesktop API call. Basically it just attempts to switch the the default desktop which will fail if it's locked.

Note1: the following function loads the relevant dlls and functions dynamically so that apps will still run on Windows 9.x :)

Note2: DESKTOP& #95;SWITCHDESKTOP should read DESKTOP_SWITCHDESKTOP

BOOL Misc::IsWorkStationLocked()
{
    // note: we can't call OpenInputDesktop directly because it's not
    // available on win 9x
    typedef HDESK (WINAPI *PFNOPENDESKTOP)(LPSTR lpszDesktop, DWORD dwFlags, BOOL fInherit, ACCESS_MASK dwDesiredAccess);
    typedef BOOL (WINAPI *PFNCLOSEDESKTOP)(HDESK hDesk);
    typedef BOOL (WINAPI *PFNSWITCHDESKTOP)(HDESK hDesk);

    // load user32.dll once only
    static HMODULE hUser32 = LoadLibrary("user32.dll");

    if (hUser32)
    {
     static PFNOPENDESKTOP fnOpenDesktop = (PFNOPENDESKTOP)GetProcAddress(hUser32, "OpenDesktopA");
     static PFNCLOSEDESKTOP fnCloseDesktop = (PFNCLOSEDESKTOP)GetProcAddress(hUser32, "CloseDesktop");
     static PFNSWITCHDESKTOP fnSwitchDesktop = (PFNSWITCHDESKTOP)GetProcAddress(hUser32, "SwitchDesktop");

     if (fnOpenDesktop && fnCloseDesktop && fnSwitchDesktop)
     {
      HDESK hDesk = fnOpenDesktop("Default", 0, FALSE, DESKTOP_SWITCHDESKTOP);

      if (hDesk)
      {
       BOOL bLocked = !fnSwitchDesktop(hDesk);

       // cleanup
       fnCloseDesktop(hDesk);

       return bLocked;
      }
     }
    }

    // must be win9x
    return FALSE;
}
A: 

@John Boker - I don't see an answer there, even if I scroll right to the bottom as per standard expertsexchange operating procedure. Can you paste the answer here, if it's still in your cache?

Blorgbeard
A: 

I know you may have been looking forward to coding something yourself, but you might want to look into a service called RescueTime. I've been running it for a few months and it's helped me pinpoint how much time I waste on various websites and media programs (a lot).

Kyle Cronin
A: 

@AgentConundrum

unfortunately i cannot see it anymore either.

John Boker
A: 

Google cached the experts-exchange answer:

http://www.google.com/search?q=cache:http://www.experts-exchange.com/Programming/Languages/.NET/Visual_Basic.NET/Q_22518173.html

I won't post the whole thing here, but the gist is that you can trap the WM_WTSESSION_CHANGE windows message. Might be a better solution for you than polling.

Blorgbeard
+16  A: 

I would create a Windows Service (a visual studio 2005 project type) that handles the OnSessionChange event as shown below:

    protected override void OnSessionChange(SessionChangeDescription changeDescription)
    {
        if (changeDescription.Reason == SessionChangeReason.SessionLock)
        { 
            //I left my desk
        }
        else if (changeDescription.Reason == SessionChangeReason.SessionUnlock)
        { 
            //I returned to my desk
        }
    }

What and how you log the activity at that point is up to you, but a Windows Service provides quick and easy access to windows events like startup, shutdown, login/out, along with the lock and unlock events.

Timothy Carter
+6  A: 

The solution below uses the Win32 API. OnSessionLock is called when the workstation is locked, and OnSessionUnlock is called when it is unlocked.

[DllImport("wtsapi32.dll")]
private static extern bool WTSRegisterSessionNotification(IntPtr hWnd,
int dwFlags);

[DllImport("wtsapi32.dll")]
private static extern bool WTSUnRegisterSessionNotification(IntPtr
hWnd);

private const int NotifyForThisSession = 0; // This session only

private const int SessionChangeMessage = 0x02B1;
private const int SessionLockParam = 0x7;
private const int SessionUnlockParam = 0x8;

protected override void WndProc(ref Message m)
{
    // check for session change notifications
    if (m.Msg == SessionChangeMessage)
    {
        if (m.WParam.ToInt32() == SessionLockParam)
            OnSessionLock(); // Do something when locked
        else if (m.WParam.ToInt32() == SessionUnlockParam)
            OnSessionUnlock(); // Do something when unlocked
    }

    base.WndProc(ref m);
    return;
}

void OnSessionLock() 
{
    Debug.WriteLine("Locked...");
}

void OnSessionUnlock() 
{
    Debug.WriteLine("Unlocked...");
}

private void Form1Load(object sender, EventArgs e)
{
    WTSRegisterSessionNotification(this.Handle, NotifyForThisSession);
}

// and then when we are done, we should unregister for the notification
//  WTSUnRegisterSessionNotification(this.Handle);
adeel825
+11  A: 

I hadn't found this before, but from any application you can hookup a SessionSwitchEventHandler. Obviously your application will need to be running, but so long as it is:

    Microsoft.Win32.SystemEvents.SessionSwitch += new Microsoft.Win32.SessionSwitchEventHandler(SystemEvents_SessionSwitch);

    void SystemEvents_SessionSwitch(object sender, Microsoft.Win32.SessionSwitchEventArgs e)
    {
        if (e.Reason == SessionSwitchReason.SessionLock)
        { 
            //I left my desk
        }
        else if (e.Reason == SessionSwitchReason.SessionUnlock)
        { 
            //I returned to my desk
        }
    }
Timothy Carter
+1 - I've just tried this in a project i'm working on and it works perfectly in .net 2.0. However, 'SessionChangeReason' should be 'SessionSwitchReason'.
Craig T
where is Microsoft.Win32? I cannot find it in project references.
David.Chu.ca
Sorry for my ignorance. It is in system.dll and there is no need to add any new reference.
David.Chu.ca
+6  A: 

I just registered to post this (can't downvote yet): Do NOT use the dirty hack suggested by dan_g that currently has 5 upvotes. Attempting to do a desktop switch in order to find out if the desktop is locked is horrible behaviour. Why, you ask? Because it WILL actually switch the desktop if you use a multi desktop solution (not virtual desktops) or if you use tools like SuRun. A more detailed explanation (google translation) can be found here: 1

rwojtech
+1 hopefully you'll be able to comment soon.
kenny