我正在使用PInvoke在.NET中使用SetForegroundWindow API。
当我在Visual Studio中调试时使用API时,它的工作非常完美。但是,当应用程序正常运行时,它始终不起作用。
我在调用SetForegroundWindow之前放了一些日志,并确认API已被调用但有时不生效。我也看过几个关于这个问题的帖子,但我想知道它为什么会失败。
该帖子的链接如下:
答案 0 :(得分:8)
实际上这是纯粹的Win32问题,而不是.net特定问题。 .net框架位于Win32之上,Win32的规则正在向您反映。
SetForegroundWindow
的文档全面解释了您所面临的问题。基本上SetForegroundWindow
的设计面临的问题是它可以用于焦点窃取。焦点是用户应该控制的东西。改变焦点的应用程序可能很麻烦。所以SetForegroundWindow
试图防范焦点窃取者。
文档说:
系统限制哪些进程可以设置前景窗口。一个 只有在以下情况之一时,进程才能设置前台窗口 条件是真的:
- 该过程是前台过程。
- 该过程由前台进程启动。
- 该过程收到了最后一个输入事件。
- 没有前台进程。
- 正在调试该过程。
- 前台进程不是现代应用程序或开始屏幕。
- 未锁定前景(请参阅LockSetForegroundWindow)。
- 前台锁定超时已过期(请参阅SystemParametersInfo中的SPI_GETFOREGROUNDLOCKTIMEOUT)。
- 没有菜单处于活动状态。
应用程序无法在用户强制窗口到前台 正在使用另一个窗口。相反,Windows会闪烁任务栏 窗口按钮通知用户。
你几乎肯定会违反这些标准。请注意,正在调试的进程始终被授予设置前景窗口的权限。这就解释了为什么在调试时看到没有问题。但是在调试器之外,如果您的进程不是前台进程,则对SetForegroundWindow
的调用将失败。
这完全是设计的。您对此的反应应该是尝试提出一种设计,当您的流程不是前台流程时,该设计不需要您尝试调用SetForegroundWindow
。
答案 1 :(得分:2)
使用RegisterHotKey注册热键。请仔细选择热键,因为您不得干扰现有(或将来)的应用程序。
当您需要窃取焦点时(但记住它很糟糕),请使用SendInput模拟热键。
然后,您将收到一条WM_HOTKEY消息,在处理该消息期间,您将被允许使用SetForegroundWindow(我的意思是,它会成功)。
在调用SendInput和处理WM_HOTKEY(已发布)之间,您必须在要激活的窗口的HWND处存储/恢复。
参考文献:Pressing a registered hotkey gives you the foreground activation love
答案 2 :(得分:1)
诀窍是傻瓜' windows(不是试图用pleonasms说话)并将输入附加到窗口到焦点所属的新线程,我从pinvoke网站上获取了大部分内容,但添加了一个测试来恢复最小化的窗口:
private const uint WS_MINIMIZE = 0x20000000;
private const uint SW_SHOW = 0x05;
private const uint SW_MINIMIZE = 0x06;
private const uint SW_RESTORE = 0x09;
public static void FocusWindow(IntPtr focusOnWindowHandle)
{
int style = GetWindowLong(focusOnWindowHandle, GWL_STYLE);
// Minimize and restore to be able to make it active.
if ((style & WS_MINIMIZE) == WS_MINIMIZE)
{
ShowWindow(focusOnWindowHandle, SW_RESTORE);
}
uint currentlyFocusedWindowProcessId = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
uint appThread = GetCurrentThreadId();
if (currentlyFocusedWindowProcessId != appThread)
{
AttachThreadInput(currentlyFocusedWindowProcessId, appThread, true);
BringWindowToTop(focusOnWindowHandle);
ShowWindow(focusOnWindowHandle, SW_SHOW);
AttachThreadInput(currentlyFocusedWindowProcessId, appThread, false);
}
else
{
BringWindowToTop(focusOnWindowHandle);
ShowWindow(focusOnWindowHandle, SW_SHOW);
}
}
答案 3 :(得分:0)
大卫是对的,并引导我走向正确的方向。遵循代码项目文章说 “如果用户按下ALT键或采取一些导致系统自身更改前景窗口的操作,系统会自动启用对SetForegroundWindow的调用”