Узнайте программно, если процесс требует ввода пользователя

StackOverflow https://stackoverflow.com/questions/1404658

Вопрос

Как я могу программно (в C #) определить, требует ли ДРУГОЕ стороннее приложение (нативное, Java, .NET или что-то еще ...) пользовательский ввод? Можно ли это сделать полностью в управляемом коде?

Что я ищу, так это реализацию:

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

Под требованием пользовательского ввода я подразумеваю, когда приложение просит пользователя ввести некоторые данные или выйти из сообщения об ошибке (модальные диалоговые окна) и больше не может выполнять свои обычные задачи. Приложение для рисования, которое ждет, когда пользователь что-то нарисовает, здесь не подразумевается.

PS: После редактирования, чтобы отразить комментарии внизу и прояснить проблему, некоторые комментарии и ответы могут не соответствовать 100% вопроса. Учитывайте это при оценке ответов и замечаний.

Это было полезно?

Решение

Это вообще невозможно. Возьмем, к примеру, обычное приложение, текстовый процессор. В настоящее время, когда будут выполняться проверки орфографии в фоновом режиме, он периодически автоматически сохраняет ваш документ и так далее. Тем не менее, с точки зрения пользователей, он все время ждет ввода.

Другим распространенным случаем может быть просмотрщик слайд-шоу. В любой момент вы можете нажать клавишу для продвижения слайда. Тем не менее, ваш обычный пользователь не будет рассматривать это как «ожидающий ввода».

Подводя итог: "ожидание ввода" является субъективным состоянием и поэтому не может быть определено программно.

Другие советы

Как тебе это нравится?

Я разработал решение, которое, кажется, работает, пожалуйста, сообщите мне в случае проблем с этим кодом, чтобы я также получил выгоду от улучшений. Это работает для Excel, насколько я тестировал. Единственная проблема, которая мне не нравится, это то, что мне приходилось использовать неуправляемые звонки. Он также обрабатывает случай, когда приложение основано на диалоговом окне, подобном MFC, производном от 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;
        }
    }
}

Если я вас хорошо понимаю, вы можете попытаться перечислить потоки процесса и проверить их состояния. Диспетчер задач Windows делает нечто подобное. Для этого, однако, потребуются функции Win32 - Thread32First и Thread32Next, среди прочих - но вы можете добиться этого с помощью простейшего использования P / Invoke в C #:

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

(Точная подпись может отличаться).

РЕДАКТИРОВАТЬ: Хорошо, в библиотеке .NET есть соответствующие функции.

Если возможно, перепишите другой код, чтобы он был параллельным входным процессором (аналогично алгоритму для параллельного веб-сервера):

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

Конечно, вы все равно можете выполнять свою функцию:

static Boolean IsWaitingForUserInput(String processName) {
    return true;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top