ダイアログボックス上でWM_KEYDOWNを捕まえる


へろり  2004-11-23 19:03:38  No: 55305

現在リソーススクリプトに書いたダイアログをDialogBox()マクロを使い表示させています。
そのダイアログ上でキー入力を検出したいのですが、WM_KEYDOWNが飛んできません。

ダイアログ上でキー入力の検出が出来ればWM_KEYDOWNにこだわるわけではありませんが、キーボードフックだけは使わないようにしたいと思っています。

どなたか分かる方いらっしゃいましたらよろしくお願いします。


DotL  2004-11-24 03:47:52  No: 55306

ダイアログに1つでもボタンなどのコントロールがあれば、
普通は来ないでしょう。
WM_KEYDOWN メッセージは、ダイアログ内で入力フォーカスが当てられているコントロールに送られます。
つまり、拾うには、SetWindowLong 関数を使ってコントロールのウインドウハンドルのプロシージャをダイアログのプロシージャに変更すればいくと思います。


へろり  2004-11-24 08:23:49  No: 55307

返信ありがとうございます。

>ダイアログに1つでもボタンなどのコントロールがあれば、
>普通は来ないでしょう。

件のダイアログには画像が表示されているだけで、各キー操作により
その画像に変更を加えたいと考えていますので、コントロールは一つもありません。

>WM_KEYDOWN メッセージは、ダイアログ内で入力フォーカスが当てられているコントロールに送られます。

ダイアログそのものに入力フォーカスを当てることは出来ないのでしょうか。
WM_INITDIALOGのタイミングでSetFocus()APIを使いダイアログにフォーカスを
設定しているのですが、依然としてWM_KEYDOWNは飛んできません。

>つまり、拾うには、SetWindowLong 関数を使ってコントロールのウインドウハンドルのプロシージャをダイアログのプロシージャに変更すればいくと思います。

私はウィンドウプロシージャはそのウィンドウの挙動を決定するものだと考えています。
また、ダイアログボックスはサブクラス化などをするまでも無くプロシージャを
いじれる立場にありますし、他のコントロールからプロシージャを引っ張ってくる
のは出来れば避けたいと思います。

現在DotLさんの返信を参考に、キー入力を受け付けるためだけのコントロールを作成し、
そのコントロールがキー入力を受け取ったら、ダイアログにWM_KEYDOWNメッセージを
SendMessage()で送るようにするしか無いかなと考えていますが、どうも煩雑な
気がします。
どうにかもっとスマートに書く手は無いでしょうか。

非常にわがままを言っているとは思いますが、よろしくお願いします。


カラカラ  2004-11-24 10:43:11  No: 55308

PreTranslateMessage()で拾えないですか?


へろり  2004-11-24 11:12:05  No: 55309

>PreTranslateMessage()で拾えないですか?

まずは最初の説明で言葉が足りなかったことをお詫びします。

現在 Win2k VC++5.0 にて開発を行っており、非MFCです。
ですので、上記のメンバ関数は使用できません。

わざわざお手数かけていただいたのに申し訳ありません。


Ban  2004-11-24 12:24:03  No: 55310

# 申し訳ありませんが、直接的な回答にはなってません。

> 私はウィンドウプロシージャはそのウィンドウの挙動を決定する
> ものだと考えています。

ウィンドウプロシージャの位置づけについてはおおむね同意しますが、

> また、ダイアログボックスはサブクラス化などをするまでも無く
> プロシージャをいじれる立場にありますし、

ダイアログのプロシージャは、あくまでウィンドウプロシージャの
サブセットであって両者は等価でなく、用法の簡便化と、それに伴う
制約は表裏一体ではないかと思ってます。

ダイアログの枠を超えた制御をするのであれば、通常のウィンドウを
作成してスタイルをダイアログ風に変更するのが素直な実装では
ないかと思いますが、それでどうでしょうか。

また、

> キー入力を受け付けるためだけのコントロールを作成し、
> SendMessage()で送るようにするしか無いかなと考えていますが、

この実装よりはキー入力をローカルフックする方がまだやりたいことに
素直だと感じるのですが、ローカルフックを避ける理由は不可避の
ものですか。


へろり  2004-11-24 14:39:10  No: 55311

>ダイアログの枠を超えた制御をするのであれば、通常のウィンドウを
>作成してスタイルをダイアログ風に変更するのが素直な実装では
>ないかと思いますが、それでどうでしょうか。

上記の案も最初検討してみましたが、通常のウィンドウをモーダルにする方法が
分からなかった事とDialogBox()マクロを使った方が楽っぽかたったため現在の
方向で検討しています。

>この実装よりはキー入力をローカルフックする方がまだやりたいことに
>素直だと感じるのですが、ローカルフックを避ける理由は不可避の
>ものですか。

