Hotkeys not working

vendiddy

Well-known member
Joined
May 14, 2006
Messages
64
Programming Experience
1-3
I am trying to use the classes provided in the example from the url below. It encapsulates the same hotkey functionality in a couple of classes.
http://www.codeproject.com/vb/net/mclhotkeynet.asp

- I'm pretty sure I shouldn't be using 123 and 321 as the ID, but I'm not sure what to do there.
- I'm also not sure how to unregister the hotkey when the form closes.
- When I press Windows Key + H, or I press I, both of the Subs are triggered. How do I have each Sub execute seperately?
- With my code, the events are cancelled and not passed on. How do I allow the events to be passed on?

Here is my code which attempts to use these classes. I uploaded the classes I used in GlobalHotkeyListener.zip.
VB.NET:
Public Class frmInput
    Public WithEvents myActivatorKey As GlobalHotkeyListener
    Public WithEvents mySecondKey As GlobalHotkeyListener

    Private Sub frmInput_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
        'How do I unregister the hotkeys?
    End Sub

    Private Sub frmInput_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        myActivatorKey = New GlobalHotkeyListener(123, HotkeyModifierFlags.MOD_WIN, Keys.H)
        AddHandler myActivatorKey.HotkeyPressed, AddressOf HotkeyPressed
        mySecondKey = New GlobalHotkeyListener(321, 0, Keys.I)
        AddHandler mySecondKey.HotkeyPressed, AddressOf YouPressedI
    End Sub

    Public Sub HotkeyPressed(ByVal sender As Object, ByVal e As System.EventArgs)
        MessageBox.Show("You pressed Windows Key + H!")
    End Sub

    Public Sub YouPressedI(ByVal sender As Object, ByVal e As System.EventArgs)
        MessageBox.Show("You pressed I!")
    End Sub
End Class

Thanks. :)
 

Attachments

  • GlobalHotkeyListener.zip
    4.3 KB · Views: 19
My guess is the event or something from within the GlobalHotKeyListener Class is Shared causing it to fire for both objects.

The subs are not passed on? You mean the keys? That would mean either a property or method in the GlobalHotKeyListener Class returns True for the keys being handled or by default it returns true in which your screwed. Unless you wrote the class then you can modify it.
 
I'm unable to make it work with multiple hotkeys. For example, I wanted my program to activate when Windows + H is pressed and my program to exit when Ctrl + Q is pressed. Thanks.

Also, how would I seperate the code from form and maybe put it into a class of its own?
 
The example shows only one hotkey, but all you need to do is add one to the atom array and follow the same schema to register it, handle it, unregister it. IE, all code lines there that regards AtomIds(0) you must also do it you got AtomIds(1) etc.
 
Thanks is this the best way to be doing this?

VB.NET:
Public Class frmInput

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

#Region "HotKey"
    Dim AtomIDs(1) As Int32
    Dim AtomNames() As String = {"My Hotkey 1", "My Hotkey 2"}
    Dim vKeys() As Int32 = {Keys.H, Keys.I}
    Dim fsModifiers() As Int32 = {Win32.HotkeyModifierFlags.MOD_WIN, Win32.HotkeyModifierFlags.MOD_CONTROL}


    Private Sub SetupHotKey()
        'register hotkeys
        For I As Integer = 0 To vKeys.Length - 1
            AtomIDs(I) = Win32.GlobalAddAtom(AtomNames(I))
            If Win32.RegisterHotKey(Me.Handle, AtomIDs(I), fsModifiers(I), vKeys(I)) = False Then
                MsgBox("error registering hotkey" & I)
            End If
        Next
    End Sub

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        Select Case (m.Msg)
            Case Win32.WM_HOTKEY
                For I As Integer = 0 To vKeys.Length - 1
                    If m.WParam.ToInt32() = AtomIDs(I) Then
                        MessageBox.Show("You activated " & AtomNames(I) & "!")

                        m.Result = New IntPtr(1)
                    End If
                Next
        End Select
        MyBase.WndProc(m)
    End Sub


    Private Sub DeHotKey()
        For I As Integer = 0 To vKeys.Length - 1
            Win32.UnregisterHotKey(Me.Handle, AtomIDs(I))
            Win32.GlobalDeleteAtom(AtomIDs(I))
        Next
    End Sub

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

#End Region 'Hotkey

End Class

