我知道这是个糟糕的想法,因为当一个应用程序窃取输入焦点时,我不喜欢它,但这仅适用于纯个人使用,并且我希望它发生;它不会干扰任何事情。
(对于好奇的人:我正在NetBeans中运行单元测试,生成日志文件。当我的后台应用程序看到日志文件的时间戳更改时,我希望它解析日志文件并前置显示结果)。
这个问题没有帮助,谷歌搜索也没有。似乎BringToFront()
很长时间以来都没有起作用,我找不到任何其他可以起作用的替代方法。
有什么想法吗?
我知道这是个糟糕的想法,因为当一个应用程序窃取输入焦点时,我不喜欢它,但这仅适用于纯个人使用,并且我希望它发生;它不会干扰任何事情。
(对于好奇的人:我正在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);
如果需要,您可以进一步调整它,例如,对于最小化的应用程序等。
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;
另一个解决方案是不窃取焦点,只是将窗口置于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;
SystemParametersInfo(SPI_SET...
。设定系统级别的参数是一个好主意吗?在UAC环境下,标准用户能够做到吗? - kobikSystemParametersInfo()
的第三个参数是一个 Pointer
,所以代码应该使用 nil
而不是 TObject(0)
。 - Remy LebeautmpFormStyle := Application.MainForm.FormStyle;
Application.MainForm.FormStyle := fsStayOnTop;
Application.Restore; // optional
Application.BringToFront;
Application.MainForm.FormStyle := tmpFormStyle;
需要注意的是,此方法仅在没有其他置顶应用程序的情况下才有效。
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');
这应该可以在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/185565Form.bringtofront; ?
应该可以。 如果你把它放在计时器里,它会一直保持在最前面。
BringToFront()
没有起作用",而你甚至没有明确指出这应该在哪个Windows版本中可靠地工作。 - undefined