1

First of all, I am not a true coder however I am happy with what I have done thus far.

I have written this code below which is to do with robotics. The code below allows me to user keyboard shortcuts when you click on the form. However, I need the hotkeys to work whether the application is or is not in focus or even minimized.

I have looked already online but it is not very clear.

Public Class MainForm

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

        If (e.KeyCode And Not Keys.Modifiers) = Keys.T AndAlso e.Modifiers = Keys.Control Then
            DF1Com1.Write("O:1/0", "1")  ' (O:9/0) (R)  
        End If

        'If e.KeyCode = Keys.R Then
        'DF1Com1.Write("O:1/0", "1")  ' (O:9/0) (R)   
        'End If

    End Sub

    Private Sub MainForm_KeyUp(sender As Object, e As KeyEventArgs) Handles Me.KeyUp

        If (e.KeyCode And Not Keys.Modifiers) = Keys.T AndAlso e.Modifiers = Keys.Control Then
            DF1Com1.Write("O:1/0", "0")  ' (O:9/0) (R) 
        End If

        'If e.KeyCode = Keys.R Then
        'DF1Com1.Write("O:1/0", "0") ' (O:9/0) (R) 
        'End If

    End Sub

Public Class MainForm


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

        If (e.KeyCode And Not Keys.Modifiers) = Keys.T AndAlso e.Modifiers = Keys.Control Then
            DF1Com1.Write("O:1/0", "1")  ' (O:9/0) (R) 
        End If

        'If e.KeyCode = Keys.R Then
        'DF1Com1.Write("O:1/0", "1")  ' (O:9/0) (R)
        'End If

    End Sub

    Private Sub MainForm_KeyUp(sender As Object, e As KeyEventArgs) Handles Me.KeyUp

        If (e.KeyCode And Not Keys.Modifiers) = Keys.T AndAlso e.Modifiers = Keys.Control Then
            DF1Com1.Write("O:1/0", "0")  ' (O:9/0) (R)  
        End If

        'If e.KeyCode = Keys.R Then
        'DF1Com1.Write("O:1/0", "0") ' (O:9/0) (R) 
        'End If

    End Sub

End Class

Update:

Right. I have added a class now which allow GlobalKey to be registered.

In my main form I now have this key:

   Public Class MainForm
    Dim hkr As New HotKeyRegistryClass(Me.Handle)

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        hkr.Register(HotKeyRegistryClass.Modifiers.MOD_CTRL, Keys.A).ToString()
        hkr.Register(HotKeyRegistryClass.Modifiers.MOD_CTRL, Keys.S).ToString()
        hkr.Register(HotKeyRegistryClass.Modifiers.MOD_CTRL, Keys.D).ToString()
        hkr.Register(HotKeyRegistryClass.Modifiers.MOD_CTRL, Keys.F).ToString()
        hkr.Register(HotKeyRegistryClass.Modifiers.MOD_CTRL, Keys.G).ToString()
        hkr.Register(HotKeyRegistryClass.Modifiers.MOD_SHIFT Or HotKeyRegistryClass.Modifiers.MOD_CTRL, Keys.H).ToString()
    End Sub

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        If m.Msg = HotKeyRegistryClass.Messages.WM_HOTKEY Then 'NOT THE ACTUAL WINDOWS NAMESPACE
            Dim ID As String = m.WParam.ToString()
            Select Case ID
                Case 0 : If DF1Com1.Write("O:1/0", "1") Then DF1Com1.Write("O:1/0", "0")
                Case 1 : MessageBox.Show("S")
                Case 2 : MessageBox.Show("D")
                Case 3 : MessageBox.Show("F")
                Case 4 : MessageBox.Show("G")
                Case 5 : MessageBox.Show("H")
            End Select
        End If
        MyBase.WndProc(m)
    End Sub

If you have a look at Case 0. It works however it does not put the key back up. It leaves it pressed down all the time. I need it when the key is depressed it 'DF1Com1.Write("O:1/0", "0")'

Class Code

Public NotInheritable Class HotKeyRegistryClass
    Private Declare Function RegisterHotKey Lib "user32.dll" (ByVal handle As IntPtr, ByVal id As Int32, ByVal fsModifier As Int32, ByVal vk As Int32) As Int32
    Private Declare Function UnregisterHotKey Lib "user32.dll" (ByVal handle As IntPtr, ByVal id As Int32) As Int32
    Private Handle As IntPtr = IntPtr.Zero
    Private Registry As New System.Collections.Generic.List(Of Int32)
    Public Enum Messages
        [WM_HOTKEY] = &H312
    End Enum
    Public Enum Modifiers
        [MOD_ALT] = &H1
        [MOD_CTRL] = &H2
        [MOD_SHIFT] = &H4
    End Enum
    Sub New(ByVal Handle As IntPtr)
        Me.Handle = Handle
    End Sub
    Public Function Register(ByVal Modifier As Int32, ByVal Key As System.Windows.Forms.Keys) As Int32
        Dim ret As Int32
        ret = NextAvailableIndex()
        Call RegisterHotKey(Me.Handle, ret, Modifier, Key)
        Registry.Insert(ret, ret)
        Return ret
    End Function
    Public Sub Unregister(ByVal ID As Int32)
        Call UnregisterHotKey(Me.Handle, ID)
        Registry.Remove(ID)
    End Sub
    Private Function NextAvailableIndex() As Int32
        Dim ret As Int32 = 0
        Dim n As Int32 = 0
        For i As Int32 = 0 To Registry.Count - 1
            If Registry(i) = n Then
                n = n + 1
            ElseIf n < Registry(i) Then
                Return n
            End If
        Next
        If n = Registry.Count Then
            Return Registry.Count
        End If
        Return ret
    End Function
