-2

I've developed an application using vb.net that indicates the caps lock status of my system. There's a checkbox in the main form, whenever the caps lock is switched on by pressing the caps lock key, the checkbox gets checked and when turned off the checkbox gets unchecked. Below are the codes:

Public Class Form1

Dim keyStCL As Boolean = False

Private WithEvents KeyHook As New KeyboardHook

Private Const VK_CAPSLOCK As Integer = &H14
Private Const VK_SCROLLLOCK As Integer = &H91
Private Const VK_NUMLOCK As Integer = &H90

Private Const KEYEVENTF_EXTENDEDKEY As Integer = &H1
Private Const KEYEVENTF_KEYUP As Integer = &H2

 Private Sub SimulateKeyPressCaps(ByVal bVKCode As Byte, ByVal bScanCode As Byte)
    Call keybd_event(VK_CAPSLOCK, &H45, KEYEVENTF_EXTENDEDKEY Or 0, 0)
    Call keybd_event(VK_CAPSLOCK, &H45, KEYEVENTF_EXTENDEDKEY Or KEYEVENTF_KEYUP, 0)
End Sub

Private Sub checkbutton_caps_CheckedChanged(sender As Object, e As EventArgs) Handles checkbutton_caps.Click

    If keyStCL = True Then
        SimulateKeyPressCaps(VK_CAPSLOCK, &H45)
        checkbutton_caps.Image = Image.FromFile("resources\btn_ico_caps_off.png")
        checkbutton_caps.Checked = False

    ElseIf keyStCL = False Then
        SimulateKeyPressCaps(VK_CAPSLOCK, &H45)
        checkbutton_caps.Image = Image.FromFile("resources\btn_ico_caps_on.png")
        checkbutton_caps.Checked = True

    End If
End Sub

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Activated

    Me.KeyPreview = True

    If My.Computer.Keyboard.CapsLock = True Then
        keyStCL = True
        My.Computer.Audio.PlaySystemSound(Media.SystemSounds.Beep)
        checkbutton_caps.Checked = True
        checkbutton_caps.Image = Image.FromFile("resources\btn_ico_caps_on.png")
    ElseIf My.Computer.Keyboard.CapsLock = False Then
        keyStCL = False
        checkbutton_caps.Checked = False
        checkbutton_caps.Image = Image.FromFile("resources\btn_ico_caps_off.png")
    End If

Private Sub Form1_KeyPressCaps(ByVal sender As Object, e As KeyEventArgs) Handles Me.KeyDown

    If e.KeyCode = Keys.CapsLock Then
        If keyStCL = True Then
            checkbutton_caps.Checked = False
            checkbutton_caps.Image = Image.FromFile("resources\btn_ico_caps_off.png")
            keyStCL = False

        ElseIf keyStCL = False Then
            checkbutton_caps.Checked = True
            checkbutton_caps.Image = Image.FromFile("resources\btn_ico_caps_on.png")
            keyStCL = True

        End If
    End If
End Sub

Now the problem is all the events mentioned in the above codes get raised only when the main form has focus and not when it has lost focus. I am planning to run this program in background so that whenever I hit the caps lock key, I get notified, but this is not possible since the keyboard events do not get raised when the program has lost focus. How can I make my program raise events even when it does not have focus?

And below is the Keyboard Hook class I've used. (I haven't changed anything in the Keyboard Hook class i used it as it is on https://sim0n.wordpress.com/2009/03/28/vbnet-keyboard-hook-class/) After using this class I am now getting an error: Could not set Keyboard Hook. This error message was created by the Keyboard Hook class itself.

Imports System.Runtime.InteropServices

Public Class KeyboardHook

<DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
Private Overloads Shared Function SetWindowsHookEx(ByVal idHook As Integer, ByVal HookProc As KBDLLHookProc, ByVal hInstance As IntPtr, ByVal wParam As Integer) As Integer
End Function
<DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
Private Overloads Shared Function CallNextHookEx(ByVal idHook As Integer, ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
End Function
<DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
Private Overloads Shared Function UnhookWindowsHookEx(ByVal idHook As Integer) As Boolean
End Function

<StructLayout(LayoutKind.Sequential)> _
Private Structure KBDLLHOOKSTRUCT
    Public vkCode As UInt32
    Public scanCode As UInt32
    Public flags As KBDLLHOOKSTRUCTFlags
    Public time As UInt32
    Public dwExtraInfo As UIntPtr
End Structure

<Flags()> _
Private Enum KBDLLHOOKSTRUCTFlags As UInt32
    LLKHF_EXTENDED = &H1
    LLKHF_INJECTED = &H10
    LLKHF_ALTDOWN = &H20
    LLKHF_UP = &H80
End Enum

Public Shared Event KeyDown(ByVal Key As Keys)
Public Shared Event KeyUp(ByVal Key As Keys)

Private Const WH_KEYBOARD_LL As Integer = 13
Private Const HC_ACTION As Integer = 0
Private Const WM_KEYDOWN = &H100
Private Const WM_KEYUP = &H101
Private Const WM_SYSKEYDOWN = &H104
Private Const WM_SYSKEYUP = &H105

Private Delegate Function KBDLLHookProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer

Private KBDLLHookProcDelegate As KBDLLHookProc = New KBDLLHookProc(AddressOf KeyboardProc)
Private HHookID As IntPtr = IntPtr.Zero

Private Function KeyboardProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
    If (nCode = HC_ACTION) Then
        Dim struct As KBDLLHOOKSTRUCT
        Select Case wParam
            Case WM_KEYDOWN, WM_SYSKEYDOWN
                RaiseEvent KeyDown(CType(CType(Marshal.PtrToStructure(lParam, struct.GetType()), KBDLLHOOKSTRUCT).vkCode, Keys))
            Case WM_KEYUP, WM_SYSKEYUP
                RaiseEvent KeyUp(CType(CType(Marshal.PtrToStructure(lParam, struct.GetType()), KBDLLHOOKSTRUCT).vkCode, Keys))
        End Select
    End If
    Return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam)
