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

时间:2020-03-05 18:47:45  来源:igfitidea点击:

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

我可以在MFC中使用OnIdle()并调用GetFocus(),但这似乎有些hacky。

解决方案

回答

Win32 GetForegroundWindow怎么样?

回答

因此,从我们表达问题的方式来看,我要推断的是,我们希望有一个事件处理程序,只要焦点在窗口之间切换,该事件处理程序就会被调用。我们想收到通知,而不必轮询。

实际上,我不认为从OnIdle调用GetFocus确实可以确保它轮询,但它是一种低开销,无副作用的轮询,但是如果我们真的想跟踪它,Windows Hooks可能是最佳选择。具体来说,我们可以安装CBT挂钩(WH_CBT)并监听HCBT_SETFOCUS通知。

Windows calls the WH_CBT hook with this hook code when Windows is about to set the focus to any window. In the case of thread-specific hooks, the window must belong to the thread. If the filter function returns TRUE, the focus does not change.

我们也可以使用WH_CALLWNDPROC挂钩来监听WM_SETFOCUS消息。

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

回答

我们可以监视WM_ACTIVATE事件的消息。

参考

回答

好吧,这可能不是很优雅……但是我们可以很容易地检索当前的焦点控件。因此,我们可以考虑设置一个计时器,该计时器每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>

回答

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

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进行了后台操作。但是,我们必须使用.Net Framework ...

回答

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

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

回答

如果我们使用.net 3.5进行编程,到目前为止olorin提到的自动化程序包是最简单的,但是要当心在具有UI的程序中使用它,至少如果UI是在WPF中完成的,则焦点跟踪挂钩会在自己的应用中被事件弄糊涂了,并迅速锁定了UI。我向MS发送了一个错误报告。使用传统的Windows Forms UI,我还没有观察到相同的问题。当然,我们可以将跟踪代码放在单独的控制台应用程序中,并使用某种ipc来传输所需的信息。

使用Interop从Cw访问WH_CBT Windows Hook的诱人选择不起作用-只能从Care鼠标和键盘处获得的唯一全局钩子。