Запобігання програмам крадіжок фокусу


191

Чи є рішення, які не дозволяють програмам красти фокус з активного вікна?

Це особливо дратує, коли я запускаю програму, перемикаюся на щось інше, і нова програма починає отримувати половину речення тексту.


9
@Ivo Windows 7 у моєму випадку, але я думаю, що для SuperUser всі версії Windows були б актуальними
svandragt

3
Модератор об'єднав це питання: superuser.com/questions/199821/… з поточним. Це неправильно, відповідь на поточне питання не стосується Windows 7, тому його не слід об’єднувати. Поки що я не зміг знайти рішення цієї проблеми в Windows 7
Alex Angelico

17
Це один із моїх підлітків, який я колись використовував. Ви друкуєте і звинувачуєте, якесь кричуще діалогове вікно вкрадає фокус і половина ваших натискань клавіш переходить кудись інше. Можна подумати, що реалізатори віконних систем розібралися б у цьому десятки років тому. Якщо у вікні є активність, відкладіть експозицію нового вікна. Наприклад, не з’являйтеся нічого на графічному інтерфейсі протягом трьох-чотирьох секунд з моменту останнього натискання кнопки чи натискання клавіші у вікні, що фокусується на даний момент. До!
Каз

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.Це ще більше дратує, коли з'являється діалогове вікно, і ви ненавмисно відхиляєте його, навіть не бачачи повідомлення, оскільки вам трапилось натиснути Spaceабо Enterпід час введення речення.
Synetech

3
Це насправді набагато більше, ніж дратує, я б сказав, що це ризик для безпеки. Ніщо не може зупинити появу програми, коли ви в середині вводите пароль і захоплюєте свій вклад.
Кріс Павич

Відповіді:


51

Це неможливо без обширних маніпуляцій із внутрішніми системами Windows, і вам потрібно це подолати.

Бувають моменти щоденного використання комп’ютера, коли дійсно важливо зробити одну дію, перш ніж операційна система дозволить зробити іншу. Для цього потрібно заблокувати фокус на певних вікнах. У Windows контроль над такою поведінкою значною мірою залишається розробникам окремих програм, якими ви користуєтесь.

Не кожен розробник приймає правильні рішення, коли йдеться про цю тему.

Я знаю, що це дуже засмучує і дратує, але ти не можеш торт і їсти його теж. Напевно, є багато випадків у вашому повсякденному житті, коли ви прекрасно справляєтеся з тим, що фокус переміщується на певний елемент інтерфейсу або програму, яка вимагає, щоб фокус залишався заблокованим. Але більшість застосувань дещо рівні, коли справа стосується того, щоб вирішити, хто зараз ведучий, і система ніколи не може бути ідеальною.

Деякий час тому я робив великі дослідження щодо вирішення цього питання раз і назавжди (і не вдалося). Результат мого дослідження можна знайти на сторінці проекту роздратування .

Проект також включає додаток, який неодноразово намагається захопити фокус, зателефонувавши:

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;

Як ми бачимо з цього фрагмента, моє дослідження було також зосереджене на інших аспектах поведінки інтерфейсу користувача, які мені не подобаються.

Я намагався вирішити це - завантажувати DLL у кожен новий процес і підключати виклики API, які викликають активацію інших вікон.
Остання частина - найпростіша, завдяки чудовим бібліотекам, що підключають API. Я використовував дуже велику бібліотеку 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;
}

З моїх тестів тоді це спрацювало чудово. За винятком частини завантаження DLL у кожен новий процес. Як можна собі уявити, це не є надто легковажним. Тоді я використовував підхід AppInit_DLLs (що просто недостатньо).

В основному, це чудово працює. Але я так і не знайшов часу написати щось, що належним чином вводить мою DLL у нові процеси. І час, вкладений у це, значною мірою затьмарює роздратування, яке викликає у мене крадіжка фокусу.

Окрім проблеми з ін'єкцією DLL, існує також метод крадіжки фокусу, який я не висвітлював у впровадженні Google Code. Співробітник фактично провів додаткові дослідження і висвітлив цей метод. Проблему обговорювали на SO: https://stackoverflow.com/questions/7430864/windows-7-prevent-application-from-losing-focus


Як ви думаєте, це ваше рішення можна перенести на Java? Я шукав і задавав питання, але нічого не знайшов. Можливо, я можу імпортувати саму бібліотеку гаків у Java, використовуючи jne?
Томаш Зато

@ TomášZato: Не маю ідеї. Я сам активно не використовую цей код.
Der Hochstapler

