Impedire alle applicazioni di rubare attenzione


191

Esistono soluzioni per evitare che le applicazioni rubino lo stato attivo dalla finestra attiva?

Questo è particolarmente fastidioso quando avvio un'applicazione, passa a fare qualcos'altro e la nuova applicazione inizia a ricevere mezza frase di testo.


9
@Ivo Windows 7 nel mio caso, ma penso che per SuperUser tutte le versioni di Windows sarebbero rilevanti
svandragt

3
Il moderatore ha unito questa domanda: superuser.com/questions/199821/… con quella attuale. Questo è sbagliato, la risposta alla domanda attuale non si applica a Windows 7, quindi non dovrebbe essere unita. Finora non sono riuscito a trovare una soluzione a questo problema in Windows 7
Alex Angelico,

17
Questo è uno dei miei animali domestici numero uno con ogni interfaccia grafica che abbia mai usato. Stai scrivendo e sbattendo le mani, una finestra di dialogo a forma di bip ruba il focus e metà dei tasti premuti vanno altrove. Penseresti che gli implementatori dei sistemi di finestre avrebbero capito questo decenni fa. Se è presente attività in una finestra, ritardare l'esposizione della nuova finestra. Ad esempio, non visualizzare nulla nella GUI fino a tre o quattro secondi dall'ultimo clic del pulsante o dalla sequenza di tasti nella finestra attualmente focalizzata. 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.È ancora più fastidioso quando viene visualizzata una finestra di dialogo e lo si elimina involontariamente senza nemmeno vedere il messaggio perché ti è capitato di premere Spaceo Enterdurante la digitazione di una frase.
Synetech,

3
Questo in realtà è molto più che fastidioso, direi che è un rischio per la sicurezza. Non c'è nulla che impedisca la comparsa di un'applicazione quando si è nel mezzo della digitazione di una password e della cattura dei dati immessi.
Chris Peacock,

Risposte:


51

Questo non è possibile senza un'estesa manipolazione degli interni di Windows e devi superarlo.

Ci sono momenti nell'uso quotidiano del computer in cui è davvero importante eseguire un'azione prima che il sistema operativo ti consenta di eseguirne un'altra. Per fare ciò, deve bloccare l'attenzione su determinate finestre. In Windows, il controllo su questo comportamento è in gran parte lasciato agli sviluppatori dei singoli programmi che usi.

Non tutti gli sviluppatori prendono le giuste decisioni quando si tratta di questo argomento.

So che è molto frustrante e fastidioso, ma non puoi avere la tua torta e mangiarla anche tu. Probabilmente ci sono molti casi nella tua vita quotidiana in cui stai perfettamente bene che lo stato attivo venga spostato su un determinato elemento dell'interfaccia utente o un'applicazione che richiede che lo stato attivo rimanga bloccato su di esso. Ma la maggior parte delle applicazioni sono in qualche modo uguali quando si tratta di decidere chi è il leader in questo momento e il sistema non può mai essere perfetto.

Qualche tempo fa ho fatto ricerche approfondite per risolvere questo problema una volta per tutte (e ho fallito). Il risultato della mia ricerca può essere trovato nella pagina del progetto di fastidio .

Il progetto include anche un'applicazione che tenta ripetutamente di concentrarsi chiamando:

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;

Come possiamo vedere da questo frammento, la mia ricerca si è concentrata anche su altri aspetti del comportamento dell'interfaccia utente che non mi piacciono.

Il modo in cui ho cercato di risolvere questo problema è stato caricare una DLL in ogni nuovo processo e agganciare le chiamate API che causano l'attivazione di un'altra finestra.
L'ultima parte è quella facile, grazie alle fantastiche librerie di aggancio API disponibili. Ho usato l'eccezionale libreria 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;
}

Dai miei test di allora, ha funzionato alla grande. Tranne la parte di caricamento della DLL in ogni nuovo processo. Come si potrebbe immaginare, non è niente da prendere alla leggera. Allora ho usato l' approccio AppInit_DLLs (che semplicemente non è sufficiente).

Fondamentalmente, funziona alla grande. Ma non ho mai trovato il tempo di scrivere qualcosa che inietta correttamente la mia DLL in nuovi processi. E il tempo investito in questo oscura in gran parte il fastidio che il furto di attenzione mi provoca.

Oltre al problema di iniezione DLL, esiste anche un metodo per rubare l'attenzione che non ho trattato nell'implementazione su Google Code. Un collega ha effettivamente svolto ulteriori ricerche e ha coperto tale metodo. Il problema è stato discusso su SO: https://stackoverflow.com/questions/7430864/windows-7-prevent-application-from-losing-focus


Pensi che questa tua soluzione possa essere trasferita su Java? Ho cercato e fatto domande ma non ho trovato nulla. Forse potrei importare la libreria hook stessa in Java usando jne?
Tomáš Zato

