2010-07-20 1 views
10

Ich muss die Leerlaufzeit meiner WPF-Anwendung zählen (Leerlaufzeit = wenn keine Tastatureingabe, Mauseingabe (Bewegung + Klicks) stattgefunden hat). Bisher habe ich versucht, zwei Ansätze, aber keiner von ihnen scheinen zu funktionieren:WPF: Anwendungsfreie Zeit

  1. der Dispatcher einen Delegierten jedes Mal, wenn eine contextIdle Priorität erhalten aufgerufen, das Problem ist, dass die Bindung und viele andere Operationen auf ihn berufen und damit Das kann ich nicht wirklich gebrauchen.
  2. mit dem Eingabe-Manager Ich registriert für die "System.Windows.Input.InputManager.Current.PostProcessInput" -Ereignis und jedes Mal, wenn es aufgerufen wurde, startete ich die Ruhezeit Anzahl neu. Der zweite Ansatz schien vielversprechend, aber das Problem ist, dass, wenn die Maus über die Anwendung ist (es hat den Fokus) Ich bekomme immer das Ereignis.

Irgendwelche anderen Ideen? oder vielleicht eine Möglichkeit, um die zweite Lösung zu ändern?

Antwort

13

Ich löste das Problem ein paar verschiedene Techniken aufgerollt, um mir eine ziemlich gute Lösung. Ich benutze GetLastInput heraus zu arbeiten, wenn das System das letzte Mal war gerührt Dieses gut an anderer Stelle dokumentiert ist, aber hier ist meine Methode:

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

Das bin ich nur erzählt, wenn das System im Leerlauf war, nicht die Anwendung. Wenn der Benutzer in Word klickt und dort für eine Stunde arbeitet, möchte ich immer noch eine Zeitüberschreitung. Um diesen Fall zu behandeln, ich erinnere mich nur, wenn meine Anwendung den Fokus verliert durch die OnDeactivated zwingende und OnActivated Methoden auf dem Anwendungsobjekt:

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

Meine IsIdle Routine wurde auf das Anwendungsobjekt hinzugefügt. Es behandelt den globalen Fall, dass der App des Fokus hat, aber es ist nichts passiert (IsMachineIdle) und den speziellen Fall, wo die Anwendung des Fokus verloren, während der Benutzer andere Dinge tut (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; 
     } 
    } 

Das letzte, was ich tat, war, erstellen eine Timer-Schleife, die dieses Flag-Ereignis einige Sekunden lang abfragt.

Dies scheint gut zu funktionieren.

+0

Denken Sie daran, _lostFocusTime sollte als DateTime deklariert werden? Variable. Wenn Sie versuchen, DateTime zu deklarieren, können Sie den Wert nicht NULLEN. – Saren

+0

Environment.TickCount hat den Typ int und muss alle 24,9 Tage umbrechen. LASTINPUTINFO.dwTime hat den Typ "uint", daher wird es erst nach 49,7 Tagen umgebrochen. Wenn die Maschine länger als 24,9 Tage eingeschaltet war, dann ist die Mathematik im Beispiel vermasselt. Ich habe das Problem gemildert, indem ich Environment.TickCount durch einen Aufruf der WINAPI-Funktion GetTickCount ersetzt habe, die ebenfalls einen uint-Wert zurückgibt.Ich bin mir nicht sicher, was passiert, wenn die letzte Eingabe bei 49,5 Tagen und die aktuelle Tickzahl bei 50,0 Tagen liegt. [Nach dieser Antwort auf eine andere Frage] (http://stackoverflow.com/a/1078089/2998072) sollte es in Ordnung sein. –

5

Nun, niemand schien zu antworten, also fuhr ich fort zu graben und fand eine relativ einfache Lösung mit der letzten Eingabe + Betriebszeit des Betriebssystems. Der Code ist wirklich einfach, aber diese Lösung macht mich zu Datenabfragen, die ich nie empfehle und auch nicht auf der Anwendungsebene, sondern auf der Betriebssystemebene, die nicht die exakte Lösung ist, die ich brauche. Wenn jemand jemals diesen Thread eröffnet dies der Code ist nur GetIdleTime() verwenden:

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

Howdy, ich versuche, Ihr ursprüngliches Problem zu lösen und das gleiche Problem zu lösen, das Sie bei Ihrer Lösung gefunden haben: es ist systemweit. Haben Sie schließlich herausgefunden, wie eine anwendungsweite Leerlauferkennung durchgeführt werden kann? – dave

+0

Nicht wirklich, ich bin immer noch mit der Systemlösung fest, aber wenn Sie irgendwie die Lösung finden, wäre es toll, wenn Sie es buchen können. – Clueless

+0

Ich habe meine Lösung als Antwort hinzugefügt. Gib mir einen Ping, wenn du irgendwelche Probleme hast oder du denkst es dumm. – dave