Я намагаюся скомпілювати його як мінімум на C ++ (а потім ввести / видалити компільований DLL з Java). Але і це не надто добре. Я не хочу обговорювати це тут у коментарях, але якби ви насправді могли допомогти мені змусити його працювати, я був би дуже витончений! Я створив кімнату чату, якщо я зроблю це на роботі, опублікую коментар, як це зробити тут: chat.stackexchange.com/rooms/21637/…
Томаш Зато

23

У Windows 7 ForegroundLockTimeoutзапис реєстру більше не перевіряється, це можна підтвердити за допомогою Монітора процесів. Насправді в Windows 7 вони забороняють змінювати вікно переднього плану. Заходьте і читайте про його подробиці , він навіть там існував з Windows 2000.

Однак документація засмоктується, і вони переслідують один одного і знаходять шляхи цього .

Отже, щось відбувається з помилкою SetForegroundWindowабо подібними функціями API ...

Єдиний спосіб дійсно зробити це належним чином - зробити невелику програму, яка періодично дзвонить LockSetForegroundWindow, фактично вимикаючи будь-які дзвінки на нашу функцію API баггі.

Якщо цього недостатньо (черговий помилковий дзвінок API?), Ви можете піти ще далі і зробити деякий моніторинг API, щоб побачити, що відбувається, а потім просто підключити виклики API на кожен процес, після якого ви зможете позбутися від усіх викликів, які заплуталися передній план. Однак, як це не дивно, Microsoft це відлякує ...


3
Хтось має відтворювальний випадок використання цього в Windows 7? Зважаючи на те, що люди, скоріше, відчувають навпаки (наприклад, я часто вважаю, що Windows вимагає приховування за моїм поточним вікном), і що я ще бачу, як це відбувається в Windows 7, було б дуже прикро писати заявку, але неможливо перевірити його. Крім того, оскільки Microsoft заявляє, що це більше не повинно відбуватися з Windows 7. У кращому випадку люди виявили, що він може перемикати фокус клавіатури лише випадково, цей виклик API це виправить, але я не знаю, як перевірити, чи працює він насправді .. .
Тамара Війсман

1
Інсталятор (заснований на InnoSetup) запускає інші процеси та можливі інші (приховані) настройки, але я не знаю, на чому вони засновані.
Даніель Бек

6
@TomWijsman: відкрийте regedit, знайдіть якийсь випадковий текст, який не буде знайдено. Зайдіть в інший додаток і почніть вводити текст. Коли пошук завершено, regedit вкраде фокус.
ендоліт

1
@endolith: Не відтворюється, хоча тут використовується Windows 8 Replase Preview. Яку ОС ви використовуєте? У моєму випадку він просто підкреслює програму внизу, але зовсім не перериває перегляд ...
Tamara Wijsman

21
Так, Win7 Pro 64-розрядний. А крадіжка фокусування ще гірша для підвищених процесів, оскільки вони фіксують ваш натискання <Enter>, коли вони не повинні, і ви говорите йому, щоб випадково шлангувати вашу систему. Ніщо не повинно коли - або буде в змозі перехоплювати фокус.
ендоліт

18

У TweakUI є варіант, який робить це. Це перешкоджає більшості звичних хитрощів, які сумнівні розробники програмного забезпечення використовують для того, щоб змусити зосередитися на своїй програмі.

Хоча це тривала війна з озброєннями, тому я не знаю, чи працює вона на все.

Оновлення : За даними EndangeredMassa , TweakUI не працює в Windows 7.


2
чи tweakui сумісний з Windows 7?
Франкстер

@frankster. Не маю ідеї, вибачте, я підозрюю, що, мабуть, це не так. Завантажте його та спробуйте. Повідомте, якщо це зробите всі.
Саймон П Стівенс

5
Навіть використання налаштування реєстру, яке встановлює TweakUI, не працює на Win7.
EndangeredMassa

@EndangeredMassa, який ключ реєстру це?
n611x007

2
Ключ реєстру - HKEY_CURRENT_USER \ Панель управління \ Desktop \ ForegroundLockTimeout (у мілісекундах). І так, він більше не працює в Windows 7.
foo

14

Я вважаю, що може виникнути певна плутанина, оскільки існують два способи "крадіжки фокусу": (1) вікно, яке виходить на передній план, і (2) вікно, яке отримує натискання клавіш.

Проблема, про яку йдеться тут, мабуть, друга, де вікно вимагає фокусування, виводячи себе на перший план - без запиту чи дозволу користувача.

Тут обговорення має бути розділене між XP та 7.

Windows XP

