Empêcher les applications de voler le focus

191

Existe-t-il des solutions pour empêcher les applications de voler le focus de la fenêtre active?

C’est particulièrement agaçant lorsque je démarre une application, changez d’action et la nouvelle application commence à recevoir une demi-phrase de texte.

svandragt
la source
9
@ Ivo Windows 7 dans mon cas, mais je pense que pour SuperUser toutes les versions de Windows seraient pertinentes
svandragt
3
Le modérateur a fusionné cette question: superutilisateur.com/questions/199821/… avec l'actuel. C'est faux, la réponse à la question actuelle ne s'applique pas à Windows 7, elle ne devrait donc pas être fusionnée. Jusqu'à présent, je n'ai pas trouvé de solution à ce problème dans Windows 7
Alex Angelico
17
C’est l’un de mes marmites numéro un avec toutes les interfaces graphiques que j’ai utilisées. Vous tapez et blam, une boîte de dialogue sonne vole le focus et la moitié de vos frappes vont ailleurs. On pourrait penser que les développeurs de systèmes de fenêtrage auraient compris cela il y a plusieurs décennies. S'il y a de l'activité dans une fenêtre, retardez l'exposition de la nouvelle fenêtre. Par exemple, ne rien afficher dans l'interface graphique avant trois ou quatre secondes après le dernier clic ou la dernière frappe sur le bouton dans la fenêtre en cours de mise au point. Doh!
Kaz
24
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.C'est encore plus gênant quand une boîte de dialogue s'ouvre et que vous la fermez par inadvertance sans même voir le message, car il vous est arrivé d'appuyer sur Spaceou Enteren tapant une phrase.
Synetech
3
C’est bien plus qu’ennuyeux, je dirais que c’est un risque pour la sécurité. Rien n'empêche une application d'apparaître lorsque vous êtes en train de saisir un mot de passe et de saisir votre saisie.
Chris Peacock

Réponses:

51

Cela n'est pas possible sans une manipulation poussée des composants internes de Windows et vous devez vous en remettre.

Il y a des moments d'utilisation quotidienne de l'ordinateur où il est vraiment important de faire une action avant que le système d'exploitation ne vous permette d'en faire une autre. Pour ce faire, vous devez vous concentrer sur certaines fenêtres. Sous Windows, le contrôle de ce comportement est en grande partie laissé aux développeurs des programmes individuels que vous utilisez.

Tous les développeurs ne prennent pas les bonnes décisions concernant ce sujet.

Je sais que c'est très frustrant et énervant, mais vous ne pouvez pas avoir votre gâteau et le manger aussi. Il y a probablement de nombreux cas dans votre vie quotidienne où tout va parfaitement bien, le focus étant déplacé vers un certain élément de l'interface utilisateur ou une application demandant que le focus reste verrouillé dessus. Mais la plupart des applications sont quelque peu égales lorsqu'il s'agit de décider qui dirige actuellement et le système ne peut jamais être parfait.

Il y a quelque temps, j'ai effectué des recherches approfondies sur la résolution de ce problème une fois pour toutes (et j'ai échoué). Le résultat de mes recherches est disponible sur la page du projet de désagrément .

Le projet inclut également une application qui tente à plusieurs reprises d’attirer l’attention en appelant:

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;

Comme nous pouvons le voir dans cet extrait de code, mes recherches portaient également sur d'autres aspects du comportement de l'interface utilisateur que je n'aime pas.

J'ai essayé de résoudre ce problème en chargeant une DLL dans chaque nouveau processus et en raccordant les appels d'API qui activent une autre fenêtre.
La dernière partie est la plus facile, grâce à d’impressionnantes bibliothèques d’accrochage d’API. J'ai utilisé la très grande bibliothèque 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;
}

