Как вывести мое приложение на передний план?

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

(для любопытных: я запускаю модульные тесты в NetBeans, которые генерируют файл журнала. Когда мое фоновое приложение видит изменение временной метки файла журнала, я хочу, чтобы оно проанализировало файл журнала и вышло на передний план, чтобы показать результаты).

Этот вопрос не помог и не гуглить. Кажется, что BringToFront() уже давно не работает, и я не могу найти альтернативу.

Любые идеи?


person Mawg says reinstate Monica    schedule 18.10.2012    source источник
comment
Вы не можете этого сделать. В Windows XP было обходное решение, позволяющее это сделать; версии с тех пор запрещают это.   -  person Ken White    schedule 18.10.2012
comment
+1 @ken Если ты уверен на 100%, напиши ответ и я наградю. Я надеялся на какой-нибудь непонятный трюк, такой как сжатие в системный трей, а затем восстановление или скрытие, а затем снова отображение ...   -  person Mawg says reinstate Monica    schedule 18.10.2012
comment
если вы просто хотите продвинуть свое приложение вперед, но не украсть фокус ввода, вы можете использовать: if IsIconic(Application.MainForm.Handle) then ShowWindow(Handle, SW_RESTORE); Стиль формы := fsStayOnTop; FormStyle := fsNormal; Сначала он проверяет, свернуто ли приложение и восстанавливается, затем устанавливает стиль формы, чтобы он оставался сверху, и возвращается к нормальному состоянию.   -  person A Lombardo    schedule 18.10.2012
comment
@Mawg ты проверял мое решение?   -  person whosrdaddy    schedule 18.10.2012
comment
@whosrdaddy Да, ваше решение работает - для меня (и мне этого достаточно). Как утверждает Кен, это может не сработать для других. Я хотел бы наградить вас ответом, но я думаю, что Кен ответил на вопрос, который на самом деле был задан, даже если вы дали мне решение, которое я лично хотел. Я надеюсь, что вы не против. Я добавлю +1 ко всему, что вы разместили на этот вопрос.   -  person Mawg says reinstate Monica    schedule 20.10.2012
comment
@Mawg - Из любопытства, вы когда-нибудь тестировали решение в моем посте?   -  person Sertac Akyuz    schedule 24.10.2012
comment
+1 за то, что дал мне -1, @SertacAkyuz. Я извиняюсь за то, что потратил ваше время (на самом деле то, что вы опубликовали, сработало), но я уже наградил ответ, когда вы опубликовали, и он был правильным (вы не можете сделать это надежно). Что почувствует Кен, если я уберу ответ? Это был трудный звонок, и я собирался кого-то разозлить. Мне жаль, что это был ты. Вы очень помогли мне по другим вопросам. Сожалею об этом.   -  person Mawg says reinstate Monica    schedule 30.10.2012
comment
@Mawg - речь идет не о принятии (я бы не стал публиковать ответ на уже отвеченный вопрос, если бы он был), а об обратной связи - просто да, это работает или нет, это не работает. Вышеупомянутый комментарий/вопрос был не первым, его предшественник оставался там несколько дней, затем я удалил его и задал другой, когда был уверен, что вы онлайн (и этот тоже оставался там более пяти дней).   -  person Sertac Akyuz    schedule 30.10.2012


Ответы (7)


Вы не можете сделать это надежно. В Windows XP был обходной путь, который разрешал это, но версии с тех пор по большей части запрещали его (см. примечание ниже). Об этом прямо указано в разделе примечаний документации MSDN по адресу SetForegroundWindow:

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

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

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

Обратите внимание на последний абзац цитируемой документации, особенно на первое предложение.

Проблема с

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

заключается в том, что любое приложение может попытаться сделать то же самое. Как «исключительно личное использование» говорит о том, что это вы лично, а не просто какое-либо приложение? :-)

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

