2009-09-10 20 views
5

Wie kann ich programmgesteuert (in C#) bestimmen, ob eine ANDERE fremde Anwendung (nativ, Java, .NET oder was auch immer ...) gerade Benutzereingaben verlangt? Kann dies vollständig in verwaltetem Code erfolgen?Programmgesteuert herausfinden, ob ein Prozess Benutzereingaben erfordert

Was ich suche ist die Umsetzung:

static Boolean IsWaitingForUserInput(String processName) 
{ 
    ??? 
} 

Durch anspruchsvolle Benutzereingabe ich meine, wenn eine Anwendung den Benutzer auffordert, einige Daten oder beenden eine Fehlermeldung (Modal Dialoge) und ist nicht zu betreten kann seine normalen Aufgaben nicht mehr ausführen. Eine Zeichenanwendung, die darauf wartet, dass der Benutzer etwas zeichnet, ist hier nicht gemeint.

PS: Nach Änderungen, die die Kommentare am Ende widerspiegeln und die Bedenken deutlicher machen, stimmen einige Kommentare und Antworten möglicherweise nicht zu 100% mit der Frage überein. Berücksichtigen Sie dies bei der Auswertung der Antworten und Bemerkungen.

+0

Können Sie "Warten auf Benutzereingabe" in einer GUI-Anwendung definieren? Ist ein Mausklick nicht auch Benutzereingabe? –

+0

Indem ich auf Benutzereingaben warte, meine ich alles, was dem Drittanbieter-Tool vorwirft, mit seiner Arbeit automatisch fortzufahren.Dazu gehört das Warten auf das Beenden eines Dialogfeldes (in den meisten Fällen eine Fehlermeldung) – jdehaan

+0

Verstehe ich Sie gut, dass "das Third-Party-Tool" und die "ANDERE fremde Anwendung" beide auf dieselbe Anwendung verweisen, deren Status willst du das überprüfen? –

Antwort

9

Es ist im Allgemeinen unmöglich. Nehmen Sie zum Beispiel eine übliche Art von Anwendung, ein Textverarbeitungsprogramm. Heutzutage werden Rechtschreibprüfungen im Hintergrund ausgeführt, es speichert automatisch Ihr Dokument und so weiter. Aus der Sicht eines Benutzers wartet es jedoch ständig auf Eingaben.

Ein weiterer häufiger Fall wäre ein Diashow-Viewer. Zu jedem Zeitpunkt können Sie eine Taste drücken, um eine Folie zu verschieben. Ihr typischer Benutzer würde dies jedoch nicht als "Warten auf Eingabe" betrachten.

Zusammenfassend: "Warten auf Eingabe" ist ein subjektiver Zustand und kann daher nicht programmgesteuert bestimmt werden.

+0

unmöglich in einem allgemeinen Sinn zu lösen. Das anzuwendende Muster wäre eine Strategie; Sie müssen bestimmen, wie Sie die einzelnen Anwendungen, die Sie zur Lösung dieses Problems benötigen, untersuchen und herausfinden, was einen Status "Warten auf Benutzereingaben" ausmacht. Wie eine der anderen Antworten auf diese Frage zeigt, kann sie für Excel mit einem modalen Dialog gelöst werden, um Benutzereingaben zu "fordern". –

+0

Unmöglich ist nichts ;-) Ich stimme zu, es ist eine Frage der Definition. Ich habe die Frage genauer umgeschrieben, es gibt definitiv Ansätze, um das Problem zu lösen, wie das, das ich gepostet habe (das Beste, was ich bekommen konnte, und ich bin damit zufrieden) – jdehaan

+0

In den meisten dieser Fragen gibt es kein Unmögliches. Es gibt nur "wie schlecht willst du das" –

0

Wenn ich Sie gut verstehe, können Sie versuchen, die Threads des Prozesses aufzuzählen und ihre Zustände zu überprüfen. Windows Task-Manager macht etwas ähnliches. Dies wird jedoch Win32-Funktionen benötigen - Thread32First und Thread32Next unter anderem - aber Sie können dies in C# durch die einfachste Verwendung von P/Invoke erreichen:

[DllImport("Executor.dll")] 
    public static extern bool Thread32First(IntPtr handle, IntPtr threadEntry32); 

(Precise Signatur kann abweichen).

EDIT: Ok, es gibt entsprechende Funktionen in der .NET-Bibliothek.

