it-swarm-pt.tech

Impedindo que aplicativos roubem o foco

Existem soluções para impedir que aplicativos roubem o foco da janela ativa?

Isso é especialmente irritante quando estou iniciando um aplicativo, mudo para fazer outra coisa e o novo aplicativo começa a receber meia frase de texto.

189
svandragt

Isso não é possível sem extensivo manipulação de internals do Windows e você precisa superar isso.

Há momentos no uso diário do computador quando é realmente importante que você faça uma ação antes que o sistema operacional permita que você faça outra. Para fazer isso, é necessário bloquear seu foco em determinadas janelas. No Windows, o controle sobre esse comportamento é largamente deixado para os desenvolvedores dos programas individuais que você usa.

Nem todo desenvolvedor toma as decisões certas quando se trata deste tópico.

Eu sei que isso é muito frustrante e chato, mas você não pode ter seu bolo e comê-lo também. Provavelmente, há muitos casos em sua vida diária em que você está perfeitamente bem com o foco sendo movido para um determinado elemento da interface do usuário ou um aplicativo solicitando que o foco permaneça bloqueado. Mas a maioria das aplicações é um pouco igual quando se trata de decidir quem é o líder agora e o sistema nunca pode ser perfeito.

Um tempo atrás eu fiz uma extensa pesquisa sobre como resolver esse problema de uma vez por todas (e falhou). O resultado da minha pesquisa pode ser encontrado na página do projeto annoyance .

O projeto também inclui um aplicativo que tenta repetidamente chamar o foco chamando:

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 neste trecho, minha pesquisa também foi focada em outros aspectos do comportamento da interface do usuário que não gosto.

A maneira como tentei resolver isso foi carregar um DLL em cada novo processo e ligar as chamadas da API que fazem com que outras janelas sejam ativadas.
A última parte é a mais fácil, graças a incríveis bibliotecas de hooking de APIs por aí. Eu usei a grande 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 meus testes naquela época, isso funcionou muito bem. Exceto pela parte de carregar o DLL em cada novo processo. Como se pode imaginar, isso não é nada demais. Eu usei a abordagem AppInit_DLLs naquela época (que simplesmente não é suficiente).

Basicamente, isso funciona muito bem. Mas eu nunca encontrei tempo para escrever algo que propriamente injeta meu DLL em novos processos. E o tempo investido nisso, em grande parte, obscurece o aborrecimento que o foco de roubo me causa.

Além do problema de injeçãoDLL, há também um método de roubo de foco que eu não abordei na implementação do Google Code. Um colega de trabalho realmente fez algumas pesquisas adicionais e cobriu esse método. O problema foi discutido em SO: https://stackoverflow.com/questions/7430864/windows-7-prevent-application-from-losing-focus

50
Der Hochstapler

No Windows 7, a entrada de registro ForegroundLockTimeout não está mais marcada, você pode verificar isso com o Process Monitor. Na verdade, no Windows 7 eles não permitem que você altere a janela de primeiro plano. Vá e leia sobre seus detalhes , ele já esteve lá desde o Windows 2000.

No entanto, a documentação é uma droga e eles se perseguem e encontram maneiras de contornar isso .

Então, há algo com erros acontecendo com SetForegroundWindow , ou funções similares da API ...

A única maneira de realmente fazer isso corretamente é fazer um pequeno aplicativo que periodicamente chama LockSetForegroundWindow , praticamente desativando todas as chamadas para a nossa função de API com bugs.

Se isso não for suficiente (outra chamada de API com bugs), você pode ir ainda mais longe e fazer alguns API monitoring para ver o que está acontecendo, e você simplesmente hooka as chamadas da API em cada processo após o qual você pode livrar-se de any chamadas que atrapalham o primeiro plano. No entanto, ironicamente, isso é desencorajado pela Microsoft ...

23
Tom Wijsman

Existe uma opção em TweakUI que faz isso. Isso evita que a maioria dos truques usuais usados ​​por desenvolvedores de software forcem o foco no aplicativo.

É uma guerra de armas em curso, então eu não sei se funciona para tudo.

Update : De acordo com EndangeredMassa , o TweakUI não funciona no Windows 7.

18
Simon P Stevens

Acredito que possa haver alguma confusão, pois há duas maneiras de "roubar o foco": (1) uma janela chegando ao primeiro plano e (2) a janela recebendo as teclas digitadas.

O problema mencionado aqui é provavelmente o segundo, em que uma janela reivindica o foco colocando-se em primeiro plano - sem a solicitação ou permissão do usuário.

A discussão deve dividir aqui entre XP e 7.

Windows XP

