WPF: время простоя приложения

Мне нужно подсчитать время простоя моего приложения WPF (Idle time = при отсутствии ввода клавиатуры, ввода мыши (движение + клики)). Пока я пробовал 2 подхода, но ни один из них, похоже, не работает:

  • Использование диспетчера для вызова делегата каждый раз, когда он получает приоритет contextIdle, проблема в том, что привязка и многие другие операции вызывают его, и поэтому я не могу это использовать.
  • используя диспетчер ввода, я зарегистрировался на событие "System.Windows.Input.InputManager.Current.PostProcessInput" и каждый раз, когда он был вызван, я перезапустил счетчик времени простоя. Второй подход казался многообещающим, но проблема в том, что когда мышь находится над приложением (у него есть фокус), я продолжаю получать событие.

Любые другие идеи? или, возможно, способ изменить второе решение для работы?

Ответ 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;
        }
    }

Последнее, что я сделал, это создать цикл таймера, который опросил это событие флажка несколько секунд.

Кажется, что это нормально.

Ответ 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;
    }


}