2016-02-08 15 views
1

В настоящее время я используюКак улучшить использование ЦП при автоматической «блокировке» приложения (WPF) с помощью Window.PreviewMouseMoveEvent?

EventManager.RegisterClassHandler(typeof(Window), 
            Window.PreviewMouseMoveEvent, 
            new MouseEventHandler(OnPreviewMouseMove)); 

где OnPreviewMouseMove просто вызывает Stop() и Start() по таймеру, используемого для обработки автоматического тайм-аут моего приложения (пользователи должны повторно ввести учетные данные после периода бездействия).

Я заметил, что это решение использует довольно много мощности процессора (как, например, 3-12% на Core i7, когда я дрожу над окном), поэтому я задаюсь вопросом, может ли быть лучший способ справиться с этим. Я понимаю, что неустойчивое движение мыши и относительно низкое использование ЦП не будут настоящей проблемой, но я открыт для лучших способов справиться с этим.

Я также не уверен, что это можно сделать для работы с приложениями, отличными от WPF (по моему мнению, в этом случае мне понадобятся разные события), но это может иметь значение для другого вопроса.

+0

вы должны знать, если пользователь перемещает мышь, в то время как ваша программа ориентирована или просто зная, мышь перемещается в целом (даже если ваша программа была сведена к минимуму) быть приемлемым, чтобы ваш тайм-аут работал на том же делать часы, которые использует заставка? –

+0

@Scott Я бы предположил, что часы заставки будут работать. Если это сработает, даже если заставка будет отключена, конечно. – rubenvb

ответ

2

Используйте вызов Windows API GetLastInputInfo, чтобы выяснить, когда произошло последнее нажатие клавиши или движение мыши. Это тот же самый таймер, который использует скринсейвер, чтобы выяснить, когда его включить.

Вот класс обертки, который я использовал в других проектах. Событие Idle выполняется по текущему SynchronizationContext или потоку нити, если нет одного набора.

/// <summary> 
/// A timer that raises the <see cref="Idle"/> event when it detects the session 
/// </summary> 
public sealed class SystemIdleTimer : IDisposable 
{ 
    private readonly System.Threading.Timer _timer; 
    private readonly SynchronizationContext _synchronizationContext; 

    /// <summary> 
    /// This event is rasied when the sysstem's idle time is greater than <see cref="MaxIdleTime"/>. 
    /// This event is posted to the SynchronizationContext that the constructor was run under. 
    /// </summary> 
    public event EventHandler Idle; 

    /// <summary> 
    /// The amount of idle time that must pass before the <see cref="Idle"/> event is raised. 
    /// </summary> 
    public TimeSpan MaxIdleTime { get; set; } 

    /// <summary> 
    /// Is the user currently detected as idle; 
    /// </summary> 
    public bool IsDetectedIdle { get; private set; } 

    /// <summary> 
    /// Creates a new timer with a specified trigger level and a check frequency of once a minute. 
    /// </summary> 
    /// <param name="maxIdleTime">The amount of idle time that must pass before the <see cref="Idle"/> event is raised.</param> 
    public SystemIdleTimer(TimeSpan maxIdleTime) 
     : this(maxIdleTime, TimeSpan.FromMinutes(1)) 
    { 
    } 

    /// <summary> 
    /// Creates a new timer with a specified trigger level and a check frequency. 
    /// </summary> 
    /// <param name="maxIdleTime">The amount of idle time that must pass before the <see cref="Idle"/> event is raised.</param> 
    /// <param name="checkInterval">The frequency in miliseconds to check the idle timer.</param> 
    public SystemIdleTimer(TimeSpan maxIdleTime, TimeSpan checkInterval) 
    { 
     MaxIdleTime = maxIdleTime; 
     _synchronizationContext = SynchronizationContext.Current; 
     _timer = new System.Threading.Timer(TimerCallback, null, checkInterval, checkInterval); 
    } 
    public void Dispose() 
    { 
     _timer.Dispose(); 
     Idle = null; 
    } 

    private void TimerCallback(object state) 
    { 
     var idleTime = GetIdleTime(); 
     if (idleTime > MaxIdleTime) 
     { 
      if (!IsDetectedIdle) 
      { 
       IsDetectedIdle = true; 
       OnIdle(); 
      } 
     } 
     else 
     { 
      IsDetectedIdle = false; 
     } 
    } 

    private void OnIdle() 
    { 
     var idle = Idle; 
     if (idle != null) 
     { 
      if (_synchronizationContext != null) 
      { 
       _synchronizationContext.Post(state => idle(this, EventArgs.Empty), null); 
      } 
      else 
      { 
       idle(this, EventArgs.Empty); 
      } 
     } 
    } 

    /// <summary> 
    /// Returns the amout of time the system has been idle. 
    /// </summary> 
    /// <returns>A TimeSpan representing the idle time for the session.</returns> 
    public static TimeSpan GetIdleTime() 
    { 
     try 
     { 
      uint idleMiliseconds = 0; 
      LASTINPUTINFO lastInputInfo = new LASTINPUTINFO(); 
      lastInputInfo.cbSize = (uint)Marshal.SizeOf(lastInputInfo); 
      lastInputInfo.dwTime = 0; 

      uint systemUpTime = GetTickCount(); 

      if (GetLastInputInfo(ref lastInputInfo)) 
      { 
       uint lastInputTime = lastInputInfo.dwTime; 

       if (lastInputTime > systemUpTime) 
       { 
        // The elapsed time is stored as a DWORD value. Therefore, the time will wrap around to zero if the system is run continuously for 49.7 days. 
        // so, we need a bit more math... 

        // how far between last input and the current time rolling over to 0 
        idleMiliseconds = (uint.MaxValue - lastInputTime); 

        // add that to the current ticks 
        idleMiliseconds = idleMiliseconds + systemUpTime; 
       } 
       else 
       { 
        idleMiliseconds = systemUpTime - lastInputTime; 
       } 
      } 


      return TimeSpan.FromMilliseconds(idleMiliseconds); 
     } 
     catch (Exception) 
     { 
      return TimeSpan.Zero; 
     } 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    private struct LASTINPUTINFO 
    { 
     [MarshalAs(UnmanagedType.U4)] 
     public UInt32 cbSize; 
     [MarshalAs(UnmanagedType.U4)] 
     public UInt32 dwTime; 
    } 

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

    [DllImport("kernel32.dll")] 
    static extern uint GetTickCount(); 
} 
+0

Я натолкнулся на это, но вздрогнул, когда прочитал «Количество отсчетов, когда последнее входное событие было получено (см. LASTINPUTINFO), не гарантируется инкрементным. В некоторых случаях значение может быть меньше количества тиков предыдущего события. Например, это может быть вызвано временным зазором между исходной нитью потока и потоком рабочего стола или событием, созданным SendInput, которое поставляет свой собственный счетчик меток. «Наряду с необходимостью сортировки, которую я пытаюсь избежать. Идея проверки на основе таймера - это нечто, что может привести к чему-то еще. Благодаря! – rubenvb

+0

Блок 'if (lastInputTime> systemUpTime)' кода кода, который указывает, что документация говорит о том, что он не является инкрементным. Кроме того, это третье предложение гласит: «если вы сделали« GetTickCount() »изнутри« OnPreviewMouseMove »и сохранили его, значение, которое вы получите, может выглядеть« более новым », чем результат« GetLastInputInfo() ». Но мы не полагаемся на время изнутри обработчика событий сообщений Windows, поэтому для нас это не имеет значения. Кроме того, если вы не планируете работать на платформах, которые вы не можете выполнить P/Invoke, нет ничего плохого в том, что вы делаете маршалинг. –

 Смежные вопросы

  • Нет связанных вопросов^_^