Как исправить AHK-скрипт для переключения раскладки клавиатуры в системных окнах Windows?
Я использую Windows 10 и впервые работаю с AutoHotkey. Я создал скрипт для переключения раскладки клавиатуры: русский язык на правый Ctrl и английский на левый Ctrl. Вот мой скрипт:
~LCtrl up::
If (A_PriorKey = "LControl") {
SendMessage, 0x50,, 0x4090409,, A ; английский
}
~RCtrl up::
If (A_PriorKey = "RControl") {
SendMessage, 0x50,, 0x4190419,, A ; русский
}
Скрипт работает корректно в обычных окнах, но не срабатывает в системных окнах Windows, например, в стандартном диалоговом окне сохранения файла.
В чем заключается ошибка в моем скрипте и как ее исправить?
Примечание: Я знаю, что PuntoSwitcher решает эту задачу, но я не хочу использовать эту программу.
Дополнительно: Я хочу оставить в системе единственный способ переключения языков через этот скрипт, без добавления дополнительных комбинаций клавиш в системных настройках и без эмуляции их нажатий.
Проблема с вашим AHK-скриптом заключается в том, что системные окна Windows (такие как диалоги сохранения файлов) имеют особый механизм обработки ввода и не всегда реагируют на стандартные сообщения AutoHotkey. В системных окнах часто требуется прямой вызов системных API через DllCall вместо использования SendMessage.
Содержание
- Основная причина проблемы
- Решение 1: Использование DllCall с LoadKeyboardLayout
- Решение 2: Полная замена системных горячих клавиш
- Решение 3: Запуск скрипта от имени администратора
- Полный рабочий скрипт
- Настройка системы для единственного способа переключения
- Рекомендации по устранению неполадок
Основная причина проблемы
Системные окна Windows используют собственный механизм обработки ввода, который игнорирует стандартные сообщения AutoHotkey. Ваш метод SendMessage, 0x50 работает в обычных приложениях, но не в системных диалогах, потому что:
- Системные окна имеют ограниченный доступ к внешним сообщениям
- Они используют собственный обработчик ввода
- Требуются более низкоуровневые вызовы API Windows
Решение 1: Использование DllCall с LoadKeyboardLayout
Наиболее надежный метод - прямой вызов системных функций через DllCall:
~LCtrl up::
If (A_PriorKey = "LControl") {
SetKeyboardLayout(0x0409) ; английский (US)
}
return
~RCtrl up::
If (A_PriorKey = "RControl") {
SetKeyboardLayout(0x0419) ; русский
}
return
SetKeyboardLayout(LocaleID) {
; Загружаем раскладку клавиатуры
hKL := DllCall("LoadKeyboardLayout", "Str", Format("{:08X}", LocaleID), "UInt", 0x00000001)
; Устанавливаем раскладку для текущего потока
DllCall("ActivateKeyboardLayout", "UInt", hKL, "UInt", 0x00000000)
; Обновляем системные параметры
VarSetCapacity(LAN, 8, 0)
NumPut(LocaleID, LAN, 0, "UInt")
NumPut(LocaleID, LAN, 4, "UInt")
DllCall("SystemParametersInfo", "UInt", 0x005A, "UInt", 0, "Ptr", &LAN, "UInt", 0x0002)
}
Этот метод работает в большинства системных окон, так как использует прямой вызов системных функций Windows.
Решение 2: Полная замена системных горячих клавиш
Чтобы полностью заменить системные горячие клавиши, необходимо:
- Отключить стандартное переключение в настройках Windows
- Создать собственный обработчик, который будет работать везде
; Полная замена системных горячих клавиш
#NoEnv
#SingleInstance Force
SetBatchLines, -1
; Отключаем стандартное переключение (запускать от имени администратора)
OnExit, ExitSub
ExitSub:
DllCall("SystemParametersInfo", "UInt", 0x005A, "UInt", 0, "Ptr", &OldLAN, "UInt", 0x0002)
ExitApp
; Сохраняем текущие настройки
VarSetCapacity(OldLAN, 8, 0)
DllCall("SystemParametersInfo", "UInt", 0x005A, "UInt", 0, "Ptr", &OldLAN, "UInt", 0x0002)
; Горячие клавиши для переключения
~LCtrl up::
If (A_PriorKey = "LControl") {
SetKeyboardLayout(0x0409) ; английский
}
return
~RCtrl up::
If (A_PriorKey = "RControl") {
SetKeyboardLayout(0x0419) ; русский
}
return
Решение 3: Запуск скрипта от имени администратора
Некоторые системные окна требуют повышенных привилегий:
- Создайте ярлык для вашего скрипта
- Откройте свойства ярлыка
- Перейдите на вкладку “Совместимость”
- Установите галочку “Выполнить программу от имени администратора”
- Примените изменения
Полный рабочий скрипт
#NoEnv
#SingleInstance Force
SetBatchLines, -1
; Устанавливаем скрипт для работы со всеми окнами
#InstallKeybdHook
#UseHook On
; Функция установки раскладки клавиатуры
SetKeyboardLayout(LocaleID) {
try {
; Пробуем сначала через ActivateKeyboardLayout
hKL := DllCall("LoadKeyboardLayout", "Str", Format("{:08X}", LocaleID), "UInt", 0x00000001)
DllCall("ActivateKeyboardLayout", "UInt", hKL, "UInt", 0x00000000)
; Обновляем системные параметры
VarSetCapacity(LAN, 8, 0)
NumPut(LocaleID, LAN, 0, "UInt")
NumPut(LocaleID, LAN, 4, "UInt")
DllCall("SystemParametersInfo", "UInt", 0x005A, "UInt", 0, "Ptr", &LAN, "UInt", 0x0002)
; Дополнительная попытка для stubborn окон
PostMessage, 0x50, 0, % (LocaleID << 16) | LocaleID, , A
}
return
}
; Горячие клавиши
~LCtrl up::
If (A_PriorKey = "LControl") {
SetKeyboardLayout(0x0409) ; английский (US)
ToolTip, English Layout, 1000
}
return
~RCtrl up::
If (A_PriorKey = "RControl") {
SetKeyboardLayout(0x0419) ; русский
ToolTip, Русская раскладка, 1000
}
return
; Убираем подсказку через 1 секунду
SetTimer, RemoveToolTip, -1000
RemoveToolTip:
ToolTip
return
Настройка системы для единственного способа переключения
Чтобы оставить в системе только ваш способ переключения:
- Откройте Параметры → Время и язык → Язык
- Нажмите на Языковые параметры
- В разделе Сочетания клавиш для языков ввода нажмите Сочетания клавиш для языков ввода
- В окне Текст服务和输入语言 выберите вкладку Переключение языков ввода
- Установите Нет для “Переключать язык ввода”
- Примените изменения и перезагрузите компьютер
Рекомендации по устранению неполадок
Если скрипт все еще не работает:
- Проверьте коды языков: Убедитесь, что 0x0409 и 0x0419 соответствуют вашим языкам
- Добавьте отладочную информацию:autohotkey
~LCtrl up:: If (A_PriorKey = "LControl") { MsgBox, Переключение на английский SetKeyboardLayout(0x0409) } return - Используйте альтернативные коды языков:
- Английский: 0x0409 (US), 0x0809 (UK)
- Русский: 0x0419, 0x0422 (Украина)
- Попробуйте использовать WinAPI напрямую:autohotkey
SetKeyboardLayout(LocaleID) { static hKL := {} if !hKL.HasKey(LocaleID) { hKL[LocaleID] := DllCall("LoadKeyboardLayout", "Str", Format("{:08X}", LocaleID), "UInt", 1) } return DllCall("ActivateKeyboardLayout", "UInt", hKL[LocaleID], "UInt", 0) }
Важно: Некоторые системные окна могут требовать перезапуска после изменения настроек языка. Также убедитесь, что ваш скрипт запущен от имени администратора для максимальной совместимости.
Источники
- AutoHotkey Community - Switch keyboard layout
- AutoHotkey Community - How to change keyboard language layout
- Stack Overflow - AutoHotkey: Disable Ctrl+Shift
- Super User - Setting different keys for switching between keyboard layouts
- AutoHotkey Community - Keyboard layout switcher for many layouts
Заключение
Основные проблемы с переключением раскладки в системных окнах Windows решаются использованием прямых вызовов WinAPI через DllCall вместо стандартных сообщений AutoHotkey. Ключевые моменты:
- Используйте
DllCall("LoadKeyboardLayout")иDllCall("ActivateKeyboardLayout")для надежной работы - Запускайте скрипт от имени администратора для доступа ко всем системным окнам
- Отключите стандартные сочетания клавиш в настройках Windows
- Для максимальной совместимости используйте комбинацию разных методов
Представленный скрипт должен работать во всех типах окон, включая системные диалоги, и позволит вам оставить единственный способ переключения языков без использования внешних программ.