У XP є злом реєстру, який змушує XP працювати так само, як і Windows 7, не даючи додаткам красти фокус:

  1. Використовуйте редактор реєстру для переходу на: HKEY_CURRENT_USER\Control Panel\Desktop.
  2. Двічі клацніть мишкою ForegroundLockTimeoutта встановіть її значення у шістнадцятковій точці 30d40.
  3. Натисніть OK та вийдіть з regedit.
  4. Перезавантажте комп'ютер, щоб зміни вступили в силу.

Windows 7

(Нижче обговорення в основному стосується і XP.)

Будь ласка, розумійте, що немає жодного способу, за допомогою якого Windows може повністю блокувати програми з крадіжки фокусу та залишатися функціональними. Наприклад, якщо під час копіювання файлу ваш антивірус виявив можливу загрозу і хотів би спливати вікно з проханням вжити дії, якщо це вікно заблоковано, ви ніколи не зрозумієте, чому копія ніколи не припиняється.

У Windows 7 можлива лише одна модифікація поведінки самої Windows, яка полягає у використанні злому реєстру MS-Windows focus-follow-mouse , де фокус та / або активація завжди йде до вікон під курсором. Затримка може бути додана, щоб уникнути появи програм на всьому робочому столі.
Дивіться цю статтю: Windows 7 - Наведення миші робить вікно активним - увімкнено .

В іншому випадку потрібно виявити і нейтралізувати винну програму: Якщо це завжди той самий додаток, який отримує фокус, то ця програма запрограмована на фокусування, і запобігання цьому може бути зроблено, відключивши її від запуску з комп'ютера, або використовуйте налаштування, надані цією програмою, щоб уникнути такої поведінки.

Ви можете використовувати скрипт VBS, включений до коду VB, який визначає, хто краде фокус , який автор використовував для виявлення винуватця як оновлення "додому виклику" для програмного забезпечення принтера.

Відчайдушний захід, коли все інше виходить з ладу, і якщо ви визначили цю програму, яка погано запрограмована, - це мінімізувати її та сподіватися, що потім не вийде на передову. Більш сильною формою мінімізації є лоток, використовуючи один із безкоштовних продуктів, перелічених у Best Free Minimizer .

Остання ідея в порядку відчаю - це зруйнувати робочий стіл практично за допомогою виробів, таких як Настільні ПК або Dexpot , і зробити свою роботу на іншому робочому столі, ніж за замовчуванням.

[EDIT]

Коли Microsoft вийшла з галереї архівів, ось відтворений вище код VB:

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
"якщо це вікно заблоковано, ви ніколи не зрозумієте, чому копія ніколи не припиняється" Це неправда. Правильна поведінка полягає в тому, щоб сповістити користувача про миготливу піктограму панелі завдань (або, можливо, спливаюче вікно на повітряній кулі чи сповіщення про тостер або щось подібне). Перерваючи користувача у вікно, яке перехоплює його натискання клавіш, означає, що вони скажуть антивірусному програмному забезпеченню вчинити одну чи іншу дію навмання. Однозначно не гарний спосіб робити речі.
ендоліт

1
"якщо це вікно заблоковано, ви ніколи не зрозумієте, чому копія ніколи не припиняється" Це неправда. Правильна поведінка полягає в повідомленні користувача миготливою піктограмою панелі завдань ... Були випадки, коли я натискав кнопку чи щось у запущеній програмі, що спричиняє створення нового діалогового діалогу (наприклад, відкритий файл ), але потім Я переключаюсь на іншу програму до того, як буде створено діалог. У результаті діалогове вікно приховано, а іншу програму неможливо переключити, і діалог не можна відхилити. Ні його кнопка панелі завдань, ні Alt-Tabпрацює; лише примушуючи діалогове вікно вперед.
Synetech

1
@Synetech: Іноді єдине рішення діалогового діалогу, що не має фронту, - це вбити завдання. Алгоритми фокусування в Windows справді хитрі.
harrymc

2
@harrymc, мені ніколи не доведеться вдаватися до вбивства одного з додатків. Я просто запускаю свою програму, що керує вікнами ( WinSpy ++ виконує трюк просто чудово) і приховую вікно спереду, тоді я можу відхилити діалогове вікно застряглого назад, а потім повторно показати приховане вікно. Це не зручно, але це краще, ніж вбивати будь-який з процесів.
Synetech

1
@harrymc, не дуже; вбивство програми та втрата речей просто приносить більше пари, і якщо це модальне діалогове вікно (яке блокує батьківське вікно і не має кнопки панелі завдань), воно не відображатиметься у Alt+Tabсписку, і, на мій досвід, вікно, яке має відкрите модальне діалогове вікно не завжди (ніколи?) не відображає модальне діалогове вікно Alt+Tab, особливо якщо діалог ніколи не мав змін, щоб отримати фокус. :-|
Synetech

