Descobrir programaticamente se um processo está exigindo a entrada do usuário
-
05-07-2019 - |
Pergunta
Como posso programaticamente (em C #) determinar, se outra aplicação externa (nativo, java, .NET ou qualquer outra coisa ...) está exigindo a entrada do usuário? isso pode ser feito totalmente em código gerenciado?
O que eu estou procurando é a implementação de:
static Boolean IsWaitingForUserInput(String processName)
{
???
}
Ao exigir a entrada do usuário que quero dizer quando um aplicativo pede ao usuário para inserir alguns dados ou sair uma mensagem de erro (diálogos modais) e não é capaz de desempenhar as suas tarefas normais anymore. Um aplicativo de desenho que está aguardando o usuário desenhar algo que não se quer dizer aqui.
PS: Depois de edições para refletir os comentários na parte inferior e fazer a preocupação mais clara, alguns comentários e respostas podem não ser 100% compatível com a pergunta. Leve isso em conta ao avaliar as respostas e observações.
Solução
É no impossível geral. Tomemos por exemplo um tipo comum de aplicação, um processador de texto. Hoje em dia, que será executado verificação ortográfica em segundo plano, periodicamente auto-salva seu documento, etcetera. No entanto, a partir de uma perspectiva de usuários está esperando para a entrada de todo o tempo.
Outro caso comum seria um visualizador de slideshow. A qualquer momento no tempo, você poderia pressionar uma tecla para avançar um slide. No entanto, o usuário típico não iria ver isso como "aguardando entrada".
Para resumir:. "Aguardando entrada" é um estado subjetivo e, portanto, não pode ser determinado por meio de programação
Outras dicas
Como você gosta disso?
Eu trabalhei uma solução que parece funcionar, por favor avise-me em caso de problemas com este código para que eu também ganhar o benefício de melhorias. Ele trabalha para Excel, tanto quanto eu testei. O único problema que eu não gosto é que eu tive que usar chamadas não gerenciados. Ele também lida com o caso quando um aplicativo é baseado em um diálogo como para MFC, derivada de 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;
}
}
}
Se eu entendi bem, você pode tentar enumerar tópicos do processo e verificar seus estados. O Windows Task Manager faz algo semelhante. Este, porém, exigirá funções Win32 - Thread32First e Thread32Next entre outros - mas você pode conseguir isto, o uso mais simples de P / Invoke em C #:
[DllImport("Executor.dll")]
public static extern bool Thread32First(IntPtr handle, IntPtr threadEntry32);
(assinatura Precise podem diferir).
EDIT: Ok., Não são funções correspondentes na biblioteca .NET
Se possível, reescrever o outro código para ser um processador de entrada em simultâneo (similar ao algoritmo para um servidor web concorrente):
Wait for input
Fork process
Parent: Repeat
Child: (Worker) handle input
É claro, você ainda pode ter sua função:
static Boolean IsWaitingForUserInput(String processName) {
return true;
}