+0

Das Aufzählen der Threads und Überprüfen ihres Status war nicht genug, wie MSalters angemerkt hat, Input-Idle ist der Zustand, in dem die Anwendung Nachrichten verarbeitet, nicht wirklich 100% signalisieren eine Nachfrage nach Eingabe vom Benutzer. – jdehaan

3

Wie gefällt Ihnen das?

Ich habe eine Lösung ausgearbeitet, die zu funktionieren scheint. Bitte benachrichtigen Sie mich im Falle von Problemen mit diesem Code, damit ich auch von Verbesserungen profitieren kann. Soweit ich getestet habe, funktioniert es für Excel. Das einzige Problem, das ich nicht mag, ist, dass ich nicht verwaltete Anrufe verwenden musste. Es behandelt auch den Fall, wenn eine Anwendung auf einem Dialog wie für MFC basiert, abgeleitet von CDialog.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Runtime.InteropServices; 
using System.Threading; 
using System.Diagnostics; 

namespace Util 
{ 
    public class ModalChecker 
    { 
     public static Boolean IsWaitingForUserInput(String processName) 
     { 
      Process[] processes = Process.GetProcessesByName(processName); 
      if (processes.Length == 0) 
       throw new Exception("No process found matching the search criteria"); 
      if (processes.Length > 1) 
       throw new Exception("More than one process found matching the search criteria"); 
      // for thread safety 
      ModalChecker checker = new ModalChecker(processes[0]); 
      return checker.WaitingForUserInput; 
     } 

     #region Native Windows Stuff 
     private const int WS_EX_DLGMODALFRAME = 0x00000001; 
     private const int GWL_EXSTYLE = (-20); 
     private delegate int EnumWindowsProc(IntPtr hWnd, int lParam); 
     [DllImport("user32")] 
     private extern static int EnumWindows(EnumWindowsProc lpEnumFunc, int lParam); 
     [DllImport("user32", CharSet = CharSet.Auto)] 
     private extern static uint GetWindowLong(IntPtr hWnd, int nIndex); 
     [DllImport("user32")] 
     private extern static uint GetWindowThreadProcessId(IntPtr hWnd, out IntPtr lpdwProcessId); 
     #endregion 

     // The process we want the info from 
     private Process _process; 
     private Boolean _waiting; 

     private ModalChecker(Process process) 
     { 
      _process = process; 
      _waiting = false; //default 
     } 

     private Boolean WaitingForUserInput 
     { 
      get 
      { 
       EnumWindows(new EnumWindowsProc(this.WindowEnum), 0); 
       return _waiting; 
      } 
     } 

     private int WindowEnum(IntPtr hWnd, int lParam) 
     { 
      if (hWnd == _process.MainWindowHandle) 
       return 1; 
      IntPtr processId; 
      GetWindowThreadProcessId(hWnd, out processId); 
      if (processId.ToInt32() != _process.Id) 
       return 1; 
      uint style = GetWindowLong(hWnd, GWL_EXSTYLE); 
      if ((style & WS_EX_DLGMODALFRAME) != 0) 
      { 
       _waiting = true; 
       return 0; // stop searching further 
      } 
      return 1; 
     } 
    } 
} 
+1

Dies ist wahrscheinlich die einzige Definition von "Warten auf Benutzereingabe", die gültig ist. ein modaler Dialog wir sollten es wahrscheinlich "anspruchsvolle Benutzereingabe" nennen – slf

+0

Gute Idee! Ich weiß, das war mehr oder weniger eine Frage der Definition. Dieses Stück Code ist hoffentlich eine gute Basis für andere Leute, die dasselbe machen wollen. – jdehaan

+0

Ein Nachteil davon ist, dass es die Erkennung eines ausgelösten Jit-Debuggers vermisst ... Irgendwelche Ideen, um es zu lösen (nicht nur für einen speziellen Jit-Debugger)? – jdehaan

0

Wenn möglich, schreiben Sie den anderen Code, um einen gleichzeitigen Eingangsprozessor (ähnlich den Algorithmus für einen gleichzeitigen Web-Server) zu sein:

Wait for input 
Fork process 
    Parent: Repeat 
    Child: (Worker) handle input 

Natürlich können Sie immer noch Ihre Funktion haben könnten:

static Boolean IsWaitingForUserInput(String processName) { 
    return true; 
} 
Verwandte Themen