C# 中的 Linux 键盘钩子

Lui*_* A. 5 c# linux raspberry-pi

我的问题是我正在尝试运行专门为 Linux 发布的独立 C# 控制台应用程序,旨在在 Raspberry 上运行。

使用场景是在公共交通中,乘客将使用 RFID 钥匙卡,我将通过传感器读取 ID,该传感器被识别为键盘。

由于该应用程序必须始终运行,它将作为服务运行,这就是为什么我需要一个键盘挂钩,这样无论发生什么,该服务都会读取传感器。

我想知道是否有类似这个例子的东西适用于linux(警告:它是一个http网站):Low Level Global Keyboard Hook

这是代码,因此您无需访问该网站:

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Lui*_* A. 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;
    }
Run Code Online (Sandbox Code Playgroud)

该代码打开一个FileStream通常event0是您要监听输入的位置,生成的事件具有标准结构(您可以在此处找到更多信息:https ://thehackerdiary.wordpress.com/2017/04/21 /exploring-devinput-1/),根据文档,我发现它应该是timeval16 个字节,但在本例中它适用于 8 个字节。

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

 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
}
Run Code Online (Sandbox Code Playgroud)