Как заставить мое приложение выйти на передний план и получить фокус?

Я работаю над приложением, которое является начальной загрузкой для установщика, над которым я также работаю. Приложение делает несколько вызовов MSI, чтобы получить информацию, необходимую мне для сборки мастера, который является главным окном моего приложения, что вызывает открытие окна прогресса во время сбора информации, а затем исчезает после того, как это будет сделано. Затем мастер настраивается и запускается. Моя проблема в том, что мастер (производный от CPropertySheet) не хочет выходить на передний план и быть активным приложением, если я не добавляю для этого некоторые вызовы.

Я решил проблему вывода его на передний план с помощью следующего кода в моем методе OnInitDialog():

SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); // force window to top
SetWindowPos(&wndNoTopMost, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); // lose the topmost status that the previous line gave us

Моя проблема в том, что я до сих пор не понял, как заставить окно самоактивироваться (т. е. сделать себя тем, на котором находится фокус). SetFocus() не будет работать в этом контексте. Мне нужно что-то, что переместит окно в верхнюю часть Z-порядка и активирует его, желательно за как можно меньшее количество вызовов.

Я предполагаю, что окно прогресса, открытое в начале вызовами MSI, вызывает сбои в главном окне, но у меня нет способа предотвратить появление этого окна. Кроме того, не имеет смысла скрывать его, потому что он позволяет пользователю узнать, что происходит, до появления главного окна.


person RobH    schedule 27.03.2009    source источник


Ответы (5)


Андрей не совсем прав. Windows очень старается помешать вам украсть фокус, но это возможно с помощью следующего метода.

  1. Присоединиться к потоку окна, которое в данный момент имеет фокус.
  2. Поместите свое окно в фокус.
  3. Отделить от нити.

И код для этого будет выглядеть примерно так:

DWORD dwCurrentThread = GetCurrentThreadId();
DWORD dwFGThread      = GetWindowThreadProcessId(GetForegroundWindow(), NULL);


AttachThreadInput(dwCurrentThread, dwFGThread, TRUE);

// Possible actions you may wan to bring the window into focus.
SetForegroundWindow(hwnd);
SetCapture(hwnd);
SetFocus(hwnd);
SetActiveWindow(hwnd);
EnableWindow(hwnd, TRUE);

AttachThreadInput(dwCurrentThread, dwFGThread, FALSE);

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

person Caleb Bartholomew    schedule 19.12.2013
comment
Это не работает в Windows 10 1703 (обновление Creators) - person Robin Andersson; 13.10.2017

Вы не можете украсть фокус. Период.

См. эту статью «Старая новая вещь»:

https://web.archive.org/web/20190209213353/https://blogs.msdn.microsoft.com/oldnewthing/20090220-00/?p=19083

person nobody    schedule 27.03.2009
comment
Спасибо, я искал эту статью как ответ на этот вопрос. Пожалуйста, все, кто читает это, усвойте это и исправьте вирусный мем, который пытается украсть активацию переднего плана. Но мое приложение особенное! Ваше приложение враждебно пользователю. Не делай этого! - person 1800 INFORMATION; 27.03.2009
comment
Я согласен, и хотя, скорее всего, это относится к автору, у него (или у кого-то еще) может быть веская причина нуждаться в этой функции (закрытая система, только частный или домашний компьютер). Дайте рекомендации по использованию, но не скрывайте информацию. - person Arnold Spence; 27.03.2009
comment
Я надеюсь, что Microsoft когда-нибудь наконец исправит все случаи кражи фокуса раз и навсегда. Программистов, занимающихся кражей фокуса, надо отправлять в Сибирь. Голый. - person snemarch; 27.03.2009
comment
Моя причина кражи фокуса заключается в том, что пользователь только что запустил приложение, оно запускает окно, которое получает фокус, это окно исчезает, и приложение запускает другое окно, которое не получает фокус. Это заставляет пользователя чесать затылок, если я это не исправлю. - person RobH; 27.03.2009
comment
Эта статья бесполезна для меня, потому что я не запускаю второй экземпляр того же приложения. Мое приложение (точнее, MSI вызывает то, что вызывает мое приложение) запускает окно и затем закрывает его, а затем мое приложение запускает свое главное окно. Он не фокусируется, но должен. - person RobH; 27.03.2009
comment
Похоже, что это применимо точно к вашей ситуации. У вас есть процесс A, запускающий процесс B, и вы хотите, чтобы процесс B получил фокус. Решение, как поясняется в статье, состоит в том, что процесс A должен передавать фокус процессу B — процесс B не может взять фокус на себя. - person nobody; 28.03.2009
comment
Нет, моя программа вызывает функции MSI, которые в конечном итоге открывают окно прогресса (которое получает фокус). Последний вызов функции MSI закрывает окно, а затем мое приложение открывает главное окно (которое не получает фокуса). Все это происходит в одном и том же процессе. - person RobH; 30.03.2009
comment
Для тех, кто последует: ситуация Роба ТОЧНО, почему вы не должны красть фокус. Если я запускаю приложение, которое запускает какой-то процесс, вы можете поспорить, что я набираю электронное письмо, ожидая его загрузки. Теперь приходит ваше приложение для кражи фокуса, которое, скорее всего, спрашивает, можно ли что-то сделать, в то время как я нажимаю Enter.. Просто не делайте этого. - person NotMe; 10.05.2011

не работает ShowWindow(youwindow,SW_SHOWNORMAL)? -Дон

person Don Dickinson    schedule 27.03.2009
comment
Не пробовал, но предполагаю, что это не более эффективно, чем BringWindowToTop(). (См. мой комментарий к ответу Митча Уита.) - person RobH; 27.03.2009

Вы обнаружите, что BringWindowToTop или SetForegroundWindow имеют требования, которые должны быть выполнены, прежде чем окно будет фактически принудительно перемещено на передний план по сравнению со всеми другими окнами (приложениями). Если они не выполняются, Windows будет мигать только значком приложения на панели задач. В этой статье представлен способ обойти это, но, как указывает 1800 INFORMATION, это не рекомендуется. Думаю, вам просто придется это принять.

person Arnold Spence    schedule 27.03.2009
comment
Не делай этого. Рэймонд Чен объясняет, почему это часто приводит к зависанию вашего приложения: блоги. msdn.com/oldnewthing/archive/2008/08/01/8795860.aspx - person 1800 INFORMATION; 27.03.2009

ЕСТЬ веские причины, по которым приложение может «украсть» фокус. Мое приложение представляет собой сервер, загружающий множество библиотек DLL драйверов. Другое приложение, подключающееся к серверу, имеет кнопку, которая отправляет сообщение на сервер для отображения подробной информации в одной из этих библиотек DLL (принадлежит серверу, а не клиентскому приложению) для удобства. К сожалению, это открытое окно обычно скрыто под несколькими окнами.

person Lee Davis    schedule 19.02.2021