@ TomášZato: Nessuna idea. Non sto usando attivamente questo codice da solo.
Der Hochstapler,

Sto provando a compilarlo almeno come C ++ (e quindi iniettare / rimuovere la DLL compilata da Java). Ma neanche quello va bene. Non voglio discuterne qui nei commenti, ma se potessi davvero aiutarmi a farlo funzionare, sarei molto gentile! Ho creato una chat room, se riesco a farlo funzionare posterò un
Tomáš Zato

23

In Windows 7, la ForegroundLockTimeoutvoce del registro non è più controllata, è possibile verificarla con Process Monitor. In effetti, in Windows 7 ti impediscono di cambiare la finestra di primo piano. Vai a leggere i suoi dettagli , è stato lì persino da Windows 2000.

Tuttavia, la documentazione fa schifo e si rincorrono e trovano il modo di aggirarlo .

Quindi, c'è qualcosa che non va SetForegroundWindow, o funzioni API simili ...

L'unico modo per farlo davvero è creare una piccola applicazione che periodicamente chiama LockSetForegroundWindow, disabilitando virtualmente qualsiasi chiamata alla nostra funzione API buggy.

Se ciò non bastasse (un'altra chiamata errata dell'API?) Puoi andare ancora oltre e fare un po 'di monitoraggio dell'API per vedere cosa sta succedendo, quindi agganciare semplicemente le chiamate API su ogni processo dopo il quale puoi sbarazzarti di tutte le chiamate che incasinano il primo piano. Tuttavia, ironicamente, questo è scoraggiato da Microsoft ...


3
Qualcuno ha un caso d'uso riproducibile di questo in Windows 7? Dato che le persone preferiscono sperimentare il contrario (ad esempio, trovo spesso che Windows sia nascosto dietro la mia finestra attuale) e che devo ancora vederlo accadere in Windows 7, sarebbe piuttosto fastidioso scrivere un'applicazione ma non riuscire a Provalo. Inoltre, come afferma Microsoft, ciò non dovrebbe più accadere con Windows 7. Nella migliore delle ipotesi, le persone hanno scoperto che poteva cambiare la messa a fuoco della tastiera solo per caso, questa chiamata API lo avrebbe risolto, ma non so come testare se funziona davvero. .
Tamara Wijsman

1
Il programma di installazione (basato su InnoSetup) avvia altri processi e possibili altre configurazioni (nascoste), ma non so su quale creatore delle impostazioni si basano.
Daniel Beck

6
@TomWijsman: Apri regedit, cerca del testo casuale che non verrà trovato. Vai in un'altra app e inizia a digitare. Al termine della ricerca, regedit ruberà il focus.
endolith

1
@endolith: non riproducibile, utilizzando Windows 8 Sostituisci anteprima qui. Quale sistema operativo stai usando? Nel mio caso evidenzia solo l'applicazione in basso ma non interrompe affatto la mia navigazione ...
Tamara Wijsman,

21
Sì, Win7 Pro 64-bit. E il furto della messa a fuoco è ancora peggio per i processi elevati, poiché catturano il tuo <Invio> quando non dovrebbero, e tu gli dici di fare il tubo accidentalmente al tuo sistema. Niente dovrebbe mai essere in grado di rubare la concentrazione.
endolith

18

C'è un'opzione in TweakUI che lo fa. Impedisce la maggior parte dei soliti trucchi che gli sviluppatori di software discutono impiegano per forzare l'attenzione sulla loro app.

È una guerra agli armamenti in corso, quindi non so se funziona per tutto.

Aggiornamento : Secondo EndangeredMassa , TweakUI non funziona su Windows 7.


2
tweakui è compatibile con Windows 7?
Frank

@frankster. Nessuna idea, scusa, sospetto che probabilmente non lo sia. Scaricalo e provalo. Segnala se lo fai tutti lo sanno.
Simon P Stevens,

5
Anche l'utilizzo delle impostazioni del registro impostate da TweakUI non funziona su Win7.
EndangeredMassa,

@EndangeredMassa quale chiave di registro è quella?
n611x007,

2
La chiave di registro è HKEY_CURRENT_USER \ Control Panel \ Desktop \ ForegroundLockTimeout (in millisecondi). E sì, non funziona più in Windows 7.
foo

14

Credo che possa esistere un po 'di confusione, poiché esistono due modi per "rubare il focus": (1) una finestra che arriva in primo piano e (2) la finestra che riceve i tasti.

Il problema a cui si fa riferimento qui è probabilmente il secondo, in cui una finestra rivendica il focus portandosi in primo piano, senza la richiesta o l'autorizzazione dell'utente.

La discussione deve essere divisa qui tra XP e 7.

Windows XP

