C# 以编程方式查明进程是否需要用户输入

C# 以编程方式查明进程是否需要用户输入,c#,user-interface,automation,modal-dialog,C#,User Interface,Automation,Modal Dialog,我如何以编程方式(在C语言中)确定另一个外部应用程序(本机、java、.NET或其他什么…)当前是否需要用户输入?这可以完全在托管代码中完成吗 我期待的是实施: static Boolean IsWaitingForUserInput(String processName) { ??? } 通过要求用户输入,我的意思是当应用程序要求用户输入一些数据或退出错误消息(模式对话框),并且无法再执行其正常任务时。这里不指等待用户绘制的绘图应用程序 注:在编辑以反映底部的评论并使问题更清楚后,一

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

我期待的是实施:

static Boolean IsWaitingForUserInput(String processName)
{
    ???
}
通过要求用户输入,我的意思是当应用程序要求用户输入一些数据或退出错误消息(模式对话框),并且无法再执行其正常任务时。这里不指等待用户绘制的绘图应用程序


注:在编辑以反映底部的评论并使问题更清楚后,一些评论和答案可能与问题不完全一致。在评估答案和备注时要考虑到这一点。

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

(准确的签名可能有所不同)


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

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

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

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

您觉得如何

我制定了一个似乎可行的解决方案,请在代码出现问题时通知我,这样我也能从改进中获益。据我测试,它适用于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;
        }
    }
}

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

当然,您仍然可以使用您的功能:

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

您能在GUI应用程序中定义“等待用户输入”吗?鼠标点击不也是用户输入吗?等待用户输入,我指的是让第三方工具自动继续工作的一切。这包括等待退出对话框(在我们大多数情况下都是错误消息框)。我是否很好地理解您,“第三方工具”和“另一个外部应用程序”都指向同一个应用程序,您要检查其状态?对!我想了很多关于如何清楚地表述这个问题,令人惊讶的是人类交流中总是有这么多问题:-)我知道这并没有回答你的问题,但我相信你正在寻找的应用程序状态的名称是“输入空闲”。尝试搜索一下,看看是否有任何有用的点击。这可能是“等待用户输入”唯一有效的定义。模态对话框。我们应该称之为“要求用户输入”的好主意!我知道这或多或少是个定义问题。这段代码有望为其他想要做同样事情的人提供一个良好的基础。它的一个缺点是它没有检测到触发的jit调试器。。。有没有解决这个问题的想法(不仅仅是针对一个特殊的jit调试器)?从一般意义上说是不可能解决的。应用的模式将是一种战略;您必须确定如何检查解决此问题所需的每个应用程序,并找出“等待用户输入”状态的构成。正如这个问题的另一个答案所示,Excel可以通过使用模式对话框“要求”用户输入来解决这个问题。不可能什么都不是;-)我同意这是一个定义问题。我重写了这个问题,更准确地说,有一些明确的方法可以解决这个问题,就像我发布的那样(我能得到的最好的,我很满意),这些问题中的大多数都没有不可能。只有“你有多想要这个”感谢Oplopanax,我有时想知道,一个肯定无助于解决我和其他人(很差或没有,但至少比没有好)的真正问题的答案会被投票这么多次!!![在回答此问题时,问题是以一种误导性的方式写的,这不应该是指责MSalters的注释;-)]枚举线程并检查其状态是不够的,正如MSalters所说,Input Idle是应用程序处理消息的状态,实际上并不是100%表示需要用户输入。
Wait for input
Fork process
  Parent: Repeat
  Child: (Worker) handle input
static Boolean IsWaitingForUserInput(String processName) {
    return true;
}