我知道为什么这是一个坏主意的所有原因。我不喜欢应用程序窃取输入焦点,但这纯粹是为了个人使用,我希望它发生;它不会打扰任何事情。
(出于好奇:我正在 NetBeans 中运行单元测试,它生成一个日志文件。当我的后台应用程序看到日志文件的时间戳更改时,我希望它解析日志文件并来到前面显示结果)。
这个问题没有帮助,谷歌搜索也没有帮助。看来
BringToFront()
已经很长时间没有工作了,而且我找不到任何替代方案。
有什么想法吗?
这里有一些简单的东西,似乎可以工作,并在包括 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);
您可以进一步调整它:如果需要的话,可以使用最小化的应用程序或类似的应用程序。
你无法可靠地做到这一点。 Windows XP 有一个解决方法允许这样做,但此后的版本在很大程度上禁止它(请参阅下面的注释)。这在 MSDN 文档的备注部分有明确说明:
SetForegroundWindow
:
系统限制哪些进程可以设置前台窗口。仅当满足以下条件之一时,进程才能设置前台窗口:
- 该进程为前台进程。
- 该进程是由前台进程启动的。
- 进程接收到最后一个输入事件。
- 没有前台进程。
- 前台进程正在调试中。
- 前景未锁定(请参阅LockSetForegroundWindow)。
- 前台锁定超时已过期(请参阅 SystemParametersInfo 中的 SPI_GETFOREGROUNDLOCKTIMEOUT)。
- 没有任何菜单处于活动状态。
当用户正在使用另一个窗口时,应用程序无法强制一个窗口进入前台。相反,Windows 会闪烁窗口的任务栏按钮来通知用户。
注意引用文档的最后一段,尤其是第一句。
问题
这纯粹是为了个人使用,我希望它发生;它不会打扰任何事情。
任何应用程序都可以尝试做同样的事情。 “纯粹个人使用”如何表明这是您个人使用而不是任何应用程序? :-)
注意:当我单击帮助工具按钮时,我已经尝试了所有我能想到的让 IDE 的文档显示在前台的方法,但我所能得到的只是闪烁的任务栏按钮(并且我作为用户希望它这样做) ).
我正在我的一个应用程序中做类似的事情,这个功能对我有用 在 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;
假设没有其他 StayOnTop 应用程序在运行,您可以临时将表单 FormStyle 设置为 fsStayOnTop,然后执行应用程序恢复和 BringToFront,然后将表单样式更改回原来的样式。
tmpFormStyle := Application.MainForm.FormStyle;
Application.MainForm.FormStyle := fsStayOnTop;
Application.Restore; // optional
Application.BringToFront;
Application.MainForm.FormStyle := tmpFormStyle;
同样,这仅在没有其他stayontop应用程序的情况下才有效。
即使在 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
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');
您可以编写一个可执行文件来解决该限制。以一个简单(非可视)应用程序(基本上是一个控制台应用程序,但没有 {$APPTYPE CONSOLE})为例,其唯一目的是将您想要的窗口置于前面。
每当需要您的bringtofront操作时,您的监控后台应用程序都会通过命令行调用(例如ShellExecute)来调用帮助程序应用程序。由于此应用程序正在成为前台进程,因此它能够将其他窗口置于前台(SetForeGroundWindow)。您可以使用 FindWindow 获取目标窗口的句柄或在启动帮助程序应用程序时传递适当的参数。
一种解决方案是使用 Windows 事件
WMMoving
。
procedure TForm2.WMMoving( var msg : TWMMoving );
begin
BringtoFront;
inherited;
end;
当然你需要将其放在 Form 类中:
procedure WMMoving( var msg : TWMMoving ); message WM_MOVING;
这适用于 D11 和 D12。我没有旧的可以测试。
Form.bringtofront; ?
应该可以。 如果您将其放入计时器中,它将保留在前面。