Friend Class Win32
    Declare Function RegisterHotKey Lib "user32" (ByVal hwnd As IntPtr, ByVal Id As Int32, ByVal fsModifiers As Int32, ByVal vkey As Int32) As Boolean
    Declare Function UnregisterHotKey Lib "user32" (ByVal hwnd As IntPtr, ByVal Id As Int32) As Boolean
    Public Enum HotkeyModifierFlags
        MOD_ALT = &H1
        MOD_CONTROL = &H2
        MOD_SHIFT = &H4
        MOD_WIN = &H8
    End Enum
    Declare Function GlobalAddAtom Lib "kernel32" Alias "GlobalAddAtomA" (ByVal lpString As String) As Int32
    Declare Function GlobalDeleteAtom Lib "kernel32" (ByVal nAtom As Int32) As Int32
    Public Const WM_HOTKEY As Integer = &H312
End Class 'Win32

Also, is it possible to seperate this hotkey code into a class of its own? (Or am I better off leaving it in main form's code?)

Here's the trouble I'm having with doing this:
- It seems like I cannot move WndProc to a seperate class (I don't know why).
- Me.Handle only seems to be a property of forms so I'm unable to move SetupHotkey and DeHotkey to a seperate class.

Thanks for the help.
 
Last edited:
It can be made a class, but it is still dependent of the form/control because this is where the messages are passed from system, the window handle is used for registering the hotkey. The class needs to implement IMessageFilter and the instance of this class must be passed to shared Application.AddMessageFilter/RemoveMessageFilter methods.

I have taken the previous example and made it a user-friendly class (+ related classes). I choose here to key the hotkeys to a user chosen atomstring, it have to be unique for this application only. The same string is passed back from the hotkey event and as a user of the class you must keep/use these strings to distinguish different hotkeys. Same is used if you need to dynamically remove a hotkey. The class is attached to this post, add it to a project with 'Add Existing Item...'

Example usage:
VB.NET:
Private Sub frmDiv_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) _
Handles Me.FormClosed
    hots.[SIZE=2]Unregister[/SIZE]()
End Sub
 
Private Sub frmDiv_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles MyBase.Load
    hots = New NSHotkey.Hotkeys(Me)
    setuphotkeys()
End Sub
 
WithEvents hots As NSHotkey.Hotkeys
 
Sub setuphotkeys()
    'Ctrl+J
    hots.Add(New NSHotkey.HotKey("ctrlj", Keys.J, NSHotkey.HotkeyModifiers.MOD_CONTROL))
    'Shift+H
    hots.Add(New NSHotkey.HotKey("shifth", Keys.H, NSHotkey.HotkeyModifiers.MOD_SHIFT))
    'Win+A
    hots.Add(New NSHotkey.HotKey("wina", Keys.A, NSHotkey.HotkeyModifiers.MOD_WIN))
End Sub
 
Private Sub hots_Hotkey(ByVal e As NSHotkey.HotKeyEventArgs) Handles hots.Hotkey
    MsgBox(e.Key.AtomString)
    Select Case e.Key.AtomString
        Case "ctrlj"
        Case "shifth"
        Case "wina"
    End Select
End Sub
A little note, you might notice the GlobalAddAtom used in previous example is no longer used in class, reason is this class is intended for application usage, I found a comment in new documentation that registering the global atom was for shared dlls only, didn't see it or understand it before, this also affects the id range used which for applications should be 0-49251 or something, the class uses a static counter to ensure adding/removing hotkeys don't overlap ids. If you add/remove more than 49000 hotkeys within the runtime of the application you should implements this counter differently.

New info: I forgot to add None=0 option to the HotkeyModifiers enumeration, so you can do this, or just use 0 as value when using the HotKey constructor.
New info2: There is a gotcha when hiding/showing form with system tray in that the forms handle is destroyed and new one created, so you can accomodate this by creating the Hotkeys instance and configure it in HandleCreated event and unregister in HandleDestroyed event. (a slight rewrite of the Hotkeys class is probably due for better management of such cases)
 

Attachments

  • vbnet20-clsHotkeys.zip
    1.1 KB · Views: 36
Last edited:
Thank you I found that class very helpful and had no problems getting it to work. I changed it a bit to fit my application.
 
Hi! I have just use the class above and it works well, but now I get problem with finding the hot key, I want to use Ctrl Key + two key named with left square bracket and right one (on the left of "P" key) for zoom in and out the pictureBox
Could you tell me the code of these key, thanks !!!
 
Back
Top