Search code examples
c#visual-studiohotkeysregisterhotkey

C# Registerhotkey() won't read some keycodes


While trying to create an auto clicker application using a Visual Studio Windows Form that allows you to select the hotkey you want, I stumbled upon a problem

When a user inputs keys such as ~ or / the hotkey won't work, However, if I put in 0xC0 (for tilde) or 0xDC (for slash) instead of (int)[tilde] or (int)'/' it will work.

On top of that letters have to be capitalized, but thats ok because I can use ToUpper() to capitalize it

The part that allows the user to select a character is fine and it returns the letter as a string

Heres where I implement RegisterHotKey() and UnregisterHotKey() as well as setup the events for the form:

    [DllImport("user32.dll")]
    public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
    [DllImport("user32.dll")]
    public static extern bool UnregisterHotKey(IntPtr hWnd, int id);

    protected override void WndProc(ref Message m) {
                if (m.Msg == 0x0312 && m.WParam.ToInt32() == 1)
                {

                    toggleClicker(); //This turns my autoclicker off and on
                }
                base.WndProc(ref m);
    }
    public Form1(){
                InitializeComponent();
                /* Ignore this:
                VersionText.Text = "Version " + version;
                comboBox1.SelectedIndex = 0;
                */
                RegisterHotKey(this.Handle, 1, 0x0002, (int)Keys.P); //This is the default and it works just fine
    }

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
                UnregisterHotKey(this.Handle, 1);
    }

And this is the code for when the user has selected the key they wanted

    private void button2_Click(object sender, EventArgs e)
    {
        /*Ignore this
        if (isClickerRunning)
        {
            //Console.WriteLine("Stopping...");
            clickerThread.Abort();
            isClickerRunning = false;
        }
        */
        int Prefix = 0;
        //Set Prefix to the value chosen in the combobox
        if (comboBox1.SelectedIndex == 1) { Prefix = 0x0002; } //Ctrl
        if (comboBox1.SelectedIndex == 2) { Prefix = 0x0004; } //Shift
        if (comboBox1.SelectedIndex == 3) { Prefix = 0x0001; } //Alt
        if (comboBox1.SelectedIndex == 4) { Prefix = 0x0002 + 0x0004; } //Ctrl + Shift
        if (comboBox1.SelectedIndex == 5) { Prefix = 0x0002 + 0x0001; } //Ctrl + Alt
        if (comboBox1.SelectedIndex == 6) { Prefix = 0x0002 + 0x0001 + 0x0004; } //Ctrl + Alt + Shift


        bool Success1 = UnregisterHotKey(this.Handle, 1);
        bool Success2 = RegisterHotKey(this.Handle, 1, prefix, (int)KeyCharacter); //KeyCharacter is the key that the user has selected
        //Test if RegisterHotKey and UnregisterHotKey failed
        if(Success1 == false || Success2 == false)
        {
            //Set the hotkey back to default if it failed
            Success1 = UnregisterHotKey(this.Handle, 1);
            Success2 = RegisterHotKey(this.Handle, 1, 0x0002, (int)Keys.P);
        }

        //Test if it failed again
        if(Success1 == false || Success2 == false)
        {
            MessageBox.Show("FATAL ERROR!\nCould not register hotkey. Quiting...", "FATAL ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
            Application.Exit();
        }
    }

I have a default hotkey CTL + P setup when the program starts The default hotkey works fine but when I type in a key like / into the hotkey selection textbox and set it by clicking a button it will stop the default hotkey from working but when I press the hotkey I just set it won't work. But it works fine with capital A for example

Basically what I'm asking is how can I turn ~ into 0xC0 or / into 0xDC

Any help is appreciated


Solution

  • You will need to use the VkKeyScan Win32 API function to get the virtual key code of the character.

    In your case, then, you will call the VirtualKeyCodeFromChar function below with your KeyCharacter just before you call RegisterHotKey.

    It is actually better to use the VkKeyScanEx function because keyboard layouts differ and so OEM keys may produce different key codes for the same character because of the different layout. But I'll leave you to explore using that function on your own. Meanwhile...

    using System;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    
    namespace ConsoleApp1
    {
        class Program
        {
            [DllImport("user32.dll")] static extern short VkKeyScan(char c);
    
            static void Main(string[] args)
            {
                var s = "~|-.";
                foreach(var c in s)
                {
                    var key = VirtualKeyCodeFromChar(c);
                    Console.WriteLine($"{c}, {key}, 0x{key:X}\n");
                }
    
                Console.ReadKey();
            }
    
            static int VirtualKeyCodeFromChar(char c)
            {
                var composite = VkKeyScan(c);
    
                byte keyCode = (byte)(composite & 255);
                byte shiftState = (byte)((composite >> 8) & 255);
    
                Keys key = (Keys)keyCode;
    
                if ((shiftState & 1) != 0) key |= Keys.Shift;
                if ((shiftState & 2) != 0) key |= Keys.Control;
                if ((shiftState & 4) != 0) key |= Keys.Alt;
    
                return (int)key;
            }
        }
    }