In XP esiste un hack del registro che fa funzionare XP come Windows 7 per impedire alle applicazioni di rubare il focus:

  1. Utilizzare regedit per andare a: HKEY_CURRENT_USER\Control Panel\Desktop.
  2. Fare doppio clic su ForegroundLockTimeoute impostare il valore in esadecimale su 30d40.
  3. Premere OK ed uscire da regedit.
  4. Riavvia il PC per rendere effettive le modifiche.

Windows 7

(La discussione che segue si applica principalmente anche a XP.)

Si prega di comprendere che non esiste alcun modo in cui Windows può bloccare totalmente le applicazioni dal rubare l'attenzione e rimanere funzionali. Ad esempio, se durante una copia di file il tuo antivirus rileva una possibile minaccia e desideri far apparire una finestra che ti chiede di intraprendere l'azione, se questa finestra è bloccata non capiresti mai perché la copia non termina mai.

In Windows 7 esiste una sola modifica possibile al comportamento di Windows stesso, che consiste nell'utilizzare gli hack del registro focus-segue-mouse di MS-Windows , in cui lo stato attivo e / o l'attivazione vanno sempre alle finestre sotto il cursore. È possibile aggiungere un ritardo per evitare che le applicazioni vengano visualizzate su tutto il desktop.
Vedi questo articolo: Windows 7 - Il passaggio del mouse rende la finestra attiva - Abilita .

Altrimenti, è necessario rilevare e neutralizzare il programma colpevole: se questa è sempre la stessa applicazione che sta ottenendo il focus, allora questa applicazione è programmata per focalizzarsi e impedire che ciò possa essere fatto disabilitando l'avvio dal computer, oppure utilizzare alcune impostazioni fornite da tale applicazione per evitare questo comportamento.

È possibile utilizzare lo script VBS incluso nel codice VB che identifica chi ruba il focus , che l'autore ha usato per identificare il colpevole come un programma di aggiornamento "call home" per un software di stampa.

Una misura disperata quando tutto il resto fallisce, e se hai identificato questa applicazione mal programmata, è minimizzarla e sperare che non si porti poi in primo piano. Una forma più forte di minimizzazione è sul vassoio utilizzando uno dei prodotti gratuiti elencati in Best Minimizer applicazione gratuita .

L'ultima idea in ordine di disperazione è quella di fratturare il desktop virtualmente utilizzando un prodotto come Desktop o Dexpot e svolgere il proprio lavoro su un desktop diverso da quello predefinito.

[MODIFICARE]

Poiché Microsoft ha ritirato la Galleria archivio, ecco il codice VB sopra riprodotto:

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

48
"se questa finestra è bloccata, non capiresti mai perché la copia non termina mai" Non è vero. Il comportamento corretto è di avvisare l'utente con un'icona della barra delle attività lampeggiante (o forse un popup a comparsa o una notifica tostapane o qualcosa del genere). L'interruzione dell'utente con una finestra che intercetta i tasti premuti significa che dicono al software antivirus di compiere un'azione o un'altra a caso. Sicuramente non è un buon modo di fare le cose.
endolith

1
"se questa finestra è bloccata, non capiresti mai perché la copia non termina mai" Non è vero. Il comportamento corretto è di avvisare l'utente con un'icona della barra delle attività lampeggiante ... Ci sono state volte in cui ho fatto clic su un pulsante o qualcosa in un programma in esecuzione che provoca la creazione di una nuova finestra di dialogo modale (ad esempio, apri file ), ma poi Passo a un altro programma prima che venga creata la finestra di dialogo. Di conseguenza, la finestra di dialogo è nascosta e non è possibile passare all'altro programma e la finestra di dialogo non può essere chiusa. Né il pulsante della barra delle applicazioni né Alt-Tabfunziona; forzando solo il dialogo in primo piano.
Synetech,

1
@Synetech: a volte l'unica soluzione alla finestra di dialogo non frontale è quella di terminare l'attività. Gli algoritmi di messa a fuoco in Windows sono davvero pessimi.
harrymc,

2
@harrymc, non devo mai ricorrere all'uccisione di una delle app. Ho appena eseguito il mio programma di manipolazione delle finestre ( WinSpy ++ fa il trucco semplicemente fantastico) e nascondo la finestra di fronte, quindi posso chiudere la finestra di dialogo bloccata, quindi mostrare nuovamente la finestra nascosta. Non è conveniente, ma è meglio che uccidere uno dei processi.
Synetech,

1
@harrymc, non proprio; uccidere un'app e perdere cose fa solo più vapore, e se si tratta di una finestra di dialogo modale (che blocca la finestra principale e non ha un pulsante sulla barra delle applicazioni), non verrà visualizzata Alt+Tabnell'elenco e, nella mia esperienza, una finestra che ha una finestra di dialogo modale aperta non sempre (mai?) mostra la finestra di dialogo modale Alt+Tab, soprattutto se la finestra di dialogo non ha mai avuto una modifica per ottenere lo stato attivo. :-|
Synetech,

