キーフックを設定するには?

解決


あきたいぬ  2007-05-10 00:05:46  No: 136350

VB2005で開発しています。

画面起動時にキーボードのフックを設定して、[F1] ボタン押下時に
自作のヘルプ画面を表示させるような処理を作成しようとしています。

マウスのフック設定方法はネット検索にて見つけられたのですが、
http://support.microsoft.com/kb/319524/ja
キーボードのフックにうまく当てはめれなくて困っています。

実際には以下のように設定しています。

    hookproc = AddressOf KeyHookProc
    hHook = SetWindowsHookEx(WH_KEYBOARD, _
                             hookproc, _
                             IntPtr.Zero, _
                             AppDomain.GetCurrentThreadId)

    Public Function KeyHookProc( _
        ByVal nCode As Integer, _
        ByVal wParam As IntPtr, _
        ByVal lParam As IntPtr) As Integer

        If (nCode < 0) Then
            Return CallNextHookEx(hHook, nCode, wParam, lParam)
        End If

        If wParam = Keys.F1 Then
            Shell("explorer.exe D:\DataDrive\test.htm", _
                  AppWinStyle.NormalFocus)
        End If
        Return CallNextHookEx(hHook, nCode, wParam, lParam)
    End Function

実行して[F1]押下すると、画面が2〜3枚表示されます。
複数のキーイベントで呼ばれているのだと思うのですが、イベントの
認識はどうやって判定すればいいのでしょうか。

また、マウスフック設定の確認をしていて感じたんですが、
SetWindowsHookExの第4パラメータに設定している
AppDomain.GetCurrentThreadId  はコンパイル時に「旧形式」と
警告が出るため、Thread.CurrentDomain.ManagedThreadidを
設定すると今度はフックの設定がNGになってしまいます。
警告は無視してかまわないのでしょうか。

合わせてご教授いただけませんでしょうか。
よろしくお願いします。


Hongliang  URL  2007-05-10 01:54:43  No: 136351

> 複数のキーイベントで呼ばれているのだと思うのですが、イベントの
> 認識はどうやって判定すればいいのでしょうか。
http://msdn.microsoft.com/library/ja/default.asp?url=/library/ja/jpipc/html/_win32_keyboardproc.asp
lParam がキーの状態を保持してるようですね。ビット演算が必要ですが。

> SetWindowsHookExの第4パラメータに設定している
> AppDomain.GetCurrentThreadId  はコンパイル時に「旧形式」と
> 警告が出るため、Thread.CurrentDomain.ManagedThreadidを
> 設定すると今度はフックの設定がNGになってしまいます。
マネージドな(.NET 上の)スレッドと、Windows のネイティブなスレッドは別物ですから。
フックは Windows の機能なんで、マネージドなスレッドがどうなってようが関係ありません。
そして .NET 上では、ネイティブなスレッド ID を使ってもスレッドの識別に役立ちません。ので、Obsolete になったんでしょう。
AppDomain.CurrentThreadId の警告が目障りなら、Win32API の GetCurrentThreadId 関数を呼び出すことになりますかね。

それはそれとして、自アプリケーションだけなら Application.AddMessageFilter を使った方がスマートかもしれません。


我龍院  2007-05-10 03:19:49  No: 136352

VB2005なら下のコードではだめですか。

Public Class Form1
    Private ReadOnly VK_F1 As Integer = &H70
    Private ReadOnly WM_KWYDOWN As Integer = &H100
    Protected Overrides Sub WndProc(ByRef m As Message)
        If m.Msg = WM_KWYDOWN And m.WParam = VK_F1 Then
            Shell("explorer.exe D:\DataDrive\test.htm", AppWinStyle.NormalFocus)
        End If
        MyBase.WndProc(m)
    End Sub
End Class


あきたいぬ  2007-05-10 20:42:42  No: 136353

Hongliangさん回答ありがとうございます。

> lParam がキーの状態を保持してるようですね。ビット演算が必要ですが。
このビット演算方法がいまいちよくわかりません。
やり方教えてもらえませんでしょうか。

> マネージドな(.NET 上の)スレッドと、Windows のネイティブなスレッドは別物ですから。
> フックは Windows の機能なんで、マネージドなスレッドがどうなってようが関係ありません。
> そして .NET 上では、ネイティブなスレッド ID を使ってもスレッドの識別に役立ちません。ので、Obsolete になったんでしょう。
そうなんですか。勉強になりました。
警告説明にThreadのManagedThreadidを使用しろとあったので、
深く考えずに使用できるものと思っていました。

>AppDomain.CurrentThreadId の警告が目障りなら、Win32API の GetCurrentThreadId 関数を呼び出すことになりますかね。
はい。確認が出来ました。
ありがとうございます。

> それはそれとして、自アプリケーションだけなら Application.AddMessageFilter を使った方がスマートかもしれません。
すみません、これはいまいちよくわかりませんでした。
これを実行する場合はフックの設定自体は現状のままで
大丈夫なのでしょうか?
また、AddMessageFileterのパラメータにはどんな値を入れたら
いいのでしょうか。

我龍院さん回答ありがとうございます。
記述していただいたロジックだけで[F1]監視ができるという
ことですよね?
単独画面での確認は出来ました。ありがとうございます。

しかし、同一アプリケーション内に複数のフォームがある場合、
全てに同じロジックを入れなければならないのでしょうか。

アプリケーション起動中はどの画面においても[F1]の制御を
持たせたいのですが。。。

すみません、再度ご教授お願いします。


Hongliang  URL  2007-05-10 22:05:21  No: 136354

> これを実行する場合はフックの設定自体は現状のままで
> 大丈夫なのでしょうか?
> また、AddMessageFileterのパラメータにはどんな値を入れたら
> いいのでしょうか。

フックの代用としての提案です。採用する場合、フックは不要になります。
機能を簡単に解説すると、メッセージをアプリケーションが処理する前に覗き見て事前処理を施すことができるようになります。
引数に渡す IMessageFilter を実装するクラスは自分で作成することになります。この作成するクラスの PreFilterMessage メソッドで、渡ってきたメッセージに応じた処理を行うことができます。
例えば、キーが押されたとき、WM_KEYDOWN メッセージが送信されます。このとき、AddMessageFilter しておけば、そこで渡したインスタンスの PreFilterMessage メソッドが呼び出されます。
どのメッセージが送られてきたかは、PreFilterMessage メソッドの引数である Message 構造体の Msg プロパティで確認できます。
// WM_KEYDOWN がどんな値かは調べる必要がありますが、大抵はネットですぐ見つかります。
どのキーが押されたかは同じく Message 構造体の WParam プロパティですね。詳しくはネットなどで WM_KEYDOWN を調べてください。

Form の WndProc をオーバーライドして WM_KEYDOWN を調べる方法では、他のコントロールにフォーカスが移ったときに WM_KEYDOWN が飛んでこなくなる気がします。
それなら KeyPreview して KeyDown イベントで判断した方が自然かな。
もちろん全てのフォームに仕込む必要がありますけど。


あきたいぬ  2007-05-10 22:25:40  No: 136355

Hongliangさん、丁寧な説明ありがとうございます。

フックにしてもメッセージにしても、かなり奥が深いですね。
それでもどうにかビット演算も出来、[F1]押下にてヘルプ画面が
(1画面)表示されるところまで確認が出来ました。

説明していただいたAddMessageFileterの方はさっそく試して
見たいと思います。


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




  


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