如果进程要求用户输入,请以编程方式查找

时间:2009-09-10 11:10:17

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

如果另外一个外国应用程序(本机,java,.NET或其他......)目前要求用户输入,我如何以编程方式(在C#中)确定?这可以在托管代码中完全完成吗?

我正在寻找的是:

的实施
static Boolean IsWaitingForUserInput(String processName)
{
    ???
}

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

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

4 个答案:

答案 0 :(得分:9)

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

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

总结:“等待输入”是一种主观状态,因此无法以编程方式确定。

答案 1 :(得分:3)

你觉得这样吗?

我制定了一个似乎有效的解决方案,如果此代码出现问题,请通知我,以便我也从中获益。就我测试而言,它适用于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;
        }
    }
}

答案 2 :(得分:0)

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

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

(精确签名可能有所不同)。

编辑:好的,.NET库中有相应的功能。

答案 3 :(得分:0)

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

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

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

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