Questão Impedindo que aplicativos roubem o foco


Há alguma solução 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.


174


origem


@Ivo Windows 7 no meu caso, mas eu acho que para SuperUser todas as versões do Windows seria relevante - svandragt
O moderador fundiu essa questão: superuser.com/questions/199821/… com o atual. Isso está errado, a resposta para a pergunta atual não se aplica ao Windows 7, por isso não deve ser mesclado. Até agora não consegui encontrar uma solução para este problema no Windows 7 - Alex Angelico
Este é um dos meus números um pet peeves com cada GUI que eu já usei. Você está digitando e blam, alguma caixa de diálogo bleeping rouba o foco e metade do seu teclado vai para outro lugar. Você pensaria que os implementadores de sistemas de janelas teriam descoberto isso décadas atrás. Se houver atividade em uma janela, atrase a exposição da nova janela. Por exemplo. não insira nada na GUI até três ou quatro segundos desde o último clique no botão ou pressionamento de tecla na janela atualmente focalizada. Doh! - Kaz
Eu encontrei quando eu tenho meu meu disco rígido externo (feito pela Seagate) ou meu ipod nano (ahem, apple) conectado ao meu Windows 7 máquina, parece que o "desktop" iria roubar o foco a cada 30 segundos ou mais, do que nunca Estava navegando, seja músicas do iTunes, resultados de pesquisa do Google Chrome ou e-mails do Firefox. Desativei o recurso de reprodução automática, e isso ajudou por um tempo, mas o problema voltou mesmo depois que a reprodução automática foi desativada. Eu acho que eu tenho que manter o meu HD externo e flash drives desconectados para a maior parte, o que é uma porcaria que é onde toda a minha música é :( É um bug muito chato que me faz querer S
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.É ainda mais irritante quando uma caixa de diálogo aparece e você sem querer a ignora sem ver a mensagem porque você pressionou Space ou Enter enquanto digita uma frase. - Synetech


Respostas:


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 no página do projeto de aborrecimento.

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 se concentrou em outros aspectos do comportamento da interface do usuário que não gosto.

A maneira como tentei resolver isso foi carregar uma 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 o muito grande biblioteca de 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 a DLL em todo novo processo. Como se pode imaginar, isso não é nada demais. Eu usei o AppInit_DLLs abordagem então (o que simplesmente não é suficiente).

Basicamente, isso funciona muito bem. Mas eu nunca encontrei tempo para escrever algo que devidamente injeta minha 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ção de DLL, há também um método de roubo de foco que 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 no SO: https://stackoverflow.com/questions/7430864/windows-7-prevent-application-from-losing-focus


45



Você acha que essa sua solução poderia ser portada para Java? Eu tenho procurado e fazendo perguntas, mas não encontrei nada. Talvez eu pudesse importar a própria biblioteca de gancho em java usando jne? - Tomáš Zato
@ TomášZato: Não faço ideia. Eu não estou usando ativamente este código. - Der Hochstapler
Eu estou tentando compilá-lo como C ++, pelo menos (e, em seguida, injetar / remover a DLL compilada de Java). Mas isso também não vai bem. Eu não quero discutir isso aqui nos comentários, mas se você pudesse realmente me ajudar a fazê-lo funcionar, eu seria muito graciosa! Eu criei uma sala de bate-papo, se eu conseguir que isso funcione, postarei um comentário sobre como fazer isso aqui: chat.stackexchange.com/rooms/21637/… - Tomáš Zato


No Windows 7, o ForegroundLockTimeout entrada de registro 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, já existe desde o Windows 2000.

Contudo, a documentação é uma droga e eles perseguem uns aos outros e encontrar maneiras de contornar isso.

Então, há algo buggy acontecendo SetForegroundWindowou funções de API semelhantes ...

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 algumas Monitoramento de API para ver o que está acontecendo, e então você simplesmente ligar as chamadas da API em todos os processos após o qual você pode se livrar de qualquer chama isso de bagunçar o primeiro plano. No entanto, ironicamente, isso é desencorajado pela Microsoft ...


22



Alguém tem um caso de uso reproduzível disso no Windows 7? Dado que as pessoas experimentam o contrário (por exemplo, muitas vezes acho exigente que o Windows fique escondido atrás da minha janela atual) e que ainda estou vendo isso acontecer no Windows 7, seria muito irritante escrever um aplicativo, mas não conseguir teste-o. Além disso, como a Microsoft afirma, isso não deve mais acontecer com o Windows 7. Na melhor das hipóteses, as pessoas descobriram que só podiam mudar o foco do teclado por acidente, essa chamada da API corrigia isso, mas eu não sei como testar se realmente funciona. . - Tom Wijsman
O instalador (baseado no InnoSetup) inicia outros processos e outras possíveis configurações (ocultas), mas eu não sei em qual instalação o criador está baseado. - Daniel Beck♦
@TomWijsman: Abra o regedit, procure por algum texto aleatório que não seja encontrado. Entre em outro aplicativo e comece a digitar. Quando a pesquisa terminar, o regedit irá roubar o foco. - endolith
@endolith: Não reproduzível, usando o Windows 8 Replase Preview aqui. Qual sistema operacional você está usando? No meu caso, apenas destaca a aplicação na parte inferior, mas não interrompe a minha navegação a todos ... - Tom Wijsman
Sim, o Win7 Pro de 64 bits. E o roubo de foco é ainda pior para processos elevados, já que eles capturam o pressionamento de <Enter> quando não deveriam, e você diz para mangueira do seu sistema acidentalmente. Nada deveria sempre ser capaz de roubar o foco. - endolith


Existe uma opção em TweakUI que faz isso. Isso evita que a maioria dos truques comuns usados ​​pelos desenvolvedores de software duvidosos force o foco em seu aplicativo.

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

Atualizar: De acordo com Morte em perigo, O TweakUI não funciona no Windows 7.


18



é compatível com o windows 7? - frankster
@ frankter. Não faço ideia, desculpe, eu suspeito que provavelmente não é. Faça o download e experimente. Relatório de volta, se você fizer isso todo mundo sabe. - Simon P Stevens
Mesmo usando a configuração do registro que o TweakUI define não funciona no Win7. - EndangeredMassa
@ EndangeredMassa qual chave de registro é essa? - n611x007
A chave de registro é HKEY_CURRENT_USER \ Painel de controle \ Desktop \ ForegroundLockTimeout (em milissegundos). E sim, não funciona mais no Windows 7. - foo


Acredito que possa existir 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 referido aqui é provavelmente o segundo, onde um O Windows 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

No XP existe um hack de registro que faz o XP funcionar 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 ao 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 abrir uma janela solicitando a ação, se esta janela estiver bloqueada então você nunca entenderia porque a cópia nunca termina.

No Windows 7, há apenas uma modificação possível no comportamento do próprio Windows, que é para usar o MS-Windows focus-segue-mouse Hackers de 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 está programado para tirar o foco e impedir que isso seja feito, seja desativando-o desde o início com o computador, ou usando alguma configuração fornecida por esse aplicativo para evitar esse comportamento.

Você poderia usar o script VBS incluído em 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 mais falha, e se você identificou esta aplicação mal programada, é minimizá-lo e espero que não seja levado para a frente. Uma forma mais forte de minimização é a bandeja usando um dos produtos gratuitos listados Melhor Minimizador de Aplicações Grátis.

Última idéia na ordem de desespero é fraturar sua área de trabalho virtualmente usando um produto tal como Desktops ou Dexpot, e faça seu trabalho em outro desktop que o padrão.

[EDITAR]

Como a Microsoft retirou a Galeria de arquivos, aqui está o código VB acima 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



"Se esta janela estiver bloqueada, você nunca entenderá porque a cópia nunca termina" Isso não é verdade. O comportamento correto é notificar o usuário com um ícone da barra de tarefas piscando (ou talvez uma notificação pop-up de balão ou uma torradeira ou algo assim). Interromper o usuário com uma janela que intercepta o pressionamento de teclas significa dizer ao software antivírus para realizar uma ação ou outra aleatoriamente. Definitivamente não é uma boa maneira de fazer as coisas. - endolith
"Se esta janela estiver bloqueada, você nunca entenderá porque a cópia nunca termina" Isso não é verdade. O comportamento correto é notificar o usuário com um ícone da barra de tarefas piscando ...   Houve momentos em que cliquei em um botão ou algo em um programa em execução que faz com que uma nova caixa de diálogo modal seja criada (por exemplo, abrir arquivo), mas depois mudo para outro programa antes de o diálogo ser criado. Como resultado, a caixa de diálogo fica oculta e o outro programa não pode ser alternado e a caixa de diálogo não pode ser descartada. Nem o botão da barra de tarefas nem Alt-Tab trabalho; apenas forçando o diálogo para a frente. - Synetech
@Synetech: Às vezes, a única solução para o diálogo não frontal é matar a tarefa. Os algoritmos de foco no Windows são realmente ruins. - harrymc
@harrymc, eu nunca tenho que recorrer para matar um dos apps. Eu apenas corro meu programa manipulador de janelas (WinSpy ++ faz o truque ótimo) e esconde a janela na frente, então eu posso dispensar a caixa de diálogo travada e mostrar novamente a janela oculta. Não é conveniente, mas é melhor que matar qualquer um dos processos. - Synetech
@harrymc, não realmente; Matar um aplicativo e perder coisas só faz mais vapor, e se for um diálogo modal (que bloqueia a janela pai e não tem botão da barra de tarefas), ele não aparecerá no Alt+Tab lista, e, na minha experiência, uma janela que tem um diálogo modal aberto nem sempre (nunca?) mostra o diálogo modal com Alt+Tab, especialmente se o diálogo nunca teve uma mudança para obter foco. :-| - Synetech


Ghacks tem um solução possível:

Acontece várias vezes ao dia que   algumas aplicações roubam o foco de   a janela ativa, aparecendo. este   pode acontecer por várias razões,   quando eu extraio arquivos ou uma transferência   termina por exemplo. Isso não   importa na maioria das vezes 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 perdi a concentração e   tem que clicar para recuperar o foco.

o Revisor Profissional site tem uma dica sobre   como evitar que isso aconteça.   A maneira mais fácil de evitar o foco   roubar é usar Tweak UI, que tem   uma configuração chamada "Impedir   aplicações de roubar o foco ”.   Marcar esta opção impede que   outras aplicações aparecem de repente e   roubar o foco da janela que você é   atualmente trabalhando em.

Isso só funciona quando o aplicativo   foi minimizado antes. Ao invés de   roubando o foco vai piscar um   número de vezes que pode ser definido   no mesmo menu em Tweak UI. Se vocês   não quer usar Tweak UI você pode   alterar a configuração no Windows   Registro.

Navegue até a chave do registro   HKEY_CURRENT_USER> Painel de Controle>   Desktop e alterar o   Valor ForegroundLockTimeout para 30d40   (Hexadecimal) ou 200000 (decimal). o   chave ForeGroundFlashCount define o   quantidade de flashes de uma janela para alertar   o usuário onde 0 significa ilimitado.


2



Isso não funciona em nenhum sistema operacional após o XP. Esse valor do registro já está definido para isso (por padrão, acredito) e não funciona de qualquer maneira. - EndangeredMassa
Apenas para segundo que estou no Windows 7 (64 bits), experimentando o roubo de foco (VS 2012 quando finalmente ativo, por exemplo), e a sugestão de registro acima já está no local. Confirmação técnica nesta resposta: superuser.com/a/403554/972 - Michael Paulukonis