2

Ghacks ha una possibile soluzione:

Succede più volte al giorno che alcune applicazioni rubano il focus della finestra attiva spuntando. Questo può accadere per una serie di motivi, ad esempio quando estraggo i file o termina un trasferimento. Non importa la maggior parte delle volte quando questo accade, ma a volte sto scrivendo un articolo e non significa solo che devo digitare di nuovo alcune parole, ma anche che ho perso concentrazione e devo fare clic per riaccendere.

Il sito Web Pro Reviewer offre un suggerimento su come evitare che ciò accada. Il modo più semplice per prevenire il furto del focus è usare l'interfaccia utente di Tweak che ha un'impostazione che si chiama "Impedisci alle applicazioni di rubare il focus". Il controllo di questa opzione impedisce che altre applicazioni si aprano improvvisamente e rubino lo stato attivo della finestra in cui si sta attualmente lavorando.

Funziona solo quando l'applicazione è stata minimizzata in precedenza. Invece di rubare la messa a fuoco, lampeggerà un numero di volte che può essere definito nello stesso menu nell'interfaccia utente di Tweak . Se non si desidera utilizzare Tweak UI, è possibile modificare l'impostazione nel registro di Windows.

Passare alla chiave del Registro di sistema HKEY_CURRENT_USER> Pannello di controllo> Desktop e modificare il valore ForegroundLockTimeout su 30d40 (esadecimale) o 200000 (decimale). La chiave ForeGroundFlashCount definisce la quantità di lampi di una finestra per avvisare l'utente dove 0 significa illimitato.


20
Questo non funziona su nessun sistema operativo dopo XP. Quel valore di registro è già impostato su quello (per impostazione predefinita, credo) e non funziona comunque.
EndangeredMassa,

1
Solo per il secondo che sono su Windows 7 (64 bit), sto vivendo focus-rubando (VS 2012 quando finalmente attivo, per esempio), e il suggerimento di registro sopra è già in atto. Conferma tecnica in questa risposta: superuser.com/a/403554/972
Michael Paulukonis

2

Ispirato dalla risposta di Der Hochstapler , ho deciso di scrivere un iniettore DLL, che funziona con processi a 64 e 32 bit e impedisce il furto di focus su Windows 7 o versioni successive: https://blade.sk/stay-focused/

Il modo in cui funziona è che controlla le finestre appena create (usando SetWinEventHook) e inietta DLL molto simile a quella di Der Hochstapler nel processo della finestra se non è già presente. Scarica le DLL e ripristina la funzionalità originale all'uscita.

Dai miei test, funziona molto bene finora. Tuttavia, il problema sembra andare più in profondità della semplice chiamata di app SetForegroundWindow. Ad esempio, quando viene creata una nuova finestra, questa viene automaticamente portata in primo piano, il che interferisce anche con la digitazione di un utente in un'altra finestra.

Per gestire altri metodi di furto del focus, sono necessari ulteriori test e apprezzerei qualsiasi feedback sugli scenari in cui sta accadendo.


0

Ho capito come impedire a TaskBar di far lampeggiare una finestra di destinazione appena attivata dopo aver attivato, massimizzato e focalizzato la finestra principale di quel processo da un altro processo. Prima di tutto, ci sono molte restrizioni sul fatto che questa operazione sia consentita.

"Il sistema limita i processi che possono impostare la finestra di primo piano. Un processo può impostare la finestra di primo piano solo se è vera una delle seguenti condizioni:

  • Il processo è il processo in primo piano.
  • Il processo è stato avviato dal processo in primo piano.
  • Il processo ha ricevuto l'ultimo evento di input.
  • Non esiste un processo in primo piano.
  • Il processo in primo piano è in fase di debug.
  • Il primo piano non è bloccato (vedi LockSetForegroundWindow).
  • Il timeout del blocco in primo piano è scaduto (vedere SPI_GETFOREGROUNDLOCKTIMEOUT in SystemParametersInfo).
  • Nessun menu attivo.

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

Pertanto, se il processo di controllo è in primo piano, può temporaneamente abilitare un altro processo a rubare completamente il primo piano chiamando AllowSetForegroundWindow con l' id del processo di destinazione. Quindi, il processo di destinazione può chiamare SetForegroundWindow stesso, utilizzando la propria maniglia della finestra, e funzionerà.

Ovviamente, ciò richiede un certo coordinamento tra i due processi, ma funziona, e se lo stai facendo per implementare un'app a istanza singola che reindirizza tutti i lanci Explorer-clic nell'istanza dell'app esistente, allora già avere una (ad esempio) pipe denominata per coordinare le cose comunque.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.