キーボードフォーカス有無を判定するには?


2tom  2013-04-01 14:50:30  No: 73655  IP: 192.*.*.*

立て続けの質問になってしまい、申し訳ございません。
常駐アプリで、エクスプローラのリストビューにフォーカス(キーボードフォーカス)があるときだけキーのフックを行いたいのですが、
以下の関数をタイマー(100ms)で実行するとエクスプローラでマウスの
ダブルクリックが動作しなくなってしまいます。

下記のHPで対処された方がいますが、どちらもアクティブウィンドウが切り替わった時だけ「AttachThreadInput」をコールするというものでした。
(今回は同じエクスプローラのウィンドウ内でも、アドレス欄にキーボードフォーカスがある時はフックをしない様にしたいです。

タイマーの間隔を延ばすとダブルクリックが発生しなくなる確率は減りますが、
根本的な解決になっていないと思います。

アドバイスお願いします。

void CaaaDlg::OnTimer(UINT nIDEvent) 
{
    // TODO: この位置にメッセージ ハンドラ用のコードを追加するかまたはデフォルトの処理を呼び出してください
    KillTimer(10);

    // エクスプローラか?
    if (IsHookWindow(::GetForegroundWindow()) == TRUE) {
        // フック開始
    } else {
        // フック終了
    }
    SetTimer(10, 100, NULL);

    CDialog::OnTimer(nIDEvent);
}

BOOL CaaaDlg::IsHookWindow(HWND hWnd)
{
    CString clsName;
    char buffer[80];
    int len;
    BOOL bRet = FALSE;

    memset(buffer, 0, sizeof(buffer));
    len = ::GetClassName(hWnd, buffer, 80); //アクティブなウィンドウ名
    if (len > 0) {
        // Win+Eで開いたウィンドウとマイコンピュータから開いたウィンドウでクラス名が違う
        if ((strcmp(buffer, "CabinetWClass") == 0) || (strcmp(buffer, "ExploreWClass") == 0)) {
            // エクスプローラ内のフォーカスがあるウィンドウハンドルを取得
            DWORD ActiveProcessID, ActiveThreadID;
            ActiveThreadID = ::GetWindowThreadProcessId(hWnd, &ActiveProcessID);
            if (::AttachThreadInput(::GetCurrentThreadId(), ActiveThreadID, TRUE)) {
                // キーボードフォーカスがあるウィンドウハンドル取得
                HWND tmpWnd = ::GetFocus();
                ::AttachThreadInput(::GetCurrentThreadId(), ActiveThreadID, FALSE);
                memset(buffer, 0, sizeof(buffer));
                len = ::GetClassName(tmpWnd, buffer, 80);   //アクティブなウィンドウ名
                if (len > 0) {
                    if (strcmp(buffer, "SysListView32") == 0) {
                        // フック対象ウィンドウ
                        bRet = TRUE;
                    }
                }
            }
        } else {
            bRet = FALSE;
        }
    }
    return bRet;
}

環境はVC6(MFC)です。

編集 削除
オショウ  2013-04-01 20:22:17  No: 73656  IP: 192.*.*.*

フォーカス取った時だけフック・・・
そんな危ないことするのは間違い。

フックは行っておいて、フォーカスの移動イベントを
拾って、フック時の必要な機能を呼ぶか呼ばないか。
と言う動作にするべきです。

結局、Windowメッセージを取得する為に、もう1個、
別のフックを行わないといけないことになるのかナ〜

以上。参考まで

編集 削除
2tom  2013-04-03 08:17:32  No: 73657  IP: 192.*.*.*

>オショウさん
返答ありがとうございます。

必要な時だけフックを行った方が他への影響が少ないと思いこのようにしました。
今後の為に教えて頂きたいのですが、なぜ必要な時だけフックを行うのは危ないのでしょうか?

エクスプローラのフォーカス移動のイベントを拾うためにDLL内で

//フックインストール
extern "C" __declspec(dllexport) void HookInstall(HWND hWnd, LONG fcsMsg)
{
  if (g_hDll == NULL) {
    return;
  } else {
    g_hWnd = hWnd;
    g_fcsMsg = fcsMsg;

    hHookCbt = SetWindowsHookEx(WH_CBT, (HOOKPROC)CbtProc, g_hDll, 0 );
    if ( hHookCbt == NULL){
      MessageBox(NULL,"CBTフック失敗","エラー",MB_OK | MB_TOPMOST);
    } else {
//      MessageBox(NULL,"CBTフック成功","成功",MB_OK | MB_TOPMOST);
    }
  }
}

// Cbtプロシージャ
LRESULT CALLBACK CbtProc(int nCode, WPARAM wParam, LPARAM lParam)
{
  BOOL bHandle = FALSE;

  TRACE("CBT %d\n", nCode);

  if ( nCode < 0 ) {
    return CallNextHookEx(hHookCbt, nCode, wParam, lParam);
  }

  switch (nCode) {
  case HCBT_SETFOCUS:
    // wParam:キーボードフォーカスを受け取るハンドル
    // lParam:キーボードフォーカスを失うハンドル
    // 自分にメッセージ送信
    if (g_hWnd) {
      ::PostMessage(g_hWnd, g_fcsMsg, wParam, lParam);
    }
    break;
  default:
    break;
  }

// フックを行わない処理は、必ず次の関数に処理を委ねる
    return CallNextHookEx(hHookCbt, nCode, wParam, lParam);
}

としましたが、CbtProc関数が自アプリのイベントしか拾えません。
エクスプローラのフォーカスイベントはこの方法では拾えないのでしょうか?

よろしくお願いします。

編集 削除
2tom  2013-04-03 08:49:29  No: 73658  IP: 192.*.*.*

CbtProcが発生しないのはhHookCbtを初期化していないためでした。

#pragma data_seg("TEST_DATA")
HHOOK hHookCbt = 0;
#pragma data_seg()

としたフックできるようになりました。

編集 削除