Winapi 如何监视当前有键盘焦点的窗口
有没有办法跟踪哪个窗口当前有键盘焦点。我可以为每个窗口处理WM_SETFOCUS,但我想知道是否有另一种更简单的方法(即某个地方的单个消息处理程序)Winapi 如何监视当前有键盘焦点的窗口,winapi,mfc,Winapi,Mfc,有没有办法跟踪哪个窗口当前有键盘焦点。我可以为每个窗口处理WM_SETFOCUS,但我想知道是否有另一种更简单的方法(即某个地方的单个消息处理程序) 我可以在MFC中使用OnIdle()并调用GetFocus(),但这似乎有点不太妥当。Win32怎么样?因此,从您对问题的措辞来看,我推断您希望有一个事件处理程序,在焦点在窗口之间切换时调用它。你希望得到通知,而不是投票 事实上,我并不认为从OnIdle调用GetFocus是一种黑客行为——当然是轮询,但它是低开销轮询,没有副作用——但如果您真的想
我可以在MFC中使用OnIdle()并调用GetFocus(),但这似乎有点不太妥当。Win32怎么样?因此,从您对问题的措辞来看,我推断您希望有一个事件处理程序,在焦点在窗口之间切换时调用它。你希望得到通知,而不是投票 事实上,我并不认为从OnIdle调用GetFocus是一种黑客行为——当然是轮询,但它是低开销轮询,没有副作用——但如果您真的想跟踪这一点,它可能是您的最佳选择。具体来说,您可以安装CBT钩子(WH_CBT)并侦听HCBT_SETFOCUS通知 当Windows将焦点设置为任何窗口时,Windows使用此钩子代码调用WH_CBT钩子。对于特定于线程的挂钩,窗口必须属于线程。如果filter函数返回TRUE,则焦点不会改变 您还可以使用WH_CALLWNDPROC钩子来监听WM_SETFOCUS消息
根据您是将其设置为全局钩子还是应用程序本地,您可以跨系统上的所有窗口跟踪焦点,或者仅跟踪进程拥有的窗口。您可以监视事件的消息
嗯,这可能不是很优雅。。。但是您可以很容易地检索当前的焦点控件。所以你可以考虑设置一个定时器,每1/2秒询问一次定时器“当前的焦点在哪里?”然后你可以观察变化。下面是Delphi代码示例;它应该很容易适应,因为真正的工作是在WindowsAPI调用中
<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>
函数TForm1.GetCurrentHandle:整数;
变量
activeWinHandle:HWND;
focusedThreadID:DWORD;
开始
//返回当前焦点控件的Windows句柄
结果:=0;
activeWinHandle:=GetForegroundWindow;
focusedThreadID:=GetWindowThreadProcessID(activeWinHandle,nil);
如果AttachThreadInput(GetCurrentThreadID,focusedThreadID,true),则开始
尝试
结果:=GetFocus;
最后
AttachThreadInput(GetCurrentThreadID,focusedThreadID,false);
结束;
结束//如附
结束;
程序TForm1.Timer1Timer(发送方:TObject);
开始
//如果句柄已更改,则发出通知
//(此代码由计时器触发)
CurrentHandle:=GetCurrentHandle;
如果CurrentHandle之前的Handle,则开始
标签1.标题:=“上次焦点更改发生@”+DateTimeToStr(现在);
结束;
PreviousHandle:=CurrentHandle;
结束;
使用.Net Framework 3.5有一种简单的方法:库UI Automation提供了一个事件焦点更改,每次焦点更改到新控件时都会触发该事件焦点
样本:
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钩子来实现这一点。但是,您必须使用.Net Framework…
有一个窗口焦点跟踪器示例 如果你是在.net 3.5中编程,olorin提到的自动化软件包是目前为止最简单的,但是要小心在一个本身有UI的程序中使用它,至少如果UI是在WPF中完成的——焦点跟踪挂钩会被自己应用程序中的事件弄糊涂,并很快锁定UI。我给她发了一封信。我在使用传统的Windows窗体UI时没有发现同样的问题。当然,你可以将跟踪代码放在一个单独的控制台应用程序中,并使用某种ipc来传输你需要的信息
使用Interop从C#访问Whu CBT Windows钩子的诱人替代方案是行不通的——。我知道如何获得焦点窗口,我正在寻找一种在焦点更改时在单个位置收到通知的方法。我在任何地方都找不到源代码的下载,而且SDK的安装中也没有包含该源代码。有人知道我该如何得到它吗?焦点追踪者似乎已经移到这里了:(抱歉,我没有足够的声誉将此作为对上述相关答案的评论)。