【问题标题】:Find out programmatically if a process is demanding user input以编程方式找出进程是否需要用户输入
【发布时间】:2009-09-10 11:10:17
【问题描述】:

我如何以编程方式(在 C# 中)确定另一个外部应用程序(本机、java、.NET 或其他什么...)当前是否需要用户输入?这可以完全在托管代码中完成吗?

我正在寻找的是:

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

要求用户输入是指当应用程序要求用户输入一些数据或退出错误消息(模式对话框)并且不再能够执行其正常任务时。此处不指等待用户绘制内容的绘图应用程序。

PS:经过编辑以反映底部的 cmets 并使问题更清晰,某些 cmets 和答案可能与问题不是 100% 一致。在评估答案和评论时要考虑到这一点。

【问题讨论】:

  • 您能在 GUI 应用程序中定义“等待用户输入”吗?鼠标点击不也是用户输入吗?
  • 等待用户输入我的意思是指一切让第三方工具自动继续工作的事情。这包括等待退出一个对话框(在我们大多数情况下就像一个错误消息框)
  • 我理解你的意思吗,“第三方工具”和“ANOTHER外国应用程序”都是指同一个应用程序,你要检查谁的状态?
  • 对!我想了很多关于如何清楚地表述问题,令人惊讶的是人类交流中总是存在这么多问题:-)
  • 我知道这没有回答您的问题,但我相信您正在寻找的应用程序状态的名称是“input-idle”。尝试搜索它,看看是否有任何有用的点击。

标签: c# user-interface automation modal-dialog


【解决方案1】:

这通常是不可能的。以一种常见的应用程序为例,文字处理器。现在它将在后台运行拼写检查,它会定期自动保存您的文档等。然而,从用户的角度来看,它一直在等待输入。

另一种常见的情况是幻灯片查看器。您可以随时按下一个键来推进幻灯片。然而,您的典型用户不会将此视为“等待输入”。

总而言之:“等待输入”是一种主观状态,因此无法通过编程方式确定。

【讨论】:

  • 一般意义上无法解决。应用的模式将是一种策略;您必须确定如何检查解决此问题所需的每个应用程序,并弄清楚什么构成“等待用户输入”状态。正如该问题的其他答案之一所示,可以使用模式对话框“要求”用户输入来解决 Excel 的问题。
  • 不可能什么都不是 ;-) 我同意这是一个定义问题。我重写了更准确的问题,有明确的方法可以解决我发布的问题(我能得到最好的,我很满意)
  • 这些问题中的大多数都不是不可能的。只有“你想要这个有多糟糕”
  • 感谢 Oplopanax,我有时想知道,一个明确无助于解决我和其他人可能遇到的实际问题的答案(无论是否好,但至少总比没有好)会被多次投票! ! [在此回复时,问题是以一种误导性的方式编写的,这不应该是指责 MSalters 的评论;-)]
【解决方案2】:

你觉得这个怎么样?

我制定了一个似乎可行的解决方案,如果此代码出现问题,请通知我,这样我也可以从改进中受益。据我测试,它适用于 Excel。我不喜欢的唯一问题是我不得不使用非托管调用。它还可以处理应用程序基于从 CDialog 派生的 MFC 等对话框的情况。

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

【讨论】:

  • 这可能是唯一有效的“等待用户输入”定义。模态对话框。我们或许应该称之为“要求用户输入”
  • 好主意!我知道这或多或少是定义问题。这段代码有望成为其他想要做同样事情的人的良好基础。
  • 它的一个缺点是它错过了对触发的 jit 调试器的检测......有什么想法可以解决它(不仅仅是一个特殊的 jit 调试器)?
【解决方案3】:

如果我理解你,你可以尝试枚举进程的线程并检查它们的状态。 Windows 任务管理器执行类似的操作。然而,这将需要 Win32 函数 - Thread32First 和 Thread32Next 等 - 但您可以通过在 C# 中最简单地使用 P/Invoke 来实现:

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

(精确签名可能不同)。

编辑:好的,.NET 库中有相应的函数。

【讨论】:

  • 枚举线程并检查它们的状态是不够的,就像 MSalters 所说的那样,Input-Idle 是应用程序处理消息的状态,并不是真正 100% 表示对用户输入的需求。跨度>
【解决方案4】:

如果可能,将其他代码重写为并发输入处理器(类似于并发 Web 服务器的算法):

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

当然,你仍然可以拥有你的功能:

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

【讨论】:

    猜你喜欢
    • 2013-04-09
    • 1970-01-01
    • 2017-04-14
    • 2021-03-19
    • 1970-01-01
    • 2015-07-03
    • 1970-01-01
    • 1970-01-01
    • 2021-07-24
    相关资源
    最近更新 更多