person Ken White    schedule 18.10.2012
comment
+1 (и за ваш комментарий выше ;-) Очевидно, я буду жить надеждой и оставлю это открытым на несколько часов до награждения. Как я уже сказал, я надеялся на какой-нибудь непонятный трюк, такой как сжатие в системный трей, а затем восстановление или скрытие, а затем снова отображение - но ничего из того, что я пробовал, не работает :-( Это много работы, просто чтобы избежать Alt-Tab (хм, может быть, если бы у меня было приложение запуска, которое затем запускало бы приложение процесса. Запустилось бы новое приложение на переднем плане?) - person Mawg says reinstate Monica; 18.10.2012
comment
Вы можете использовать стиль AlwaysOnTop, не то чтобы он был дружественным к другим приложениям, но привлекал бы внимание пользователя. Хотя, возможно, некоторые специальные службы оповещения, такие как Growl/Snarl, лучше подходят для этого. - person Arioch 'The; 18.10.2012

Вот что-то простое, что, кажется, работает, протестировано с несколькими коробками, состоящими из XP, Server2003, Vista, Server2008, W7. Тестовое приложение запускалось со стандартной (или администраторской) учетной записью, украл фокус ввода из блокнота при записи на переднем плане.

var
  Input: TInput;
begin
  ZeroMemory(@Input, SizeOf(Input));
  SendInput(1, Input, SizeOf(Input)); // don't send anyting actually to another app..
  SetForegroundWindow(Handle);

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

person Sertac Akyuz    schedule 18.10.2012
comment
Спасибо! Работает и в Windows 8.1. - person Albert Wiersch; 16.10.2015
comment
Хорошо работает, когда окно скрыто, но не восстанавливает окно, которое было свернуто на панели задач. - person AlainD; 15.12.2020

Я делаю что-то подобное в одном из своих приложений, и эта функция у меня работает в xp/vista/w7:

function ForceForegroundWindow(hwnd: THandle): Boolean;
const
  SPI_GETFOREGROUNDLOCKTIMEOUT = $2000;
  SPI_SETFOREGROUNDLOCKTIMEOUT = $2001;
var
  ForegroundThreadID: DWORD;
  ThisThreadID: DWORD;
  timeout: DWORD;
begin
  if IsIconic(hwnd) then ShowWindow(hwnd, SW_RESTORE);

  if GetForegroundWindow = hwnd then Result := True
  else
  begin
    // Windows 98/2000 doesn't want to foreground a window when some other
    // window has keyboard focus

    if ((Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion > 4)) or
      ((Win32Platform = VER_PLATFORM_WIN32_WINDOWS) and
      ((Win32MajorVersion > 4) or ((Win32MajorVersion = 4) and
      (Win32MinorVersion > 0)))) then
    begin
      Result := False;
      ForegroundThreadID := GetWindowThreadProcessID(GetForegroundWindow, nil);
      ThisThreadID := GetWindowThreadPRocessId(hwnd, nil);
      if AttachThreadInput(ThisThreadID, ForegroundThreadID, True) then
      begin
        BringWindowToTop(hwnd); // IE 5.5 related hack
        SetForegroundWindow(hwnd);
        AttachThreadInput(ThisThreadID, ForegroundThreadID, False);
        Result := (GetForegroundWindow = hwnd);
      end;
      if not Result then
      begin
        // Code by Daniel P. Stasinski
        SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, @timeout, 0);
        SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(0),
          SPIF_SENDCHANGE);
        BringWindowToTop(hwnd); // IE 5.5 related hack
        SetForegroundWindow(hWnd);
        SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(timeout), SPIF_SENDCHANGE);
      end;
    end
    else
    begin
      BringWindowToTop(hwnd); // IE 5.5 related hack
      SetForegroundWindow(hwnd);
    end;

    Result := (GetForegroundWindow = hwnd);
  end;
end;

Другое решение — не красть фокус, а просто поместить окно TOPMOST в z-буфер:

procedure ForceForegroundNoActivate(hWnd : THandle);

begin
 if IsIconic(Application.Handle) then
  ShowWindow(Application.Handle, SW_SHOWNOACTIVATE);
 SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOACTIVATE or SWP_NOMOVE);
 SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOACTIVATE or SWP_NOMOVE);
