DOBON.NET DOBON.NETプログラミング掲示板過去ログ

registerHotKey で複数ホットキーを登録するには?

環境/言語:[Win XP]
分類:[.NET]

はじめまして。Misonoと申します。
VB.Net2005 Win XP を使用して開発しています。

RegisterHotKey による複数のホットキーを登録する方法がわからず困っています
http://smdn.invisiblefulmoon.net/ikimasshoy/dotnettips/tips025.html

別サイトの情報で大変申し訳ないのですが、
他に、どこできいたらよいかわからなかったので、お詳しい方々のいらっしゃるこちらへ投稿させていただくことにしました。

たとえば、ホーム1に「スタート」と「ストップ」というボタンを配置して、
スタートには Ctrl+1、ストップには Ctrl+2 を割り当てたいと思っております。
ところが、上記サイトのコードではホットキーを1つしか登録できないようなのです。
どのように書き換えれば複数のホットキーに対応できるようになるでしょうか?
MSDNや海外のサイトなども参照してみたのですが VB.net+複数ホットキーの情報がなく、困り果てています。よろしくお願いします。

Imports System.Runtime.InteropServices

Public Class Form1
Inherits System.Windows.Forms.Form

' ホットキーを登録する
<DllImport("user32", EntryPoint:="RegisterHotKey")> _
Private Shared Function RegisterHotKey( _
ByVal hWnd As IntPtr, _
ByVal id As Integer, _
ByVal fsModifier As Integer, _
ByVal vk As Integer) _
As Integer
End Function

' ホットキーを解除する
<DllImport("user32", EntryPoint:="UnregisterHotKey")> _
Private Shared Function UnregisterHotKey( _
ByVal hWnd As IntPtr, _
ByVal id As Integer) _
As Integer
End Function

' ホットキーの修飾キー
Private Const MOD_ALT As Byte = &H1
Private Const MOD_CONTROL As Byte = &H2
Private Const MOD_SHIFT As Byte = &H4

<Flags()> _
Private Enum KeyModifiers As Integer

None = 0
Alt = MOD_ALT
Control = MOD_CONTROL
Shift = MOD_SHIFT

End Enum



Dim id As Integer ' ホットキーのID
Dim lParam As IntPtr ' WndProc()メソッドでホットキーを識別するためのlParam値

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

' Alt + Ctrl + Spaceをホットキーに指定する
RegisterHotKey(Me.Handle, Keys.Space, KeyModifiers.Alt Or KeyModifiers.Control, id, lParam)

End Sub

Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing

' 指定したホットキーを解除する
UnregisterHotKey(Me.Handle, id)

End Sub

' ホットキーの設定を行う
Private Function RegisterHotKey(ByVal hWnd As IntPtr, ByVal key As Keys, ByVal modifier As KeyModifiers, ByRef id As Integer, ByRef lParam As IntPtr) As Boolean

' これらの値は戻り値として返される
id = CInt(key) Or CInt(modifier) * &H100
lParam = New IntPtr(CInt(modifier) Or CInt(key) * &H10000)

' ホットキーの指定
Dim result As Integer

result = RegisterHotKey(hWnd, id, CInt(modifier), CInt(key))

Return (result <> 0)

End Function

' ホットキーの入力メッセージを取得する
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)

Const WM_HOTKEY As Integer = &H312

If m.Msg = WM_HOTKEY AndAlso m.LParam.Equals(lParam) Then

' フォームをアクティブにする
Me.Activate()

Else

' 基底クラスでのメッセージ処理
MyBase.WndProc(m)

End If

End Sub

#End Region

End Class
idとキーの組み合わせを変えればよいだけです。
「1つしか登録できないようだ」と判断した理由は何でしょうか。
あと、VB.NETであることは関係ありません。
あと、同じキーの組み合わせで複数のアプリケーションが登録した場合は早い者勝ちです。
なるべく重ならないような組み合わせにするなど工夫も必要でしょう。
■No20933に返信(まどかさんの記事)
> idとキーの組み合わせを変えればよいだけです。
> 「1つしか登録できないようだ」と判断した理由は何でしょうか。

さっそくのお返事ありがとうございます!
「1つしか登録できないようだ」の理由は
ホームをロードするときに RegisterHotKey を Ctrl+1 と Ctrl+2 のそれぞれについて登録しましたが、コンパイルしてそれぞれのキーを押すと、
後の方で登録した Ctrl +2 のみが有効になっており、
先に登録した Ctrl +1 が無効になっていたからです。

おそらく callbackの理解・使い方がまずいのだと思うのですがどのように解決したらよいかわかりません。

下記のようなコードに修正しましたがまだうまくいっていません
すいませんが、続けて、どうぞアドバイスをいただけないでしょうか?



Private Declare Function RegisterHotKey _
Lib "user32" (ByVal hwnd As IntPtr, _
ByVal id As Integer, ByVal fsModifiers _
As Integer, ByVal vk As Keys) As Integer

Private Declare Function UnregisterHotKey _
Lib "user32" (ByVal hwnd As IntPtr, _
ByVal id As Integer) As Integer

Private Declare Function GlobalAddAtom _
Lib "kernel32" Alias "GlobalAddAtomA" _
(ByVal lpString As String) As Short

Private Declare Function GlobalDeleteAtom _
Lib "kernel32" (ByVal nAtom As Short) As Short

Private Const MOD_ALT As Integer = &H1
Private Const MOD_CONTROL As Integer = &H2
Private Const MOD_SHIFT As Integer = &H4
Private Const MOD_WIN As Integer = &H8
Private Const WM_HOTKEY As Integer = &H312

Dim hotkeyID1 As Short
Dim hotkeyID2 As Short

Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles MyBase.Load

