10

Raymond 在博客中介绍了程序如何通过 using 获取/窃取“前景之爱” RegisterHotkey,当被调用时,它将把前景转移到您的应用程序中。

尝试手动执行此操作会惨遭失败(例如,使用SetForegroundWindowSwitchToWindow等),因为应用程序不能从用户那里窃取焦点(以便按键不会转到错误的位置)。

麻烦的是,今天我注意到了一些奇怪的事情:

  1. 我尝试安全删除外部驱动器。

  2. 有约 7 秒的停顿。

  3. 停顿的时候,我在窗内用力打字。

  4. 突然,一个消息框从我的应用程序中窃取了前景,而我的输入却进入了消息框。

显然,这没有使用热键机制——然而,Windows 能够从我的应用程序中窃取焦点。

我真的怀疑是否有类似“后门”之类的东西仅用于此特定目的(尽管如果我错了请纠正我),因此,假设不是这种情况,必须有一种方法可以正确地做到这一点, 使用热键机制。

那么问题来了,这是如何实现的呢?

笔记:

汉斯指出“后门”是AttachInputThread,但我并不真正相信这就是这里发生的事情——特别是因为雷蒙德说这种方法会导致挂起。想法?

4

3 回答 3

1

我想不出比我现在有时间更复杂的测试方法,但仔细查看 SetForegroundWindow 文档, http: //msdn.microsoft.com/en-us/library/ms633539 (VS.85).aspx,在有关可以设置前景的进程的备注下列出的条件之一是:

  • 进程收到最后一个输入事件。

除非我弄错了,否则 Windows 资源管理器会接收所有输入事件以检查热键、其他此类窃取焦点的按键以及当前窗口边界之外的鼠标点击等。

由于其永久的“收到最后一个输入事件”状态,Explorer 有资格作为可以设置前景的东西,因此可以导致它导致的 MessageBox 显示为前景,而没有任何特殊功能或未记录的行为。

于 2012-07-14T14:14:47.090 回答
1

我已经做了一些实验,据我所知,当且仅当新窗口属于 Windows 资源管理器时才会发生这种情况。例如,某些控制面板在 Explorer 中实现或作为 Explorer 插件实现。我可以通过从“开始”菜单打开“操作中心”来最轻松地重现它(将“开始”菜单配置为在菜单中显示控制面板项目)。

因此,我怀疑这种行为是 Windows 资源管理器拥有桌面窗口这一事实的结果,GUI 将其视为一种特殊情况。

唯一有点奇怪的是,我无法使用您正在谈论的 USB 对话框重现此行为,该对话框(当我尝试时)是由单独的进程(rundll32.exe 的实例)生成的。不过,这可能取决于其他因素。

于 2012-07-13T05:18:14.043 回答
0

如前所述,不同线程的窗口输入是独立处理的。 AttachThreadInputAPI 允许共享线程状态,特别是:

通过使用 AttachThreadInput 函数,一个线程可以将其输入处理机制附加到另一个线程。[...] 这也允许线程共享它们的输入状态,因此它们可以调用 SetFocus 函数将键盘焦点设置到不同线程的窗口。

现在,当您看到当前处于前台的窗口时,如果您与前台窗口线程的线程状态共享您的线程状态,您SetFocus将从那里窃取焦点。

CWindow Window = GetForegroundWindow();
if(Window)
{
  const DWORD nWindowThreadIdentifier = Window.GetWindowThreadID();
  const DWORD nThreadIdentifier = GetCurrentThreadId();
  AttachThreadInput(nThreadIdentifier, nWindowThreadIdentifier, TRUE);
  GetDlgItem(IDC_EDIT).SetFocus(); // This succeeds now as we are sharing thread state with foreground window
  AttachThreadInput(nThreadIdentifier, nWindowThreadIdentifier, FALSE);
  m_sAction = _T("Done");
} else
  m_sAction = _T("Nothing to do");

另见:源代码片段二进制

于 2012-07-08T11:02:36.043 回答