Em XP, há um hack de registro que faz com que XP funcione da mesma forma que o Windows 7, impedindo que os aplicativos roubem o foco:

  1. Use o regedit para ir para: HKEY_CURRENT_USER\Control Panel\Desktop.
  2. Clique duas vezes em ForegroundLockTimeout e defina seu valor em hexadecimal para 30d40.
  3. Pressione OK e saia do regedit.
  4. Reinicie o seu PC para que as alterações entrem em vigor.

Windows 7

(A discussão abaixo aplica-se principalmente a XP também.)

Por favor, entenda que não há nenhuma maneira em que o Windows pode bloquear totalmente aplicativos de roubar o foco e permanecer funcional. Por exemplo, se durante uma cópia de arquivo seu antivírus detectou uma possível ameaça e gostaria de exibir uma janela solicitando a ação, se essa janela estiver bloqueada, você nunca entenderá porque a cópia nunca termina.

No Windows 7, há apenas uma modificação possível no comportamento do próprio Windows, que é usar os MS-Windows foco-segue-mouse hacks do Registro , onde o foco e/ou ativação vai sempre para as janelas. sob o cursor. Um atraso pode ser adicionado para evitar que os aplicativos sejam exibidos em toda a área de trabalho.
Veja este artigo: Windows 7 - Mouse Hover Torna a Janela Ativa - Habilita .

Caso contrário, é preciso detectar e neutralizar o programa culpado: se este é sempre o mesmo aplicativo que está recebendo o foco, esse aplicativo é programado para tirar o foco e impedir que isso seja feito, seja desativando-o desde o início com o computador ou use alguma configuração fornecida por esse aplicativo para evitar esse comportamento.

Você pode usar o script VBS incluído no código VB que identifica quem está roubando o foco , que o autor usou para identificar o culpado como um atualizador de "call home" para um software de impressora.

Uma medida desesperada, quando tudo o mais falha, e se você identificou esse aplicativo mal programado, é minimizá-lo e esperar que isso não seja levado à frente. Uma forma mais forte de minimização é a bandeja usando um dos produtos gratuitos listados em Best Free Application Minimizer .

A última idéia na ordem de desespero é fragmentar sua área de trabalho virtualmente usando um produto como Desktops ou Dexpot e fazer seu trabalho em outra área de trabalho que o padrão.

[EDITAR]

Como a Microsoft retirou a Galeria de Arquivos, aqui está o código acima VB reproduzido:

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

Inspirado pela resposta do Der Hochstapler , eu decidi escrever um DLL injetor, que funciona com processos de 64 e 32 bits e previne o roubo de foco no Windows 7 ou mais recente: https: // blade.sk/stay-focused/

O modo como funciona é que ele assiste a janelas recém-criadas (usando SetWinEventHook) e injeta DLL muito semelhante ao do Der Hochstapler no processo da janela, se ainda não estiver presente. Ele descarrega as DLLs e restaura a funcionalidade original na saída.

Do meu teste, funciona muito bem até agora. No entanto, o problema parece ser mais profundo do que apenas aplicativos chamando SetForegroundWindow. Por exemplo, quando uma nova janela é criada, ela é automaticamente colocada em primeiro plano, o que também interfere com o usuário digitando em outra janela.

Para lidar com outros métodos de roubo de foco, são necessários mais testes e agradeço qualquer feedback sobre cenários em que isso está acontecendo.

2
blade

Ghacks tem uma solução possível:

Acontece várias vezes ao dia que alguns aplicativos roubam o foco da janela ativa ao aparecer. Isso pode acontecer por vários motivos, quando eu extraio arquivos ou quando uma transferência termina, por exemplo. Não importa a maior parte do tempo quando isso acontece, mas às vezes eu estou escrevendo um artigo e isso não significa apenas que eu tenho que digitar algumas palavras novamente, mas também que eu perdi a concentração e tenho que clicar para recuperar o foco.

O site Pro Reviewer tem uma dica sobre como evitar que isso aconteça. A maneira mais fácil de evitar o roubo de foco é usar o Tweak UI, que tem uma configuração chamada “Impedir que aplicativos roubem o foco”. Marcar essa opção impede que outros aplicativos apareçam de repente e roubem o foco da janela em que você está trabalhando atualmente.

Isso só funciona quando o aplicativo foi minimizado antes. Em vez de roubar o foco, ele pisca um número de vezes que pode ser definido no mesmo menu em Tweak UI . Se você não quiser usar o Tweak UI, poderá alterar a configuração no Registro do Windows.

Navegue até a chave do Registro HKEY_CURRENT_USER> Painel de Controle> Área de Trabalho e altere o valor ForegroundLockTimeout para 30d40 (Hexadecimal) ou 200000 (Decimal). A chave ForeGroundFlashCount define a quantidade de flashes de uma janela para alertar o usuário onde 0 significa ilimitado.

2
Ivo Flipse