Проблемы со считыванием штрих-кода с использованием PreFilterMessage

У меня есть задача прочитать штрих-код со сканера штрих-кода Symbol LS2208.
Сканер настроен по умолчанию, но с префиксом F13 и надстройкой «Enter». Используя это, сканер эмулирует клавиатуру США. Моя клавиатура датская, и язык ОС установлен на датский.
Мне нравится быть независимым от региональных настроек пользователя.

Прямо сейчас я реализую IMessageFilter, используя:

    private const int WM_KEYDOWN = 0x100;
    private List<Keys> keysSequence = new List<Keys>();
    public bool PreFilterMessage(ref Message m)
    {
        if (m.Msg == WM_KEYDOWN)
        {
            Keys keyCode = (Keys)(int)m.WParam & Keys.KeyCode;
            if (keyCode == Keys.F13)
            {
                ...
                return true;
            }
            if (keyCode == Keys.Enter)
            {
                ...
                string barcode = GenerateBarcode(keysSequence);
                return true;
            }

            keysSequence.Add(keyCode);
        }
    }

Когда получена клавиша Surfix 'Enter', последовательность клавиш используется для создания строки штрих-кода с помощью метода GenerateBarcode:

    public static string GenerateBarcode(Keys[] captureKeysSequence)
    {
        StringBuilder barcodeBuffer = new StringBuilder();
        bool shift = false;
        bool altGr = false;
        foreach (Keys keyCode in captureKeysSequence)
        {
            if (keyCode == Keys.Shift || keyCode == Keys.ShiftKey)
            {
                shift = true;
                continue;
            }
            if (keyCode == (Keys.Control | Keys.Alt))
            {
                altGr = true;
                continue;
            }
            barcodeBuffer.Append(GetCharsFromKeys(keyCode, shift, altGr));
            shift = false;
            altGr = false;
        }
        return barcodeBuffer.ToString();
    }

Здесь я работаю с клавишей shift и altGr.

Теперь причина моих проблем - метод GetCharsFromKeys:

    [DllImport("user32.dll")]
    public static extern int ToUnicodeEx(uint virtualKeyCode, uint scanCode,
            byte[] keyboardState,
            [Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)]
            StringBuilder receivingBuffer,
            int bufferSize, uint flags, IntPtr dwhkl);

    public static string GetCharsFromKeys(Keys keys, bool shift, bool altGr)
    {
        var buf = new StringBuilder(256);
        var keyboardState = new byte[256];
        if (shift)
            keyboardState[(int)Keys.ShiftKey] = 0xff;
        if (altGr)
        {
            keyboardState[(int)Keys.ControlKey] = 0xff;
            keyboardState[(int)Keys.Menu] = 0xff;
        }

        // Because the Symbol LS2208 maps a US keyboard we need to convert the Keys correctly to chars
        ToUnicodeEx((uint)keys, 0, keyboardState, buf, buf.Capacity, 0, InputLanguage.FromCulture(new System.Globalization.CultureInfo("en-US")).Handle);

        return buf.ToString();
    }

Я пытаюсь преобразовать ввод ключей со сканера штрих-кода в строку. И для большинства обычных символов это работает отлично (как строчные, так и верхние символы и цифры), а также некоторые специальные символы, найденные над цифрами на клавиатуре, работают (например, «$»).

Но при тестировании со штрих-кодом типа "12/34 - 56" я получаю вывод "12 - 34 = 56"?

Я думаю, что это как-то связано с отображением между американской и датской клавиатурой, но я не могу понять, почему?
Может ли кто-нибудь помочь мне с этим преобразованием?


person kaarelyngejensen    schedule 26.11.2014    source источник
comment
Попытка синтезировать состояние клавиатуры впоследствии обречена на неудачу. Особенно, если вы пытаетесь справиться с мертвыми ключами. Просто не делайте этого таким образом, сгенерируйте символ, когда вы нажмете клавишу. Вызовите GetKeyboardState() в это время, чтобы получить точное состояние.   -  person Hans Passant    schedule 26.11.2014
comment
Спасибо за ответ @HansPassant. Я не уверен, что правильно вас понял, но я попытался получить состояние клавиатуры с помощью GetKeyboardState, как вы предложили, и использовать вывод в качестве ввода для ToUnicodeEx. Но все тот же результат, со странным выводом штрих-кода. Можете ли вы привести пример?   -  person kaarelyngejensen    schedule 27.11.2014


Ответы (1)


Я не смог решить эту проблему так, как хотел начать:
"С помощью настройки сканера штрих-кода для использования клавиатуры США и получения символов независимо от установленных настроек клавиатуры региона Windows".

Поэтому мне пришлось ограничить свои требования следующим:
«Сканер штрих-кода и Windows должны иметь одинаковые настройки клавиатуры».

Это делает все намного проще. Я все еще реализую IMessageFilter и использую как VM_KEYDOWN, так и VM_MSG. Вот код, который я использую (хотя все еще не хватает обработки ошибок/тайм-аутов):

public class BarcodeScannerMessageFilter : IMessageFilter
{
    public event EventHandler<BarcodeScannerReadyEventArgs> BarcodeScannerReady;

    private bool barcodeStarted = false;
    private StringBuilder barcodeBuilder = new StringBuilder();

    public BarcodeScannerMessageFilter()
    {
        Application.AddMessageFilter(this);
    }

    #region IMessageFilter Members
    private const int WM_KEYDOWN = 0x100;
    private const int WM_MSG = 0x102;
    public bool PreFilterMessage(ref Message m)
    {
        if (m.Msg == WM_KEYDOWN) // Use KeyDown to look for prefix and surfix
        {
            Keys keyCode = (Keys)(int)m.WParam & Keys.KeyCode;

            if (!barcodeStarted) // Prevent F13 Capture until previous is ended
            {
                // Check for start capture key (F13)
                if (keyCode == Keys.F13)
                {
                    barcodeStarted = true;
                    return true;
                }
            }
            else
            {
                // Check for end capture key (Enter)
                if (keyCode == Keys.Enter)
                {
                    // Raise barcode capture event with barcode
                    RaiseBarcodeReadyEventAsync(barcodeBuilder.ToString());

                    // End sequence
                    barcodeBuilder.Clear();
                    barcodeStarted = false;
                    return true;
                }
            }
        }
        else if (m.Msg == WM_MSG) // Catch all char messages
        {
            char c = (char)m.WParam;
            // Else just append char to barcodeBuilder to generate barcode
            barcodeBuilder.Append(c);
            return true;
        }

        return false;
    }

    #endregion

    private void RaiseBarcodeReadyEventAsync(string barcode)
    {
        Task.Factory.StartNew(() =>
        {
            try
            {
                // Generate barcode
                Console.WriteLine("F13 activated barcode [" + barcode + "]");

                if (BarcodeScannerReady != null)
                {
                    BarcodeScannerReady(this, new BarcodeScannerReadyEventArgs(barcode));
                }
            }
            catch (Exception ex)
            {
                // Do some error logging if needed
                Console.WriteLine(ex);
            }
        });
    }
}

public class BarcodeScannerReadyEventArgs : EventArgs
{
    public string Barcode { get; private set; }

    public BarcodeScannerReadyEventArgs(string barcode)
    {
        this.Barcode = barcode;
    }
}

Я надеюсь, что это поможет другим, которые борются с такими проблемами, как моя.

person kaarelyngejensen    schedule 03.12.2014