全局热键,允许用户按住修改键

时间:2014-07-30 20:31:38

标签: c# hotkeys

我正在使用此处给出的代码的修改版本: https://stackoverflow.com/a/3654821/3179989

如果用户按下 Ctrl + B ,则热键会激活。

如果用户按 Ctrl + B ,它会激活,但继续按住 Ctrl 然后按 B 再次,它确实活跃。

有没有办法让热键的行为更像copy/paste在Windows中的作用?

例如,按住 Ctrl 并点击 V 将多次粘贴。

编辑:

问题是由于我在推送热键时添加了SendKeys.Send()。上面的原始代码不包含此问题。现在的问题是如何在不丢失此功能的情况下发送密钥?

1 个答案:

答案 0 :(得分:1)

编辑:

回答你的新问题:)

如果您编写自己的全局钩子,则可以指定这些类型的情况。

我相信您遇到的问题是,如果您发送 CTRL + V ,则会发送KeyDownKeyUp对于Control,这使得热键程序假定您不再持有它。 您需要通过在密钥发送期间不更改切换来明确处理此方案。

    private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (!SENDING_KEYS) //If we're sending keys, ignore everything below
        {
            if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) //KeyDown
            {
                int vkCode = Marshal.ReadInt32(lParam);
                string theKey = ((Keys)vkCode).ToString();
                Console.Write(theKey);
                if (theKey.Contains("ControlKey"))
                {
                    //Our Program still thinks CTRL is down even if we send it using SendKeys
                    CONTROL_DOWN = true; 
                }
                else if (CONTROL_DOWN && theKey == "B")
                {
                    Console.WriteLine("\n***HOTKEY PRESSED***"); //Our hotkey has been pressed
                    SENDING_KEYS = true; //Now we will be sending keys
                    SendKeys.Send("^v"); //Send the keys (CTRL+V) - Paste
                    SENDING_KEYS = false; //Now we are done sending the keys
                    return (IntPtr)1; //Block our hotkey from being sent anywhere
                }
                else if (theKey == "Escape")
                {
                    UnhookWindowsHookEx(_hookID);
                    Environment.Exit(0);
                }
            }
            else if (nCode >= 0 && wParam == (IntPtr)WM_KEYUP) //KeyUP
            {
                int vkCode = Marshal.ReadInt32(lParam);
                string theKey = ((Keys)vkCode).ToString();
                if (theKey.Contains("ControlKey"))
                {
                    //During send keys, this will not be triggered
                    CONTROL_DOWN = false; 
                }
            }
        }
        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }

原始答案:

您可以创建自己的Global Keyhook。

以下是使用Windows窗体的示例:

enter image description here

以下是控制台中的示例:

enter image description here

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace ConsoleKeyhook
{
    class Hooky
    {
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook,
            LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
            IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]

        private static extern IntPtr GetModuleHandle(string lpModuleName);
        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100;
        private const int WM_KEYUP = 0x0101;
        private static LowLevelKeyboardProc _proc = HookCallback;
        private static IntPtr _hookID = IntPtr.Zero;
        private static bool CONTROL_DOWN = false;

        public static void Main()
        {
            _hookID = SetHook(_proc);
            Application.Run();
        }

        private static IntPtr SetHook(LowLevelKeyboardProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
                    GetModuleHandle(curModule.ModuleName), 0);
            }
        }

        private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

        private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) //KeyDown
            {
                int vkCode = Marshal.ReadInt32(lParam);
                string theKey = ((Keys)vkCode).ToString();
                Console.Write(theKey);
                if (theKey.Contains("ControlKey"))
                {
                    CONTROL_DOWN = true;
                }
                else if (CONTROL_DOWN && theKey == "B")
                {
                    Console.WriteLine("\n***HOTKEY PRESSED***");
                }
                else if (theKey == "Escape")
                {
                    UnhookWindowsHookEx(_hookID);
                    Environment.Exit(0);
                }
            }
            else if (nCode >= 0 && wParam == (IntPtr)WM_KEYUP) //KeyUP
            {
                int vkCode = Marshal.ReadInt32(lParam);
                string theKey = ((Keys)vkCode).ToString();
                if (theKey.Contains("ControlKey"))
                {
                    CONTROL_DOWN = false;
                }
            }
            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }
    }
}