Binding some global hotkeys fails on Windows 10

Hi everyone!

Here is a problem that’s been frustrating me for a few years. I’ve asked on Stack Overflow and even offered a bounty, but no one seems to know the answer.

After upgrading from Windows 7 to Windows 10, some global hotkeys can’t bind anymore. This happens both in wxPython and it other apps (such as Ditto.)

I have a wxPython app where I run this code:

        self.RegisterHotKey(
            python_toolbox.string_cataloging.string_to_integer(
                'global_hotkey'
            ),
            win32con.MOD_WIN,
            74 # 'J'
        )

This code registers Win-J as a global hotkey. This works fine in Windows 7, but in Windows 10 it’s silently ignored.

After some trial and error, I noticed that it’s just certain combinations of hotkeys that get blocked, and others don’t. These include Win-J, Win-V, Win-Comma and many others.

Another interesting phenomenon is that AutoHotKey is able to bind to these “forbidden” hotkeys.

Does anyone have a clue how to bind to these keys in Windows 10?

So the problem is the keybindings changed from windows 7 to windows 10.
Here is where i checked: https://support.microsoft.com/en-us/help/12445/windows-keyboard-shortcuts

If you look on Windows 10 the key you are trying to bind to is reserved in Windows 10 as show in the picture:

What i suggest is verify that that the key you are trying to bind to is not already taken by checking the return value from the self.RegisterHotKey(...) function. It will return true if it was successfull and false if something already has it. See documentation here: https://wxpython.org/Phoenix/docs/html/wx.Window.html?highlight=registerhotkey#wx.Window.RegisterHotKey.

Since wxpython is binding globally it is whatever keys are registered to the OS in this case Windows 10 vs Windows 7. The OS handles these events and get registered with it so it knows what to look for and who to call when the keys are seen.

Here is sample code I drafted up for python3 to check if it is valid by adding a print statement:

import wx
import win32con #for the VK keycodes

# Got this class from example here: https://wiki.wxpython.org/RegisterHotKey
class FrameWithHotKey(wx.Frame):
	def __init__(self, *args, **kwargs):
		wx.Frame.__init__(self, *args, **kwargs)
		self.regHotKeys()
		self.Bind(wx.EVT_HOTKEY, self.handleHotKey, id=self.hotKeyId)
		self.Bind(wx.EVT_HOTKEY, self.handleHotKey, id=self.hotKeyId+1)
	
	def regHotKeys(self):
		"""
		This function registers the hotkey Alt+F1 with id=100
		"""
		self.hotKeyId = 100

		# output will be False due to binding already in Windows 10!!!
		# WIN + J
		print(self.RegisterHotKey(self.hotKeyId, win32con.MOD_WIN,74) ) # 'J'

		# output will be True now because none of my programs / Windows use CTRL + J
		print(self.RegisterHotKey(self.hotKeyId+1, win32con.MOD_CONTROL,74) ) # 'J'

	def handleHotKey(self, evt):
		"""
		Prints a simple message when a hotkey event is received.
		"""
		print("do hot key actions")

def main():
	app = wx.App(False)  # Create a new app, don't redirect stdout/stderr to a window.
	frame = FrameWithHotKey(None, wx.ID_ANY, "Menu Testing") # A Frame is a top-level window.
	frame.Show(True)     # Show the frame.
	app.MainLoop()

if __name__ == "__main__":
	main()

Sample Output:image

I wish there was an easy answer but it comes down to check what is taken already based on everything that is installed. Microsoft shows a way to change Windows Shortcuts here: https://support.microsoft.com/en-us/help/4052277/accessories-how-do-i-reassign-hot-keys-for-my-keyboard but I haven’t tested it just an FYI.

Got to love Microsoft… :frowning:

And to answer why AutoHotKey works is because it overrides these somehow based on my understanding. I don’t know all the details but this is based on some quick research and WIKI.

Let me know if this answers everything!

Well, I think you already knows that it doesn’t :slight_smile:

Thanks for the useful information, that list of shortcuts is interesting. What I’d really like to know is how we could get the same results that AHK does in wxPython.

Thanks for your help,
Ram.

@cool-RR

Sorry I didn’t fully answer your question. I might have found an answer for this.

So i went and did some research on how AHK does it by reading some source code. They try to register the shortcut just like you do. Upon failure however they add the call to what windows calls a hook using SetWindowsHookEx(). This means that it calls the function that you hook over whatever was previously registered without disrupting anything. Once your app is done it releases the hook and Windows start calling the old function again.

Looking through the source wxPython source I see that wxWidgets (c++) that wxPython wraps does one hook for the entire application. Not very useful if you want to wrap certain keys. I was doing some more digging and it doesn’t look like wxPython / wxWidgets support this and isn’t easy to do…

Here is one that seems like what you are trying to do: https://bytes.com/topic/python/answers/546675-intercepting-keypresses-mouse-movements-joystick-movements. I haven’t investigated this too closely but will here soon. If you attempt anything, I would advise starting with this link.

Here is another link that shows how to hack this in as if a keylogger: https://0x00sec.org/t/malware-writing-python-malware-part-2-keylogging-with-ctypes-and-setwindowshookexa/11858. This link contains a class for installing hooks using python. For this we will want to register the function as a hook if the key register fails. I will take a stab at this soon but for now i wanted to let you know what i have found.

Trying to make windows calls in python is difficult which is why it isn’t/ probably won’t be in wxPython. The alternative is just deal with the fact that some keys are forbidden but that is no fun! Some really cool rabbit holes here!

Here are some references I found:

There are some examples in the wiki for hooking the WndProc for catching events that are not processed by wxWidgets. IIRC, it’s not quite the same thing as what would be needed for global hotkeys, but pretty close conceptually and it’s always good to have more examples.

https://wiki.wxpython.org/HookingTheWndProc

@Robin Thanks for your answer and @LinuxDwag Thanks for your in-depth research!