現在どのウィンドウにキーボード フォーカスがあるかを追跡する方法はありますか。すべてのウィンドウで WM_SETFOCUS を処理できますが、代替のより単純な方法 (つまり、どこかに単一のメッセージ ハンドラー) があるかどうか疑問に思っています。
MFC で OnIdle() を使用して GetFocus() を呼び出すこともできますが、それは少しハックなようです。
したがって、質問の言い方から、フォーカスがウィンドウ間で切り替わるたびに呼び出されるイベントハンドラーが必要であると推測しています。ポーリングするのではなく、通知を受け取りたい。
実際、OnIdleからGetFocusを呼び出すことは、それほど多くのハックではないと思います-確かにポーリングですが、副作用のない低オーバーヘッドのポーリングです-しかし、これを本当に追跡したい場合は、Windowsフックがおそらく最良の選択です。具体的には、CBTフック(WH_CBT)をインストールして、HCBT_SETFOCUS通知をリッスンできます。
Windowsがフォーカスを任意のウィンドウに設定しようとしているときに、Windowsはこのフックコードを使用してWH_CBTフックを呼び出します。スレッド固有のフックの場合、ウィンドウはスレッドに属している必要があります。フィルタ関数がTRUEを返した場合、フォーカスは変更されません。
WH_CALLWNDPROCフックを使用して、WM_SETFOCUSメッセージをリッスンすることもできます。
グローバルフックにするかアプリローカルにするかに応じて、システム上のすべてのウィンドウでフォーカスを追跡することも、プロセスが所有するウィンドウのみでフォーカスを追跡することもできます。
.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 を使用する必要があります...
Win32 GetForegroundWindowはどうですか?
.net 3.5でプログラミングしている場合、olorinが言及している自動化パッケージははるかに簡単ですが、少なくともUIがWPF(フォーカス追跡フック)で行われている場合は、それ自体がUIを持つプログラムで使用することに注意してください。独自のアプリのイベントに混乱し、UIをすばやくロックします。MSにバグレポートを送信しました。従来のWindowsフォームUIを使用して同じ問題を確認したことはありません。もちろん、トラッキングコードを別のコンソールアプリに配置し、ある種のipcを使用して必要な情報を送信することもできます。
Interopを使用してC#からWH_CBT Windowsフックにアクセスするという魅力的な代替手段は機能しません。C#から取得できるグローバルフックは、マウスとキーボードだけです。
WM_ACTIVATEイベントのメッセージを監視できます。
まあ、これはあまり優雅ではないかもしれません...しかし、現在のフォーカスされたコントロールをかなり簡単に取得できます。そのため、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>
http://msdn.microsoft.com/en-us/library/ms771428.aspx
ウィンドウフォーカストラッカーのサンプルがあります。