End Class
Arthor
  • 654
  • 2
  • 12
  • 36
  • 1
    Look into `Windows.Input.Keyboard.GetKeyStates` and instead of capturing key events on your form, poll the key states in a timer or background thread. – Chase Rocker Nov 17 '17 at 22:47
  • 1
    A -1 for asking this. A little unjust. As I stated. I am not a hardcore coder. – Arthor Nov 18 '17 at 00:08
  • 1
    I have updated the code now, I am almost their. Still going at it but I am stuck with the last part. – Arthor Nov 18 '17 at 01:11
  • 1
    There must be more code you aren't posting. Nothing here would keep the key pressed. Listening for hotkeys is much better with a keyboard hook because you can use any key combination or even a single key. You have to do it all yourself though as far as keeping track of which modifiers are pressed and making sure you don't send the keys down the hook chain if you handle them yourself. – Michael Z. Nov 18 '17 at 02:10
  • @MichaelZ. Well the issue is as such. When I release the key I need this to be executed `DF1Com1.Write("O:1/0", "0")`, When I press the key `DF1Com1.Write("O:1/0", "1")`. It work one way I.e. when I press the key. It also work when not focusing on the application which is great. Evidently I have this code wrong. `Case 0 : If DF1Com1.Write("O:1/0", "1") Then DF1Com1.Write("O:1/0", "0")` – Arthor Nov 18 '17 at 02:15
  • I have also added the class code. – Arthor Nov 18 '17 at 02:20
  • 1
    You can't do that with `RegisterHotKey`. The key up and down events you have only work in your app and `RegisterHotkey` only reports that the key combination was activated. If you want global key up and down then you need a low level keyboard hook. Check my answer here...https://stackoverflow.com/questions/43268643/how-to-disable-override-windows-10-hotkeys-with-c-sharp/43302972#43302972 – Michael Z. Nov 18 '17 at 02:24
  • @MichaelZ. Right, dammit. Would it not be possible to execute the `DF1Com1.Write("O:1/0", "0")` after the `DF1Com1.Write("O:1/0", "1")`. I know it sound like a stupid question and I guess the system has not way of knowing you have depressed the key. I just want to confirm before I try you solution. Thank you very much! – Arthor Nov 18 '17 at 02:28
  • Why not just execute them one after the other. I don't quite understand why it's wrapped in an `If` – Michael Z. Nov 18 '17 at 02:30
  • 1
    @MichaelZ. Wow, BTW, great article you have made!!! – Arthor Nov 18 '17 at 02:30
  • 1
    You should upvote then ;) – Michael Z. Nov 18 '17 at 02:30
  • 1
    What's even better is the app that I link to in that question. zVirtualDesktop – Michael Z. Nov 18 '17 at 02:31
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/159272/discussion-between-arthor-and-michael-z). – Arthor Nov 18 '17 at 02:31
  • Do not poll key states as was suggested. That would be lazy and creates its own problems. Polling keys prevents you from stopping the keys from going up the chain. So if you handle a hotkey you have no way of stopping another app or the OS itself from handling the same hotkey. You can only do this with `RegisterHotkey` and a keyboard hook. With a hook you have to prevent it from going up the chain yourself, but with `RegisterHotkey` the OS handles that for you. – Michael Z. Nov 18 '17 at 18:01

2 Answers2

1

Unfortunately, RegisterHotkey only tells you when a key combination has been activated. Also, as you already realize, the KeyDown and KeyUp events you have will only work when your app has focus.

The only way you can get truly global KeyDown and KeyUp is by using a low level keyboard hook. Since I don't want to copy my entire answer I will simply link to it. The link will provide you some direction in setting up a keyboard hook.

How to disable/override Windows 10 Hotkeys with C#