2

У Ghacks можливе рішення:

Кілька разів на день трапляється, що деякі програми крадуть фокус активного вікна, вискакуючи. Це може статися з кількох причин, коли, наприклад, я витягую файли або закінчується передача. Це не має значення в більшості випадків, коли це трапляється, але іноді я пишу статтю, і це не означає, що мені потрібно знову набрати деякі слова, але і те, що я втратив зосередженість і потрібно натиснути, щоб повернути фокус.

На веб-сайті Pro Reviewer є порада, як не допустити цього. Найпростіший спосіб запобігти крадіжці фокусу - це використовувати інтерфейс Tweak, який має налаштування, яке називається "Захистити програми від крадіжки фокусу". Перевірка цієї опції запобігає появі інших програм раптово і вкраде фокус вікна, в якому ви зараз працюєте.

Це працює лише тоді, коли програма була мінімізована раніше. Замість того, щоб красти фокус, він буде блимати кілька разів, що можна визначити в одному меню в Tweak UI . Якщо ви не хочете використовувати інтерфейс Tweak, ви можете змінити налаштування в Реєстрі Windows.

Перейдіть до ключа реєстру HKEY_CURRENT_USER> Панель управління> Робочий стіл та змініть значення ForegroundLockTimeout на 30d40 (шістнадцятковий) або 200000 (десяткове). Ключ ForeGroundFlashCount визначає кількість спалахів вікна, щоб сповістити користувача, де 0 означає необмежену кількість.


20
Це не працює в будь-якій ОС після XP. Це значення реєстру вже встановлено на це (я вважаю, що за замовчуванням я вважаю) і не працює.
EndangeredMassa

1
Лише на друге, що я перебуваю в Windows 7 (64-розрядному), відчуваю крадіжку фокусу (VS 2012, коли нарешті активний, наприклад), і вищенаведена пропозиція щодо реєстру вже є. Технічне підтвердження у цій відповіді: superuser.com/a/403554/972
Michael Paulukonis

2

Надихнувшись відповіддю Дер Хохстаплера , я вирішив написати інжектор DLL, який працює як з 64, так і з 32-бітними процесами і запобігає крадіжці фокусів у Windows 7 або новіших версіях: https://blade.sk/stay-focused/

Як це працює, він спостерігає за новоствореними вікнами (використовуючи SetWinEventHook) та вводить DLL, дуже схожий на той, який використовується у Der Hochstapler, якщо він ще не присутній. Він вивантажує DLL та відновлює оригінальну функціональність при виході.

З мого тестування, це працює дуже добре поки що. Однак проблема, схоже, заглиблюється не лише в те, що телефонує програма SetForegroundWindow. Наприклад, коли створюється нове вікно, воно автоматично виводиться на передній план, що також заважає користувачеві вводити інше вікно.

Для вирішення інших методів фокусування крадіжок потрібно більше тестування, і я буду вдячний за будь-який відгук про сценарії, де це відбувається.


0

Я зрозумів, як запобігти миготіння недавно активованого цільового вікна TaskBar після того, як ви програмно активуєте, максимізуєте та фокусуєте головне вікно цього процесу з іншого процесу. Перш за все, існує маса обмежень щодо того, чи буде дозволена ця операція.

"Система обмежує, які процеси можуть встановлювати вікно переднього плану. Процес може встановлювати вікно переднього плану лише за умови виконання однієї з таких умов:

  • Процес - це процес переднього плану.
  • Процес розпочався процесом переднього плану.
  • Процес отримав останню подію введення.
  • Процесу переднього плану немає.
  • Процес переднього плану налагоджується.
  • Передній план не заблокований (див. LockSetForegroundWindow).
  • Термін очікування блокування переднього плану закінчився (див. SPI_GETFOREGROUNDLOCKTIMEOUT в SystemParametersInfo).
  • Жодне меню не активне.

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

Отже, якщо процес контролю знаходиться на передньому плані, він може тимчасово дозволити іншому процесу повністю викрасти передній план, зателефонувавши AllowSetForegroundWindow з ідентифікатором процесу цільового процесу. Потім після цього цільовий процес може викликати SetForegroundWindow сам, використовуючи власну ручку вікна, і він буде працювати.

Очевидно, що для цього потрібна певна координація між двома процесами, але це працює, і якщо ви робите це для того, щоб реалізувати додаток для одного примірника, який перенаправляє всі запуски, що натискають на Explorer, у існуючий екземпляр програми, тоді ви вже будете мати (наприклад) трубу з назвою, щоб все одно координувати речі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.