C#中的Linux键盘钩子

5
我的问题是,我正在尝试运行一个针对Linux发布的、特别是为了在树莓派上运行的自包含C#控制台应用程序。
使用场景是在公共交通工具中,乘客会使用RFID钥匙卡,我将通过传感器读取ID,这个传感器被识别为键盘。
由于这个应用程序必须一直运行,它将作为服务运行,这就是为什么我需要一个键盘钩子,这样无论发生什么,服务都会读取传感器。
我想知道是否有像这个网站所示的适用于Linux的示例(警告:这是一个http网站):低级全局键盘钩子 以下是代码,因此您不需要访问该网站:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Input;
 
namespace DesktopWPFAppLowLevelKeyboardHook
{
    public class LowLevelKeyboardListener
    {
        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100;
        private const int WM_SYSKEYDOWN = 0x0104;
 
        [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);
 
        public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
 
        public event EventHandler<KeyPressedArgs> OnKeyPressed;
 
        private LowLevelKeyboardProc _proc;
        private IntPtr _hookID = IntPtr.Zero;
 
        public LowLevelKeyboardListener()
        {
            _proc = HookCallback;
        }
 
        public void HookKeyboard()
        {
            _hookID = SetHook(_proc);
        }
 
        public void UnHookKeyboard()
        {
            UnhookWindowsHookEx(_hookID);
        }
 
        private 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 IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN)
            {
                int vkCode = Marshal.ReadInt32(lParam);
 
                if (OnKeyPressed != null) { OnKeyPressed(this, new KeyPressedArgs(KeyInterop.KeyFromVirtualKey(vkCode))); }
            }
 
            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }
    }
 
    public class KeyPressedArgs : EventArgs
    {
        public Key KeyPressed { get; private set; }
 
        public KeyPressedArgs(Key key)
        {
            KeyPressed = key;
        }
    }
}
1个回答

3

我找到了一种不使用任何dll文件的方法来实现这个,而是读取/dev/input/eventX 这个文件,当键盘或其他外设连接时,系统会生成这个文件,用于记录该设备所产生的事件。

以下是C#代码:

public static string EvdevReader()
    {
        string readMessage = "";
        try
        {
            FileStream stream = new FileStream("/dev/input/event0", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            byte[] buffer = new byte[24];

            while (true)
            {
                stream.Read(buffer, 0, buffer.Length);

                // parse timeval (8 bytes)
                int offset = 8;
                short type = BitConverter.ToInt16(new byte[] { buffer[offset], buffer[++offset] }, 0);
                short code = BitConverter.ToInt16(new byte[] { buffer[++offset], buffer[++offset] }, 0);
                int value = BitConverter.ToInt32(
                    new byte[] { buffer[++offset], buffer[++offset], buffer[++offset], buffer[++offset] }, 0);

                if (value == 1 && code != 28)
                {
                    Console.WriteLine("Code={1}, Value={2}", type, code, value);

                    var key = (((KEY_CODE)code).ToString()).Replace("KEY_", "");
                    key = key.Replace("MINUS", "-");
                    key = key.Replace("EQUAL", "=");
                    key = key.Replace("SEMICOLON", ";");
                    key = key.Replace("COMMA", ",");
                    key = key.Replace("SLASH", "/");

                    Console.WriteLine(key);

                    readMessage += key;
                }

                if (code == 28)
                {
                    return readMessage;
                }

            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.ToString());
            Main();
        }
        return readMessage;
    }

这段代码打开了一个 FileStream,监听输入的位置是event0,生成的事件具有标准结构(您可以在此处找到更多信息:https://thehackerdiary.wordpress.com/2017/04/21/exploring-devinput-1/)。根据我找到的文档,timeval应该是16字节,但在这种情况下只使用了8字节。

事件有一个type属性,表示事件类型;code表示按下键的代码;value表示键状态:pressed = 1, unpressed = 0 (在此处查找更多信息:https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h#L38-L51)。 对于每个代码,我们需要找到其可读形式,为此我创建了一个枚举器,包含需要读取的键(28是回车键)。这些代码可以在上面的链接中找到。

 public enum KEY_CODE
{
    KEY_1 = 2,
    KEY_2,
    KEY_3,
    KEY_4,
    KEY_5,
    KEY_6,
    KEY_7,
    KEY_8,
    KEY_9,
    KEY_0,
    KEY_MINUS,
    KEY_EQUAL,
    KEY_BACKSPACE,
    KEY_TAB,
    KEY_Q,
    KEY_W,
    KEY_E,
    KEY_R,
    KEY_T,
    KEY_Y,
    KEY_U,
    KEY_I,
    KEY_O,
    KEY_P,
    KEY_LEFTBRACE,
    KEY_RIGHTBRACE,
    KEY_ENTER,
    KEY_LEFTCTRL,
    KEY_A,
    KEY_S,
    KEY_D,
    KEY_F,
    KEY_G,
    KEY_H,
    KEY_J,
    KEY_K,
    KEY_L,
    KEY_SEMICOLON,
    KEY_APOSTROPHE,
    KEY_GRAVE,
    KEY_LEFTSHIFT,
    KEY_BACKSLASH,
    KEY_Z,
    KEY_X,
    KEY_C,
    KEY_V,
    KEY_B,
    KEY_N,
    KEY_M,
    KEY_COMMA,
    KEY_DOT,
    KEY_SLASH,
    KEY_RIGHTSHIFT,
    KEY_KPASTERISK,
    KEY_LEFTALT,
    KEY_SPACE,
    KEY_CAPSLOCK,
    KEY_F1,
    KEY_F2,
    KEY_F3,
    KEY_F4,
    KEY_F5,
    KEY_F6,
    KEY_F7,
    KEY_F8,
    KEY_F9,
    KEY_F10,
    KEY_NUMLOCK,
    KEY_SCROLLLOCK,
    KEY_KP7,
    KEY_KP8,
    KEY_KP9,
    KEY_KPMINUS,
    KEY_KP4,
    KEY_KP5,
    KEY_KP6,
    KEY_KPPLUS,
    KEY_KP1,
    KEY_KP2,
    KEY_KP3,
    KEY_KP0,
    KEY_KPDOT
}

快速问题,你如何检测大写和小写字符? - Farhad Nowzari

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接