Saltar al contenido

Detectar cuando se abre o se cierra una ventana específica en otro proceso

Ten en cuenta que en las ciencias un problema casi siempre tiene más de una soluciones, pero aquí mostraremos lo más óptimo y mejor.

Solución:

Puede utilizar cualquiera de estas opciones:

  • Usando el método SetWinEventHook
  • Manejo de eventos de automatización de la interfaz de usuario (preferido) (sugerido por Hans en los comentarios)

Solución 1: uso del método SetWinEventHook

Utilizando SetWinEventHook puede escuchar algunos eventos de otros procesos y registrar un WinEventProc método de devolución de llamada para recibir el evento cuando se produjo el evento.

Aquí EVENT_SYSTEM_FOREGROUND nos puede ayudar.

Limitamos el receptor de eventos para recibir este evento de un proceso específico y luego verificamos si el texto de la ventana que causa el evento es igual a Page Setup entonces podemos decir el Page Setup La ventana en el proceso de destino está abierta; de lo contrario, podemos decirle al Page Setup el diálogo no está abierto.

Para mantener las cosas simples en el siguiente ejemplo, supuse un notepad instancia está abierta cuando se inicia la aplicación, pero también puede utilizar Win32_ProcessStartTrace para detectar cuando un notepad la aplicación se ejecuta.

Para ser más específico y decir cuándo se cierra el diálogo, puede escuchar EVENT_OBJECT_DESTROY y detectar si el mensaje es para la ventana que nos interesa.

public const uint EVENT_SYSTEM_FOREGROUND = 0x0003;
public const uint EVENT_OBJECT_DESTROY = 0x8001;
public const uint WINEVENT_OUTOFCONTEXT = 0;
public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd,
    int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
[DllImport("user32.dll")]
public static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr
    hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess,
    uint idThread, uint dwFlags);
[DllImport("user32.dll")]
public static extern bool UnhookWinEvent(IntPtr hWinEventHook);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
IntPtr hook = IntPtr.Zero;
protected override void OnLoad(EventArgs e)

    base.OnLoad(e);
    var p = System.Diagnostics.Process.GetProcessesByName("notepad").FirstOrDefault();
    if (p != null)
        hook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND,
        IntPtr.Zero, new WinEventDelegate(WinEventProc), 
        (uint)p.Id, 0, WINEVENT_OUTOFCONTEXT);

protected override void OnFormClosing(FormClosingEventArgs e)

    UnhookWinEvent(hook);
    base.OnFormClosing(e);

void WinEventProc(IntPtr hWinEventHook, uint eventType,
    IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)

    string s = "Page Setup";
    StringBuilder sb = new StringBuilder(s.Length + 1);
    GetWindowText(hwnd, sb, sb.Capacity);
    if (sb.ToString() == s)
        this.Text = "Page Setup is Open";
    else
        this.Text = "Page Setup is not open";

Solución 2: manejo de eventos de automatización de la interfaz de usuario

Como sugirió Hans en los comentarios, puede usar las API de automatización de la interfaz de usuario para suscribirse a WindowOpenedEvent y WindowClosedEvent.

En el siguiente ejemplo, supongo que hay una instancia abierta de notepad y detectado apertura y cierre de su Page Setup diálogo:

protected override void OnLoad(EventArgs e)

    base.OnLoad(e);
    var notepad = System.Diagnostics.Process.GetProcessesByName("notepad")
                        .FirstOrDefault();
    if (notepad != null)
    
        var notepadMainWindow = notepad.MainWindowHandle;
        var notepadElement = AutomationElement.FromHandle(notepadMainWindow);
        Automation.AddAutomationEventHandler(
            WindowPattern.WindowOpenedEvent, notepadElement,
            TreeScope.Subtree, (s1, e1) =>
            
                var element = s1 as AutomationElement;
                if (element.Current.Name == "Page Setup")
                
                    //Page setup opened.
                    this.Invoke(new Action(() =>
                    
                        this.Text = "Page Setup Opened";
                    ));
                    Automation.AddAutomationEventHandler(
                        WindowPattern.WindowClosedEvent, element,
                        TreeScope.Subtree, (s2, e2) =>
                        
                            //Page setup closed.
                            this.Invoke(new Action(() =>
                            
                                this.Text = "Closed";
                            ));
                        );
                
            );
    

protected override void OnFormClosing(FormClosingEventArgs e)

    Automation.RemoveAllEventHandlers();
    base.OnFormClosing(e);

No olvide agregar una referencia a UIAutomationClient y UIAutomationTypes ensamblajes y agregar using System.Windows.Automation;.

Necesita utilizar las importaciones de user32.dll para esto, diría yo.

En primer lugar, en sus usos asegúrese de tener:

using System.Runtime.InteropServices;
using System.Linq;

Luego, en su clase en la parte superior, inserte este código para importar métodos desde la DLL.

    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll")]
    public static extern IntPtr GetParent(IntPtr hWnd);

Ahora, en su propio método, el siguiente código debería funcionar:

        var _notepadProcess = System.Diagnostics.Process.GetProcesses().Where(x => x.ProcessName.ToLower().Contains("notepad")).DefaultIfEmpty(null).FirstOrDefault();
        if ( _notepadProcess != null )
        
            var _windowHandle = FindWindow(null, "Page Setup");
            var _parent = GetParent(_windowHandle);
            if ( _parent == _notepadProcess.MainWindowHandle )
            
                //We found our Page Setup window, and it belongs to Notepad.exe - yay!
            
        

Esto debería ayudarte a empezar.

***** EDITAR ******

De acuerdo, tienes esto:

mew.EventArrived += (sender, args) =>  AppStarted(); ;

Esto garantizará que el método AppStarted () se active cuando se inicie el proceso notepad.exe.

Luego esperas 300ms (¡¿por alguna razón?!) Y luego llamas a PopInFront:

async void AppStarted()
         
    await Task.Delay(300);
    BeginInvoke(new System.Action(PoPInFront));

Dentro de PopInFront () intentas encontrar la ventana “Configuración de página”

var _windowHandle = FindWindow(null, "Page Setup");

Sin embargo, mi consulta aquí es: dentro de los 0.3 segundos que han pasado, ¿puede decir que ha podido abrir el bloc de notas, esperar a que se inicie la GUI y navegar al menú Archivo -> Configuración de página en .3 de segundo para que la siguiente área de código encuentre la ventana? – Supongo que no, lo que necesitas aquí es un bucle.

Lo que deberías estar haciendo es:

  1. Evento de consulta de WMI activado
  2. Inicie un trabajador en segundo plano con un ciclo while que se repite mientras el proceso notepad.exe está activo
  3. En el ciclo while, siga comprobando la ventana Configuración de página
  4. Una vez que lo encuentre, abra su propio cuadro de diálogo, marque otra variable para realizar un seguimiento de que se muestra su cuadro de diálogo
  5. Una vez que el cuadro de diálogo de configuración de página ya no se muestra (FindWindow devolverá cero), vuelva a marcar la variable en la etapa 4 y permita que la ventana de configuración de página se encuentre nuevamente.

Sección de Reseñas y Valoraciones

Te invitamos a auxiliar nuestro estudio escribiendo un comentario y puntuándolo te damos las gracias.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *