История простая: после очередного обновления Windows 10 у меня начали странно работать мультимедийные клавиши. Привязка к текущему приложению иногда теряется и работать вообще перестает. Плюс ещё бесило стандартное всплывающее окно управления музыкой — оно висит вечность и закрыть его нельзя.

В какой‑то момент я понял, что терпеть это уже не хочется, и пошёл разбираться, как всё починить.
Почему всё сломалось
После обновления Windows система начала периодически терять активное медиа‑приложение. Проще говоря — Windows иногда просто не понимает, какое приложение сейчас должно получать команды «пауза», «следующий трек» и т.д.
Первая попытка — PowerToys
Сначала я попробовал пойти по простому пути и использовать Microsoft PowerToys — переназначал горячие клавиши, экспериментировал с биндингами. Но быстро стало понятно, что это не решает корневую проблему. Если Windows теряет активное медиа‑приложение, то никакие красивые биндинги не спасают.
Поэтому пришлось идти дальше и брать более гибкий инструмент — AutoHotkey.
Эксперименты с AutoHotkey
Попытка №1 — просто отправлять медиа‑кнопки
Самый очевидный вариант — просто пересылать мультимедийные команды:
Media_Play_Pause::Send, {Media_Play_Pause} Media_Next::Send, {Media_Next} Media_Prev::Send, {Media_Prev}
Но из‑за той же самой проблемы с «потерей активного плеера» это работало нестабильно: иногда команда проходила, иногда — нет.
Попытка №2 — побороть зависающее системное окно
Меня ещё раздражало, что при переключении трека появляется системное окно (Media OSD), которое долго висит на экране. Я попробовал хитрость — дополнительно дергать громкость, чтобы окно быстрее закрывалось:
Media_Next:: Send {Volume_Up} Send {Volume_Down} return
Работает… но костыльно. Понятно что фокус всё теряется.
Попытка №3 — свои комбинации клавиш
Дальше я решил повесить управление музыкой на собственные сочетания, например Ctrl + Alt + стрелки:
^!Right:: ControlSend,, {Media_Next} return ^!Left:: ControlSend,, {Media_Prev} return
Стало удобнее переключаться, но двигаемся дальше.
Попытка №4 — жёстко управлять фокусом окна
В какой‑то момент стало понятно: надо не надеяться на Windows, а самому управлять окном Яндекс Музыки.
Логика такая:
Найти окно Яндекс Музыки.
Активировать его.
Отправить нужную клавишу (в настройках приложения можно найти список горячих клавиш).
Свернуть обратно.
Финальный вариант выглядел примерно так:
PlayPause: if WinExist("ahk_exe Яндекс Музыка.exe") { WinActivate Sleep, 200 Send, k ; Play/Pause в Яндекс Музыке WinMinimize } return
Да, окно на долю секунды мелькает, но зато команды работают стабильно и больше не появляется раздражающее системное окно.
Неожиданная проблема — раскладка клавиатуры
Когда всё вроде бы заработало, всплыла новая проблема: при русской раскладке буквы (k, n, p) отправлялись неправильно.
Решение простое — отправлять не буквы, а скан‑коды клавиш:
Send, {sc019} ; вместо буквы P
После этого раскладка вообще перестала иметь значение.
А что насчёт RDP?
Естественно, мне захотелось, чтобы всё это работало и когда я нахожусь в RDP. Тут я потратил кучу времени: пробовал разные программы, советы ИИ, хаки — но почти всё упиралось в ограничения самого RDP. Он очень агрессивно перехватывает ввод.
В итоге стало понятно: чисто программно эту задачу нормально не решить.
Поспрашивал ИИ, сначала он мне предложили купить отдельный USB‑нумпад. Но почти все такие устройства — это просто цифры от 0 до 9. Для управления музыкой это неудобно.
И тут я вспомнил, что у меня валяется джойстик от PS5. Подключил его — Windows увидела его сразу, AutoHotkey тоже без проблем начал ловить кнопки.
Клавиши ловил через вот этот небольшой скрипт:
#NoEnv #SingleInstance Force ; Тест всех кнопок геймпада SetTimer, ShowJoystick, 100 return ShowJoystick: ; Проверяем все кнопки buttons := "" Loop, 32 { if GetKeyState("Joy" . A_Index) buttons .= A_Index . " " } ; Проверяем D-Pad (POV) pov := GetKeyState("JoyPOV") ; Проверяем стики x := GetKeyState("JoyX") y := GetKeyState("JoyY") z := GetKeyState("JoyZ") r := GetKeyState("JoyR") ; Показываем всё ToolTip, Button: %buttons%`nPOV (D-Pad): %pov%`nleft stick X/Y: %x% / %y%`nrighr stick Z/R: %z% / %r%, 0, 0 return Esc::ExitApp ; Закрыть по Esc
Назначил кнопки:
Joy1::Gosub, PrevTrack ; предыдущий трек Joy3::Gosub, NextTrack ; следующий трек Joy14::Gosub, PlayPause ; пауза / воспроизведение Joy13::Gosub, ActivateMusic ; активировать яндекс музыку, если нет окна, то запустить
И внезапно это оказалось максимально удобным решением. Работает стабильно, в том числе и если находимся в RDP.
Управление громкостью и дополнительные бонусы
На свободные кнопки я повесил громкость:
Joy4:: Send {Volume_Up} Joy2:: Send {Volume_Down}
Но в RDP такие команды улетают в удалённую машину. Поэтому я переделал это на прямое управление системной громкостью:
Joy4:: SoundSet +1 Joy2:: SoundSet -1
А потом сделал приятный бонус — чтобы при удержании кнопки громкость плавно менялась:
Joy4:: while GetKeyState("Joy4", "P") { SoundSet +1 Sleep 50 } return
Добавил установку лайков и дизлайков
Подумал и на клавиши вверх и вниз добавил лайки и дизлайки. Тут все немного посложнее, но ИИ как обычно справился.
CheckPOV: POV := GetKeyState("JoyPOV") if (POV = 0 && !POVPressed) ; Вверх - лайк { POVPressed := true Gosub, LikeTrack } else if (POV = 18000 && !POVPressed) ; Вниз - дизлайк { POVPressed := true Gosub, DislikeTrack } else if (POV = -1) ; Отпущен POVPressed := false return
Что получилось в итоге
В результате у меня получилось:
стабильное управление музыкой;
не зависит от раскладки клавиатуры;
работает даже когда находимся в окне RDP;
нет раздражающего системного окна;
удобное управление с отдельного устройства;
установка лайков и дизлайков;
плавная регулировка громкости.
Бонусом оказалось то, что джойстики стоят недорого, легко подключаются к Windows и имеют много удобных кнопок разной формы — их легко запомнить на ощупь. В этом плане они даже удобнее нумпадов и клавиатур.
Если у вас потребность в дополнительных клавишах для любой вашей программы, можете также купить джойстик и модифицировать мой скрипт 🙂
Итоговый скрипт
Запускать его надо на AHK 1.1, скачать можно тут: https://www.autohotkey.com/
#NoEnv SendMode Input SetWorkingDir %A_ScriptDir% #SingleInstance Force if not A_IsAdmin ; К сожалению зачем то нужны права админа, без них Яндекс Музыка не вызывается { Run *RunAs "%A_ScriptFullPath%" ExitApp } ; === Проверка D-Pad === SetTimer, CheckPOV, 100 ; запускаем таймер return CheckPOV: POV := GetKeyState("JoyPOV") if (POV = 0 && !POVPressed) ; Вверх - лайк { POVPressed := true Gosub, LikeTrack } else if (POV = 18000 && !POVPressed) ; Вниз - дизлайк { POVPressed := true Gosub, DislikeTrack } else if (POV = -1) ; Отпущен POVPressed := false return Media_Play_Pause::Gosub, PlayPause ; === Кнопки PS5 === Joy1::Gosub, PrevTrack ; Квадрат (□) - предыдущий Joy3::Gosub, NextTrack ; Круг (○) - следующий Joy14::Gosub, PlayPause ; Тачпад - воспроизведение\пауза Joy13::Gosub, ActivateMusic ; PS - активировать яндекс музыку ; === Процедуры === Joy4:: ; Треугольник (△) - громче while GetKeyState("Joy4", "P") { SoundSet +1 Sleep 50 } return Joy2:: ; Крест (✕) - тише while GetKeyState("Joy2", "P") { SoundSet -1 Sleep 50 } return PlayPause: if WinExist("ahk_exe Яндекс Музыка.exe") { WinActivate Sleep, 200 Send, {sc025}; k WinMinimize } return NextTrack: if WinExist("ahk_exe Яндекс Музыка.exe") { WinActivate Sleep, 200 Send, {sc031}; n WinMinimize } return PrevTrack: if WinExist("ahk_exe Яндекс Музыка.exe") { WinActivate Sleep, 200 Send, {sc019}; p WinMinimize } return LikeTrack: if WinExist("ahk_exe Яндекс Музыка.exe") { WinActivate Sleep, 200 Send, {sc021}; f WinMinimize } return DislikeTrack: if WinExist("ahk_exe Яндекс Музыка.exe") { WinActivate Sleep, 200 Send, {sc020}; d WinMinimize } return ActivateMusic: if WinExist("ahk_exe Яндекс Музыка.exe") { WinActivate } else { Run, %LOCALAPPDATA%\Programs\YandexMusic\Яндекс Музыка.exe } return
