2009-09-10 6 views
5

Jak programowo (w języku C#) określić, czy KAŻDA inna aplikacja (natywna, java, .NET czy jakakolwiek ...) jest obecnie wymagająca dla użytkownika? Czy można to w pełni zrealizować w kodzie zarządzanym?Dowiedz się programowo, jeśli proces wymaga podania danych przez użytkownika

Co szukam jest realizacja:

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

Przez wymagających wprowadzania danych przez użytkownika mam na myśli, gdy aplikacja prosi użytkownika o wprowadzenie pewnych danych lub zamknąć komunikat o błędzie (dialogi modalnych) i nie jest w stanie wykonywać już normalne zadania. Aplikacja do rysowania, która czeka na użytkownika, aby coś narysować, nie jest tutaj przeznaczona.

PS: Po wprowadzeniu poprawek w celu odzwierciedlenia komentarzy na dole i wyjaśnienia problemu, niektóre komentarze i odpowiedzi mogą nie być w 100% spójne z pytaniem. Weź to pod uwagę przy ocenie odpowiedzi i uwag.

+0

Czy można zdefiniować "oczekiwanie na dane wejściowe użytkownika" w aplikacji GUI? Czy kliknięcie myszą nie jest również wprowadzane przez użytkownika? –

+0

Oczekując na dane wprowadzane przez użytkownika mam na myśli wszystko, co powoduje, że narzędzie strony trzeciej automatycznie uruchamia swoją pracę.Obejmuje to czekanie na zamknięcie okna dialogowego (jak w większości przypadków w oknie komunikatu o błędzie). – jdehaan

+0

Czy dobrze cię rozumiem, że "narzędzie strony trzeciej" i "KOLEJNA zagraniczna aplikacja" odnoszą się do tej samej aplikacji, której stan chcesz sprawdzić? –

Odpowiedz

9

Jest to zasadniczo niemożliwe. Weźmy na przykład powszechny rodzaj aplikacji, procesor tekstu. W dzisiejszych czasach będzie to działało w tle, okresowo automatycznie zapisuje dokument, itd. Jednak z perspektywy użytkownika cały czas czeka na wejście.

Innym częstym przypadkiem byłaby przeglądarka pokazów slajdów. W dowolnym momencie możesz nacisnąć klawisz, aby przesunąć slajd. Jednak typowy użytkownik nie postrzegałby tego jako "oczekiwanie na dane wejściowe".

Podsumowując: "oczekiwanie na wejście" jest stanem subiektywnym i dlatego nie można go określić programowo.

+0

niemożliwe do rozwiązania w sensie ogólnym. Wzór do zastosowania byłby strategią; musisz ustalić, jak zbadać każdą aplikację, której potrzebujesz, aby rozwiązać ten problem, i dowiedzieć się, co stanowi stan "oczekiwanie na dane wejściowe użytkownika". Jak pokazuje jedna z pozostałych odpowiedzi na to pytanie, można go rozwiązać w Excelu, używając modalnego okna dialogowego do "żądania" danych wprowadzanych przez użytkownika. –

+0

Niemożliwe to nic ;-) Zgadzam się, to kwestia definicji. Napisałem to pytanie dokładniej, istnieją ostateczne podejścia do rozwiązania problemu, takiego jak ten, który opublikowałem (najlepiej, gdy mogę uzyskać i jestem z niego zadowolony). – jdehaan

+0

W większości z tych pytań nie ma niemożliwości. Jest tylko "jak źle tego chcesz" –

0

Jeśli dobrze cię rozumiem, możesz spróbować wyliczyć wątki procesu i sprawdzić ich stany. Windows Task Manager robi coś podobnego. jednak będzie to wymagało funkcji Win32 - Thread32First i Thread32Next między innymi - ale można to osiągnąć przez najprostszym wykorzystaniem P/Invoke w C#:

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

(Precise podpis może się różnić).

EDYCJA: OK, istnieją odpowiednie funkcje w bibliotece .NET.

+0

Wyliczanie wątków i sprawdzanie ich stanu nie było wystarczające, jak zauważył MSalters, Input-Idle jest stanem, w którym aplikacja przetwarza komunikaty, a nie 100% sygnalizacją zapotrzebowania na dane wejściowe od użytkownika. – jdehaan

3

Jak ci się to podoba?

Opracowałem rozwiązanie, które wydaje się działać, proszę o poinformowanie mnie w przypadku problemów z tym kodem, więc również zyskuję na ulepszeniach. Działa dla Excela tak długo jak testowałem. Jedyną rzeczą, której nie lubię, jest to, że musiałem używać połączeń niezarządzanych. Obsługuje także przypadek, gdy aplikacja jest oparta na dialogu podobnym do MFC, pochodzącym z 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

jest to prawdopodobnie jedyna definicja "oczekiwania na dane wejściowe użytkownika", która jest ważna. modalne okno dialogowe. powinniśmy chyba nazwać to "wymagającym wkładem użytkownika". – slf

+0

Dobry pomysł! Wiem, że to była mniej więcej kwestia definicji. Ten kawałek kodu ma nadzieję, że jest dobrą bazą dla innych ludzi, którzy chcą zrobić to samo. – jdehaan

+0

Jedną z wad tego rozwiązania jest to, że pomija detekcję uruchomionego debuggera Jit ... Jakieś pomysły na rozwiązanie (nie tylko dla jednego specjalnego debuggera)? – jdehaan

0

Jeśli to możliwe, przepisać inny kod będzie procesor jednoczesnego wejścia (podobny do algorytmu jednoczesnym serwer WWW):

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

oczywiście można nadal mieć swoją funkcję:

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