如何监控当前哪个窗口具有键盘焦点

时间:2008-09-04 17:27:35

标签: winapi mfc

有没有办法跟踪哪个窗口当前有键盘焦点。我可以为每个窗口处理WM_SETFOCUS,但我想知道是否有一个替代的,更简单的方法(即某处的单个消息处理程序)。

我可以在MFC中使用OnIdle()并调用GetFocus(),但这看起来有点hacky。

7 个答案:

答案 0 :(得分:17)

因此,从你提出问题的方式来看,我推断你想要一个事件处理程序,只要焦点在windows之间切换就会调用它。您希望收到通知,而不是必须进行投票。

我实际上并不认为从OnIdle调用GetFocus是一个很大的问题 - 确定它是轮询,但它是低开销的轮询而没有副作用 - 但是如果你真的想跟踪它,Windows Hooks可能是你最好的选择。具体来说,您可以安装CBT挂钩(WH_CBT)并监听HCBT_SETFOCUS通知。

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

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

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

答案 1 :(得分:3)

Win32 GetForegroundWindow怎么样?

答案 2 :(得分:3)

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

Page on MSDN

样品:

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

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

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

答案 3 :(得分:1)

如果您使用.net 3.5进行编程,那么自动化软件包olorin提到是最简单的,但要注意在自己有UI的程序中使用它,至少如果UI是在WPF中完成的 - 焦点跟踪钩子会被自己的应用程序中的事件搞糊涂,并快速锁定UI。我向MS发送了bug report。我没有使用传统的Windows窗体UI观察到同样的问题。当然,您可以将跟踪代码放在一个单独的控制台应用程序中,并使用某种ipc来传输您需要的信息。

使用Interop从C#访问WH_CBT Windows Hook的诱人选择将无效 - the only global hooks you can get at from C# are the mouse and keyboard

答案 4 :(得分:0)

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

ref

答案 5 :(得分: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>

答案 6 :(得分:0)

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

有一个窗口焦点跟踪器样本。