2010-04-19 15 views
7

Estoy tratando de encontrar una forma confiable de activar/establecer el foco en una ventana de una aplicación externa usando C#. Actualmente estoy tratando de lograr esto usando las siguientes llamadas a la API de Windows:¿Hay alguna manera confiable de activar/establecer el foco en una ventana usando C#?

SetActiveWindow(handle); 
SwitchToThisWindow(handle, true); 

Anteriormente también tenían ShowWindow(handle, SW_SHOWMAXIMIZED); ejecutar antes de que los otros 2, pero que eliminarlo porque estaba causando un comportamiento extraño.

El problema que tengo con mi implementación actual es que ocasionalmente el foco no se configurará correctamente. La ventana se volverá visible, pero la parte superior seguirá apareciendo atenuada como si no estuviera enfocada.

¿Hay alguna manera de hacer esto de manera confiable que funcione el 100% del tiempo, o el comportamiento incoherente es un efecto secundario del que no puedo escapar? Por favor, avíseme si tiene alguna sugerencia o implementación que siempre funcione.

Respuesta

8

Es necesario utilizar AttachThreadInput

Windows creado en diferentes subprocesos generalmente procesa la entrada de forma independiente. Es decir, tienen sus propios estados de entrada (foco, activo, ventanas de captura, estado de clave, estado de la cola, etc.) y no están sincronizados con el procesamiento de entrada de otros hilos. Al utilizar la función AttachThreadInput, un hilo puede adjuntar su procesamiento de entrada a otro hilo. Esto también permite a los subprocesos compartir sus estados de entrada, por lo que pueden llamar a la función SetFocus para establecer el foco del teclado en una ventana de un hilo diferente. Esto también permite que los hilos obtengan información de estado clave. Estas capacidades generalmente no son posibles.

No estoy seguro de las consecuencias de usar esta API de (presumiblemente) Windows Forms. Dicho esto, lo he usado en C++ para obtener este efecto. Código sería algo así como la manera siguiente:

 DWORD currentThreadId = GetCurrentThreadId(); 
    DWORD otherThreadId = GetWindowThreadProcessId(targetHwnd, NULL); 
    if(otherThreadId == 0) return 1; 
    if(otherThreadId != currentThreadId) 
    { 
     AttachThreadInput(currentThreadId, otherThreadId, TRUE); 
    } 

    SetActiveWindow(targetHwnd); 

    if(otherThreadId != currentThreadId) 
    { 
     AttachThreadInput(currentThreadId, otherThreadId, FALSE); 
    } 

targetHwnd ser el HWND de la ventana que desea fijar el enfoque. Supongo que puede calcular la (s) firma (s) de P/Invocar porque ya está usando API nativas.

+0

Tu publicación fue muy informativa. Muchas gracias. Intentaré esto tan pronto como tenga la oportunidad de hacerlo. Tengo las firmas de P/Invoke, nunca entendí realmente la imagen completa detrás de las colas de entrada, pero lo pones muy claramente. :) – gtaborga

+0

¡Gracias! Acabo de terminar de escribir una aplicación simple que es útil para teclas rápidas, llamada OpenOrSwitchTo. Básicamente, cuando se invoca con un nombre de exe (OpenOrSwitchTo.exe c: \ path \ to \ someapp.exe) si se está ejecutando someapp.exe, cambiará a él para que pueda comenzar a usarlo, y si no, se iniciará alguna aplicación. .exe. Intenté al menos media docena de métodos para cambiar a la ventana (SwitchToThisWindow, ShowWindow, BringWindowToTop, SetForegroundWindow, etc.). Su código finalmente funcionó, con una llamada rápida a ShowWindow para restaurar la ventana si se minimizaba primero. – iano

+0

Para mí (Windows 7), la solución con 'AttachThreadInput()' no funciona.Resolví el problema al permitir que el _otro hilo_ estableciera su ventana de propietario en el _camino corriente_, entonces simplemente puedo 'SetActiveWindow()'. Ver mi publicación en el blog http://code.fitness/post/2017/09/how-to-activate-window-of-foreign-process.html –

1

Si todo es interno en su aplicación, entonces puede obtener la ventana padre o esa ventana, y de esta manera se active (vb lo siento):

Public Class Form1 : Inherits Form 

    Protected Overrides Sub OnLoad(e As EventArgs) 
     Dim form2 As New Form2 
     form2.Show() 
    End Sub 
End Class 

Class Form2 : Inherits Form 

    Protected Overrides Sub OnLoad(e As EventArgs) 
     MyBase.OnLoad(e) 
     Me.Owner.Activate() 
    End Sub 
End Class 
+0

Por desgracia, no está dentro de mi solicitud. Es una aplicación externa a la que no puedo acceder el código de. Voy a aclarar en mi post. Gracias por tu esfuerzo. – gtaborga

2
[DllImport("user32.dll")] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    static extern bool SetForegroundWindow(IntPtr hWnd); 

Esto se trabajó para mí

Cuestiones relacionadas