2010-07-20 11 views
10

Potrzebuję policzyć czas bezczynności mojej aplikacji WPF (czas bezczynności = gdy nie wystąpiło wejście klawiatury, wejście myszy (ruch + kliknięcia)). Dotychczas starałem 2 podejścia, ale żaden z nich nie wydają się działać:WPF: aplikacja Czas bezczynności

  1. Korzystanie dyspozytora powołać delegata za każdym razem dostać priorytet contextIdle, problemem jest to, że wiążące i wiele innych operacji powołać go, a tym samym Naprawdę nie mogę tego użyć.
  2. przy użyciu menedżera wejść I zarejestrowanego w zdarzeniu "System.Windows.Input.InputManager.Current.PostProcessInput" i przy każdym wywołaniu zrestartowałem licznik czasu bezczynności. Drugie podejście wydawało się obiecujące, ale problem polega na tym, że gdy mysz znajduje się nad aplikacją (ma fokus), ciągle otrzymuję to wydarzenie.

jakieś inne pomysły? a może sposób na zmodyfikowanie drugiego rozwiązania?

Odpowiedz

13

I rozwiązać problem przy użyciu kilku różnych technik zwinięte dać mi całkiem dobre rozwiązanie. Używam GetLastInput wypracować gdy system został ostatni dotknął ten jest dobrze udokumentowany gdzie indziej, ale tutaj jest moja metoda:

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

To tylko mówi mi, gdy system jest bezczynny, a nie aplikacji. Jeśli użytkownik kliknie w program Word i będzie tam pracować przez godzinę, nadal oczekuję limitu czasu. Aby obsłużyć ten przypadek, po prostu pamiętam, kiedy moja aplikacja traci ostrość poprzez nadpisanie OnDeactivated i OnActivated metod na obiekcie aplikacji:

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

Moja IsIdle rutyna została dodana do obiektu aplikacji. Obsługuje globalny przypadek, w którym aplikacja ma skupić, ale nic się nie stało (IsMachineIdle) oraz szczególnym przypadku, gdy aplikacja stracił ostrość, gdy użytkownik robi inne rzeczy (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; 
     } 
    } 

Ostatnią rzeczą, jaką zrobiłem było stworzenie pętla timera, która odpytała to zdarzenie flagowe kilka sekund.

To wydaje się działać prawidłowo.

+0

Należy pamiętać, _lostFocusTime należy zadeklarować jako DateTime? zmienna. Jeśli próbujesz zadeklarować jako DateTime, nie możesz NULL wartość out. – Saren

+0

Środowisko.TickCount ma typ int i jest zmuszany do owijania co 24,9 dni. LASTINPUTINFO.dwTime ma typ Uint, więc nie jest zawijany do 49,7 dni. Jeśli maszyna była włączona przez ponad 24,9 dni, to matematyka w tym przykładzie została skręcona. Zmniejszyłem problem, zamieniając Environment.TickCount na wywołanie funkcji WINAPI GetTickCount, która również zwraca wartość uint.Nie jestem pewien, co się stanie, jeśli ostatnie wejście trwało 49,5 dnia, a aktualna liczba pasków wynosi 50,0 dni. [Zgodnie z tą odpowiedzią na inne pytanie] (http://stackoverflow.com/a/1078089/2998072) powinno być OK. –

5

Cóż, nikt nie odpowiedział, więc kontynuowałem kopanie i znalazłem stosunkowo proste rozwiązanie, korzystając z ostatniego wejścia i czasu pracy OS. kod jest naprawdę prosty, ale to rozwiązanie sprawia, że ​​robię zbieranie danych, którego nigdy nie polecam, a także, zamiast być na poziomie aplikacji, jest to poziom operacyjny, który nie jest dokładnie tym, czego potrzebowałem. Jeśli ktoś kiedykolwiek otwiera ten wątek jest to kod, wystarczy użyć 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; 
    } 


} 
+0

Cześć, Próbuję rozwiązać oryginalny problem i uderzyć ten sam problem jak ty z roztworu znaleziono: to sytem szerokości. Czy w końcu dowiedzieć się, jak to zrobić wykrywanie bezczynności aplikacji szeroki? – dave

+0

Niezupełnie, nadal mam problem z rozwiązaniem systemowym, ale jeśli w jakiś sposób znajdziesz rozwiązanie, byłoby wspaniale, gdybyś mógł je opublikować. – Clueless

+0

Dodałem moje rozwiązanie jako odpowiedź. Daj mi ping, jeśli masz jakieś problemy lub uważasz, że to głupie. – dave

Powiązane problemy