Repository navigation
fix(win): ANSI-маршалинг ToUnicodeEx, Backspace в буфере, клик мыши при выделении - #43
MikeSviblov wants to merge 3 commits into
Conversation
…порча кучи У DllImport для ToUnicodeEx не был указан CharSet → дефолтный ANSI: нативный pwszBuff выделялся по 1 байту на символ, а API пишет UTF-16 (2 байта), считая cchBuff количеством WCHAR. Два следствия: 1. Любой многосимвольный вывод (dead keys) переполнял буфер — heap corruption 0xc0000374, падение в MngdNativeArrayMarshaler.ClearNative (подтверждено полным дампом: стек ConvertLastWord → ConvertWord → TranslateIn → ToUnicodeEx). 2. Обратная ANSI-конвертация буфера возвращала младший байт каждого WCHAR: «вылетает» (0432 044B 043B 0435 0442 0430 0435 0442) превращалось в "2K;5B05B". На системах с ANSI=cp1252 ручная конверсия кириллицы не работала вовсе. Фикс: CharSet.Unicode у ToUnicodeEx (char[] маршалится как WCHAR, размер и содержимое совпадают с контрактом API), той же природы латентный баг у GetModuleHandleW (ANSI-строка в W-функцию), буфер расширен до 16 WCHAR как запас под композицию dead keys. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZjjdW8bam8gQ872kmquy9
…а стёртое Ручной Backspace не убирал последнюю клавишу из KeystrokeBuffer (он не typing key и не word boundary), буфер расходился с экраном, и конверсия печатала и стёртые опечатки: «dsk,» + Backspace + «sktnftn» давало «вылбылетает» вместо «вылетает». macOS-монитор удаление обрабатывает (issue rashn#21) — паритет: Backspace → PopLast, Ctrl+Backspace (удаление слова) → Reset, в обоих случаях отменяется отложенный reconvert. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZjjdW8bam8gQ872kmquy9
…м последнего слова Выделение мышью не очищало KeystrokeBuffer, и триггер при непустом буфере уходил в ConvertLastWord: его backspace'ы первым нажатием съедали всё выделение, а повторный тап шёл по циклу кандидатов («ghbdtn hello руддщ» выделенное превращалось в «ηελλο»). Добавлен WH_MOUSE_LL-хук (только button-down, кликов мы не инжектим): любой клик = каретка/ выделение сместились → буфер и отложенный цикл/undo сбрасываются, и следующий триггер корректно уходит в путь конверсии выделения. Тот же резон, по которому за кликами следит macOS event tap. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZjjdW8bam8gQ872kmquy9
|
Добавил третий фикс той же серии (живое тестирование беты): клик мыши не сбрасывал буфер клавиш. Сценарий: набрать текст, выделить его мышью, нажать триггер — триггер при непустом буфере уходит в путь «последнее слово», и его backspace'ы первым же нажатием съедают всё выделение, после чего поверх печатается конверсия последнего слова. Воспроизводится на Фикс: WH_MOUSE_LL-хук (только button-down; кликов приложение не инжектит, фильтрация маркером не нужна) — любой клик означает, что каретка/выделение сместились и буфер больше не отражает слово у каретки: буфер и отложенный reconvert сбрасываются, а следующий триггер корректно уходит в путь конверсии выделения. Это та же причина, по которой macOS event tap слушает клики. Тесты: 30/30 на реальном Windows, вылетов и потери текста в сценарии «набрал → выделил мышью → триггер» больше нет (проверено на Win11 24H2). |
Два бага Windows-беты, пойманные живым тестированием на Windows 11 24H2 (ANSI=cp1252). Нашёл их, пока готовил греческую часть #42, но от неё они не зависят — это текущее поведение
main.1.
ToUnicodeExмаршалился как ANSIУ
DllImportвWin32.csне указанCharSet→ дефолтныйCharSet.Ansi. Для[Out] char[] pwszBuffэто означает нативный буфер 1 байт на символ, тогда какToUnicodeExпишет UTF-16 и считаетcchBuffколичеством WCHAR. Два следствия:а) Ручная конверсия кириллицы не работает вовсе. Обратная ANSI-конвертация буфера возвращает младший байт каждого WCHAR:
вылетает(U+0432 U+044B U+043B U+0435 U+0442 U+0430 U+0435 U+0442) превращается в2K;5B05B. На системах с ANSI=cp1251 низкобайтовая часть случайно совпадает с кириллицей — вероятно, поэтому баг не всплывал у русскоязычных тестеров; на cp1252/UTF-8 он проявляется сразу.б) Порча кучи. API может легально записать до
cchBuffWCHAR = 16 байт в 8-байтовый блок. У меня это дважды роняло приложение при печати (0xc0000374STATUS_HEAP_CORRUPTION). Полный дамп (WER LocalDumps) показывает падение ровно при освобождении этого буфера:Фикс:
CharSet.UnicodeуToUnicodeEx(массив маршалится как WCHAR — размер и содержимое совпадают с контрактом API), той же природы латентный баг уGetModuleHandleW(ANSI-строка в W-функцию; сейчас вызывается только сnull), и буфер расширен до 16 WCHAR как запас под многосимвольный вывод dead keys.2. Backspace не отражался в буфере клавиш
VK_BACK— не typing key и не word boundary, поэтому ручное удаление не убирало клавишу изKeystrokeBuffer, буфер расходился с экраном, и конверсия перепечатывала стёртые опечатки:dsk,+ Backspace + допечатка давали «вылбылетает» вместо «вылетает». macOS-монитор удаление обрабатывает (та же семья, что #21) — порт догнан: Backspace →PopLast, Ctrl+Backspace (удаление слова) →Reset, в обоих случаях сбрасывается отложенный reconvert.Проверка
SendInputTests— им нужен интерактивный десктоп), + 2 новых наPopLast.2K;5B05Bи два краша за 5 минут печати; после —dsktnftn→ «вылетает», сценарий с опечаткой+Backspace корректен, крашей нет.windows/).🤖 Generated with Claude Code
https://claude.ai/code/session_01PZjjdW8bam8gQ872kmquy9