私はそれが悪い考えである理由をすべて知っています。アプリケーションが入力フォーカスを盗むのは嫌いですが、これは純粋に個人的な使用のためであり、私はそれを実現したいと考えています。それは何も邪魔しません。
(奇妙なことに、ログファイルを生成するユニットテストをNetBeansで実行しています。バックグラウンドアプリケーションがログファイルのタイムスタンプの変更を確認したら、ログファイルを解析して結果を表示するために先頭に移動します)。
この質問 は役に立たず、グーグルもしませんでした。 BringToFront()
は長い間機能しておらず、機能する代替手段を見つけることができません。
何か案は?
これを確実に行うことはできません。 Windows XPにはそれを可能にする回避策がありましたが、それ以降のバージョンではほとんどの場合それを禁止しています(下記の注を参照)。これは、MSDNドキュメントの備考セクションに明示的に記載されています- SetForegroundWindow
:
システムは、フォアグラウンドウィンドウを設定できるプロセスを制限します。プロセスは、次の条件のいずれかに該当する場合にのみ、フォアグラウンドウィンドウを設定できます。
- プロセスはフォアグラウンドプロセスです。
- プロセスはフォアグラウンドプロセスによって開始されました。
- プロセスは最後の入力イベントを受け取りました。
- フォアグラウンドプロセスはありません。
- フォアグラウンドプロセスをデバッグしています。
- フォアグラウンドはロックされていません(LockSetForegroundWindowを参照)。
- フォアグラウンドロックのタイムアウトが時間切れになりました(SystemParametersInfoのSPI_GETFOREGROUNDLOCKTIMEOUTを参照)。
- アクティブなメニューはありません。
ユーザーが別のウィンドウで作業している間、アプリケーションはウィンドウを強制的に前面に表示できません。代わりに、Windowsはウィンドウのタスクバーボタンを点滅させてユーザーに通知します。
引用されたドキュメントの最後の段落、特に最初の文に注意してください。
の問題
これは純粋に個人的な使用のためのものであり、私はそれを実現したいと思っています。それは何も邪魔しません。
どのアプリケーションも同じことをしようとすることができるということです。 「純粋に個人的な使用」は、アプリケーションだけでなく、個人的なものであることをどのように伝えますか? :-)
注:ヘルプツールボタンをクリックしたときにIDEのドキュメントがフォアグラウンドで表示されるように、考えられる限りのことをすべて試しましたが、タスクバーボタンが点滅するだけで取得できます(ユーザーが望んでいるとおりです)。 。
これは、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.必要に応じて最小化されたアプリなど。
私は自分のアプリケーションの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;
これは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
その制限を回避する実行可能ファイルを作成できます。たとえば、目的のウィンドウを前面に表示することのみを目的としたシンプルな(非ビジュアル)アプリケーション(基本的にコンソールアプリですが、{$ APPTYPE CONSOLE}はありません)を考えてみます。
モニタリングバックグラウンドアプリケーションは、bringtofrontアクションが必要なときはいつでも、コマンドラインコール(ShellExecuteなど)を介してヘルパーアプリを呼び出します。このアプリはフォアグラウンドプロセスになっているため、他のウィンドウを前面に表示することができます(SetForeGroundWindow)。 FindWindowを使用して、ターゲットウィンドウのハンドルを取得するか、ヘルパーアプリの起動時に適切なパラメーターを渡すことができます。
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アプリケーションがない場合にのみ機能します。