Pregunta Evitar que las aplicaciones roban el foco


¿Hay alguna solución para evitar que las aplicaciones roben el foco de la ventana activa?

Esto es especialmente molesto cuando estoy iniciando una aplicación, cambio para hacer otra cosa y la nueva aplicación comienza a recibir media oración de texto.


174


origen


@Ivo Windows 7 en mi caso, pero creo que para SuperUser todas las versiones de Windows serían relevantes - svandragt
El moderador fusionó esta pregunta: superuser.com/questions/199821/... con el actual. Esto es incorrecto, la respuesta a la pregunta actual no se aplica a Windows 7, por lo que no debe fusionarse. Hasta ahora no he podido encontrar una solución a este problema en Windows 7 - Alex Angelico
Este es uno de mis peeves número uno con cada GUI que he usado. Estás escribiendo y blam, un cuadro de diálogo de pitido te roba el foco y la mitad de las teclas se dirigen a otro lugar. Uno pensaría que los implementadores de los sistemas de ventanas se habrían dado cuenta de esto hace décadas. Si hay actividad en una ventana, retrase la exposición de la nueva ventana. P.ej. no muestre nada en la GUI hasta que pasen tres o cuatro segundos desde el último clic del botón o la pulsación de la tecla en la ventana actualmente enfocada. Doh! - Kaz
descubrí que cuando tenía mi disco duro externo (fabricado por Seagate) o mi ipod nano (ejem, manzana) conectado a mi máquina con Windows 7, parecía que el "escritorio" se robaba el foco cada 30 segundos más o menos, de lo que Estaba navegando, ya sea música itunes, resultados de búsqueda de Chrome o correos electrónicos de Firefox. Desactivé la función de reproducción automática, y eso ayudó durante un tiempo, pero el problema volvió incluso después de que la reproducción automática se deshabilitó. Supongo que tengo que mantener mi HD externo y las unidades de memoria flash desconectadas en su mayor parte, lo cual es una mierda porque ahí está toda mi música :( Es un error realmente molesto que me hace desear
This is especially annoying when I'm starting an application, switch to do something else and the new application starts receiving half a sentence of text.Es aún más molesto cuando aparece un cuadro de diálogo y lo descarta involuntariamente sin siquiera ver el mensaje porque sucedió que presionó Space o Enter mientras escribes una oración. - Synetech


Respuestas:


Hace un tiempo investigué extensamente para resolver este problema de una vez por todas (y fallé). El resultado de mi investigación se puede encontrar en página de proyecto de molestia.

El proyecto también incluye una aplicación que intenta repetidamente enfocarse llamando:

switch( message ) {
  case WM_TIMER:
    if( hWnd != NULL ) {
      // Start off easy
      // SetForegroundWindow will not move the window to the foreground,
      // but it will invoke FlashWindow internally and, thus, show the
      // taskbar.
      SetForegroundWindow( hWnd );

      // Our application is awesome! It must have your focus!
      SetActiveWindow( hWnd );

      // Flash that button!
      FlashWindow( hWnd, TRUE );
    }
    break;

Como podemos ver en este fragmento, mi investigación también se centró en otros aspectos del comportamiento de la interfaz de usuario que no me gustan.

La forma en que traté de resolver esto fue cargar un archivo DLL en cada nuevo proceso y enganchar las llamadas a la API que hacen que se activen otras ventanas.
La última parte es la más fácil, gracias a las increíbles bibliotecas de enganche API que existen. Usé el muy buen biblioteca mhook:

#include "stdafx.h"
#include "mhook-2.2/mhook-lib/mhook.h"

typedef NTSTATUS( WINAPI* PNT_QUERY_SYSTEM_INFORMATION ) ( 
  __in       SYSTEM_INFORMATION_CLASS SystemInformationClass,     
  __inout    PVOID SystemInformation, 
  __in       ULONG SystemInformationLength, 
  __out_opt  PULONG ReturnLength    
);

// Originals
PNT_QUERY_SYSTEM_INFORMATION OriginalFlashWindow   = 
  (PNT_QUERY_SYSTEM_INFORMATION)::GetProcAddress( 
  ::GetModuleHandle( L"user32" ), "FlashWindow" );

PNT_QUERY_SYSTEM_INFORMATION OriginalFlashWindowEx = 
  (PNT_QUERY_SYSTEM_INFORMATION)::GetProcAddress( 
  ::GetModuleHandle( L"user32" ), "FlashWindowEx" );

PNT_QUERY_SYSTEM_INFORMATION OriginalSetForegroundWindow = 
  (PNT_QUERY_SYSTEM_INFORMATION)::GetProcAddress( 
  ::GetModuleHandle( L"user32" ), "SetForegroundWindow" );

// Hooks
BOOL WINAPI
HookedFlashWindow(
  __in  HWND hWnd,
  __in  BOOL bInvert
  ) {
  return 0;
}

BOOL WINAPI 
HookedFlashWindowEx(
  __in  PFLASHWINFO pfwi
  ) {
  return 0;
}

BOOL WINAPI 
HookedSetForegroundWindow(
  __in  HWND hWnd
  ) {
  // Pretend window was brought to foreground
  return 1;
}


BOOL APIENTRY 
DllMain( 
  HMODULE hModule,
  DWORD   ul_reason_for_call,
  LPVOID  lpReserved
  ) {
  switch( ul_reason_for_call ) {
    case DLL_PROCESS_ATTACH:
      Mhook_SetHook( (PVOID*)&OriginalFlashWindow,         HookedFlashWindow );
      Mhook_SetHook( (PVOID*)&OriginalFlashWindowEx,       HookedFlashWindowEx );
      Mhook_SetHook( (PVOID*)&OriginalSetForegroundWindow, HookedSetForegroundWindow );
      break;

    case DLL_PROCESS_DETACH:
      Mhook_Unhook( (PVOID*)&OriginalFlashWindow );
      Mhook_Unhook( (PVOID*)&OriginalFlashWindowEx );
      Mhook_Unhook( (PVOID*)&OriginalSetForegroundWindow );
      break;
  }
  return TRUE;
}

De mis pruebas en aquel entonces, esto funcionó muy bien. Excepto por la parte de cargar la DLL en cada nuevo proceso. Como uno podría imaginar, eso no es nada a tomar a la ligera. Usé el AppInit_DLLs enfoque en ese momento (que simplemente no es suficiente).

Básicamente, esto funciona genial. Pero nunca encontré tiempo para escribir algo que correctamente inyecta mi DLL en nuevos procesos. Y el tiempo invertido en esto eclipsa en gran medida la molestia que el robo de enfoque me causa.

Además del problema de inyección de DLL, también hay un método de robo de enfoque que no cubrí en la implementación en Google Code. Un compañero de trabajo hizo alguna investigación adicional y cubrió ese método. El problema fue discutido en SO: https://stackoverflow.com/questions/7430864/windows-7-prevent-application-from-losing-focus


45



¿Crees que esta solución tuya podría ser portada a Java? He estado buscando y haciendo preguntas pero no encontré nada. Tal vez podría importar la biblioteca de gancho en java usando jne? - Tomáš Zato
@ TomášZato: No tengo idea. No estoy usando activamente este código yo mismo. - Der Hochstapler
Estoy tratando de compilarlo como C ++ al menos (y luego inyectar / eliminar la DLL compilada de Java). Pero eso tampoco va demasiado bien. No quiero discutirlo aquí en comentarios, pero si realmente me pueden ayudar a ponerlo a trabajar, ¡sería muy elegante! Creé una sala de chat, si logro que funcione, publicaré un comentario sobre cómo hacerlo aquí: chat.stackexchange.com/rooms/21637/... - Tomáš Zato


En Windows 7, el ForegroundLockTimeout la entrada de registro ya no está marcada, puede verificar esto con Process Monitor. De hecho, en Windows 7 no le permiten cambiar la ventana de primer plano. Ve y lee sobre sus detalles, incluso ha estado allí desde Windows 2000.

Sin embargo, la documentación apesta y se persiguen unos a otros y encuentran formas de evitarlo.

Por lo tanto, hay algo problemático pasando con SetForegroundWindow, o funciones API similares ...

La única forma de hacer esto correctamente es hacer una pequeña aplicación que periódicamente llame LockSetForegroundWindow, prácticamente deshabilitando cualquier llamada a nuestra función de API con errores.

Si eso no es suficiente (¿otra llamada falsa a la API?) Puede ir más allá y hacer algunas Monitoreo API para ver qué está pasando, y luego simplemente enganchar las llamadas API en cada proceso después de lo cual puedes deshacerte de alguna llamadas que desordenan el primer plano. Sin embargo, irónicamente, esto es desanimado por Microsoft ...


22



¿Alguien tiene un caso de uso reproducible de esto en Windows 7? Dado que las personas experimentan más bien lo contrario (por ejemplo, a menudo me parece que Windows exige estar oculto detrás de mi ventana actual) y que aún no veo que esto ocurra en Windows 7, sería bastante molesto escribir una aplicación pero no poder Pruébalo. Además, como dice Microsoft, esto ya no debería ocurrir con Windows 7. En el mejor de los casos, la gente descubrió que solo podía cambiar el foco del teclado por accidente, esta llamada a API lo arreglaría, pero no sé cómo probar si realmente funciona. . - Tom Wijsman
El instalador (basado en InnoSetup) inicia otros procesos y posibles otras configuraciones (ocultas), pero no sé en qué creador de configuración están basados. - Daniel Beck♦
@TomWijsman: Abre regedit, busca texto aleatorio que no se encuentre. Acceda a otra aplicación y comience a escribir. Cuando la búsqueda finalice, regedit robará el foco. - endolith
@endolith: no reproducible, sin embargo, con Windows 8 Replase Preview aquí. ¿Qué sistema operativo estás usando? En mi caso, solo destaca la aplicación en la parte inferior, pero no interrumpe mi navegación en absoluto ... - Tom Wijsman
Sí, Win7 Pro de 64 bits. Y el robo de foco es aún peor para los procesos elevados, ya que capturan su presionar <Intro> cuando no deberían, y usted le dice que mangue su sistema accidentalmente. Nada debería nunca ser capaz de robar el foco. - endolith


Hay una opción en TweakUI que hace esto Evita la mayoría de los trucos habituales que los desarrolladores de software dudosos emplean para forzar el enfoque en su aplicación.

Sin embargo, es una guerra de armas en curso, así que no sé si funciona para todo.

Actualizar: De acuerdo a EndangeredMassa, TweakUI no funciona en Windows 7.


18



es tweakui compatible con Windows 7? - frankster
@frankster. No tengo idea, lo siento, sospecho que probablemente no lo sea. Descárguelo y pruébelo. Reporte si lo hace, todos lo saben. - Simon P Stevens
Incluso el uso de la configuración de registro que establece TweakUI no funciona en Win7. - EndangeredMassa
@EndangeredMassa, ¿qué clave de registro es esa? - n611x007
La clave de registro es HKEY_CURRENT_USER \ Control Panel \ Desktop \ ForegroundLockTimeout (en milisegundos). Y sí, ya no funciona en Windows 7. - foo


Creo que puede existir cierta confusión, ya que hay dos formas de "robar el foco": (1) una ventana que pasa al primer plano, y (2) la ventana que recibe las teclas.

El problema al que nos referimos aquí es probablemente el segundo, donde Windows reclama el enfoque al ponerse en primer plano, sin la solicitud o permiso del usuario.

La discusión debe dividirse aquí entre XP y 7.

Windows XP

En XP hay un hack de registro que hace que XP funcione de la misma manera que Windows 7 para evitar que las aplicaciones se roben el foco:

  1. Use regedit para ir a: HKEY_CURRENT_USER\Control Panel\Desktop.
  2. Haga doble clic en ForegroundLockTimeout y establecer su valor en hexadecimal a 30d40.
  3. Presione OK y salga de regedit.
  4. Reinicie su PC para que los cambios surtan efecto.

Windows 7

(La discusión a continuación también se aplica principalmente a XP).

Comprenda que no hay forma de que Windows bloquee por completo las aplicaciones para que no puedan robar el foco y permanezcan funcionales. Por ejemplo, si durante una copia de archivo su antivirus detectó una posible amenaza y quisiera abrir una ventana que le pregunte qué acción tomar, si esta ventana está bloqueada entonces nunca entenderías por qué la copia nunca termina.

En Windows 7 solo hay una modificación posible del comportamiento de Windows, que es usar el MS-Windows focus-follows-mouse Cambios en el registro, donde el foco y / o la activación van siempre a las ventanas debajo del cursor. Se puede agregar un retraso para evitar que las aplicaciones aparezcan en todo el escritorio.
Ver este artículo: Windows 7 - Mouse Hover hace que la ventana esté activa - Habilitar.

De lo contrario, uno debe detectar y neutralizar el programa culpable: Si esta es siempre la misma aplicación que obtiene el foco, entonces esta aplicación está programado para tomar el foco y evitar que esto se pueda hacer ya sea inhabilitándolo para que comience con la computadora, o use alguna configuración proporcionada por esa aplicación para evitar este comportamiento.

Podría usar el script VBS incluido en Código VB que identifica quién está robando el foco, que el autor usó para identificar el culpable como un programa de actualización de "llamada a casa" para un software de impresora.

Una medida desesperada cuando todo lo demás falla, y si ha identificado esta aplicación mal programada, es minimizarlo y esperar que eso no lo lleve al frente. Una forma más sólida de minimizar es a la bandeja mediante el uso de uno de los productos gratuitos enumerados en Mejor minimizador de aplicación gratuita.

La última idea en el orden de la desesperación es fracturar su escritorio virtualmente mediante el uso de un producto como Escritorios o Dexpot, y haz tu trabajo en otro escritorio diferente al predeterminado.

[EDITAR]

Como Microsoft ha retirado la Galería de archivos, aquí se reproduce el código VB anterior:

Declare Auto Function GetForegroundWindow Lib "user32.dll" () As Integer
Declare Auto Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hwnd As Integer, ByRef procid As Integer) As UInteger

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.RichTextBox1.AppendText("Starting up at " & Now & vbCrLf)
    End Sub

    Private Sub GoingAway(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Deactivate, Me.LostFocus

        Dim hwnd As Integer = GetForegroundWindow()
        ' Note that process_id will be used as a ByRef argument
        ' and will be changed by GetWindowThreadProcessId
        Dim process_id As Integer = 1
        GetWindowThreadProcessId(hwnd, process_id)

        If (process_id <> 1) Then
            Dim appExePath As String = Process.GetProcessById(process_id).MainModule.FileName() 
            Me.RichTextBox1.AppendText("Lost focus at " & Now & " due to " & appExePath & vbCrLf)
        Else
            Me.RichTextBox1.AppendText("Lost focus due to unknown cause.")
        End If

    End Sub

14



"si esta ventana está bloqueada, nunca entenderá por qué la copia nunca termina". Eso no es cierto. El comportamiento correcto es notificar al usuario con un icono de barra de tareas parpadeante (o tal vez un globo emergente o notificación de tostadora o algo así). Interrumpir al usuario con una ventana que intercepta sus teclas significa que le dice al software antivirus que realice una acción u otra al azar. Definitivamente no es una buena manera de hacer las cosas. - endolith
"si esta ventana está bloqueada, nunca entenderá por qué la copia nunca termina". Eso no es cierto. El comportamiento correcto es notificar al usuario con un icono de barra de tareas parpadeante ...   Hubo momentos en que hice clic en un botón o algo en un programa en ejecución que causa la creación de un nuevo cuadro de diálogo modal (por ejemplo, abrir documento), pero luego cambio a otro programa antes de que se cree el diálogo. Como resultado, el diálogo está oculto y no se puede cambiar el otro programa y no se puede descartar el diálogo. Ni su botón de barra de tareas ni Alt-Tab trabajos; solo forzando el diálogo al frente. - Synetech
@Synetech: a veces la única solución para el diálogo no frontal es matar la tarea. Los algoritmos de enfoque en Windows son realmente pésimos. - harrymc
@harrymc, nunca tengo que recurrir a matar a una de las aplicaciones. Simplemente ejecuto mi programa de manipulación de ventanas (WinSpy ++ el truco es genial) y esconder la ventana al frente, luego puedo descartar el diálogo retrógrado, luego volver a mostrar la ventana oculta. No es conveniente, pero es mejor que matar cualquiera de los procesos. - Synetech
@harrymc, no realmente; matar una aplicación y perder cosas simplemente genera más vapor, y si se trata de un diálogo modal (que bloquea la ventana principal y no tiene botón de la barra de tareas), entonces no aparecerá en el Alt+Tab lista, y, en mi experiencia, una ventana que tiene un diálogo modal abierto no siempre (¿nunca?) muestra el diálogo modal con Alt+Tab, especialmente si el diálogo nunca tuvo un cambio para obtener el foco. :-| - Synetech


Ghacks tiene un solución posible:

Sucede varias veces al día que   algunas aplicaciones roban el foco de   la ventana activa apareciendo. Esta   puede suceder por una serie de razones,   cuando extraigo archivos o una transferencia   termina por ejemplo. No es asi   importa la mayor parte del tiempo cuando esto   sucede, pero a veces estoy escribiendo un   artículo y no solo significa que   Tengo que escribir algunas palabras de nuevo, pero   también que perdí la concentración y   tiene que hacer clic para recuperar el foco.

los Pro Reviewer sitio web tiene un consejo sobre   cómo evitar que esto suceda.   La forma más fácil de evitar el enfoque   robar es usar Tweak UI que tiene   una configuración que se llama "Prevenir"   aplicaciones de robar el foco ".   Controlar esta opción evita que   otras aplicaciones aparecen de repente y   robar el foco de la ventana que eres   Actualmente trabajando en.

Esto solo funciona cuando la aplicación   ha sido minimizado antes En lugar de   robando el foco parpadeará una   número de veces que se pueden definir   en el mismo menú en Tweak UI. Si tu   No quiero usar Tweak UI, puedes   cambiar la configuración en Windows   Registro.

Navegue a la clave del Registro   HKEY_CURRENT_USER> Panel de control>   Escritorio y cambiar el   Valor de ForegroundLockTimeout a 30d40   (Hexadecimal) o 200000 (Decimal). los   clave ForeGroundFlashCount define el   cantidad de flashes de una ventana para alertar   el usuario donde 0 significa ilimitado.


2



Esto no funciona en ningún sistema operativo después de XP. Ese valor de registro ya está configurado para eso (de manera predeterminada, creo) y no funciona de todos modos. - EndangeredMassa
Solo para decir que estoy en Windows 7 (64 bits), experimentando robo de foco (VS 2012 cuando finalmente está activo, por ejemplo), y la sugerencia de registro anterior ya está en su lugar. Confirmación técnica en esta respuesta: superuser.com/a/403554/972 - Michael Paulukonis