C# NET中的SetForeGroundIndow问题

C# NET中的SetForeGroundIndow问题,c#,winapi,visual-studio-2012,pinvoke,C#,Winapi,Visual Studio 2012,Pinvoke,我正在使用PInvoke在.NET中使用SetForeGroundindow API 当我在VisualStudio中调试时使用API时,它可以完美地工作。但当应用程序正常运行时,它并不总是起作用 我在调用SetForeGroundIndow之前放了一些日志,确认API正在被调用,但有时不会生效。我也看到了一些关于这个问题的帖子,但我想知道为什么失败了 该帖的链接如下: 事实上,这是一个纯Win32问题,而不是特定于.net的问题。net framework位于Win32之上,在这里,Wi

我正在使用PInvoke在.NET中使用SetForeGroundindow API

当我在VisualStudio中调试时使用API时,它可以完美地工作。但当应用程序正常运行时,它并不总是起作用

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

该帖的链接如下:


事实上,这是一个纯Win32问题,而不是特定于.net的问题。net framework位于Win32之上,在这里,Win32的规则正在向您反映

的文档对您面临的问题进行了全面的解释。本质上,
setforegroundindow
的设计面临的问题是它可以用于焦点窃取。焦点是用户应该控制的东西。更改焦点的应用程序可能会很麻烦。因此,
setforegroundindow
试图防御焦点窃取者

文件说:

系统限制哪些进程可以设置前台窗口。A. 只有满足以下条件之一时,进程才能设置前景窗口 条件是正确的:

  • 这个过程就是前景过程
  • 该进程由前台进程启动
  • 进程接收到最后一个输入事件
  • 没有前台进程
  • 进程正在调试中
  • 前台进程不是现代应用程序或开始屏幕
  • 前景未锁定(请参见LockSetForegroundWindow)
  • 前台锁定超时已过期(请参阅SystemParametersInfo中的SPI_GETFOREGROUNDLOCKTIMEOUT)
  • 没有激活的菜单
应用程序无法在用户关闭窗口时将窗口强制置于前台 正在使用另一个窗口。相反,Windows会在任务栏上闪烁 用于通知用户的窗口的按钮

你几乎肯定违反了这些标准。请注意,正在调试的进程始终被授予设置前景窗口的权限。这就解释了为什么调试时不会出现问题。但是在调试器外部,如果您的进程不是前台进程,则调用
setforegroundindow
失败

这都是故意的。您对此的反应应该是尝试提出一种设计,当您的进程不是前台进程时,不需要尝试调用
setforegroundindow

向注册热键。小心选择热键,因为您不能干扰现有(或未来)应用程序

当你需要窃取焦点时(但记住这是不好的),用模拟热键

然后,您将收到一条消息,在处理该消息的过程中,您将被允许使用setForeGroundIndow(我的意思是,它将成功)

在调用SendInput和处理已发布的WM_热键之间,您必须在某个位置存储/记住要激活的窗口的HWND


参考资料:

大卫是对的,他把我引向了正确的方向。遵循代码项目的文章,其中说 “如果用户按ALT键或采取某些操作导致系统本身更改前台窗口,系统将自动启用对SetForegroundWindow的调用”


诀窍是“愚弄”窗口(不要试图用通俗易懂的语言交谈)并将输入附加到要聚焦的窗口所属的新线程,我从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);
    }
}

Getlasterror返回0。但是在日志的帮助下,我发现SetForeGroundIndow失败了。那么我们能做些什么呢?我也尝试过切换到这个窗口API,但没有成功。我发布的链接正在执行AttachThreadInput,但我发现了一个问题。问题是,系统可能会减速,这是在该链接上报告的。我不确定我是否能更清楚地解释它。如果您的进程不是前台进程,那么它不能期望能够影响前台窗口。解决方案是要么是前台进程,要么放弃修改前台进程的尝试。不,不要调用
AttachThreadInput
。这会导致痛苦。当您调用
SetForegroundWindow
时,您确定您的应用程序是前台进程吗?是什么让你认为其他应用程序正在调用LockSetForegroundWindow?前台的另一个应用程序是IE,在我的例子中,也可以在记事本中看到这个例子。我保存了日志,正在检查SetForeGroundIndow的返回值,在某些情况下,它返回false,getlasterror也返回0。这绝对是一个焦点问题,我觉得你还是不太明白。如果IE是前台进程,那么您不能期望
setforegroundindow
有任何效果。IE当然不需要调用
locksetforegroundindow
,如果它这样做了,我会感到惊讶。它所需要做的就是前台进程,而其他进程无法更改前台进程。当IE处于顶端时,IE根本没有任何输入。SetForeGroundIndow工作正常,只有当IE收到输入时,它才不总是工作。但是alt-key模拟解决了这个问题为了搜索者的利益,我把它放到了一个任务中,它解决了我的问题。不发布作为答案,因为它可能是特定于我的申请。不过,可能会帮助某人-Task.Factory.StartNew(()=>{SetForegroundWindow(windowHandle);});我喜欢这个答案。“这是我尝试过的所有解决方案中最强大的一个。”约翰梅施克,谢谢。我刚刚修复了与旧事物和新事物之间的死链接。