web-dev-qa-db-ja.com

アプリケーションを前面に表示するにはどうすればよいですか?

私はそれが悪い考えである理由をすべて知っています。アプリケーションが入力フォーカスを盗むのは嫌いですが、これは純粋に個人的な使用のためであり、私はそれを実現したいと考えています。それは何も邪魔しません。

(奇妙なことに、ログファイルを生成するユニットテストをNetBeansで実行しています。バックグラウンドアプリケーションがログファイルのタイムスタンプの変更を確認したら、ログファイルを解析して結果を表示するために先頭に移動します)。

この質問 は役に立たず、グーグルもしませんでした。 BringToFront()は長い間機能しておらず、機能する代替手段を見つけることができません。

何か案は?

これを確実に行うことはできません。 Windows XPにはそれを可能にする回避策がありましたが、それ以降のバージョンではほとんどの場合それを禁止しています(下記の注を参照)。これは、MSDNドキュメントの備考セクションに明示的に記載されています- SetForegroundWindow

システムは、フォアグラウンドウィンドウを設定できるプロセスを制限します。プロセスは、次の条件のいずれかに該当する場合にのみ、フォアグラウンドウィンドウを設定できます。

  • プロセスはフォアグラウンドプロセスです。
  • プロセスはフォアグラウンドプロセスによって開始されました。
  • プロセスは最後の入力イベントを受け取りました。
  • フォアグラウンドプロセスはありません。
  • フォアグラウンドプロセスをデバッグしています。
  • フォアグラウンドはロックされていません(LockSetForegroundWindowを参照)。
  • フォアグラウンドロックのタイムアウトが時間切れになりました(SystemParametersInfoのSPI_GETFOREGROUNDLOCKTIMEOUTを参照)。
  • アクティブなメニューはありません。

ユーザーが別のウィンドウで作業している間、アプリケーションはウィンドウを強制的に前面に表示できません。代わりに、Windowsはウィンドウのタスクバーボタンを点滅させてユーザーに通知します。

引用されたドキュメントの最後の段落、特に最初の文に注意してください。

の問題

これは純粋に個人的な使用のためのものであり、私はそれを実現したいと思っています。それは何も邪魔しません。

どのアプリケーションも同じことをしようとすることができるということです。 「純粋に個人的な使用」は、アプリケーションだけでなく、個人的なものであることをどのように伝えますか? :-)

注:ヘルプツールボタンをクリックしたときにIDEのドキュメントがフォアグラウンドで表示されるように、考えられる限りのことをすべて試しましたが、タスクバーボタンが点滅するだけで取得できます(ユーザーが望んでいるとおりです)。 。

17
Ken White

これは、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.必要に応じて最小化されたアプリなど。

32
Sertac Akyuz

私は自分のアプリケーションの1つで同様のことをしていますが、この関数は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;
12
whosrdaddy

これはWindows7 + 8 OSでも機能するはずです。要件は、アプリケーション自体が関数を呼び出せることだけです。別のプロセスのウィンドウフロントを設定するために外部アプリを使用するのはより簡単です。

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);

編集Okこれに問題が1つ見つかりました。アプリケーションは前面に表示されますが、フォーカスは元のアプリケーションに保持されます。この回答を使用して問題を修正します。その非常に複雑な方法ですが、copypasteがうまくいきます。ForceForegroundWindow(wnd)を呼び出す前に、ウィンドウを最小化せずにApplication.Restoreを呼び出す必要がある場合があります https://stackoverflow.com/a/5885318/185565

3
Whome

その制限を回避する実行可能ファイルを作成できます。たとえば、目的のウィンドウを前面に表示することのみを目的としたシンプルな(非ビジュアル)アプリケーション(基本的にコンソールアプリですが、{$ APPTYPE CONSOLE}はありません)を考えてみます。

モニタリングバックグラウンドアプリケーションは、bringtofrontアクションが必要なときはいつでも、コマンドラインコール(ShellExecuteなど)を介してヘルパーアプリを呼び出します。このアプリはフォアグラウンドプロセスになっているため、他のウィンドウを前面に表示することができます(SetForeGroundWindow)。 FindWindowを使用して、ターゲットウィンドウのハンドルを取得するか、ヘルパーアプリの起動時に適切なパラメーターを渡すことができます。

3
Erik Virtel
function WinActivate(const AWinTitle: string): boolean;
var
  _WindowHandle: HWND;
begin
  Result := false;
  _WindowHandle := FindWindow(nil, PWideChar(AWinTitle));
  if _WindowHandle <> 0 then
  begin
    if IsIconic(_WindowHandle) then
      Result := ShowWindow(_WindowHandle, SW_RESTORE)
    else
      Result := SetForegroundWindow(_WindowHandle);
  end;
end;

if WinActivate(self.Caption) then
  ShowMessage('This works for me in Windows 10');

実行中の他のStayOnTopアプリケーションがないと仮定すると、フォームのFormStyleを一時的にfsStayOnTopに設定してから、Application RestoreとBringToFrontを実行して、formstyleを元の状態に戻すことができます。

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

繰り返しますが、これは他のstayontopアプリケーションがない場合にのみ機能します。

2
Toby