当Windows处于非活动状态时如何判断

11

各种程序只能在您一段时间没有使用计算机时才能执行某些操作(例如屏幕保护程序、Google 桌面索引等)。

它们是如何知道何时处于非活动状态的?Windows 中是否有某个函数告诉您它已经处于非活动状态了多长时间,或者您必须使用某种键盘/鼠标挂钩来自行跟踪活动情况?

我正在使用 C#,但对任何确定非活动状态的方法都感兴趣。

3个回答

14

编辑:更改答案,提供Shy的回答背后的文本和详细信息(应该是并且已经被接受)。随意合并和删除此内容。

GetLastInputInfo 函数 GetLastInputInfo函数检索最后一次输入事件的时间。

从P/Invoke粘贴到此处。

此函数检索自上次用户输入以来的时间。

[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: 我在 AnAppADay 期间实现了全局键盘和鼠标钩子。查看此应用程序的源代码 - 它与您想要的非常接近。您需要的类位于 AnAppADay.Utils 命名空间中。[因链接过期而删除]


请注意,返回的int值是以秒为单位的值,在上面并不立即清晰。 (个人认为我会将该函数称为SecondsSinceLastInput或类似名称) - Gareth
我完成了这个任务,然后使用psexec将其发送到了我的同事的远程计算机。它显示超过2000秒并且还在增加。但是我看到他正在使用他的电脑,所以这很误导人。似乎它只是显示自从计算机开启以来的时间。 - fjleon
能否将此功能集成到 PowerShell 脚本中,以检查远程计算机上的用户是否在积极使用它?虽然我对 PowerShell 很熟悉,但对于在 PowerShell 中添加 C# 并不太了解。如果有关此功能的指针,将不胜感激。 - Rakha

5

我在谷歌搜索“inactive”和“idle”。GetLastInputInfo正是我所需要的。谢谢。 - dan gibson
请注意,这仅适用于当前会话。可能会有其他用户活动的会话。我不确定是否可以跨会话执行此操作,因为那可能会造成安全漏洞。 - shoosh
我不知道为什么会被踩?我唯一能想到的原因是它只有链接没有实际答案?还是因为使用 GetLastInputInfo 存在问题? - dan gibson
有些人太急于点踩答案,即使它是正确的,只是因为他们不喜欢它。 - Robert Gould
此外,许多人认为赞/踩意味着“我喜欢/不喜欢这个答案”,而不是“这个答案与问题相关是否有帮助”。 - tzot
显示剩余2条评论

0

我认为键盘和鼠标钩子是最有价值的。下面的类可以被插入,你只需要想出如何处理关于键盘和鼠标更新的信息。

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 );
        }

    }
}

当然,这仅适用于您正在挂钩的应用程序内部。如果您需要跟踪整个系统中的不活动状态,则需要创建一个可以加载到所有其他窗口的地址空间中的DLL。不幸的是,我没有听说过任何黑客技巧,可以允许在此场景中使用编译为.net的.dll;我们有一个用于此目的的C++ DLL。


你知道GetLastInputInfo吗?你这样做而不是使用GetLastInputInfo有什么原因吗? - dan gibson
这更加复杂一些;-) 人们喜欢这样 :) - Skuta
实际上,我不知道GetLastInputInfo。但是考虑到通常情况下,当我们做这些钩子的时候,我们不仅仅是寻找超时信息,那个单独的调用对我们来说并没有用处。我们通常会钩取更广泛范围的键盘和鼠标处理功能。 - Bill

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接