Another possibility is to execute both commands on the hotkey activation like this...

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        If m.Msg = HotKeyRegistryClass.Messages.WM_HOTKEY Then 'NOT THE ACTUAL WINDOWS NAMESPACE
            Dim ID As String = m.WParam.ToString()
            Select Case ID
                Case 0 : 
                    DF1Com1.Write("O:1/0", "1")
                    System.Threading.Thread.Sleep(2000)
                    DF1Com1.Write("O:1/0", "0")
                Case 1 : MessageBox.Show("S")
                Case 2 : MessageBox.Show("D")
                Case 3 : MessageBox.Show("F")
                Case 4 : MessageBox.Show("G")
                Case 5 : MessageBox.Show("H")
            End Select
        End If
        MyBase.WndProc(m)
    End Sub
Michael Z.
  • 1,453
  • 1
  • 14
  • 21
  • Just a quick question. I know you have your solution in C#. Is there a VB.NET solution as my robotic system uses VB.NET. As before, I do not mind if I cannot go to the lowest level, I just need a way deactivate the `DF1Com1.Write("O:1/0", "0")` once I have released the key. Thank you – Arthor Nov 18 '17 at 03:09
  • You should be able to take the project from CodeProject and compile the c# into a DLL library. Then you can reference the library and write your handlers in VB.net. – Michael Z. Nov 18 '17 at 16:33
  • Maybe....each hotkey activation via `RegisterHotkey` should execute the first code, `DF1Com1.Write("O:1/0", "1")` and then sleep for a second and then execute the last bit, `DF1Com1.Write("O:1/0", "0")`. Basically this means that each key press only moves the robot a certain amount even if you are holding the keys. If that doesn't work then I can only think of a keyboard hook. – Michael Z. Nov 18 '17 at 16:37
  • I'm adding this to my answer. – Michael Z. Nov 18 '17 at 16:38
  • Is there any way to read what was written as in maybe `DF1Com1.Read` – Michael Z. Nov 18 '17 at 16:41
  • @Arthor I think more examples are written in c# than they are in vb.net so you will run into this problem a lot. I suggest learning c#. I started with VB6 and then went to VB.net. I learned c# about 4 years ago and now it's my favorite to code in. Give it a try, it uses the same framework that VB uses. It's just different syntax. – Michael Z. Nov 18 '17 at 17:53
  • @Arthor My implementation of a hotkey manager in zVirtualDesktop would be perfect for your application and would give you the ability to configure the hotkeys at run-time. The implementation uses a class named `HotkeyAction` which contains the code to perform the various actions. The methods in that class are called from the `HookHandler` class with a big case statement that identifies an action associated to current key states. It would be easy for you to implement this. Soon I'll have some code on GitHub for you to use and will be back with a link. I'll do a VB.net version and c# version. – Michael Z. Nov 18 '17 at 18:17
  • Fantastic. Please keep me updated as I have been racking my brains over this. Thank you!!! – Arthor Nov 19 '17 at 02:47
  • Thank you for the all the information. Unfortunately you can read the `DF1Com1` – Arthor Nov 19 '17 at 02:47
0

Another option for accomplishing this could be to use boolean to keep track. Doing this means, press the hotkey to start then press again to stop.

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Static toggle As Boolean
        If m.Msg = HotKeyRegistryClass.Messages.WM_HOTKEY Then 'NOT THE ACTUAL WINDOWS NAMESPACE
            Dim ID As String = m.WParam.ToString()
            Select Case ID
                Case 0 :
            If toggle Then
             DF1Com1.Write("O:1/0", "0")
            Else
                        DF1Com1.Write("O:1/0", "1")
            End If
            toggle = Not toggle
                Case 1 : MessageBox.Show("S")
                Case 2 : MessageBox.Show("D")
                Case 3 : MessageBox.Show("F")
                Case 4 : MessageBox.Show("G")
                Case 5 : MessageBox.Show("H")
            End Select
        End If
        MyBase.WndProc(m)
    End Sub
Michael Z.
  • 1,453
  • 1
  • 14
  • 21
  • Hello, great idea. However, unfortunately it is not feasible to do that. I can only but imagine the amount of users that would make mistakes. As this system is not just used by myself, – Arthor Nov 20 '17 at 19:15
  • Hello, any update with the suggested solution proposed the other day. Thank you – Arthor Nov 26 '17 at 12:23
  • I have made some progress but the holiday has delayed me. I’ll try to get it done today. – Michael Z. Nov 26 '17 at 17:15
  • Thank you ever so much, I am truly grateful. Would like to buy you a beer ! – Arthor Nov 26 '17 at 20:28
  • I’m out of town for the week. I expected to get it done, but all of my time went to setting zVirtualDesktop as a commercial application. As soon as I get back from my business trip I’ll get on this...promise. – Michael Z. Dec 03 '17 at 04:31
  • Congrats on your launch. Much appreciated. Thank you very much. – Arthor Dec 03 '17 at 14:43
  • Also, I am just looking at your app. It looks great. I think I will use that as well. Does it manage multi-monitors as well? – Arthor Dec 03 '17 at 14:45
  • Not yet. That’s something I might add. – Michael Z. Dec 03 '17 at 16:44