Предотвращение кражи приложений

191

Есть ли какие-нибудь решения для предотвращения кражи приложений из активного окна?

Это особенно раздражает, когда я запускаю приложение, переключаюсь на что-то еще, и новое приложение начинает получать половину предложения текста.

svandragt
источник
9
@ Ivo Windows 7 в моем случае, но я думаю, что для SuperUser все версии Windows были бы актуальны
svandragt
3
Модератор объединил этот вопрос: superuser.com/questions/199821/… с текущим. Это неправильно, ответ на текущий вопрос не относится к Windows 7, поэтому его не следует объединять. До сих пор я не мог найти решение этой проблемы в Windows 7
Alex Angelico
17
Это одна из моих любимых мозолей номер один с каждым графическим интерфейсом, который я когда-либо использовал. Вы печатаете и раздаете, какое-то громкое диалоговое окно крадет фокус, и половина ваших нажатий клавиш куда-то еще. Вы могли бы подумать, что разработчики оконных систем выяснили бы это десятилетия назад. Если в окне есть активность, отложите показ нового окна. Например, ничего не всплывать в GUI до трех или четырех секунд с момента последнего нажатия кнопки или нажатия клавиши в текущем окне. Doh!
Каз
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

Der Hochstapler
источник
Как вы думаете, это ваше решение может быть перенесено на Java? Я искал и задавал вопросы, но ничего не нашел. Может быть, я мог бы импортировать саму библиотеку хуков в Java, используя jne?
Томаш Зато
@ TomášZato: Понятия не имею. Я не активно использую этот код сам.
Der Hochstapler
Я пытаюсь скомпилировать его как минимум на C ++ (а затем внедрить / удалить скомпилированную DLL из Java). Но это тоже не слишком хорошо. Я не хочу обсуждать это здесь в комментариях, но если бы вы могли реально помочь мне заставить его работать, я был бы очень изящен! Я создал чат, если я получу это на работу, я оставлю
Tomáš
23

В Windows 7 ForegroundLockTimeoutзапись реестра больше не проверяется, это можно проверить с помощью Process Monitor. Фактически, в Windows 7 они запрещают вам изменять окно переднего плана. Пойдите и прочитайте о его деталях , это было даже там с Windows 2000.

Тем не менее, документация отстой, и они преследуют друг друга и находят способы обойти это .

Итак, происходит что-то глючное SetForegroundWindowили похожие API-функции ...

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

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

Тамара Вийсман
источник
3
У кого-нибудь есть воспроизводимый вариант использования этого в Windows 7? Учитывая, что люди скорее испытывают противоположное (например, я часто нахожу требовательную Windows скрытой за моим текущим окном), и что я еще не видел, как это происходит в Windows 7, было бы довольно неприятно написать приложение, но не смогу Проверь это. Более того, поскольку Microsoft заявляет, что это больше не должно происходить с Windows 7. В лучшем случае люди обнаружили, что она может переключить фокус клавиатуры только случайно, этот вызов API это исправит, но я не знаю, как проверить, работает ли она на самом деле. .
Тамара Wijsman
1
Установщик (на основе InnoSetup) запускает другие процессы и, возможно, другие (скрытые) установки, но я не знаю, на каком создателе установки они основаны.
Даниэль Бек
6
@ TomWijsman: Откройте regedit, найдите случайный текст, который не будет найден. Зайдите в другое приложение и начните печатать. Когда поиск будет завершен, regedit украдет фокус.
эндолит
1
@endolith: Невозможно воспроизвести, используя Windows 8 Replase Preview здесь. Какую ОС вы используете? В моем случае это просто подсвечивает приложение внизу, но совсем не
мешает
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 \ Панель управления \ Рабочий стол \ 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, который определяет, кто ворует фокус , который автор использовал для определения виновника как средства обновления «call home» для программного обеспечения принтера.

Отчаянная мера, когда все остальное терпит неудачу, и если вы определили это плохо запрограммированное приложение, состоит в том, чтобы свести его к минимуму и надеяться, что тогда он не окажется на переднем плане. Более сильная форма минимизации - использование лотка с помощью одного из бесплатных продуктов, перечисленных в Best Free Application Minimizer .

Последняя идея в порядке отчаяния состоит в том, чтобы виртуально сломать ваш рабочий стол, используя такой продукт, как Desktops или Dexpot , и выполнять свою работу на другом рабочем столе, чем по умолчанию.

[РЕДАКТИРОВАТЬ]

