.NET中的SetForegroundWindow问题

时间:2013-12-07 18:09:11

标签: c# winapi visual-studio-2012 pinvoke

我正在使用PInvoke在.NET中使用SetForegroundWindow API。

当我在Visual Studio中调试时使用API​​时,它的工作非常完美。但是,当应用程序正常运行时,它始终不起作用。

我在调用SetForegroundWindow之前放了一些日志,并确认API已被调用但有时不生效。我也看过几个关于这个问题的帖子,但我想知道它为什么会失败。

该帖子的链接如下:

4 个答案:

答案 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的调用”

How to bring window to top with SetForegroundWindow()