D'après mes tests à l'époque, cela a très bien fonctionné. À l'exception de la partie du chargement de la DLL dans chaque nouveau processus. Comme on peut l’imaginer, rien n’est à prendre à la légère. J'ai utilisé l' approche AppInit_DLLs à l'époque (ce qui n'est tout simplement pas suffisant).

Fondamentalement, cela fonctionne très bien. Mais je n'ai jamais trouvé le temps d'écrire quelque chose qui injecte correctement ma DLL dans de nouveaux processus. Et le temps investi dans ceci masque en grande partie le désagrément que me cause le vol de la cible.

En plus du problème d'injection de DLL, il existe également une méthode de vol de focus que je n'ai pas abordée dans l'implémentation de Google Code. Un collègue a en fait fait des recherches supplémentaires et a couvert cette méthode. Le problème a été discuté sur le SO: https://stackoverflow.com/questions/7430864/windows-7-prevent-application-from-losing-focus

Der Hochstapler
la source
Pensez-vous que votre solution pourrait être portée en Java? J'ai cherché et posé des questions mais je n'ai rien trouvé. Peut-être que je pourrais importer la bibliothèque de hook elle-même en Java en utilisant jne?
Tomáš Zato
@ TomášZato: Aucune idée. Je n'utilise pas activement ce code moi-même.
Der Hochstapler
J'essaye de le compiler au moins en C ++ (puis injecter / supprimer la DLL compilée à partir de Java). Mais cela ne va pas trop bien non plus. Je ne veux pas en discuter ici dans les commentaires, mais si vous pouviez m'aider à le faire fonctionner, je serais très gracieux! J'ai créé un salon de discussion. Si je réussis
Tomáš Zato
23

Sous Windows 7, l' ForegroundLockTimeoutentrée de registre n'est plus vérifiée, vous pouvez le vérifier avec Process Monitor. En fait, dans Windows 7, ils vous interdisent de modifier la fenêtre de premier plan. Allez lire sur ses détails , il est même là depuis Windows 2000.

Cependant, la documentation est nulle et ils se poursuivent pour trouver une solution .

Il y a donc un problème avec SetForegroundWindowun bug ou des fonctions similaires de l'API ...

Le seul moyen de le faire correctement consiste à créer une petite application qui appelle périodiquement LockSetForegroundWindow, en désactivant virtuellement tous les appels à notre fonction API buggy.

Si cela ne vous suffit pas (un autre appel d'API bogué?), Vous pouvez aller encore plus loin et surveiller certaines API pour voir ce qui se passe, puis vous pouvez simplement connecter les appels d'API à chaque processus, après quoi vous pouvez vous débarrasser de tous les appels gâchés. le premier plan. Cependant, ironiquement, cela est découragé par Microsoft ...

Tamara Wijsman
la source
3
Quelqu'un a-t-il un cas reproductible dans Windows 7? Etant donné que les gens ressentent plutôt le contraire (par exemple, je trouve souvent que Windows exige d'être caché derrière ma fenêtre actuelle) et que cela ne se voit pas encore dans Windows 7, il serait assez ennuyant de pouvoir écrire une application, mais être incapable de le faire. Essaye-le. De plus, comme Microsoft le dit, cela ne devrait plus arriver avec Windows 7. Au meilleur des gens qui ont découvert qu'il ne pouvait changer que accidentellement le focus du clavier, cet appel d'API résoudrait ce problème, mais je ne sais pas comment vérifier s'il fonctionne réellement. .
Tamara Wijsman
1
Le programme d’installation (basé sur InnoSetup) lance d’autres processus et d’autres configurations (cachées) possibles, mais je ne sais pas sur quel créateur de configuration ils se basent.
Daniel Beck
6
@TomWijsman: Ouvrez regedit, recherchez du texte aléatoire qui ne sera pas trouvé. Allez dans une autre application et commencez à taper. Lorsque la recherche est terminée, regedit va voler le focus.
endolith
1
@endolith: non reproductible, utilisez cependant Windows 8 Replase Preview. Quel système d'exploitation utilisez-vous? Dans mon cas, cela ne fait que mettre en valeur l’application au bas de la page mais cela n’interrompt absolument pas ma navigation ...
Tamara Wijsman
21
Oui, Win7 Pro 64 bits. Et voler en focus est encore pire pour les processus surélevés, car ils capturent votre pression sur <Entrée> alors qu'ils ne le devraient pas, et vous lui dites d'arrêter accidentellement votre système. Rien ne devrait jamais être capable de voler la concentration.
endolith
18

Il y a une option dans TweakUI qui le fait. Cela évite la plupart des astuces habituelles utilisées par les développeurs de logiciels douteux pour forcer la concentration sur leur application.

C'est une guerre d'armes en cours, donc je ne sais pas si ça marche pour tout.

Mise à jour : selon EndangeredMassa , TweakUI ne fonctionne pas sous Windows 7.

Simon P. Stevens
la source
2
tweakui est-il compatible avec Windows 7?
Frankrex
@frankster. Aucune idée, désolé, je suppose que ce n'est probablement pas le cas. Téléchargez-le et essayez-le. Faites rapport si vous le faites, alors tout le monde le sait.
Simon P Stevens le
5
Même en utilisant le paramètre de registre défini par TweakUI, cela ne fonctionne pas sur Win7.
En
@ EndangeredMassa quelle clé de registre est-ce?
n611x007
2
La clé de registre est HKEY_CURRENT_USER \ Control Panel \ Desktop \ ForegroundLockTimeout (en millisecondes). Et oui, cela ne fonctionne plus sous Windows 7.
foo
14

Je pense qu’une certaine confusion peut exister, car il existe deux manières de "voler le focus": (1) une fenêtre qui s’affiche au premier plan et (2) la fenêtre qui reçoit des frappes au clavier.

Le problème dont il est question ici est probablement le deuxième, dans lequel une fenêtre revendique le focus en se plaçant au premier plan - sans demande ni autorisation de l'utilisateur.

La discussion doit être partagée ici entre XP et 7.

Windows XP

Dans XP, il existe un hack de registre qui permet à XP de fonctionner de la même manière que Windows 7 pour empêcher les applications de voler le focus:

  1. Utilisez regedit pour aller à: HKEY_CURRENT_USER\Control Panel\Desktop.
  2. Double-cliquez sur ForegroundLockTimeoutet définissez sa valeur en hexadécimal sur 30d40.
  3. Appuyez sur OK et quittez regedit.
  4. Redémarrez votre PC pour que les modifications prennent effet.

Windows 7

(La discussion ci-dessous s'applique principalement à XP également.)

S'il vous plaît, comprenez qu'il est impossible pour Windows d'empêcher totalement les applications de voler le focus et de rester fonctionnel. Par exemple, si pendant la copie d'un fichier, votre anti-virus a détecté une menace potentielle et souhaite afficher une fenêtre vous demandant de prendre l'action, si cette fenêtre est bloquée, vous ne comprendrez jamais pourquoi la copie ne se termine jamais.

Dans Windows 7, il n’ya qu’une modification possible du comportement de Windows lui-même, qui consiste à utiliser les piratages du registre focus-suiv-souris de MS-Windows , dans lesquels l’activation et / ou l’activation est toujours dirigée vers la fenêtre située sous le curseur. Un délai peut être ajouté pour éviter que des applications n'apparaissent sur le bureau.
Voir cet article: Windows 7 - Survol de la souris rendant la fenêtre active - Activer .

Sinon, il faut détecter et neutraliser le programme coupable: s’il s’agit toujours de la même application qui obtient le focus, cette application est programmée pour prendre le focus et l’empêcher cela peut être fait soit en le désactivant en commençant par l’ordinateur, ou utilisez certains paramètres fournis par cette application pour éviter ce problème.

Vous pouvez utiliser le script VBS inclus dans VB Code, qui identifie le focus , que l'auteur a utilisé pour identifier le coupable en tant que programme de mise à jour "Call Home" pour un logiciel d'impression.

Une mesure désespérée lorsque tout le reste échoue et, si vous avez identifié cette application mal programmée, consiste à la minimiser et à espérer que cela ne se présentera pas au premier plan. Une forme plus efficace de minimisation consiste à utiliser l'un des produits gratuits répertoriés dans Best Free Application Minimizer .

La dernière idée dans l’ordre du désespoir est de fractionner votre bureau virtuellement en utilisant un produit tel que Desktops ou Dexpot et d’effectuer votre travail sur un autre bureau que celui par défaut.

[MODIFIER]

Comme Microsoft a retiré la galerie d'archives, voici le code VB ci-dessus reproduit:

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
harrymc
la source
48
"Si cette fenêtre est bloquée, vous ne comprendrez jamais pourquoi la copie ne se termine jamais" Ce n'est pas vrai. Le comportement correct consiste à avertir l'utilisateur avec une icône de barre de tâches clignotante (ou peut-être une bulle ou une notification de grille-pain ou autre). Interrompre l'utilisateur avec une fenêtre qui intercepte ses frappes signifie qu'il demande au logiciel antivirus d'effectuer une action ou une autre de manière aléatoire. Ce n'est vraiment pas une bonne façon de faire les choses.
endolith
1
"Si cette fenêtre est bloquée, vous ne comprendrez jamais pourquoi la copie ne se termine jamais" Ce n'est pas vrai. Le comportement correct consiste à avertir l'utilisateur avec une icône de barre de tâches clignotante ... Il est arrivé que je clique sur un bouton ou sur un élément du programme en cours qui entraîne la création d'un nouveau dialogue modal (par exemple, un fichier ouvert ), puis Je passe à un autre programme avant la création du dialogue. En conséquence, la boîte de dialogue est masquée, vous ne pouvez pas basculer vers l’autre programme et vous ne pouvez pas la fermer. Ni son bouton de la barre des tâches ni ne Alt-Tabfonctionne; forçant uniquement le dialogue au premier plan.
Synetech
1
@ Synetech: Parfois, la seule solution à la boîte de dialogue non frontale consiste à tuer la tâche. Les algorithmes de focus de Windows sont vraiment moche.
harrymc
2
@ charrymc, je n'ai jamais à tuer l'une des applications. Je viens de lancer mon programme de manipulation de fenêtre ( WinSpy ++ fait l’affaire tout simplement géniale) et de cacher la fenêtre devant, je peux alors fermer la boîte de dialogue masquée, puis afficher à nouveau la fenêtre masquée. Ce n'est pas pratique, mais c'est mieux que de tuer l'un ou l'autre des processus.
Synetech
1
@harrymc, pas vraiment; le fait de tuer une application et de perdre des choses fait plus de vapeur, et si c'est une boîte de dialogue modale (qui verrouille la fenêtre parente sans bouton dans la barre des tâches), elle n'apparaîtra pas dans la Alt+Tabliste et, selon mon expérience, une fenêtre a un dialogue modal ouvert ne montre pas toujours (jamais?) le dialogue modal avec Alt+Tab, surtout si le dialogue n'a jamais eu un changement pour obtenir le focus. :-|
Synetech
2

Ghacks a une solution possible:

Il arrive plusieurs fois par jour que certaines applications volent le focus de la fenêtre active en surgissant. Cela peut se produire pour plusieurs raisons, par exemple lorsque j'extrais des fichiers ou qu'un transfert se termine. La plupart du temps, cela n'a pas d'importance, mais parfois, j'écris un article et cela ne signifie pas seulement que je dois taper à nouveau des mots, mais aussi que j'ai perdu ma concentration et que je dois cliquer pour retrouver le focus.

Le site Web de Pro Reviewer contient une astuce sur la manière d'éviter cela. Le moyen le plus simple d'empêcher le vol de focus consiste à utiliser Tweak UI, dont le paramètre est appelé «Empêcher les applications de voler le focus». En cochant cette option, vous évitez que d'autres applications apparaissent soudainement et volent le focus de la fenêtre dans laquelle vous travaillez.

Cela ne fonctionne que lorsque l'application a déjà été réduite au minimum. Au lieu de voler le focus, il clignotera un certain nombre de fois, ce qui peut être défini dans le même menu dans Tweak UI . Si vous ne souhaitez pas utiliser Tweak UI, vous pouvez modifier le paramètre dans le registre Windows.

Accédez à la clé de Registre HKEY_CURRENT_USER> Panneau de configuration> Bureau et modifiez la valeur ForegroundLockTimeout en 30d40 (Hexadécimale) ou 200000 (Décimale). La clé ForeGroundFlashCount définit le nombre de clignotements d’une fenêtre pour alerter l’utilisateur, où 0 signifie illimité.

Ivo Flipse
la source
20
Cela ne fonctionne sur aucun OS après XP. Cette valeur de registre est déjà définie sur celle (par défaut, je crois) et ne fonctionne pas de toute façon.
En
1
Juste pour être deuxième que je suis sur Windows 7 (64 bits), le vol de focus est flagrant (VS 2012 lorsqu'il est enfin actif, par exemple), et la suggestion de registre ci-dessus est déjà en place. Confirmation technique dans cette réponse: superuser.com/a/403554/972
Michael Paulukonis
2

Inspiré par la réponse de Der Hochstapler , j'ai décidé d'écrire un injecteur de DLL, qui fonctionne avec les processus 64 et 32 ​​bits et empêche le vol de focus sur Windows 7 ou une version plus récente: https://blade.sk/stay-focused/

Cela fonctionne normalement: il surveille les fenêtres nouvellement créées (avec SetWinEventHook) et injecte une DLL très similaire à celle de Der Hochstapler dans le processus de la fenêtre, si elle n’est pas déjà présente. Il décharge les DLL et restaure la fonctionnalité d'origine à la sortie.

D'après mes tests, cela fonctionne très bien jusqu'à présent. Cependant, le problème semble aller plus loin que le simple fait d'appeler des applications SetForegroundWindow. Par exemple, lorsqu'une nouvelle fenêtre est créée, elle est automatiquement mise au premier plan, ce qui gêne également la saisie par un utilisateur dans une autre fenêtre.

Pour traiter d'autres méthodes de vol de focus, davantage de tests sont nécessaires et j'apprécierais tout retour sur les scénarios dans lesquels cela se produit.

lame
la source
0

J'ai compris comment empêcher la barre des tâches de flasher une fenêtre cible nouvellement activée après l'activation, la maximisation et la focalisation par programme de la fenêtre principale de ce processus à partir d'un autre processus. Tout d'abord, il y a beaucoup de restrictions quant à l'autorisation de cette opération.

"Le système limite les processus pouvant définir la fenêtre de premier plan. Un processus ne peut définir la fenêtre de premier plan que si l'une des conditions suivantes est remplie:

  • Le processus est le processus de premier plan.
  • Le processus a été lancé par le processus de premier plan.
  • Le processus a reçu le dernier événement d'entrée.
  • Il n'y a pas de processus de premier plan.
  • Le processus de premier plan est en cours de débogage.
  • Le premier plan n'est pas verrouillé (voir LockSetForegroundWindow).
  • Le délai de verrouillage de premier plan a expiré (voir SPI_GETFOREGROUNDLOCKTIMEOUT dans SystemParametersInfo).
  • Aucun menu n'est actif.

https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-allowsetforegroundwindow

Ainsi, si le processus de contrôle est au premier plan, il peut temporairement permettre à un autre processus de voler complètement le premier plan en appelant AllowSetForegroundWindow avec l' ID de processus du processus cible. Après cela, le processus cible peut appeler SetForegroundWindow lui-même, en utilisant son propre handle de fenêtre, et cela fonctionnera.

Évidemment, cela nécessite une certaine coordination entre les deux processus, mais cela fonctionne et si vous le faites pour implémenter une application à instance unique qui redirige tous les lancements de clics de l'Explorateur vers l'instance d'application existante, vous avez déjà avoir un tuyau nommé (par exemple) pour coordonner les choses quand même.

Glenn Slayden
la source