【问题标题】:WPF: application Idle TimeWPF:应用程序空闲时间
【发布时间】:2010-07-20 11:31:10
【问题描述】:

我需要计算我的 WPF 应用程序的空闲时间(空闲时间 = 没有键盘输入,鼠标输入(移动 + 点击)发生时)。 到目前为止,我尝试了 2 种方法,但似乎都没有奏效:

  1. 使用调度程序在每次获得 contextIdle 优先级时调用委托,问题是绑定和许多其他操作调用它,因此我不能真正使用它。
  2. 使用我注册到“System.Windows.Input.InputManager.Current.PostProcessInput”事件的输入管理器,每次调用它时,我都会重新启动空闲时间计数。 第二种方法看起来很有希望,但问题是当鼠标悬停在应用程序上时(它有焦点),我不断收到事件。

还有其他想法吗?或者可能是修改第二个解决方案的方法?

【问题讨论】:

    标签: c# wpf


    【解决方案1】:

    我使用了几种不同的技术解决了这个问题,为我提供了一个很好的解决方案。我使用 GetLastInput 来计算上次触摸系统的时间 这在其他地方有很好的记录,但这是我的方法:

    public static class User32Interop
    {
        public static TimeSpan GetLastInput() 
        { 
            var plii = new LASTINPUTINFO();
            plii.cbSize = (uint)Marshal.SizeOf(plii); 
            if (GetLastInputInfo(ref plii))       
                return TimeSpan.FromMilliseconds(Environment.TickCount - plii.dwTime); 
            else       
                throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); 
        }
    
        [DllImport("user32.dll", SetLastError = true)]
        static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
        struct LASTINPUTINFO { 
            public uint cbSize; 
            public uint dwTime;   
        }
    }
    

    这只会告诉我系统何时空闲,而不是应用程序。如果用户单击 Word 并在那里工作一个小时,我仍然想要超时。为了处理这种情况,我只记得当我的应用程序通过覆盖应用程序对象上的 OnDeactivated 和 OnActivated 方法而失去焦点时:

        override protected void OnDeactivated(EventArgs e)
        {
            this._lostFocusTime = DateTime.Now;
            base.OnDeactivated(e);
        }
    
        protected override void OnActivated(EventArgs e)
        {
            this._lostFocusTime = null;
            base.OnActivated(e);
        }
    

    我的 IsIdle 例程已添加到应用程序对象中。它处理应用程序具有焦点但没有发生任何事情的全局情况(IsMachineIdle)以及应用程序在用户做其他事情时失去焦点的特定情况(isAppIdle):

        public bool IsIdle
        {
            get
            {
                TimeSpan activityThreshold = TimeSpan.FromMinutes(1);
                TimeSpan machineIdle = Support.User32Interop.GetLastInput();
                TimeSpan? appIdle = this._lostFocusTime == null ? null : (TimeSpan?)DateTime.Now.Subtract(_lostFocusTime.Value);
                bool isMachineIdle = machineIdle > activityThreshold ;
                bool isAppIdle = appIdle != null && appIdle > activityThreshold ;
                return isMachineIdle || isAppIdle;
            }
        }
    

    我做的最后一件事是创建一个计时器循环,在几秒钟内轮询这个标志事件。

    这似乎工作正常。

    【讨论】:

    • 记住,_lostFocusTime 应该声明为 DateTime?多变的。如果您尝试声明为 DateTime,则不能将值设为 NULL。
    • Environment.TickCount 的类型为 int,并且每 24.9 天强制换行一次。 LASTINPUTINFO.dwTime 的类型为 uint,因此它直到 49.7 天才会换行。如果机器的运行时间超过 24.9 天,那么示例中的数学运算就搞砸了。我通过调用 WINAPI 函数 GetTickCount 替换 Environment.TickCount 来缓解这个问题,该函数也返回一个 uint 值。我不确定如果最后一次输入是在 49.5 天并且当前滴答计数是在 50.0 天会发生什么。 According to this answer to another question应该没问题。
    【解决方案2】:

    好吧,似乎没有人回答,所以我继续挖掘并找到了一个使用操作系统上次输入 + 正常运行时间的相对简单的解决方案。代码非常简单,但这个解决方案让我进行数据轮询,我从不推荐,而且不是在应用程序级别,而是在操作系统级别,这不是我需要的确切解决方案。 如果有人打开此线程,这是代码,只需使用 GetIdleTime():

     public class IdleTimeService
    {
        //Importing the Dll & declaring the necessary function
        [DllImport("user32.dll")]
        private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
    
    
        /// <summary>
        /// return the current idle time (in ms)
        /// </summary>
        /// <returns>current idle time in ms</returns>
        public static int GetIdleTime()
        {
    
            //Creating the object of the structure
            LASTINPUTINFO lastone = new LASTINPUTINFO();
    
            //Initialising  
            lastone.cbSize = (uint)Marshal.SizeOf(lastone);
            lastone.dwTime = 0;
    
            int idleTime = 0;
    
            //To get the total time after starting the system.
            int tickCount = System.Environment.TickCount;
    
            //Calling the dll function and getting the last input time.
            if (GetLastInputInfo(ref lastone))
            {
                idleTime = tickCount - (int)lastone.dwTime;
                return idleTime;
            }
            else
                return 0;
        }
    
    
    }
    

    【讨论】:

    • 您好,我正在尝试解决您的原始问题并遇到与您的解决方案相同的问题:它是系统范围的。您最终确定了如何进行应用范围内的空闲检测吗?
    • 不是真的,我仍然坚持使用系统解决方案,但如果你以某种方式找到解决方案,如果你能发布它会很棒。
    • 我添加了我的解决方案作为答案。如果您有任何问题或您认为这很愚蠢,请给我 ping。
    猜你喜欢
    • 1970-01-01
    • 2015-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-05
    • 2020-09-23
    相关资源
    最近更新 更多