如何停止在C#

问题描述

我正在尝试创建一个将对全局热键作出反应的C#应用​​程序; ALT+H,然后在我松开ALT键时。这实际上确实可以很好地工作,但是我的问题是,当我的应用程序完成了对热键应执行的操作后,它将阻止该热键被其他应用程序处理。我从下面的StackOverflow帖子Global keyboard capture in C# application中获得了大部分代码,但是我在其他地方看到return (IntPtr)1;应该能够停止进一步处理密钥。

但是...当我在Word或写字板,然后按ALT+H,然后Word显示所有类型的菜单-我不想要它,因为我只希望我的应用程序执行某项操作:-)

对于这段相当长的代码,我感到很抱歉,但是我认为它很重要,因此您可以获得完整的概述。我只有1个使用return (IntPtr)1;的地方。

我的代码

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Windows.Input;
    
namespace MyNewProgram
{
    static class Program
    {
    
        // Enable hotkeys
        // https://stackoverflow.com/a/604417/2028935
        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100;
        private const int WM_KEYUP = 0x0101;
        private const int WM_SYSKEYUP = 0x0105;
        private const int VK_SHIFT = 0x10;
        private const int VK_MENU = 0x12;
        private static LowLevelKeyboardProc _proc = HookCallback;
        private static IntPtr _hookID = IntPtr.Zero;

        // Other variables
        public static bool isAltpressedInThisApp = false;

        private static MainWindow MyMainWindow;

        // --------------------------------------------------------------------------------------

        [STAThread] // STAThreadAttribute indicates that the COM threading model for the application is single-threaded apartment,https://stackoverflow.com/a/1361048/2028935

        static void Main()
        {
            // Hook application to keyboard
            _hookID = SetHook(_proc);

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            MyMainWindow = new MainWindow();
            Application.Run(MyMainWindow);

            // Unhook application from keyboard
            UnhookWindowsHookEx(_hookID);
        }

        // --------------------------------------------------------------------------------------
        // required functions for globally hooking the keyboard

        [DllImport("user32.dll",CharSet = CharSet.Auto,SetLastError = true)]
        public static extern short GetKeyState(int keyCode);

        [DllImport("user32.dll",SetLastError = true)]
        private static extern IntPtr SetwindowsHookEx(int idHook,LowLevelKeyboardProc lpfn,IntPtr hMod,uint dwThreadId);

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

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

        [DllImport("kernel32.dll",SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

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

        // --------------------------------------------------------------------------------------
        // Hook the keyboard - action

        private static IntPtr HookCallback(int nCode,IntPtr lParam)
        {
            // React on KEYDOWN
            if (nCode >= 0 && ((wParam == (IntPtr)WM_KEYDOWN) || (wParam == (IntPtr)WM_SYSKEYUP)))
            {
                int vkCode = Marshal.ReadInt32(lParam);

                // H
                if ((Keys)vkCode == Keys.H)
                {
                    isAltpressedInThisApp = true;

                    // Is ALT pressed down
                    if ((GetKeyState(VK_MENU) & 0x8000) != 0)
                    {
                        Console.WriteLine("ALT + H");
                        return (IntPtr)1; // do not allow others to hook this key combo
                    }
                }
            }

            // React on KEYUP
            if (nCode >= 0 && wParam == (IntPtr)WM_KEYUP)
            {
                int vkCode = Marshal.ReadInt32(lParam);

                // Is ALT not pressed down
                if ((Keys)vkCode == Keys.LMenu)
                {
                    Console.WriteLine("ALT UP");
                }
            }

            return CallNextHookEx(_hookID,nCode,wParam,lParam);
        }

        // --------------------------------------------------------------------------------------
        // Hook the keyboard

        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);
            }
        }
    }
}

我不是一个扎实的C#开发人员,因为我在这里刚迈出了第一步-那么为什么我要把自己放在全球热键的深水里作为首要任务之一;-)有没有人可以提供一些提示在这里,因为我可能在某个地方犯了一个愚蠢的错误

解决方法

背后的想法是,每当按下alt键时,它就会开始将键吞入一个本地列表中。如果已经看到我们需要查看的模式,它将不会发送,但是对于其他所有模式,它将开始按照接收的顺序再次发送密钥。

考试阶段代码:

    enum WM
    {
        WM_KEYDOWN = 0x0100,WM_KEYUP = 0x0101,WM_SYSKEYUP = 0x0105,WM_SYSKEYDOWN = 0x0104,}

    private static IntPtr HookCallback(int nCode,IntPtr wParam,IntPtr lParam)
    {
        if (nCode < 0)
            return CallNextHookEx(_hookID,nCode,wParam,lParam);

        KBDLLHOOKSTRUCT kbd = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam,typeof(KBDLLHOOKSTRUCT));
        Enum.TryParse<Keys>($"{kbd.vkCode}",out Keys key);
        Enum.TryParse<WM>($"{wParam}",out WM message);

        Console.WriteLine($"{message}:{key}");

        return CallNextHookEx(_hookID,lParam);
    }

当按下alt + h并释放时,输出:

WM_SYSKEYDOWN:LMenu
WM_SYSKEYDOWN:H
WM_SYSKEYUP:H
WM_KEYUP:LMenu