end;
person whosrdaddy    schedule 18.10.2012
comment
Это не работает на Win7, работающей от имени администратора, и не работает должным образом на машине, работающей в домене Win2008, поэтому оно всегда не работает, поэтому я дал ответ I сделал. :-) Конечно, что я (и создатели самой операционной системы) знаю? - person Ken White; 18.10.2012
comment
Этот и подобные подходы никогда не работали для меня, когда мое приложение не было активным. - person Erik Virtel; 18.10.2012
comment
@KenWhite, эта функция используется в приложении для конечных пользователей, и у них есть сочетание XP и W7 (UAC включен), и у меня не было никаких проблем в ограниченных средах. - person whosrdaddy; 18.10.2012
comment
меня вот что смущает: SystemParametersInfo(SPI_SET.... Это хорошая идея, чтобы установить общесистемные параметры? будет ли это работать даже для обычного пользователя в среде UAC? - person kobik; 18.10.2012
comment
@kobik: SPI_SETFOREGROUNDLOCKTIMEOUT будет работать до тех пор, пока у вызывающего потока есть права на установку окна переднего плана. Я не думаю, что UAC влияет на это. - person Remy Lebeau; 18.10.2012
comment
Кстати, третий параметр SystemParametersInfo() — это Pointer, поэтому код должен использовать nil вместо TObject(0). - person Remy Lebeau; 18.10.2012
comment
Я голосую за этот ответ, если кто-то еще не может доказать, что этот код не работает (я сам не проверял это). @Ken, ты действительно тестировал этот код с Win7, работающим без прав администратора? - person kobik; 19.10.2012
comment
@kobik: Да, и это не работает надежно, как я сказал в первых пяти словах моего собственного ответа. Я не отрицал этот ответ; Я просто прокомментировал, что это не будет работать должным образом в некоторых версиях Windows с некоторыми ограничениями доступа, и что MS говорит, что это невозможно сделать в документации API (которую я также связал и процитировал в своем ответе). - person Ken White; 19.10.2012
comment
@ken, извините за назойливость, но ранее вы заявляли, что это не работает на Win7, работающей без прав администратора. теперь вы говорите надежно. что ты имеешь в виду под надежно? разве это не выводит окно на передний план? или это не соответствует или что? - person kobik; 19.10.2012
comment
@kobik: Ненадежно означает именно это - это может работать, а может и не работать. На моем не работает. Кроме того (как я уже говорил трижды), Microsoft говорит, что это не будет работать, кроме как при определенных условиях. - person Ken White; 19.10.2012
comment
БОЛЬШОЙ лайк тебе! - person SnowStorm; 01.05.2020
comment
К сожалению, это не работает в Windows 8.1 и Windows 10... см. последнюю документацию, указанную в ответе Кена Уайта. - person AlainD; 15.12.2020

Предполагая, что другие приложения StayOnTop не запущены, вы можете временно установить для своих форм FormStyle значение fsStayOnTop, а затем выполнить восстановление приложения и BringToFront, а затем изменить стиль формы обратно на то, что было.

tmpFormStyle := Application.MainForm.FormStyle;
Application.MainForm.FormStyle := fsStayOnTop;
Application.Restore; // optional
Application.BringToFront;
Application.MainForm.FormStyle := tmpFormStyle;

Опять же, это работает только в том случае, если нет других приложений Stayontop.

person Toby    schedule 11.12.2015

Это должно работать даже в ОС Windows7+8. Единственным требованием является то, что само приложение может вызывать функции. Сложнее использовать внешнее приложение для установки фронта окна другого процесса.

Application.Restore; // unminimize window, makes no harm always call it
SetWindowPos(self.Handle, HWND_NOTOPMOST,0,0,0,0, SWP_NOMOVE or SWP_NOSIZE);
SetWindowPos(self.Handle, HWND_TOPMOST,0,0,0,0, SWP_NOMOVE or SWP_NOSIZE);
SetWindowPos(self.Handle, HWND_NOTOPMOST,0,0,0,0, SWP_SHOWWINDOW or SWP_NOMOVE or SWP_NOSIZE);

Изменить Хорошо, я обнаружил одну проблему с этим. Приложение выводится на передний план, но фокус сохраняется в исходном приложении. Используйте этот ответ, чтобы решить проблему, это довольно сложный метод, но копипаста помогает. Перед вызовом ForceForegroundWindow(wnd) вам, возможно, придется вызвать Application.Restore, чтобы развернуть окно. https://stackoverflow.com/a/5885318/185565

person Whome    schedule 20.12.2013

Вы можете написать исполняемый файл, чтобы обойти это ограничение. Возьмем, к примеру, простое (невизуальное) приложение (в основном консольное приложение, но без {$APPTYPE CONSOLE}) с единственной целью вывести желаемое окно на передний план.

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

person Erik Virtel    schedule 18.10.2012

Бланк.вынести на передний план; ?

Должно сработать. Если вы поместите его в таймер, он останется впереди.

person Rudi    schedule 25.10.2012