преобразовать код ключа в соответствующий символ дисплея - PullRequest
5 голосов
/ 16 декабря 2008

В проекте C # Windows.Forms у меня есть элемент управления, который не предоставляет событие KeyPressed (Это элемент управления COM - карта ESRI).

Предоставляет только события KeyUp и KeyDown, содержащие структуру KeyEventArgs .

Как преобразовать информацию в KeyEventArgs в отображаемый символ Unicode, учитывая текущую активную раскладку клавиатуры и т. Д .?

Ответы [ 3 ]

11 голосов
/ 17 декабря 2008

Хитрость заключается в использовании набора функций user32.dll: GetWindowThreadProcessId , GetKeyboardLayout , GetKeyboardState и ToUnicodeEx .

  1. Используйте функцию GetWindowThreadProcessId с дескриптором управления для получения соответствующего идентификатора собственного потока.
  2. Передайте идентификатор этого потока в GetKeyboardLayout , чтобы получить текущую раскладку клавиатуры.
  3. Вызовите GetKeyboardState , чтобы получить текущее состояние клавиатуры. Это помогает следующему методу решить, какой символ генерировать в соответствии с состоянием модификаторов.
  4. Наконец, вызовите функцию ToUnicodeEx с требуемым кодом виртуальной клавиши и кодом сканирования (эти два могут быть одинаковыми), текущим состоянием клавиатуры, строителем строки в качестве держателя строки (для хранения результата ), без флагов (0) и текущего указателя раскладки клавиатуры.

Если результат не равен нулю, просто верните первый возвращенный символ.

  public class KeyboardHelper
    {
        [DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
        private static extern int ToUnicodeEx(
            uint wVirtKey,
            uint wScanCode,
            Keys[] lpKeyState,
            StringBuilder pwszBuff,
            int cchBuff,
            uint wFlags,
            IntPtr dwhkl);

        [DllImport("user32.dll", ExactSpelling = true)]
        internal static extern IntPtr GetKeyboardLayout(uint threadId);

        [DllImport("user32.dll", ExactSpelling = true)]
        internal static extern bool GetKeyboardState(Keys[] keyStates);

        [DllImport("user32.dll", ExactSpelling = true)]
        internal static extern uint GetWindowThreadProcessId(IntPtr hwindow, out uint processId);

        public static string CodeToString(int scanCode)
        {
            uint procId;
            uint thread = GetWindowThreadProcessId(Process.GetCurrentProcess().MainWindowHandle, out procId);
            IntPtr hkl = GetKeyboardLayout(thread);

            if (hkl == IntPtr.Zero)
            {
                Console.WriteLine("Sorry, that keyboard does not seem to be valid.");
                return string.Empty;
            }

            Keys[] keyStates = new Keys[256];
            if (!GetKeyboardState(keyStates))
                return string.Empty;

            StringBuilder sb = new StringBuilder(10);
            int rc = ToUnicodeEx((uint)scanCode, (uint)scanCode, keyStates, sb, sb.Capacity, 0, hkl);
            return sb.ToString();
        }
    }
8 голосов
/ 06 мая 2009

Решение Itai Bar-Haim хорошее, но есть проблема с мертвыми ключами.

При первом вызове CodeToString (int scanCode) с кодом сканирования мертвого ключа код возврата ToUnicodeEx () равен -1, и ваш метод возвращает правильное значение.

Но при следующем вызове ToUnicodeEx () вернет 2. Строка результата будет содержать мертвый ключ перед ожидаемым результатом, за которым следуют данные нежелательной памяти.

Например, я вызываю ваш метод с помощью ^ (мертвый ключ), а затем с помощью $. Первый вызов возвращает ^ (приятно), но второй вызов возвращает ^ $ azertyui.

Вы должны сделать две вещи, чтобы исправить это:

1) Вы должны использовать абсолютное значение кода возврата ToUnicodeEx (), чтобы установить длину вашего StringBuilder. Таким образом, вы избегаете получать ненужные данные после символов.

2) Когда код возврата ToUnicodeEx () равен -1, вы должны взять первый символ вашего StringBuilder в качестве результата для вашего метода. Затем необходимо удалить мертвую клавишу из состояния клавиатуры: вызвать ToUnicodeEx () со скан-кодом клавиши пробела.

Другая проблема, с которой вы можете столкнуться: если состояние клавиатуры было изменено пользователем, который нажал мертвую клавишу перед вызовом ToUnicodeEx (), возвращаемая строка будет содержать символ мертвой клавиши перед ожидаемым значением. Обходной путь, который я нашел, - это вызвать ToUnicodeEx () со скан-кодом клавиши пробела перед фактическим вызовом.

Надеюсь, это поможет!

0 голосов
/ 16 декабря 2008

Посмотрите на KeysConverter и его метод ConvertToString. Помните, что не все KeyDown соответствуют KeyPress.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...