如您所见,Windows同时发送了alt和h键。您提供的代码只能捕获H键,因此接收键盘消息的窗口会认为已按下Alt键。

没有人可以获取和过滤掉先前按下的键,因此只要看到按下键,我们就需要捕捉alt键。如果下一个键不是H,则应按收到的顺序发送已获取的键。

我已经写了下面的代码来处理这种情况,但是我不太确定它如何在真实的Windows机器上工作,因为我有一个osx操作系统,因为我在虚拟机中运行Windows。按下时也可以更改击键。

如果仍然不够,您可以等待,我也许可以尝试在真正的Windows机器中在我的办公室解决它。但我认为您可以理解并自行解决。

    [StructLayout(LayoutKind.Sequential)]
    public class KBDLLHOOKSTRUCT
    {
        public uint vkCode;
        public uint scanCode;
        public KBDLLHOOKSTRUCTFlags flags;
        public uint time;
        public UIntPtr dwExtraInfo;
    }

    [Flags]
    public enum KBDLLHOOKSTRUCTFlags : uint
    {
        LLKHF_EXTENDED = 0x01,LLKHF_INJECTED = 0x10,LLKHF_ALTDOWN = 0x20,LLKHF_UP = 0x80,}
    [DllImport("user32.dll")]
    static extern void keybd_event(byte bVk,byte bScan,uint dwFlags,UIntPtr dwExtraInfo);

    enum WM
    {
        WM_KEYDOWN = 0x0100,}

    private static void ReSendKeys(int index = -1)
    {
        _index = -1;

        var copiedKeys = _swallowedKeys.ToArray();
        for (int i = 0; i < copiedKeys.Length; ++i)
        {
            bool up = copiedKeys[i].Item1 == (IntPtr)WM.WM_SYSKEYUP || copiedKeys[i].Item1 == (IntPtr)WM.WM_KEYUP;
            keybd_event((byte)copiedKeys[i].Item2.vkCode,(byte)copiedKeys[i].Item2.scanCode,up ? 2u : 0u,UIntPtr.Zero);
        }

        _index = index;
        _swallowedKeys.Clear();
    }

    private static List<Tuple<IntPtr,KBDLLHOOKSTRUCT>> _swallowedKeys = new List<Tuple<IntPtr,KBDLLHOOKSTRUCT>>();

    private static int _index = 0;
    private static IntPtr HookCallback(int nCode,lParam);
        
        KBDLLHOOKSTRUCT kbd = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam,out WM message);

        Console.Write($"{message}:{key}");

        // we know that when _index is -1,ReSendKeys function has been called
        // so do not filter out first alt key
        if (_index == -1)
        {
            _index++;
            Console.WriteLine();
            return CallNextHookEx(_hookID,lParam);
        }

        // we are at the beginning of the sequence we will catch
        // if it's alt key filter it out,and increment the variable
        if (_index == 0)
        {
            _swallowedKeys.Add(new Tuple<IntPtr,KBDLLHOOKSTRUCT>(wParam,kbd));

            if (message == WM.WM_SYSKEYDOWN && key == Keys.LMenu)
            {
                _index++;

                Console.WriteLine(" filtered out");
                return (IntPtr)1;
            }
            else
            {
                _swallowedKeys.Clear();
                // do nothing
            }
        }

        if (_index == 1)
        {
            _swallowedKeys.Add(new Tuple<IntPtr,kbd));

            // if the next key is H,then filter it out also
            if (message == WM.WM_SYSKEYDOWN && key == Keys.H)
            {
                _index++;
                _swallowedKeys.RemoveAt(_swallowedKeys.Count - 1);

                Console.WriteLine(" filtered out");
                return (IntPtr)1;
            }
            // if not,we filtered out wrong sequence,we need to resend them
            else
            {
                Console.WriteLine();
                ReSendKeys();
                return (IntPtr)1;
            }
        }

        if (_index == 2)
        {
            _swallowedKeys.Add(new Tuple<IntPtr,kbd));

            if (message == WM.WM_SYSKEYUP && key == Keys.H)
            {
                _index++;
                _swallowedKeys.RemoveAt(_swallowedKeys.Count - 1);

                Console.WriteLine(" filtered out");
                return (IntPtr)1;
            }
            else
            {
                // if user pressed H but not released and pressed another key at the same time
                // i will pass that situation,if u need to handle something like that,you got the idea,please fill that block of code
            }
        }

        if (_index == 3)
        {
            _swallowedKeys.Add(new Tuple<IntPtr,kbd));

            if (message == WM.WM_KEYUP && key == Keys.LMenu)
            {
                _index = 0;
                _swallowedKeys.Clear();

                Console.WriteLine(" filtered out");
                Console.WriteLine("shortcut disabled");
                return (IntPtr)1;
            }
            else
            {
                Console.WriteLine();
                // user has been pressed Alt + H,H released but not alt,so we can expect one H again,but we need to send this pressed key with Alt prefixed
                ReSendKeys(1);
                return (IntPtr)1;
            }
        }

        Console.WriteLine();
        return CallNextHookEx(_hookID,lParam);
    }

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...