Search code examples
c#loggingkeyboard

Keylogging for testing a qr scanner


I need to write a program for logging input from the USB qr/line-scanners. I have to send this program to all offices of my work. The program should collect input information and send the files back for testing.

The problem is that the scanner inputs very quickly and some keys (characters) disappear.

For example, the scanner scans "01110" in the input field, but the keylogging recorded “010”.

Here is my code

...
while (true)
{
    for (int i = 0; i < 255; i++)
    {
        int keyState = GetAsyncKeyState(i);
        if (keyState == 1 || keyState == -32767)
        {
            //log...
        }
    }
}
...

I tried with the GetKeyState function, in this case all the keys are recorded, but the problem is that there are a lot of duplicates. For example: the scanner “presses” zero (0) key, but it duplicates many times and look like this “000000”.

How to write a keylogging that will log all pressed keys (even if the input comes from USB qr-scanner) ?


Solution

  • Thanks for the help (in the comments). Hooks helped solve the problem. If anyone needs it, here's the solution.

    https://learn.microsoft.com/en-us/archive/blogs/toub/low-level-keyboard-hook-in-c

    using System;
    using System.Diagnostics;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    
    class InterceptKeys
    {
        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100;
        private static LowLevelKeyboardProc _proc = HookCallback;
        private static IntPtr _hookID = IntPtr.Zero;
    
        public static void Main()
        {
            _hookID = SetHook(_proc);
            Application.Run();
            UnhookWindowsHookEx(_hookID);
        }
    
        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);
            }
        }
    
        private delegate IntPtr LowLevelKeyboardProc(
            int nCode, IntPtr wParam, IntPtr lParam);
    
        private static IntPtr HookCallback(
            int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
            {
                int vkCode = Marshal.ReadInt32(lParam);
                Console.WriteLine((Keys)vkCode);
            }
            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }
    
        [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);
    }