it-swarm.xyz

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

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

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

189
svandragt

Это невозможно без обширного манипулирования внутренними компонентами Windows, и вам нужно преодолеть это.

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

Не каждый разработчик принимает правильные решения, когда речь заходит об этой теме.

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

Некоторое время назад я провел обширные исследования по решению этой проблемы раз и навсегда (и не смог). Результат моего исследования можно найти на странице проекта annoyance .

Проект также включает в себя приложение, которое неоднократно пытается захватить фокус, вызывая:

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

50
Der Hochstapler

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

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

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

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

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

23
Tom Wijsman

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

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

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

18
Simon P Stevens

Я полагаю, что некоторая путаница может существовать, так как есть два способа «украсть фокус»: (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
14
harrymc

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

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

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

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

2
blade

Ghacks имеет возможное решение:

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

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

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

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

2
Ivo Flipse