17

Is there a way to track which window currently has keyboard focus. I could handle WM_SETFOCUS for every window but I'm wondering if there's an alternative, simpler method (i.e. a single message handler somewhere).

I could use OnIdle() in MFC and call GetFocus() but that seems a little hacky.

4

7 回答 7

17

因此,从您提出问题的方式来看,我推断您希望拥有一个事件处理程序,每当焦点在窗口之间切换时就会调用该事件处理程序。您希望收到通知,而不是进行轮询。

我实际上并不认为从 OnIdle 调用 GetFocus 是一种黑客行为——当然它是轮询,但它是没有副作用的低开销轮询——但如果你真的想跟踪这个,Windows Hooks可能是你的最佳选择。具体来说,您可以安装 CBT 挂钩 (WH_CBT) 并收听 HCBT_SETFOCUS 通知。

当 Windows 将焦点设置到任何窗口时,Windows 将使用此挂钩代码调用 WH_CBT 挂钩。在线程特定的钩子的情况下,窗口必须属于线程。如果过滤器函数返回 TRUE,则焦点不会改变。

您也可以使用 WH_CALLWNDPROC 挂钩并监听 WM_SETFOCUS 消息。

根据您是否将其设置为全局挂钩或应用程序本地,您可以跟踪系统上所有窗口的焦点,或仅跟踪您的进程拥有的窗口。

于 2008-09-04T18:00:28.793 回答
6

使用 .Net Framework 3.5 有一种简单的方法:UI 自动化库提供了一个事件焦点更改,每次焦点更改为新控件时都会触发该事件。

MSDN 上的页面

样本:

public void SubscribeToFocusChange()
{
    AutomationFocusChangedEventHandler focusHandler 
       = new AutomationFocusChangedEventHandler(OnFocusChanged);
    Automation.AddAutomationFocusChangedEventHandler(focusHandler);
}

private void OnFocusChanged(object sender, AutomationFocusChangedEventArgs e)
{
    AutomationElement focusedElement = sender as AutomationElement;
    //...
}

这个api实际上在幕后使用windows hook来做到这一点。但是,您必须使用 .Net 框架...

于 2008-09-18T15:06:50.013 回答
4

How about the Win32 GetForegroundWindow?

于 2008-09-04T17:29:47.043 回答
1

如果你在 .net 3.5 中编程,那么 olorin 提到的自动化包是迄今为止最简单的,但要小心在本身有 UI 的程序中使用它,至少如果 UI 是在 WPF 中完成的——焦点跟踪挂钩被自己的应用程序中的事件弄糊涂,并迅速锁定用户界面。我向 MS 发送了一份关于它的错误报告。在使用传统的 Windows 窗体 UI 时,我没有观察到同样的问题。当然,您可以将跟踪代码放在单独的控制台应用程序中,并使用某种 ipc 来传输您需要的信息。

使用 Interop 从 C# 访问 WH_CBT Windows Hook 的诱人替代方案将不起作用 -您可以从 C# 获得的唯一全局挂钩是鼠标和键盘

于 2009-05-20T22:24:45.597 回答
0

您可以监视WM_ACTIVATE事件的消息。

参考

于 2008-09-04T18:01:39.667 回答
0

好吧,这可能不是很优雅......但是您可以很容易地检索当前的焦点控件。因此,您可以考虑设置一个计时器,每隔 1/2 秒左右询问一次“当前焦点在哪里?”......然后您可以观察变化。示例 Delphi 代码如下;它应该很容易适应,因为真正的工作是在 Windows API 调用中。

<snip>

function TForm1.GetCurrentHandle: integer;
var
  activeWinHandle: HWND;
  focusedThreadID : DWORD;
begin
  //return the Windows handle of the currently focused control
  Result := 0;
  activeWinHandle := GetForegroundWindow;
  focusedThreadID := GetWindowThreadProcessID(activeWinHandle,nil);
  if AttachThreadInput(GetCurrentThreadID,focusedThreadID,true) then begin
    try
      Result := GetFocus;
    finally
      AttachThreadInput(GetCurrentThreadID, focusedThreadID, false);
    end;
  end;  //if attached
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  //give notification if the handle changed
  //(this code gets fired by a timer)
  CurrentHandle := GetCurrentHandle;
  if CurrentHandle <> PreviousHandle then begin
    Label1.Caption := 'Last focus change occurred @ ' + DateTimeToStr(Now);
  end;
  PreviousHandle := CurrentHandle;
end;

<snip>
于 2008-09-04T18:12:53.930 回答
0

http://msdn.microsoft.com/en-us/library/ms771428.aspx

有一个窗口焦点跟踪器示例。

于 2009-05-08T03:46:57.323 回答