WPF:应用程序空闲时间

时间:2010-07-20 11:31:10

标签: c# wpf

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

  1. 每次获取contextIdle优先级时,使用调度程序调用委托,问题是绑定和许多其他操作会调用它,因此我无法真正使用它。
  2. 使用我在“System.Windows.Input.InputManager.Current.PostProcessInput”事件中注册的输入管理器,每次调用它时都会重新启动空闲时间计数。 第二种方法看起来很有希望,但问题是,当鼠标在应用程序上方(它有焦点)时,我会继续得到这个事件。
  3. 还有其他想法吗?或者可能是修改第二种解决方案的方法吗?

2 个答案:

答案 0 :(得分:13)

我用一些不同的技术解决了这个问题,为我提供了一个很好的解决方案。我使用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;
        }
    }

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

这似乎工作正常。

答案 1 :(得分:5)

好吧没有人回答所以我继续挖掘并找到一个相对简单的解决方案,使用操作系统最后输入+运行时间。代码非常简单但是这个解决方案让我进行数据轮询,这是我从不推荐的,而且不是在应用程序级别,而是在操作系统级别,这不是我需要的确切解决方案。 如果有人打开这个帖子这就是代码,只需使用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;
    }


}