从后台服务工作者中的隐藏阅读器读取,.NET 8

问题描述 投票:0回答:1

我正在尝试从 .NET 8 / C# 后台服务中具有特定供应商 ID、产品 ID 的 HID 读卡器进行读取。我可以成功注册设备,但刷卡时无法读取读卡器。看起来

ProcessRawInput
从来没有被叫过。

读卡器模拟键盘,所以我可以刷卡并在记事本中读取卡号。读卡器和卡正在工作。

这是我的代码:

namespace HIDReaderService
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;
        private IntPtr _messageOnlyWindowHandle = IntPtr.Zero;
        private const ushort VENDOR_ID = 0xFFFF; //input vendor id of attached keyboard/reader
        private const ushort PRODUCT_ID = 0xFF; //input product id of attached keyboard/reader

        private delegate IntPtr WindowProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
        private WindowProcDelegate _windowProcDelegate;

        public Worker(ILogger<Worker> logger)
        {
            _logger = logger;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            _logger.LogInformation("Starting HID reader service...");

            _windowProcDelegate = new WindowProcDelegate(WindowProc);
            _messageOnlyWindowHandle = CreateMessageOnlyWindow();

            if (_messageOnlyWindowHandle == IntPtr.Zero)
            {
                _logger.LogError("Failed to create message-only window.");
                return;
            }
            else
            {
                _logger.LogInformation("Message-only window created successfully.");
            }

            // Register the HID devices
            if (!RegisterHIDDevice(_messageOnlyWindowHandle))
            {
                _logger.LogError("Failed to register HID device.");
                return;
            }

            _logger.LogInformation("Listening for HID device input...");

            var messageLoopThread = new Thread(MessageLoop);
            messageLoopThread.Start();

            while (!stoppingToken.IsCancellationRequested)
            {
                await Task.Delay(100);
            }
        }

        // Define a delegate matching the signature of the window procedure
        private delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

        // Update CreateMessageOnlyWindow method
        private IntPtr CreateMessageOnlyWindow()
        {
            // Create a delegate instance for the Window Procedure
            WndProcDelegate wndProcDelegate = new WndProcDelegate(WindowProc);

            // Define and register the window class
            var wndClass = new WNDCLASS
            {
                // Convert delegate to function pointer
                lpfnWndProc = Marshal.GetFunctionPointerForDelegate(wndProcDelegate),
                hInstance = Marshal.GetHINSTANCE(typeof(Program).Module),
                lpszClassName = "MessageOnlyWindow"
            };

            ushort classAtom = RegisterClass(ref wndClass);

            if (classAtom == 0)
            {
                int error = Marshal.GetLastWin32Error();
                _logger.LogError($"Failed to register window class. Error: {error}");
                return IntPtr.Zero;
            }

            // Create the message-only window
            IntPtr hWnd = CreateWindowEx(0, "MessageOnlyWindow", "Message Only Window",
                                         0, 0, 0, 0, 0, HWND_MESSAGE, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);

            if (hWnd == IntPtr.Zero)
            {
                int error = Marshal.GetLastWin32Error();
                _logger.LogError($"Failed to create message-only window. Error: {error}");
                return IntPtr.Zero;
            }

            _logger.LogInformation("Message-only window created successfully.");
            RegisterHIDDevice(hWnd);

            return hWnd;
        }

        // Window Procedure method
        private IntPtr WindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
        {
            // Log every message that arrives in WindowProc to confirm message reception
            _logger.LogInformation($"WindowProc received message: {msg}");

            // Check if we received the expected WM_INPUT message
            if (msg == WM_INPUT)
            {
                _logger.LogInformation("WM_INPUT message received.");
                ProcessRawInput(lParam);
            }

            return DefWindowProc(hWnd, msg, wParam, lParam);
        }

        private bool RegisterHIDDevice(IntPtr hWnd)
        {
            var rid = new RAWINPUTDEVICE[1];

            rid[0].UsagePage = 0x01;  // Generic Desktop Controls
            rid[0].Usage = 0x06;      // Keyboard
            rid[0].Flags = RIDEV_INPUTSINK;
            rid[0].Target = hWnd;

            bool result = RegisterRawInputDevices(rid, (uint)rid.Length, (uint)Marshal.SizeOf(typeof(RAWINPUTDEVICE)));

            if (!result)
            {
                int errorCode = Marshal.GetLastWin32Error();
                _logger.LogError($"RegisterRawInputDevices failed. Error Code: {errorCode}");
            }
            else
            {
                _logger.LogInformation("HID device registered successfully.");
            }

            return result;
        }

        private void MessageLoop()
        {
            MSG msg;

            while (GetMessage(out msg, IntPtr.Zero, 0, 0))
            {
                _logger.LogInformation($"Received message: {msg.message}");
                TranslateMessage(ref msg);
                DispatchMessage(ref msg);
            }
        }

        private void ProcessRawInput(IntPtr lParam)
        {
            uint dwSize = 0;
            GetRawInputData(lParam, RID_INPUT, IntPtr.Zero, ref dwSize, (uint)Marshal.SizeOf(typeof(RAWINPUTHEADER)));
            IntPtr buffer = Marshal.AllocHGlobal((int)dwSize);

            try
            {
                if (GetRawInputData(lParam, RID_INPUT, buffer, ref dwSize, (uint)Marshal.SizeOf(typeof(RAWINPUTHEADER))) == dwSize)
                {
                    var raw = (RAWINPUT)Marshal.PtrToStructure(buffer, typeof(RAWINPUT));
                    if (raw.header.dwType == RIM_TYPEKEYBOARD)
                    {
                        _logger.LogInformation($"RawInput Keyboard Event: VKey={raw.keyboard.VKey}, Message={raw.keyboard.Message}");

                        if (raw.keyboard.Message == WM_KEYDOWN && raw.keyboard.VKey == VK_RETURN)
                        {
                            _logger.LogInformation("Enter key pressed!");
                            // Handle enter key, e.g., launch a browser
                            Process.Start("notepad");
                        }
                    }
                }
            } 
            finally
            {
                Marshal.FreeHGlobal(buffer);
            }
        }

        private void GetDeviceInfo(IntPtr hDevice, out ushort vendorId, out ushort productId)
        {
            uint size = 0;
            GetRawInputDeviceInfo(hDevice, RIDI_DEVICENAME, IntPtr.Zero, ref size);

            if (size == 0)
            {
                vendorId = productId = 0;
                return;
            }

            var nameBuffer = Marshal.AllocHGlobal((int)size);

            try
            {
                if (GetRawInputDeviceInfo(hDevice, RIDI_DEVICENAME, nameBuffer, ref size) > 0)
                {
                    var deviceName = Marshal.PtrToStringAnsi(nameBuffer);

                    if (deviceName != null)
                    {
                        _logger.LogInformation($"Device name: {deviceName}");

                        var vidIndex = deviceName.IndexOf("VID_") + 4;
                        var pidIndex = deviceName.IndexOf("PID_") + 4;

                        vendorId = Convert.ToUInt16(deviceName.Substring(vidIndex, 4), 16);
                        productId = Convert.ToUInt16(deviceName.Substring(pidIndex, 4), 16);
                        return;
                    }
                }
            } 
            finally
            {
                Marshal.FreeHGlobal(nameBuffer);
            }

            vendorId = productId = 0;
        }

        private const int WM_KEYDOWN = 0x100;
        private const int WM_INPUT = 0x00FF;
        private const uint RID_INPUT = 0x10000003;
        private const uint RIM_TYPEKEYBOARD = 1;
        private const uint VK_RETURN = 0x0D;
        private const int RIDEV_INPUTSINK = 0x00000100;
        private const uint RIDI_DEVICENAME = 0x20000007;
        private static readonly IntPtr HWND_MESSAGE = new IntPtr(-3);

        [DllImport("user32.dll")]
        private static extern bool RegisterRawInputDevices(RAWINPUTDEVICE[] pRawInputDevices, uint uiNumDevices, uint cbSize);

        [DllImport("user32.dll")]
        private static extern uint GetRawInputData(IntPtr hRawInput, uint uiCommand, IntPtr pData, ref uint pcbSize, uint cbSizeHeader);

        [DllImport("user32.dll")]
        private static extern uint GetRawInputDeviceInfo(IntPtr hDevice, uint uiCommand, IntPtr pData, ref uint pcbSize);

        [DllImport("user32.dll")]
        private static extern IntPtr CreateWindowEx(int exStyle, string lpClassName, string lpWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern ushort RegisterClass(ref WNDCLASS lpWndClass);

        [DllImport("user32.dll")]
        private static extern bool GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);

        [DllImport("user32.dll")]
        private static extern bool TranslateMessage(ref MSG lpMsg);

        [DllImport("user32.dll")]
        private static extern IntPtr DispatchMessage(ref MSG lpMsg);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);

        [StructLayout(LayoutKind.Sequential)]
        private struct WNDCLASS
        {
            public int style;
            public IntPtr lpfnWndProc;
            public int cbClsExtra;
            public int cbWndExtra;
            public IntPtr hInstance;
            public IntPtr hIcon;
            public IntPtr hCursor;
            public IntPtr hbrBackground;
            public string lpszMenuName;
            public string lpszClassName;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct MSG
        {
            public IntPtr hWnd;
            public uint message;
            public IntPtr wParam;
            public IntPtr lParam;
            public uint time;
            public POINT pt;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct POINT
        {
            public int x;
            public int y;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct RAWINPUTHEADER
        {
            public uint dwType;
            public uint dwSize;
            public IntPtr hDevice;
            public IntPtr wParam;
        }

        [StructLayout(LayoutKind.Explicit)]
        private struct RAWINPUT
        {
            [FieldOffset(0)]
            public RAWINPUTHEADER header;
            [FieldOffset(16)]
            public RAWKEYBOARD keyboard;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct RAWKEYBOARD
        {
            public ushort MakeCode;
            public ushort Flags;
            public ushort Reserved;
            public ushort VKey;
            public uint Message;
            public uint ExtraInformation;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct RAWINPUTDEVICE
        {
            public ushort UsagePage;
            public ushort Usage;
            public int Flags;
            public IntPtr Target;
        }
    }
}

出于测试目的,可以使用任何附加的键盘供应商、产品 ID。

c# keyboard hid background-service reader
1个回答
0
投票

正如 Selvin 所指出的,我正在为 MessageLoop 启动一个新线程。在 ExecuteAsync 中调用 MessageLoop 解决了该问题。谢谢你塞尔文。

© www.soinside.com 2019 - 2024. All rights reserved.