c# - C# 中的 Linux 键盘钩子(Hook)

标签 c# linux raspberry-pi

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

使用场景是在公共(public)交通中,乘客将使用 RFID key 卡,我将通过传感器读取 ID,并将此传感器识别为键盘。

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

我想知道是否有类似这个示例的东西适用于 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;
        }
    }
}

最佳答案

我找到了一种不使用任何 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;
    }

代码打开 event0FileStream,通常是您要监听输入的地方,生成的事件具有标准结构(您可以找到更多信息在这里: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
}

关于c# - C# 中的 Linux 键盘钩子(Hook),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64340515/

相关文章:

python - Pip 未在 Ubuntu 中安装

linux - 在 OpenCL 中使用内存映射文件

java - linux arm 上的 Jinput(树莓派)

c++ - 使用 C++ 程序中的 raspistill 读取相机图像

python - 无法在树莓派中的 docker python3.7-slim 上安装 numpy

c# - 事件 - 命名约定和风格

c# - Oracle 自定义类向导无法从 Oracle 用户定义的数据类型生成自定义 c# 类

c# - 显示项目集合时忽略 DebuggerDisplay

c# - 如何访问枚举 OracleDbType?

c++ - wcslen 是 ISO/IEC 14882 :2003 C++ standard library? 的一部分吗