如何将我的申请放在首位?
Posted
技术标签:
【中文标题】如何将我的申请放在首位?【英文标题】:How to bring my application to the front? 【发布时间】:2012-10-08 09:49:33 【问题描述】:我知道为什么这是一个坏主意的所有原因。如果应用程序窃取输入焦点,我不喜欢它,但这纯粹是为了个人使用,我希望它发生;它不会打扰任何事情。
(好奇:我在 NetBeans 中运行单元测试,它会生成一个日志文件。当我的后台应用程序看到日志文件的时间戳更改时,我希望它解析日志文件并来到前面显示结果) .
This question 没有帮助,谷歌搜索也没有帮助。 BringToFront()
似乎已经很久没有工作了,我找不到任何替代方案。
有什么想法吗?
【问题讨论】:
你不能这样做。 Windows XP 有一个允许它的解决方法。此后的版本禁止它。 +1 @ken 如果您 100% 确定,请发布答案,我将奖励。我希望有一些不起眼的技巧,比如缩小到系统托盘然后恢复,或者隐藏然后再次显示...... @Mawg 你测试我的解决方案了吗? @Mawg - 出于好奇,你有没有测试过我帖子中的解决方案? @Mawg - 这不是关于接受(如果是的话,我不会发布对已经回答的问题的答案),而是关于反馈 - 只是“是的,它有效”或“不,这不不行”。上面的评论/问题不是第一个,它的前任在那里呆了几天,然后我把它删除了,当我确定你在线时问另一个(这个也呆了五天多)。 【参考方案1】:这里有一些简单但似乎可行的方法,已使用多个由 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.用于最小化的应用程序等(如果需要)。
【讨论】:
谢谢!也适用于 Windows 8.1。 当窗口被隐藏但不能恢复已最小化到任务栏的窗口时效果很好。【参考方案2】:你不能可靠地做到这一点。 Windows XP 有一个允许它的解决方法,但从那时起的版本大部分都禁止它(参见下面的注释)。这在 SetForegroundWindow
上的 MSDN 文档的备注部分中有明确说明:
系统限制哪些进程可以设置前台窗口。只有满足以下条件之一,进程才能设置前台窗口:
该进程是前台进程。 进程由前台进程启动。 进程收到最后一个输入事件。 没有前台进程。 正在调试前台进程。 前景未锁定(请参阅 LockSetForegroundWindow)。 前台锁定超时已过期(请参阅 SystemParametersInfo 中的 SPI_GETFOREGROUNDLOCKTIMEOUT)。 没有处于活动状态的菜单。当用户正在使用另一个窗口时,应用程序不能将一个窗口强制置于前台。相反,Windows 会闪烁窗口的任务栏按钮以通知用户。
注意引用文档的最后一段,尤其是第一句。
问题
这纯粹是为了个人使用,我希望它发生;它不会打扰任何事情。
是任何应用程序都可以尝试做同样的事情。 “纯粹个人使用”如何告诉您是您个人而不是任何应用程序? :-)
注意:当我单击帮助工具按钮时,我已经尝试了所有我能想到的让 IDE 的文档显示在前台的方法,但我能得到的只是闪烁的任务栏按钮(我作为用户想要它)这样做)。
【讨论】:
+1(以及您在上面的评论 ;-) 显然,我会活在希望中,并在颁奖前将其开放几个小时。正如我所说,我希望有一些不起眼的技巧,比如缩小到系统托盘然后恢复,或者隐藏然后再次显示 - 但我尝试过的任何东西都不起作用:-(这是很多工作,只是为了避免 Alt-Tab (嗯,也许如果我有一个“启动器”应用程序,然后启动一个“进程”应用程序。新应用程序会在前台启动吗?) 你可以使用 AlwaysOnTop 风格,不是说它对其他应用程序友好,但它会引起用户的注意。虽然也许像 Growl/Snarl 这样的特殊警报服务更适合它。【参考方案3】:我在我的一个应用程序中做类似的事情,这个功能对我有用 在 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;
【讨论】:
这在以非管理员身份运行的 Win7 上不起作用,在 Win2008 域上运行的机器上也无法正常工作,因此它不总是工作,这就是为什么我给出了我所做的答案。 :-) 当然,我(以及操作系统本身的制造商)知道什么? @KenWhite,此功能用于最终用户应用程序,他们混合使用 XP 和 W7 (UAC ON),我在受限环境中没有遇到任何问题 这里有一件事让我很头疼:SystemParametersInfo(SPI_SET...
。设置系统范围的参数是个好主意吗?这甚至适用于 UAC 环境中的标准用户吗?
附带说明,SystemParametersInfo()
的第三个参数是Pointer
,因此代码应该使用nil
而不是TObject(0)
。
我赞成这个答案,除非其他人可以证明这个代码不起作用(我自己没有测试过这个)。 @Ken,您实际上是否在以非管理员身份运行 Win7 时测试了此代码?【参考方案4】:
假设没有其他 StayOnTop 应用程序在运行,您可以暂时将表单 FormStyle 设置为 fsStayOnTop,然后执行 Application Restore 和 BringToFront,然后将表单样式改回原来的样子。
tmpFormStyle := Application.MainForm.FormStyle;
Application.MainForm.FormStyle := fsStayOnTop;
Application.Restore; // optional
Application.BringToFront;
Application.MainForm.FormStyle := tmpFormStyle;
同样,这仅在没有其他留在顶部的应用程序时才有效。
【讨论】:
【参考方案5】:即使在 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://***.com/a/5885318/185565
【讨论】:
【参考方案6】:您可以编写一个可执行文件来解决该限制。以一个简单的(非可视的)应用程序为例(基本上是一个控制台应用程序,但没有 $APPTYPE CONSOLE),其唯一目的是将您想要的窗口放在前面。
您的监控后台应用程序通过命令行调用(例如 ShellExecute)调用帮助应用程序,无论何时需要您的 bringtofront 操作。由于此应用程序正在成为前台进程,因此它能够将其他窗口置于前面(SetForeGroundWindow)。您可以使用 FindWindow 来获取目标窗口的句柄或在启动帮助应用程序时传递适当的参数。
【讨论】:
【参考方案7】: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');
【讨论】:
【参考方案8】:Form.bringtofront; ?
应该可以。 如果你把它放在一个计时器中,它会留在前面。
【讨论】:
以上是关于如何将我的申请放在首位?的主要内容,如果未能解决你的问题,请参考以下文章