Как в диалоговом окне "Безопасное извлечение оборудования" Windows появляется "передняя любовь"?
Раймонд рассказал о, как программы могут получить/украсть "переднюю любовь", используя RegisterHotkey
, который при вызове передаст переднюю часть ваше приложение.
Попытки сделать это вручную терпят неудачу (например, используя SetForegroundWindow
, SwitchToWindow
и т.д.), потому что приложения не могут украсть фокус у пользователя (так, чтобы нажатия клавиш не попадали в неправильное место).
Беда в том, что сегодня я заметил что-то странное:
-
Я пытаюсь безопасно удалить внешний диск.
-
Существует пауза в 7 секунд.
-
Во время паузы я активно печатаю внутри окна.
-
Внезапно окно с сообщениями перечеркивает переднюю часть моего приложения, и вместо этого я печатаю текст в поле сообщения.
Понятно, что это не использует механизм горячих клавиш - и все же Windows смогла украсть фокус из моего приложения.
Я действительно сомневаюсь, что есть что-то вроде "бэкдора", используемого только для этой конкретной цели (хотя, пожалуйста, поправьте меня, если я ошибаюсь), поэтому, предполагая, что это не так, должен быть способ сделайте это правильно, не используя механизм горячих клавиш.
Итак, вопрос в том, как это достигается?
Примечание:
Ганс отметил, что "бэкдор" -
AttachInputThread
, но я не совсем уверен, что здесь происходит, тем более что
Раймонд говорит, что метод может вызвать зависания. Идеи?
Ответы
Ответ 1
Я экспериментировал, и из того, что я вижу, это происходит тогда и только тогда, когда новое окно принадлежит Windows Explorer. Например, некоторые панели управления реализованы в проводнике Explorer или как плагины Explorer. Я мог бы наиболее легко воспроизвести его, открыв Action Center из меню "Пуск" (с меню "Пуск", настроенным для отображения элементов панели управления в меню).
Я подозреваю, что это поведение является следствием того, что Windows Explorer владеет окном рабочего стола, которое GUI рассматривает как частный случай.
Единственное, что странно, это то, что я не мог воспроизвести это поведение с помощью диалогового окна USB, о котором вы говорите, который (когда я его пробовал) генерировал отдельный процесс (экземпляр rundll32.exe). Это может зависеть от других факторов.
Ответ 2
Я не могу придумать способ протестировать это, что не сложнее, чем у меня есть время прямо сейчас, но внимательно посмотрев на документы SetForegroundWindow, http://msdn.microsoft.com/en-us/library/ms633539(VS.85).aspx, одним из условий, перечисленных в примечаниях относительно процессов, которые могут задавать передний план, является:
- Процесс получил последнее событие ввода.
Если я ошибаюсь, проводник Windows получает все входные события, чтобы проверять наличие горячих клавиш, другие такие нажатия клавиш фокуса и щелчки мыши за пределами текущего окна и т.д.
Из-за того, что его постоянное состояние "получил последнее входное событие", Explorer квалифицируется как что-то, что может установить приоритет, и поэтому может привести к тому, что MessageBox будет показано, что он станет приоритетом без каких-либо специальных функций или недокументированных действий.
Ответ 3
Как уже упоминалось, ввод окон разных потоков обрабатывается независимо. AttachThreadInput
API позволяет обмениваться состояниями потоков, в частности:
Используя функцию AttachThreadInput, поток может присоединить свой вход обрабатывающий механизм к другому потоку. [...] Это также позволяет потокам для совместного использования своих входных состояний, поэтому они могут вызывать функцию SetFocus для установите фокус клавиатуры в окно другого потока.
Теперь, когда вы видите, какое окно в настоящее время находится на переднем плане, если вы делитесь своим состоянием потока с потоком окна переднего плана, ваш SetFocus
будет украсть фокус оттуда.
CWindow Window = GetForegroundWindow();
if(Window)
{
const DWORD nWindowThreadIdentifier = Window.GetWindowThreadID();
const DWORD nThreadIdentifier = GetCurrentThreadId();
AttachThreadInput(nThreadIdentifier, nWindowThreadIdentifier, TRUE);
GetDlgItem(IDC_EDIT).SetFocus(); // This succeeds now as we are sharing thread state with foreground window
AttachThreadInput(nThreadIdentifier, nWindowThreadIdentifier, FALSE);
m_sAction = _T("Done");
} else
m_sAction = _T("Nothing to do");
Смотрите также: фрагмент исходного кода, binary