プロセスがユーザー入力を要求しているかどうかをプログラムで調べる
-
05-07-2019 - |
質問
他の外部アプリケーション(ネイティブ、java、.NETなど)が現在ユーザー入力を要求しているかどうかをプログラムで(C#で)判断するにはどうすればよいですか?マネージコードでこれを完全に行うことはできますか?
私が探しているのは、次の実装です:
static Boolean IsWaitingForUserInput(String processName)
{
???
}
ユーザー入力を要求するということは、アプリケーションがユーザーにデータの入力またはエラーメッセージの終了(モーダルダイアログ)を要求し、通常のタスクを実行できなくなることを意味します。ユーザーが何かを描くのを待っている描画アプリケーションは、ここでは意味がありません。
PS:下部のコメントを反映して懸念を明確にするために編集した後、一部のコメントと回答は質問と100%一致しない場合があります。回答と発言を評価するとき、これを考慮してください。
解決
それは一般的に不可能です。たとえば、一般的な種類のアプリケーションであるワープロを考えてみましょう。最近では、バックグラウンドでスペルチェックを実行しますが、定期的にドキュメントなどを自動保存します。しかし、ユーザーの観点からは、常に入力を待っています。
別の一般的なケースは、スライドショービューアです。いつでもキーを押してスライドを進めることができます。しかし、一般的なユーザーはこれを「入力待ち」とは見なしません。
要約すると:「入力待ち」主観的な状態であるため、プログラムで決定することはできません。
他のヒント
これはどうですか?
機能しているように見える解決策を作成しました。このコードに問題がある場合は通知してください。改善の恩恵も受けます。私がテストした限りでは、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;
}
}
}
あなたのことをよく理解しているなら、プロセスのスレッドを列挙して、その状態を確認してみてください。 Windowsタスクマネージャーも同様の処理を行います。ただし、これにはWin32関数-とりわけThread32FirstおよびThread32Nextが必要になりますが、C#でP / Invokeを最も簡単に使用することでこれを実現できます。
[DllImport("Executor.dll")]
public static extern bool Thread32First(IntPtr handle, IntPtr threadEntry32);
(正確な署名は異なる場合があります)。
編集:OK、.NETライブラリには対応する関数があります。
可能であれば、他のコードを並行入力プロセッサーに書き換えます(並行Webサーバーのアルゴリズムと同様):
Wait for input
Fork process
Parent: Repeat
Child: (Worker) handle input
もちろん、あなたはまだあなたの機能を持つことができます:
static Boolean IsWaitingForUserInput(String processName) {
return true;
}