web-dev-qa-db-fra.com

Empêcher les applications de voler le focus

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

Cela est particulièrement ennuyeux lorsque je lance une application, changez d’action et la nouvelle application commence à recevoir une demi-phrase de texte.

189
svandragt

Ce n'est pas possible sans la extensive manipulation des composants internes de Windows et vous devez vous en remettre.

Il ya des moments dans l’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 nuisance .

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 de liaisons par 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 correctement injecte mon 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.

Outre le problème d'injection DLL, il existe également une méthode de vol de focus que je n'ai pas abordée dans la mise en œuvre sur 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

50
Der Hochstapler

Dans Windows 7, l'entrée de registre ForegroundLockTimeout 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 existe même depuis Windows 2000.

Cependant, la documentation est nulle et ils se pourchassent et trouvent des moyens de contourner ce problème .

Donc, il y a quelque chose qui ne va pas avec SetForegroundWindow , 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 , 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 effectuer quelques surveillance de l'API _ pour voir ce qui se passe, puis vous simplement accrocher les appels d'API à chaque processus } après quoi vous pouvez vous débarrasser de any les appels qui gâchent le premier plan. Cependant, ironiquement, cela est découragé par Microsoft ...

23
Tom Wijsman

Il existe 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.

Update : selon EndangeredMassa , TweakUI ne fonctionne pas sous Windows 7.

18
Simon P Stevens

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 recevant des frappes au clavier.

Le problème mentionné 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 scindé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 ForegroundLockTimeout et définissez sa valeur hexadécimale 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.)

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 au comportement de Windows lui-même, à savoir l’utilisation des hacks du registre MS-Windows du suivi-souris-souris , le focus et/ou l’activation étant toujours appliqué aux fenêtres sous le curseur. Un délai peut être ajouté pour éviter que des applications n'apparaissent sur le bureau.
Voir cet article: Windows 7 - Le survol de la souris rend 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 de démarrer avec 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 identifiant le voleur de 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 Meilleur minimiseur d’application gratuit .

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 sous 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
14
harrymc

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

Cela fonctionne normalement: il surveille les fenêtres nouvellement créées (à l'aide de SetWinEventHook) et injecte DLL très similaire à celui de Der Hochstapler dans le processus de la fenêtre s'il n'est pas déjà présent. 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 au-delà des applications appelant 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 d'informations sur les scénarios dans lesquels cela se produit.

2
blade

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, peu importe le moment où cela se produit, mais parfois, j’écris un article et cela ne signifie pas seulement que je dois taper quelques mots à nouveau, mais aussi que j’ai perdu ma concentration et que je dois cliquer pour retrouver le focus.

Le site Web Pro Reviewer contient un conseil sur la manière d’empêcher que cela ne se produise. Le moyen le plus simple de prévenir 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 actuellement.

Ceci ne fonctionne que lorsque l'application a déjà été réduite. 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 l'interface utilisateur Tweak, 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é.

2
Ivo Flipse