Hi,

I'm implementing a hotkey tool that globally captures key press/release and sends out keyboard events when receiving a defined key combination.

I started implementing a global system hook that generates keyboard press/release events. Some Code snippets:

// installs an application-defined hook procedure into a hook chain //
[DllImport("user32.dll", CharSet = CharSet.Auto,
           CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        private static extern int SetWindowsHookEx(
            int idHook,
            HookProc lpfn,
            IntPtr hMod,
            int dwThreadId);

// passes the hook information to the next hook procedure in the current hook chain //
[DllImport("user32.dll", CharSet = CharSet.Auto,
             CallingConvention = CallingConvention.StdCall)]
        private static extern int CallNextHookEx(
            int idHook,
            int nCode,
            int wParam,
            IntPtr lParam);


// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/callwndproc.asp //


// Create an instance of HookProc.
                KeyboardHookProcedure = new HookProc(KeyboardHookProc);
                //install hook
                hKeyboardHook = SetWindowsHookEx(
                    WH_KEYBOARD_LL,
                    KeyboardHookProcedure,
                    Marshal.GetHINSTANCE(
                    Assembly.GetExecutingAssembly().GetModules()[0]),
                    0);

// I now am able to capture key up/press/down events //
        public event KeyEventHandler KeyDown;
        public event KeyPressEventHandler KeyPress;
        public event KeyEventHandler KeyUp;

                //read structure KeyboardHookStruct at lParam
                KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
                //raise KeyDown
                if (KeyDown != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
                {
                    Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
                    KeyEventArgs e = new KeyEventArgs(keyData);
                    KeyDown(this, e);
                    handled = handled || e.Handled;
                }

Within the main app I added the event handlers (actHook is the class which created/handles the above mentioned hook chain)

actHook.KeyDown += new KeyEventHandler(MyKeyDown);
            actHook.KeyUp += new KeyEventHandler(MyKeyUp);

I'm now listening to global key events and start doing stuff if one key combination is been triggered (here CTRL+D):

private bool working = false;
public void MyKeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.LControlKey) isCtrlDown = true;
            if (e.KeyData == Keys.D) isDDown = true;

            if (!working) CheckHotkey();
            else
            {
                e.SuppressKeyPress = true;
            }
        }

        private void CheckHotkey()
        {
            // STRG + D //
            if (isCtrlDown && isDDown)
            {
                working = true;
                DoStrgD();
                working = false;
            }
}

        private void DoStrgD()
        {
            SendKeys.SendWait("{LEFT}");
            SendKeys.Flush();
        }

An here is the problem. The code does what is should. When pressing CTRL+D, the DoStrgD() function is been called which sends out a Key event "LEFT". The strange thing is, the first time the function is been called, the SendKeys command is sending a single "LEFT" - the next time, a CTRL Key is also been send, resulting in a CTRL+LEFT command. It seems, that the CTRL key (beeing catched by the keyhook) still is in cache, being transmitted when calling SendKeys.

Is there any way to supress the CTRL Key beeing send by SendKeys?

Why is it working the first time, the function DoStrgD() is been called?

Any idea, comments?

Regards, Andi.

Recommended Answers

All 14 Replies

Thanks, I realized some guys have the same issue:

Whenever I use SendKeys.Send(string), the control keys currently in effect are applied to the string by the application.

For example, if Notepad is running, and my filter does something like

if (Keys.C == (Keys)vkCode && Keys.Control == Control.ModifierKeys)

SendKeys.Send( "hi there" );

Notepad pops up a Replace dialog box because the 'h' in "hi..." is interpreted as Ctrl+h.

I notice a couple of comments about the issue sendkeys with the control key. I'm getting as Gary mentioned, the control (and alt) keys in addition to my send key string. Can either of you elaborate on the cause and workaround / solution for this situation.

Still noone posted an answer on this issue. Any ideas?

Regards, Andi.

Have you read this article?

Yes, this is the Hook I am using. I also posted a request in the FAQ section. Still no answers. Still I'm pulling my hair...

Regards, Andi.

The same happens for each other key combination you are monitoring. If you create a bool for every keypress you are monitoring (e.g. bool isCtrlDown, bool isAltDown, ...) and start generating Keyboard events with SendKeys.send() after isCtrlDown == true && isAltDown == true, you are not able to supress all the keys pressed (CTRL, ALT) because the hook is allready advanced to the next event for at least one of the keys (CTRL, ALT). So you always will be submitting one of this keys with your SendKeys.send(method).

We need to monitor a key combination, not only one key and supress this combination when handling.

Any idea?

Regards, Andi.

if (e.KeyData == Keys.LControlKey) 
{
       isCtrlDown = true;
       e.Handled = true;
}

Use e.Handled to tell whether the key should go through to the active window.

Also I've made such an app myself and one problem I had was that the SendKeys method did not work for fullscreen games(such as GuildWars or WoW), and neither did SendInput. So just in case you later one have the same problem, I used a native api keybd_event.

if (e.KeyData == Keys.LControlKey) 
{
       isCtrlDown = true;
       e.Handled = true;
}

Doing it the way you mentioned, you always will suppress the CTRL Key for any app. And also, if you are trying to capture Key Combos you will need to suppress all they keys within the combo - that will not be possible as the keys are no longer working for any app.

I'm currently doing a workaraund that way, that I'm doing an action after keyup. That way no key is in cache and sendkeys will not send out any unwanted (e.g. CTRL) keypresses.

Still this is not what i want, i would like to take action on keydown (keycombo down) - but it seems there is no way to do this.

Regards, Andi

You can do combos by checking in KeyDown if key X is pressed, if it is set a bool value for that key to true, and in KeyUp icheck if key X is released and set its bool value to false. And in the end call a method in the KeyDown event below everything else to check your bool values.

You can do combos by checking in KeyDown if key X is pressed, if it is set a bool value for that key to true, and in KeyUp icheck if key X is released and set its bool value to false. And in the end call a method in the KeyDown event below everything else to check your bool values.

Thats the way I'm doing it. The problem is: When waiting for keydown on CTRL + ALT + D, I cannot suppress any of these keys for the other apps. But when no suppressing, the keys are still in cache when calling sendkeys. So instead of sending sendkeys.send("{LEFT}") I'm sending CTRL+LEFT because CTRL is been pressed by the user and I cannot suppress this while waiting for the whole combo to be pressed. Understand?

Regards, Andi.

Like I said, if you want to NOT have Ctrl pressed when you want to execute your macro, use e.Handled = true. Either implement some basic logic when you want to surpess a key or just supress them by default.

Like I said, if you want to NOT have Ctrl pressed when you want to execute your macro, use e.Handled = true. Either implement some basic logic when you want to surpess a key or just supress them by default.

Sorry, thats not working. As I said: I'm waiting for key combos, so I cannot simply suppress CTRL (or ALT or "D") when it occurs - other apps would not receive any of that keys.

Regards, Andi.

Jesus how hard is it to check if KeyDown is key X and if key X's bool value is true to NOT do e.Handled = true and let it go to the other application?

Jesus how hard is it to check if KeyDown is key X and if key X's bool value is true to NOT do e.Handled = true and let it go to the other application?

You don't get the point. We are talking about NOT ONLY ONE KEY but a SET OF KEYS being monitored.
Take this example: I would like do some SendKeys.send() when the user presses CTRL + ALT.

How should I decide to supress the CTRL key I actually receive when I don't know if the next one is ALT in order to trigger the action. So I cannot suppress the CTRL Key, I only can remember the key is pressed (isCtrlDown). When the user then is pressing ALT, I can go and suppress this key, but still I was not able to suppress the CTRL which then is in cache for SendKeys.send() and will be send out...

Andi.

The same way other tools use hotkeys: they either let them through or disable them. And that is why XFire uses Scrolllock as its hot key control key, that is why Logitech's G15 keyboard has separate keys for doing macros.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.