4

The question is how to have a C# program (running in Windows) check to see whether or not a key button has been pressed, or the mouse has been moved/clicked, within a set amount of time in the past.

One solution I found uses the "KeyPress" event, but this only works if the control (or application) has focus. I am seeking a solution that works even while the program is running in the background.

Additionally, it would be nice to have a way to exclude certain events (i.e., the key "Z") from the conditional.

user10478
  • 327
  • 1
  • 16
  • possible duplicate of [Detecting idle users in Winforms](http://stackoverflow.com/questions/6282298/detecting-idle-users-in-winforms) – devuxer Sep 11 '14 at 22:36
  • It's a similar concept, but I'm really hoping to find something that can exclude certain specific keys. – user10478 Sep 11 '14 at 22:48
  • Are you sure the CodePlex project referenced in the last answer wouldn't be useful for your situation (http://globalmousekeyhook.codeplex.com/SourceControl/list/changesets)? Seems like you could handle the system-wide mouse/keyboard events and simply ignore the ones you want. – devuxer Sep 11 '14 at 22:56
  • If it needs to be selective then use Application.AddMessageFilter(). – Hans Passant Sep 11 '14 at 23:05

1 Answers1

5

There is a function in the WinAPI doing just that: GetLastInputInfo.

This function is useful for input idle detection. However, GetLastInputInfo does not provide system-wide user input information across all running sessions. Rather, GetLastInputInfo provides session-specific user input information for only the session that invoked the function.

And there's even an example on pinvoke.net. Here's my version:

public static TimeSpan GetIdleTime()
{
    var lastInputInfo = new LASTINPUTINFO
    {
        cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO))
    };

    if (!GetLastInputInfo(ref lastInputInfo))
        throw new Win32Exception("GetLastInputInfo failed");

    return TimeSpan.FromMilliseconds(Environment.TickCount - lastInputInfo.dwTime);
}

Relevant P/Invoke definitions:

[DllImport("user32.dll")]
static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

[StructLayout(LayoutKind.Sequential)]
struct LASTINPUTINFO
{
    [MarshalAs(UnmanagedType.U4)]
    public UInt32 cbSize;

    [MarshalAs(UnmanagedType.U4)]
    public UInt32 dwTime;
}
Lucas Trzesniewski
  • 50,214
  • 11
  • 107
  • 158
  • 2
    That return statement is a real doozy. 40 characters to avoid a division when the numerator is zero. – Ben Voigt Sep 11 '14 at 22:44
  • @BenVoigt Yeah, and the fact the function doesn't return a `TimeSpan`... but I quoted it as-is from pinvoke.net. I'll improve that. – Lucas Trzesniewski Sep 11 '14 at 22:45
  • Thanks, that looks close to what I'm after, but it doesn't seem to return what the event in question actually was, if I understand it correctly. I'd also need a way to check the second to last event, if the last event was an exception (i.e. for the question, "was there any non-Z-key input in the past 15 seconds?", if the last input was Z and occurred 1 second ago, I'd need to check the event before it). – user10478 Sep 11 '14 at 22:49
  • @user10478 I thought it was in the *nice to have* category, but since you really need it, this solution won't cut it. You'll have to install systemwide hooks. Unfortunately, I don't have the time to write an anwser detailing this right now, sorry. I'll try tomorrow if no-one answers till then. – Lucas Trzesniewski Sep 11 '14 at 22:59
  • Sorry about that, that was my unclear way of saying I don't know for certain whether I'll need it (because my program will be outputting the equivalent of key presses, but only 4 specific keys). Not sure if the output will interfere with GetLastInputInfo yet. – user10478 Sep 11 '14 at 23:12