Question

Comment puis-je déterminer par programme (en C #) si une AUTRE application étrangère (native, java, .NET ou autre ...) requiert actuellement une intervention de l'utilisateur? Cela peut-il être entièrement réalisé en code managé?

Ce que je recherche, c’est la mise en œuvre de:

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

Par saisie utilisateur, je veux dire quand une application demande à l’utilisateur de saisir des données ou de quitter un message d’erreur (boîtes de dialogue modales) et qu’il ne peut plus exécuter ses tâches normales. Une application de dessin qui attend que l’utilisateur dessine quelque chose n’a pas de sens.

PS: Une fois les modifications apportées pour refléter les commentaires du bas et clarifier la préoccupation, certains commentaires et réponses peuvent ne pas correspondre à 100% à la question. Tenez-en compte lors de l’évaluation des réponses et des remarques.

Était-ce utile?

La solution

C'est en général impossible. Prenons par exemple un type d'application commun, un traitement de texte. De nos jours, les vérifications orthographiques en arrière-plan exécutent périodiquement des sauvegardes automatiques de votre document, etc. Cependant, du point de vue des utilisateurs, il attend tout le temps ses commentaires.

Un autre cas courant serait un visualiseur de diaporama. A tout moment, vous pouvez appuyer sur une touche pour faire avancer une diapositive. Pourtant, votre utilisateur typique ne verrait pas cela comme une "attente de saisie".

Pour résumer: " en attente d’entrée " est un état subjectif et ne peut donc pas être déterminé par programme.

Autres conseils

Comment aimez-vous cela?

J'ai mis au point une solution qui semble fonctionner. Veuillez me prévenir en cas de problème avec ce code afin que je puisse également bénéficier d'améliorations. Cela fonctionne pour Excel dans la mesure où j'ai testé. Le seul problème que je n'aime pas, c'est que j'ai dû utiliser des appels non gérés. Il gère également le cas où une application est basée sur un dialogue similaire à MFC, dérivé de 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;
        }
    }
}

Si je vous comprends bien, vous pouvez essayer d’énumérer les threads du processus et de vérifier leur état. Le gestionnaire de tâches Windows fait quelque chose de similaire. Cela nécessitera toutefois des fonctions Win32 - Thread32First et Thread32Next, entre autres - mais vous pouvez y parvenir en utilisant la plus simple utilisation de P / Invoke en C #:

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

(la signature précise peut différer).

EDIT: Ok, il existe des fonctions correspondantes dans la bibliothèque .NET.

Si possible, réécrivez l'autre code en tant que processeur d'entrée simultanée (similaire à l'algorithme d'un serveur Web simultané):

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

Bien sûr, vous pourriez toujours avoir votre fonction:

static Boolean IsWaitingForUserInput(String processName) {
    return true;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top