181

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

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

6 ответов6

46

Это невозможно без интенсивных манипуляций с внутренними компонентами 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

23

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

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

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

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

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

18

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

Это война вооружений, поэтому я не знаю, работает ли она на все.

Обновление: Согласно EndangeredMassa, TweakUI не работает в Windows 7.

14

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

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

Дискуссия должна быть разделена между XP и 7.

Windows XP

В XP есть взлом реестра, который заставляет XP работать так же, как Windows 7 в предотвращении кражи приложений:

  1. Используйте regedit, чтобы перейти к: 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
2

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

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

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

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

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

1

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

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

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

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .