0

The solution I need is 99% encapsulated in the accepted solution linked below:

How to perform .Onkey Event in an Excel Add-In created with Visual Studio 2010?

However, it does not seem to work for hooking the ALT key. I have looked around and have come across several C# examples but I am new to programming and I am learning VB .NET so with my level of knowledge I have not been able to port the code in other C# examples successfully to make it work in my VB .NET project.

Since the code linked above already does everything I need, I'd like to keep using it, but I would appreciate it if someone could show me how to make it also hook the ALT key. I suppose there is a constant that needs to be passed and checked for in a specific way but I have not been able to figure it out in the last 4 days since finding this solution. Any help will be much appreciated.

Thanks ever so much!

Edit: @Vincent, here is the code I am testing with that breaks with the overflow error:

Imports InputHelperLib

Public Class ThisAddIn

Dim KeyboardHook As InputHelper.Hooks.LocalKeyboardHook

Private Sub ThisAddIn_Startup() Handles Me.Startup
    KeyboardHook = New InputHelper.Hooks.LocalKeyboardHook
    AddHandler KeyboardHook.KeyDown, AddressOf KeyboardHook_KeyDown
    AddHandler KeyboardHook.KeyUp, AddressOf KeyboardHook_KeyUp
End Sub

Private Sub ThisAddIn_Shutdown() Handles Me.Shutdown
    If KeyboardHook IsNot Nothing Then KeyboardHook.Dispose()
End Sub

Private Sub KeyboardHook_KeyDown(sender As Object, e As InputHelper.Hooks.KeyboardHookEventArgs)
    If e.Modifiers = InputHelper.ModifierKeys.Alt AndAlso e.KeyCode = System.Windows.Forms.Keys.M Then
        System.Windows.Forms.MessageBox.Show("ALT + M was pressed!")
    End If
End Sub

Private Sub KeyboardHook_KeyUp(sender As Object, e As InputHelper.Hooks.KeyboardHookEventArgs)
    If e.Modifiers = InputHelper.ModifierKeys.Alt AndAlso e.KeyCode = System.Windows.Forms.Keys.M Then
        System.Windows.Forms.MessageBox.Show("ALT + M was released!")
    End If
End Sub
End Class
  • That code is not completely valid and doesn't use the best method of determining modifiers... I recommend you have a look at my library called [InputHelper](https://github.com/Visual-Vincent/InputHelper) instead. It contains a keyboard hook which was created from a fair amount of research in order to be as correct as possible (coding-wise), and I have tested it thoroughly in order to ensure that it works like it should. There's instructions for how you can use it on the project's wiki: [Low-level keyboard hook](https://github.com/Visual-Vincent/InputHelper/wiki/Low-level-keyboard-hook) – Visual Vincent Feb 01 '19 at 06:22
  • Hi Vincent, that is an impressive project. However, it does not seem to work reliably inside a VSTO add-in running inside Excel. What I have found is that if I implement it as per the wiki documentation, it works well inside a Winforms application but when hooked when my add-in loads, it does not fire for events inside Excel, but as soon as I click outside of Excel, it works. I believe this might be an issue with grabbing the correct thread for the relevant appdomain? – RandomOrangeCat Feb 01 '19 at 12:05
  • That's rather odd... As it installs a low-level hook and uses the WinAPI to do so it should work at all times, since Windows is managing the hook for you. The hook runs in the same thread that it was created in. Could you edit your question and include the code you used? I'll have to try this myself. Which version of Excel are you using? – Visual Vincent Feb 01 '19 at 14:27
  • Although I see now that in the post you linked earlier it hooks only the thread of the current AppDomain. It'd be odd if that tiny detail made all the difference since my library hooks _**all**_ threads in the current desktop. – Visual Vincent Feb 01 '19 at 14:38
  • I cannot seem to reproduce the issue. I tried every combination of instantiating the hook and subscribing to event handlers that I can think of, and it worked without a problem every time. I used Excel 2010 and Visual Studio 2010, targeting .NET Framework 4.0. – Visual Vincent Feb 01 '19 at 16:52
  • For me it registers the keystrokes in all windows except Excel when hooked from inside Excel. Once Excel has launched I hook the keyboard after the add-in has launched, but then it never registers the keys while I am inside Excel, but as soon as I click outside Excel (desktop, browser, anywhere else) and press the keys, it registers them. I am using Excel 365 but also tested in Excel 2016 and Excel 2013 in Windows 10 Pro and Windows 7 Pro respectively, using Visual Studio 2017 Community targeting .NET 4.62 – RandomOrangeCat Feb 01 '19 at 17:01
  • So, at present it is sort of doing the opposite of what an add-in should do; instead of capturing the shortcuts in the host application only, it captures them outside as opposed to the desired behaviour of ignoring them outside the host application and capturing them only in Excel. The code I am using at the moment does it exactly as I expect (apart from the ALT key) although I had to update the code due to deprecation of: AppDomain.GetCurrentThreadId I now use: Diagnostics.Process.GetCurrentProcess.Threads(0).Id To hook what seems to be the main thread inside Excel. – RandomOrangeCat Feb 01 '19 at 17:07
  • I tested it in Excel 2016 and I see the problem now. Something must have changed in the later versions. There wasn't much to find about it, but someone suggested that Office might be setting up its own keyboard hook (which happens before the add-in) and ignores calling the `CallNextHookEx()` function, which is supposed to call the next keyboard hook in the system's hook chain. – Visual Vincent Feb 02 '19 at 08:33
  • I see why the other post works, though. It uses a slightly different keyboard hook which requires you to inject a DLL in every process you want to hook (since our add-in is already loaded in Excel that step is already taken care of). That keyboard hook doesn't use the same hook chain as the low-level hook, and therefore isn't blocked by Excel. I created a new class for InputHelper called `LocalKeyboardHook` which hooks only one thread in the current process. It works quite well, but I didn't have the time to fix all the quirks yesterday so I'll finish it today and post an answer when I'm done! – Visual Vincent Feb 02 '19 at 08:40

1 Answers1

0

The code in that answer isn't completely valid and doesn't use the best methods of determining modifiers.

Since the standard, low-level keyboard hook didn't work I have added a LocalKeyboardHook to my InputHelper library, which uses the WH_KEYBOARD hook instead of WH_KEYBOARD_LL. The difference is that WH_KEYBOARD requires you to inject a DLL and specify a thread in every process you want to hook. Fortunately, since you're using a VSTO add-in, InputHelper is already loaded into the Excel process along with your add-in.

The compiled DLL can be downloaded in my project's Release section:
https://github.com/Visual-Vincent/InputHelper/releases

(you can also download the InputHelper.vb source file directly from the repository)

Usage example:

Imports InputHelperLib

Public Class ThisAddIn

    Dim KeyboardHook As InputHelper.Hooks.LocalKeyboardHook

    Private Sub ThisAddIn_Startup() Handles Me.Startup
        KeyboardHook = New InputHelper.Hooks.LocalKeyboardHook
        AddHandler KeyboardHook.KeyDown, AddressOf KeyboardHook_KeyDown
        AddHandler KeyboardHook.KeyUp, AddressOf KeyboardHook_KeyUp
    End Sub

    Private Sub ThisAddIn_Shutdown() Handles Me.Shutdown
        If KeyboardHook IsNot Nothing Then KeyboardHook.Dispose()
    End Sub

    Private Sub KeyboardHook_KeyDown(sender As Object, e As InputHelper.Hooks.KeyboardHookEventArgs)
        If e.Modifiers = InputHelper.ModifierKeys.Alt AndAlso e.KeyCode = System.Windows.Forms.Keys.M Then
            System.Windows.Forms.MessageBox.Show("ALT + M was pressed!")
        End If
    End Sub

    Private Sub KeyboardHook_KeyUp(sender As Object, e As InputHelper.Hooks.KeyboardHookEventArgs)
        If e.Modifiers = InputHelper.ModifierKeys.Alt AndAlso e.KeyCode = System.Windows.Forms.Keys.M Then
            System.Windows.Forms.MessageBox.Show("ALT + M was released!")
        End If
    End Sub
End Class
Visual Vincent
  • 18,045
  • 5
  • 28
  • 75
  • Hi Vincent, thanks for posting the update, I really appreciate it. I'm travelling today but will test it as soon as I am back at my PC tomorrow. – RandomOrangeCat Feb 02 '19 at 15:58
  • @RandomOrangeCat : No worries! Take your time! – Visual Vincent Feb 02 '19 at 15:59
  • Hi Vincent, I tested this morning and although it seems to definitely hook the keys now, no matter which keys I hook, I get this error as soon as the event fires: An unhandled exception of type 'System.OverflowException' occurred in Unknown Module. Arithmetic operation resulted in an overflow. To make sure it was not a problem with my add-in, I started a completely blank new one and put only the code you posted above into it and tested it and the error keeps coming up. – RandomOrangeCat Feb 03 '19 at 13:54
  • @RandomOrangeCat : Can you edit the code you used into the question? Also did you use the compiled DLL of InputHelper or the source code? – Visual Vincent Feb 03 '19 at 15:07
  • Hi Vincent, I tried both - I imported the source code as a project into my add-in and when that failed, tried referencing only the DLL of InputHelper (I tried the .NET3 one too). Do you want me to paste the code that I am using that is failing at the moment? If so, I'll do that as soon as I am back at the PC with Visual Studio on it. Thanks for your patience <3 – RandomOrangeCat Feb 03 '19 at 16:58
  • @RandomOrangeCat : I would like any failing code. Please also post the Stack Trace from the error. – Visual Vincent Feb 03 '19 at 17:13
  • Hi Vincent. The code breaks with this stacktrace: [[at InputHelperLib.InputHelper.Hooks.LocalKeyboardHook.HookCallback(Int32 nCode, IntPtr wParam, IntPtr lParam) in D:\Learning\Projects\VSTO\InputHelper Library\InputHelper .NET 4.x\InputHelper.vb:line 544]] The specific line of code where it seems to break is: Dim KeyFlags As New NativeMethods.DWORD(lParam.ToInt64()) in the HookCallback function. I'll paste my code above in a moment - it is exactly the same code as you pasted above - I actually copied your when I made the new blank test add-in. – RandomOrangeCat Feb 03 '19 at 21:39
  • @RandomOrangeCat : Ok, I see the issue. Don't exactly know why it occurs (it works perfectly in 2010) but I know how to fix it. Unfortunately I won't be home until later tonight so I might have to fix it tomorrow. – Visual Vincent Feb 04 '19 at 08:09
  • Please take your time - this is not critical and I really appreciate all the time you are putting into this! Happy Monday! – RandomOrangeCat Feb 04 '19 at 09:29
  • @RandomOrangeCat : I have uploaded a fix and a new release to GitHub. I tested it in both Excel 2010 and 2016 and it seems to be working now! – Visual Vincent Feb 05 '19 at 19:05
  • Thanks so much - I'll try to get to testing tonight or tomorrow morning at the latest! I really appreciate the effort! – RandomOrangeCat Feb 05 '19 at 20:39
  • Hi Vincent, I tested it and it now hooks the keys but I am seeing other strange behaviour now (again, using your/the code pasted in the OP); if I show a messagebox, it displays twice (even if I only use one event [KeyDown]). If I use the shortcut to execute any other command (like add a sheet or increment a value) it goes into a runaway loop. – RandomOrangeCat Feb 06 '19 at 13:42
  • @RandomOrangeCat : I know, I've noticed that as well. It's Excel's doing. I think it internally repeats the keystroke and thus causes the hook to detect it again. Likely it has something to do with why the global, low-level hook didn't work. My theory is that Excel traps the actual keystroke and then repeat it only to itself so that it is not recognized by the outside (but again it's just a theory). – Visual Vincent Feb 06 '19 at 15:55
  • @RandomOrangeCat : So there's not much one can do about the keystroke being repeated other than to have _your_ code ignore it if the delay between the two events is too small. -- Now, what do you mean by "runaway loop"? – Visual Vincent Feb 06 '19 at 15:57
  • I'll grab a Camtasia video of the problem and post it for you so you can see what I mean by the runaway as soon as I have a moment free. Thanks! – RandomOrangeCat Feb 06 '19 at 16:56
  • You can find the video of the issue here: https://1drv.ms/v/s!Ai-HOb834a0_kaBVN921vrR2xVSNvA – RandomOrangeCat Feb 06 '19 at 22:42
  • @RandomOrangeCat : Thanks for the kind video! You don't need to worry about "wasting my time" as I'm happy to help and I still have more things to test and/or suggest! -- When I tried it earlier I saw a the problem with the event being raised multiple times, though for me it always stopped after 6 and didn't continue endlessly like you experienced (I have yet to try it in Excel 2016, though. I have it on a different PC than my development machine). – Visual Vincent Feb 06 '19 at 23:03
  • @RandomOrangeCat : I still do need to say that "this is how it is", though, because what I've learned from this journey is that Excel and Office does some pretty weird things under the hood (and it only seems to get worse with every new version of it). _**However**_ that doesn't mean that that's the end of it! I still have a couple of tricks (read: workarounds) up my sleeve, and I won't give up without giving it some more testing! First and foremost, does anything change if you increment/change the value of `ActiveCell.Value2` instead of `ActiveCell.Value`? (this is what I used in my attempts) – Visual Vincent Feb 06 '19 at 23:11
  • Hi Vincent, apologies for the delayed reply - I somehow missed this message. I tried it with .Value2 and I get the same effect - the code just runs away with the incrementing of the cell or locks up the Excel thread. It is a strange problem :) – RandomOrangeCat Feb 09 '19 at 12:24
  • @RandomOrangeCat : Not a problem! Try setting `e.Block = True` after executing your command. It should stop the keystroke from reaching any other hooks, thereby also Excel. Be aware that this will stop any normal command/key from being recognized by the application, so if you'd block a single key like `Keys.M` then you would no longer be able to type the letter 'M' in Excel. – Visual Vincent Feb 09 '19 at 17:08
  • Excellent, thanks! That works exactly as intended; it stops the thread from being hijacked and it appears that after the shortcut has been invoked, the individual keys continue to work as expected, i.e. assigning the shortcut does not break anything else that depends on the individual keys. Apologies for the late reply - I was away for some days and could not get here to test. Thank you so very much! – RandomOrangeCat Feb 20 '19 at 15:11
  • @RandomOrangeCat : No worries. Glad I could help and that it worked! Happy coding! – Visual Vincent Feb 20 '19 at 15:59