Когда 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
harrymc
источник
48
«Если это окно заблокировано, вы никогда не поймете, почему копия никогда не заканчивается». Это не правда. Правильное поведение - уведомлять пользователя мигающим значком на панели задач (или, возможно, всплывающим окном или уведомлением о тостере или чем-то еще). Если пользователь прерывает окно, которое перехватывает его нажатие, это означает, что антивирусное программное обеспечение сообщает случайному или иному действию. Определенно не хороший способ сделать что-то.
эндолит
1
«Если это окно заблокировано, вы никогда не поймете, почему копия никогда не заканчивается». Это не правда. Правильное поведение - уведомлять пользователя мигающим значком на панели задач ... Были случаи, когда я нажимал кнопку или что-то в работающей программе, что приводило к созданию нового модального диалога (например, открытия файла ), но затем Я переключаюсь на другую программу перед созданием диалога. В результате диалоговое окно скрыто, и к другой программе нельзя переключиться, и диалоговое окно нельзя закрыть. Ни его кнопка на панели задач, ни Alt-Tabработает; только заставляя диалоговое окно вперед.
Synetech
1
@Synetech: Иногда единственное решение для внешнего интерфейса - убить задачу. Алгоритмы фокусировки в Windows действительно паршивые.
Harrymc
2
@harrymc, мне никогда не придется прибегать к убийству одного из приложений. Я просто запускаю свою программу управления окнами ( WinSpy ++ отлично справляется с задачей ) и скрываю окно впереди, затем я могу закрыть диалоговое окно с задержкой, а затем снова показать скрытое окно. Это не удобно, но это лучше, чем убивать любой из процессов.
Synetech
1
@harrymc, не совсем; убийство приложения и потеря материала просто делают больше, и если это модальное диалоговое окно (которое блокирует родительское окно и не имеет кнопки панели задач), то оно не появится в Alt+Tabсписке, и, по моему опыту, окно, которое если модальное диалоговое окно открыто, не всегда (никогда?) не отображается модальное диалоговое окно Alt+Tab, особенно если в диалоге никогда не было изменений, чтобы получить фокус. :-|
Synetech
2

У Гакса есть возможное решение:

Несколько раз в день некоторые приложения крадут фокус активного окна, всплывая. Это может произойти по ряду причин, например, когда я извлекаю файлы или передача завершается. В большинстве случаев это не имеет значения, но иногда я пишу статью, и это означает не только то, что я должен снова набрать несколько слов, но и то, что я потерял концентрацию и должен нажать, чтобы восстановить фокус.

На сайте Pro Reviewer есть совет, как этого избежать. Самый простой способ предотвратить кражу фокуса - использовать Tweak UI с настройкой, которая называется «Запретить краже приложений». Установка этой опции предотвращает внезапное появление других приложений и захватывает фокус окна, в котором вы сейчас работаете.

Это работает только тогда, когда приложение было свернуто ранее. Вместо кражи фокуса он будет мигать несколько раз, что можно определить в том же меню в пользовательском интерфейсе Tweak . Если вы не хотите использовать Tweak UI, вы можете изменить настройки в реестре Windows.

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

Иво Флипс
источник
20
Это не работает на любой ОС после XP. Это значение реестра уже установлено (по-моему, по умолчанию) и не работает в любом случае.
EndangeredMassa
1
Во-вторых, я нахожусь в Windows 7 (64-разрядная версия), у меня происходит кража фокуса (VS 2012, когда наконец-то активен, например), и предложенное выше предложение реестра уже на месте. Техническое подтверждение в этом ответе: superuser.com/a/403554/972
Майкл Полуконис,
2

Вдохновленный ответом Der Hochstapler , я решил написать DLL-инжектор, который работает как с 64, так и с 32-разрядными процессами и предотвращает кражу фокуса в Windows 7 или новее: https://blade.sk/stay-focused/

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

Судя по моим тестам, пока все работает очень хорошо. Тем не менее, проблема, кажется, идет глубже, чем просто вызов приложений SetForegroundWindow. Например, когда создается новое окно, оно автоматически выводится на передний план, что также мешает пользователю печатать в другом окне.

Чтобы иметь дело с другими методами кражи фокуса, требуется дополнительное тестирование, и я буду признателен за любые отзывы о сценариях, где это происходит.

лопасть
источник
0

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

«Система ограничивает, какие процессы могут устанавливать окно переднего плана. Процесс может устанавливать окно переднего плана, только если выполняется одно из следующих условий:

  • Процесс является приоритетным процессом.
  • Процесс был начат процессом переднего плана.
  • Процесс получил последнее входное событие.
  • Там нет переднего плана процесса.
  • Процесс переднего плана отлаживается.
  • Передний план не заблокирован (см. LockSetForegroundWindow).
  • Истекло время ожидания блокировки переднего плана (см. SPI_GETFOREGROUNDLOCKTIMEOUT в SystemParametersInfo).
  • Нет активных меню.

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

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

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

Гленн Слэйден
источник