hotkeyID1 = GlobalAddAtom("GlobalHotKey " & _
Me.GetHashCode().ToString())
RegisterHotKey(Me.Handle, hotkeyID1, _
MOD_CONTROL, Keys.D1)

hotkeyID2 = GlobalAddAtom("GlobalHotKey " & _
Me.GetHashCode().ToString())
RegisterHotKey(Me.Handle, hotkeyID2, _
MOD_CONTROL, Keys.D2)


End Sub

Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing

UnregisterHotKey(Me.Handle, hotkeyID1)
UnregisterHotKey(Me.Handle, hotkeyID2)

GlobalDeleteAtom(hotkeyID1)
GlobalDeleteAtom(hotkeyID2)

End Sub

Protected Overrides Sub WndProc( _
ByRef m As Message)

MyBase.WndProc(m)

If m.Msg = WM_HOTKEY Then
MessageBox.Show(m.Msg.ToString & ":" & m.WParam.ToString & ":" & m.LParam.ToString)

End If


End Sub
なにやら、CallbackとかShortとかアトムとかが急に出てきましたね。
意図した動きという結果が欲しいのはわかりますが、サンプルの内容の一つ一つを理解されていないようですね。
このまま続けると1から教えなければなりませんのでサンプルを書きます。
参考にされた元のサンプルや自分で書いたコードと比べてみて、書かれている一つ一つのことを理解されるとよいと思います。
その上でピンポイントの質問があればまた投稿したりすればと思います。

下記のコードを新規プロジェクトのフォーム(ボタン1個)に貼り付けて実行してみてください。
1.実行
2.ボタンクリック(ホットキー登録)
3.CTRL+ALT+F7 でフォームを前面に表示
4.CTRL+ALT+F8 でメッセージボックス表示

Public Class Form1

' ホットキーメッセージ
Private Const WM_HOTKEY As Integer = &H312
' 修飾キー
Private Enum ModifierFlags As Integer

None = 0
ALT = &H1
CONTROL = &H2
SHIFT = &H4
WIN = &H8

CONTROL_ALT = CONTROL Or ALT
CONTROL_SHIFT = CONTROL Or SHIFT
CONTROL_ALT_SHIFT = CONTROL Or ALT Or SHIFT
ALT_SHIFT = ALT Or SHIFT

End Enum
' 登録ID
Private Const WM_USER As Integer = &H400
Private Const WM_MyHotkeyId1 As Integer = WM_USER + 1
Private Const WM_MyHotKeyId2 As Integer = WM_USER + 2
' 登録修飾キー
Private Const MyHotkeyModifier1 As ModifierFlags = ModifierFlags.CONTROL_ALT
Private Const MyHotkeyModifier2 As ModifierFlags = ModifierFlags.CONTROL_ALT
' 登録キー
Private Const MyHotkeyVirtualKey1 As Keys = Keys.F7
Private Const MyHotkeyVirtualKey2 As Keys = Keys.F8

Private Declare Function RegisterHotKey Lib "user32" (ByVal hwnd As Integer, ByVal id As Integer, _
ByVal fsModifiers As Integer, ByVal vk As Integer) As Integer
Private Declare Function UnregisterHotKey Lib "user32" (ByVal hwnd As Integer, ByVal id As Integer) As Integer

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim regSuccess1 As Integer = RegisterHotKey(Me.Handle.ToInt32, WM_MyHotkeyId1, MyHotkeyModifier1, MyHotkeyVirtualKey1)
Dim regSuccess2 As Integer = RegisterHotKey(Me.Handle.ToInt32, WM_MyHotKeyId2, MyHotkeyModifier2, MyHotkeyVirtualKey2)
MessageBox.Show("Hotkey registered.")
End Sub

Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
Dim unregSuccess1 As Integer = UnregisterHotKey(Me.Handle.ToInt32, WM_MyHotkeyId1)
Dim unregSuccess2 As Integer = UnregisterHotKey(Me.Handle.ToInt32, WM_MyHotKeyId2)
End Sub

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Select Case m.Msg
Case WM_HOTKEY
Select Case m.WParam.ToInt32
Case WM_MyHotkeyId1
If Me.WindowState = FormWindowState.Minimized Then
Me.WindowState = FormWindowState.Normal
End If
Me.Activate()
Case WM_MyHotKeyId2
MessageBox.Show("Hotkey 2 Pressed.")
End Select
End Select
MyBase.WndProc(m)
End Sub

End Class
無事、自分のやりたいことができました!
美しいサンプルコードまで提供していただき心より感謝しております。

■No20941に返信(まどかさんの記事)
> なにやら、CallbackとかShortとかアトムとかが急に出てきましたね。
> 意図した動きという結果が欲しいのはわかりますが、サンプルの内容の一つ一つを理解されていないようですね。
> このまま続けると1から教えなければなりませんのでサンプルを書きます。

.net で開発を始めて1ヶ月半なのでご指摘は確かです(汗)。
さらにホットキー関連のコードを書くのも初めてだったので御手数をおかけしてしまいました。
はじめはグローバルフックで対応しようと思い実際にDLLで、
フックを実装していました。このCLRで作成したDLLから
うまく本体へ戻り値を得られなかったので、RegisterHotKeyで対応することにしました。

これから精進して参ります。

> 参考にされた元のサンプルや自分で書いたコードと比べてみて、書かれている一つ一つのことを理解されるとよいと思います。
> その上でピンポイントの質問があればまた投稿したりすればと思います。

ありがとうございます。じっくり腰を据えてお勉強します。
それにしても、すごいプログラマに教えていただいて感激です。
今回はほんとに助かりました。ありがとうございました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板