End Function

Public Sub New()
    HHookID = SetWindowsHookEx(WH_KEYBOARD_LL, KBDLLHookProcDelegate, System.Runtime.InteropServices.Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly.GetModules()(0)).ToInt32, 0)
    If HHookID = IntPtr.Zero Then
        Throw New Exception("Could not set keyboard hook")
    End If
End Sub

Protected Overrides Sub Finalize()
    If Not HHookID = IntPtr.Zero Then
        UnhookWindowsHookEx(HHookID)
    End If
    MyBase.Finalize()
End Sub

End Class
Xlam
  • 133
  • 2
  • 11
  • 1
    Possible duplicate of [How to listen keyboard in background and fire keystrokes on demand?](http://stackoverflow.com/questions/15038413/how-to-listen-keyboard-in-background-and-fire-keystrokes-on-demand) – Matt Wilko Jul 07 '16 at 14:40
  • You can only achieve this with a global keyhook – Mono Jul 07 '16 at 14:42
  • Reffering to the question on http://stackoverflow.com/questions/15038413/how-to-listen-keyboard-in-background-and-fire-keystrokes-on-demand I used the Keyboard Hook Class and now I am getting an error: Could not set keyboard hook – Xlam Jul 07 '16 at 15:28
  • I've edited the question. I've included the Keyboard Hook Class. – Xlam Jul 07 '16 at 15:40
  • 1
    This is now a new question – Matt Wilko Jul 07 '16 at 15:59

1 Answers1

-2

So the problem you are facing is that you want to be notified when an event happens but it only notifies you when the form that has the check box on it has the focus. Now I'm not an expert programmer but I just finished a book on visual basic. I think your problem lies in the event handler line. Right now you have:

Private Sub Form1_KeyPressCaps(ByVal sender As Object, e As KeyEventArgs) Handles Me.KeyDown

This line starts the sub routine for when any key is pressed down on the Me form which is the currently selected form. I can't experiment with VB right now because my license ran out and I'm getting another one soon. But you can try and specify all forms in the place of the Me. Or specify when the form is minimized. I really wish I had intellisense right now but something you could try may be

Private Sub Form1_KeyPressCaps(ByVal sender As Object, e As KeyEventArgs) Handles AllForms.KeyDown

or

Private Sub Form1_KeyPressCaps(ByVal sender As Object, e As KeyEventArgs) Handles Me.Minimized.KeyDown

I realize that both of these are probably the wrong syntax but the logic remains correct, or so I believe. I do not know what a global keyhook is but it may be worth researching in your case if my answer does not work for you. This is my first answer so I hope it helps

Lacuna
  • 13
  • 1
  • 1
  • 8
  • Also, you could try putting the keydown event handler inside the form.minimize event handler that way it will be ready for a keydown event after the form has been minimized. Not sure if this will work but you should try it! – Lacuna Jul 07 '16 at 15:13
  • 1
    Completely totally and utterly incorrect I'm afraid. There is no `AllForms` object and no `Me.Minimized.KeyDown` event – Matt Wilko Jul 07 '16 at 15:13
  • @MattWilko Yes sir I know there is no `AllForms` object and as I said it was probably incorrect but the logic of trying to specify every form in the project would make sense. As for the second one, I also know that there is no event for `Me.Minimized.KeyDown` event. But yet again, the logic of trying to handle the `KeyDown` event after the form has been minimized would make sense. – Lacuna Jul 07 '16 at 15:19
  • 1
    Unfortunately you can't invent events that don't exist. Keypres, Keydown and Keyup events only fire when the form has focus. To trap key events when the form doesn't have focus involves keyboard hooking as linked to in the comments to the original post – Matt Wilko Jul 07 '16 at 15:29
  • @MattWilko Thank you for taking the time to comment on my answer. And yes I know you cannot invent new events but i was just trying to explain it logically and I don't have access to MS VS at the moment so I could not test my answer. As for OP, I would look into keyboard hooking. – Lacuna Jul 07 '16 at 15:38