件のダイアログボックスを表示中にもオーナーウィンドウは様々な処理を
行います。
その際、エラーなどでなにかしらのメッセージボックスなどを出す可能性が
あります。
そのときフックが生きていると、そのメッセージボックスへのキーイベントも
フックしてしまいちょっと困ります。
また、このダイアログプロシージャはスコープの関係上オーナーウィンドウの
プロシージャからは見えません。
オーナーウィンドウ側からフックを殺せるようにフックハンドルをあらかじめ
渡しておくとか、メッセージボックスを表示するさいダイアログへ通知するとか
いうのもちょっとアレな気がします。

以上がフックを避ける理由です。  必ずしも不可避と言うわけではありませんが、
あまりいい手が思い浮かばないので出来れば避けて通りたいと考えています。

# 申し訳ありませんが、直接的な回答にはなってません。

いえいえ、私はこういうやりとりも大切だと思ってます(*^ ^*)

長くなりましたが引き続き良い案がありましたらよろしくお願いします。


DotL  2004-11-25 08:40:33  No: 55312

>件のダイアログには画像が表示されているだけで、各キー操作により
>その画像に変更を加えたいと考えていますので、コントロールは一つもありません。

その画像の出力先は、ピクチャーボックスなどのスタティックコントロールなどは一切なしで、直接、ダイアログボックスの表面に描いてるってことですか?
それで来ないのは確かにおかしいです。
(...ってだから質問してる って話なんですが。) 
GetKeyState 関数で拾うとか...同じことですよね。


(noname)  2004-11-25 13:30:22  No: 55313

(;´Д`)y-~~ 腹が痛ぇ。なんか中ったかも。

まず,ダイアログプロシージャの WM_INITDIALOG で
TRUE を返している事を確認してみな。
ここで FALSE を返していると,DialogBox() は勝手に
どっかヘンな所にフォーカスを当ててしまう。

それでもよく分かんねかったら,ダイアログプロシージャに
次のコードを適当に埋め込んでみろよ。
ダイアログの左上に "focus" と表示されていれば
ダイアログにフォーカスが当たってる事を意味する。
それだけのコードだけどな ( ´ー`)y-~~ うひひ

BOOL __stdcall YourDialogProc (HWND hwnd, UINT msg, WPARAM, LPARAM )
{
  (前略)

  // WM_PAINT に反応する
  if (msg == WM_PAINT)
  {
    PAINTSTRUCT ps;
    HDC dc = ::BeginPaint(hwnd, &ps);
    if (::GetFocus() == hwnd)
      ::TextOut(dc, 0, 0, "focus", 5);
    ::EndPaint(hwnd, &ps);
    return TRUE;
  }

  // フォーカスの変化に反応する
  if (msg == WM_SETFOCUS || msg == WM_KILLFOCUS)
  {
    ::RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
    return TRUE;
  }

  (後略)
  return FALSE;
}


へろり  2004-11-25 16:18:23  No: 55314

DotLさんへ

>その画像の出力先は、ピクチャーボックスなどのスタティックコントロールなどは一切なしで、直接、ダイアログボックスの表面に描いてるってことですか?

そうです。  ダイアログボックスのデバイスコンテキストを取得し、それに直接
描画しています。

>GetKeyState 関数で拾うとか...同じことですよね。

これもやっぱりキー入力のタイミングを検知する必要があるので。

(noname)さんへ

>まず,ダイアログプロシージャの WM_INITDIALOG で
>TRUE を返している事を確認してみな。
>ここで FALSE を返していると,DialogBox() は勝手に
>どっかヘンな所にフォーカスを当ててしまう。

もちろんTRUEを返しています。  (noname)さんのコードを走らせると確かに
focusと言う表示はあるものの依然としてWM_KEYDOWNは飛んできません。

そこで下記のコードを走らせてみると、WM_SETFOCUSが飛んできたその直後に
WM_KILLFOCUSが飛んできているのが確認できます。

BOOL CALLBACK hogeDialogProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp){
    switch(msg){
        case WM_INITDIALOG:
            SetFocus(hwnd);
        return TRUE;
        case WM_SETFOCUS:
            SetWindowText(hwnd, "FOCUS!!");
        break;
        case WM_KILLFOCUS:
            SetWindowText(hwnd, "UN FOCUS");
        break;
    }

    return FALSE;
}

どうも私のダイアログはフォーカスによほど嫌われているようです。


なーめ  2004-12-05 13:22:58  No: 55315

SetFocus() の戻り値を表示して
Spy++ で探したら、なにか根本的な問題が見つかりそうな気がする。
時間が経過しているので既に解決しているかもしれないが。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加