How to tell when Windows is inactive
Asked Answered
C

3

11

Various programs can do stuff only when you haven't used the computer for a while (eg screensaver, Google Desktop indexing, etc).

How do they know when it has been inactive? Is there some function in Windows that tells you how long it has been inactive, or do you have to use some kind of keyboard/mouse hook to track activity yourself?

I'm using C#, but I'm interested in any method of determining the inactivity.

Chargeable answered 15/10, 2008 at 1:4 Comment(0)
E
14

EDIT: changed answer, providing text and detail behind Shy's answer (which should be and was accepted). Feel free to merge and delete this one.

GetLastInputInfo Function The GetLastInputInfo function retrieves the time of the last input event.

Pasted here from P/Invoke

This function retrieves the time since last user input

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

static int GetLastInputTime()
{
    int idleTime = 0;
    LASTINPUTINFO lastInputInfo = new LASTINPUTINFO();
    lastInputInfo.cbSize = Marshal.SizeOf( lastInputInfo );
    lastInputInfo.dwTime = 0;

    int envTicks = Environment.TickCount;

    if( GetLastInputInfo( ref lastInputInfo ) )
    {
    int lastInputTick = lastInputInfo.dwTime;

    idleTime = envTicks - lastInputTick;
    }

    return (( idleTime > 0 ) ? ( idleTime / 1000 ) : idleTime );
}

[StructLayout( LayoutKind.Sequential )]
struct LASTINPUTINFO
{
    public static readonly int SizeOf = Marshal.SizeOf(typeof(LASTINPUTINFO));

    [MarshalAs(UnmanagedType.U4)]
    public int cbSize;    
    [MarshalAs(UnmanagedType.U4)]
    public UInt32 dwTime;
}

FWIW: I implemented a global keyboard and mouse hook during AnAppADay. See this app for the source - it's pretty close to what you want. The classes you'll want are in the AnAppADay.Utils namespace. [scratched due to linkrot]

Egest answered 15/10, 2008 at 1:18 Comment(3)
Note the int value returned is a value in seconds, which isn't immediately clear above. (Personally I'd call the function SecondsSinceLastInput or similar)Respectively
I did this then used psexec to send it to a remote machine to my colleague. It shows over 2000 seconds and increasing. And i see him using his computer so it's misleading. It seems to be showing rather the time since the machine was turned on.Abbey
Would it be possible to integrate this function in a powershell script targeting a remote computer to check if the user is actively using it ? Not too familiar with adding c# in powershell although I know powershell pretty well. Would appreciate pointers for this function.Zaneta
H
5

Google is your friend

Basically uses this.
don't forget to fully ready the documentation before using.

Hulburt answered 15/10, 2008 at 1:12 Comment(7)
I was googling for "inactive" vs "idle". GetLastInputInfo is exactly what I needed. Thanks.Chargeable
Do mind that it is only for the current session. There might be other sessions where users Are active. I'm not sure if it's possible to do this cross-session because that might be a security breach.Hulburt
I wonder why this was downvoted? The only reason I can think of is that it only has links and no actual answer? Or what it because there are problems with using GetLastInputInfo?Chargeable
Some people are a bit too eager to down vote just because they don't like the answer, even if it's alright.Shrill
just giving a link isn't the best way to do this. At least paste the relevant text in case one day the link dies. You can steal it from my answer below.Egest
Also, many people think that up/downvoting means "I like/don't like the answer" instead of "this is/isn't helpful in relation to the question"Goosestep
Using a link is fine if you can at least give a summary in the answer itself.Carpophagous
A
0

The keyboard and mouse hooks are what I find to be most valuable. The class below can be inserted and you just have to figure out what you want to do with the information about key and mouse updates.

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace Example {

    public class Hook {

        delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);

        [FlagsAttribute]
            public enum WindowMessage {
            WM_KEYDOWN =        0x0000000000000100, // &H100
            WM_MOUSEMOVE =      0x0000000000000200, // &H200
            WM_LBUTTONDOWN =    0x0000000000000201, // &H201
            WM_RBUTTONDOWN =    0x0000000000000204,  // &H204
            WH_KEYBOARD = 2,
            WH_MOUSE = 7,
            HC_ACTION = 0
        }

        [DllImport("user32.dll",CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)]
        private static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll",CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)]
        private static extern bool UnhookWindowsHookEx(int idHook);

        [DllImport("user32.dll",CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)]
        private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

        //Declare MouseHookProcedure as a HookProc type.
        static HookProc MouseHookProcedure;
        static HookProc KeyboardHookProcedure;

        private static int  mhMouseHook = 0;
        private static int  mhKeyboardHook = 0;

        public Hook() {}

        public static void Init() {
            MouseHookProcedure = new HookProc( MouseHookProc );
            KeyboardHookProcedure = new HookProc( KeyboardHookProc );
            mhMouseHook = SetWindowsHookEx( (int)WindowMessage.WH_MOUSE, MouseHookProcedure, (IntPtr)0, AppDomain.GetCurrentThreadId() );
            mhKeyboardHook = SetWindowsHookEx( (int)WindowMessage.WH_KEYBOARD, KeyboardHookProcedure, (IntPtr)0, AppDomain.GetCurrentThreadId() );
        }

        public static void Terminate() {
            UnhookWindowsHookEx( mhMouseHook );
            UnhookWindowsHookEx( mhKeyboardHook );
        }

        private static int MouseHookProc( int nCode, IntPtr wParam, IntPtr lParam ) {
            if ( nCode >= 0 ) {
                //do something here to update the last activity point, i.e. a keystroke was detected so reset our idle timer.
            }
            return CallNextHookEx( mhMouseHook, nCode, wParam, lParam );
        }

        private static int KeyboardHookProc( int nCode, IntPtr wParam, IntPtr lParam ) {
            if ( nCode >= 0 ) {
                //do something here to update the last activity point, i.e. a mouse action was detected so reset our idle timer.
            }
            return CallNextHookEx( mhKeyboardHook, nCode, wParam, lParam );
        }

    }
}

Of course this only works within the application you are hooking. If you need to track inactivity across the entire system, you need to create a DLL that can be loaded into the address spaces of all other windows. Unfortunately, I haven't heard of any hack that would allow a .net compiled .dll that will work in this scenario; we have a C++ DLL that hooks for this purpose.

Asa answered 15/10, 2008 at 1:14 Comment(3)
Did you know about GetLastInputInfo? Is there a reason you do it this was instead of GetLastInputInfo?Chargeable
It's more complex ;-) ppl like that :)Fionafionna
Actually, no I was not aware of GetLastInputInfo. But given that usually when we do these sorts of hooks, we are not just looking for timeout information, that call alone would not be useful for us. We are usually hooking for a broader range of key and mouse handling functionality.Asa

© 2022 - 2024 — McMap. All rights reserved.