Узнайте, какой процесс зарегистрировал глобальную горячую клавишу? (Windows API)

114

Насколько мне удалось выяснить, Windows не предлагает функцию API, чтобы узнать, какое приложение зарегистрировало глобальную горячую клавишу (через RegisterHotkey). Я могу узнать, что горячая клавиша зарегистрирована, только если RegisterHotkey возвращает false, но не то, кто «владеет» горячей клавишей.

Может ли быть обходной путь в отсутствие прямого API? Windows поддерживает дескриптор, связанный с каждой зарегистрированной горячей клавишей - это немного раздражает, что не должно быть никакого способа получить эту информацию.

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

В этом нет ничего критического, но я видел частые запросы на такую ​​функциональность, и я сам был жертвой приложений, которые регистрируют горячие клавиши, даже не раскрывая их где-либо в пользовательском интерфейсе или документации.

(Работаю в Delphi и не более чем ученик WinAPI, будьте добры.)

Марек Едлински
источник

Ответы:

20

Ваш вопрос вызвал у меня интерес, поэтому я немного покопался и, хотя, к сожалению, у меня нет для вас правильного ответа, я подумал, что поделюсь тем, что у меня есть.

Я нашел этот пример создания хука клавиатуры (в Delphi) написанный в 1998 году, но его можно скомпилировать в Delphi 2007 с парой настроек.

Это DLL с вызовом SetWindowsHookEx который проходит через функцию обратного вызова, которая затем может перехватывать нажатия клавиш: в этом случае она возится с ними для удовольствия, меняет левый курсор на правый и т. Д. Затем простое приложение вызывает DLL и сообщает об этом. его результаты основаны на событии TTimer. Если вам интересно, я могу опубликовать код на основе Delphi 2007.

Он хорошо задокументирован и прокомментирован, и вы потенциально можете использовать его как основу для определения того, где происходит нажатие клавиши. Если бы вы могли получить дескриптор приложения, отправляющего нажатия клавиш, вы могли бы отследить его таким образом. С этим дескриптором вы сможете довольно легко получить необходимую информацию.

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

uses ShlObj, ComObj, ShellAPI, ActiveX, CommCtrl;

procedure GetShellLinkHotKey;
var
  LinkFile : WideString;
  SL: IShellLink;
  PF: IPersistFile;

  HotKey : Word;
  HotKeyMod: Byte;
  HotKeyText : string;
begin
  LinkFile := 'C:\Temp\Temp.lnk';

  OleCheck(CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER, IShellLink, SL));

  // The IShellLink implementer must also support the IPersistFile
  // interface. Get an interface pointer to it.
  PF := SL as IPersistFile;

  // Load file into IPersistFile object
  OleCheck(PF.Load(PWideChar(LinkFile), STGM_READ));

  // Resolve the link by calling the Resolve interface function.
  OleCheck(SL.Resolve(0, SLR_ANY_MATCH or SLR_NO_UI));

  // Get hotkey info
  OleCheck(SL.GetHotKey(HotKey));

  // Extract the HotKey and Modifier properties.
  HotKeyText := '';
  HotKeyMod := Hi(HotKey);

  if (HotKeyMod and HOTKEYF_ALT) = HOTKEYF_ALT then
    HotKeyText := 'ALT+';
  if (HotKeyMod and HOTKEYF_CONTROL) = HOTKEYF_CONTROL then
    HotKeyText := HotKeyText + 'CTRL+';
  if (HotKeyMod and HOTKEYF_SHIFT) = HOTKEYF_SHIFT then
    HotKeyText := HotKeyText + 'SHIFT+';
  if (HotKeyMod and HOTKEYF_EXT) = HOTKEYF_EXT then
    HotKeyText := HotKeyText + 'Extended+';

  HotKeyText := HotKeyText + Char(Lo(HotKey));

  if (HotKeyText = '') or (HotKeyText = #0) then
    HotKeyText := 'None';

  ShowMessage('Shortcut Key - ' + HotKeyText);
end;

Если у вас есть доступ к Safari Books Online , есть хороший раздел о работе с ярлыками / ссылками на оболочку в Руководстве разработчика Borland Delphi 6 Стива Тейшейры и Ксавьера Пачеко. Мой пример выше - это забитая версия оттуда и с этого сайта .

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

Pauk
источник
59

Один из возможных способов - использовать инструмент Visual Studio Spy ++ .

Попробуйте:

  1. Запустите инструмент (для меня он есть C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\spyxx_amd64.exe)
  2. В строке меню выберите Spy -> Log messages ... (или нажмите Ctrl+ M)
  3. Отметьте все окна в системе во фрейме дополнительных окон
  4. Перейдите на вкладку " Сообщения"
  5. Нажмите Очистить все кнопки
  6. Выберите WM_HOTKEYв списке или установите флажок Клавиатура в группах сообщений. (если вас устраивает более высокий потенциальный шум)
  7. Нажмите кнопку ОК
  8. Нажмите соответствующую горячую клавишу (например, Win+ R)
  9. Выберите WM_HOTKEYстроку в окне Сообщения (все окна) , щелкните правой кнопкой мыши и выберите Свойства ... в контекстном меню.
  10. В диалоговом окне « Свойства сообщения » щелкните ссылку « Дескриптор окна» (это будет дескриптор окна, получившего сообщение).
  11. Нажмите кнопку « Синхронизировать» в диалоговом окне «Свойства окна». Это покажет окно в дереве главного окна Spy ++.
  12. В диалоговом окне "Свойства окна" выберите вкладку " Процесс".
  13. Щелкните ссылку ID процесса . Это покажет вам процесс (В моем Win+ Rслучае: EXPLORER)
user995048
источник
6
Отличный ответ! Обратите внимание, что 64-битная версия Spy ++ улавливает только сообщения для 64-битных приложений , поэтому, если вы не видите WM_HOTKEYсообщение в журнале сообщений после нажатия горячей клавиши, вам может потребоваться запустить 32-битную версию Spy ++. .
Давид Ференци Рогожан
СПАСИБО БОЛЬШОЕ!!! Я в основном отказался от использования некоторых ярлыков, пока не нашел это.
thewindev
3
На шаге 8 горячая клавиша не запрашивается, окно сообщений остается пустым после нажатия любой горячей клавиши. Использовались 32- и 64-битные версии с сайта github.com/westoncampbell/SpyPlusPlus (в Windows 10).
CoolMind
9

После некоторых исследований выяснилось, что вам понадобится доступ к внутренней структуре, которую MS использует для хранения горячих клавиш. ReactOS имеет реализацию «чистой комнаты», которая реализует GetHotKeyвызов путем перебора внутреннего списка и извлечения горячей клавиши, которая соответствует параметрам вызова.

В зависимости от того, насколько близка реализация ReactOS к реализации MS, вы можете покопаться в памяти, чтобы найти структуру, но это выше моей головы ...

BOOL FASTCALL
GetHotKey (UINT fsModifiers,
           UINT vk,
           struct _ETHREAD **Thread,
           HWND *hWnd,
           int *id)
{
   PHOT_KEY_ITEM HotKeyItem;

   LIST_FOR_EACH(HotKeyItem, &gHotkeyList, HOT_KEY_ITEM, ListEntry)
   {
      if (HotKeyItem->fsModifiers == fsModifiers &&
            HotKeyItem->vk == vk)
      {
         if (Thread != NULL)
            *Thread = HotKeyItem->Thread;

         if (hWnd != NULL)
            *hWnd = HotKeyItem->hWnd;

         if (id != NULL)
            *id = HotKeyItem->id;

         return TRUE;
      }
   }

   return FALSE;
}

Я предполагаю, что эта ветка на sysinternals была задана кем-то, кто связан с этим вопросом, но я решил, что все равно свяжусь с ней, чтобы сохранить их вместе. Тема выглядит очень интригующе, но я подозреваю, что потребуется некоторое глубокое исследование, чтобы понять это без доступа к внутренним компонентам MS.

Джон Велдон
источник
3

Вне моей головы вы можете попробовать перечислить все окна с помощью EnumWindows, а затем в обратном вызове отправить WM_GETHOTKEY в каждое окно.

Изменить: По-видимому, я ошибался. В MSDN есть дополнительная информация:

WM_HOTKEY не имеет отношения к горячим клавишам WM_GETHOTKEY и WM_SETHOTKEY. Сообщение WM_HOTKEY отправляется для общих горячих клавиш, в то время как сообщения WM_SETHOTKEY и WM_GETHOTKEY относятся к горячим клавишам активации окна.

Примечание. Вот программа, которая, как предполагается, обладает нужными вам функциями. Вы можете попробовать его декомпилировать.

Дэвид
источник
3
Ссылка на программу теперь полностью не работает. Что это была за программа? Я так много раз хотел выяснить, какая программа зарегистрировала мои горячие клавиши, потому что внезапно они перестали работать или делать что-то новое.
Джеймс Олтманс,
(Часто Wayback Machine может помочь в случае исчезновения веб-страницы, но здесь он также отражал только 404 Not Found: http://web.archive.org/web/*/tds.diamondcs.com.au/dse/ обнаружение / hotkeys.php )
Аарон Тома
2

Кажется, это о многом вам говорит: http://hkcmdr.anymania.com/help.html

Кавеатроб
источник
1
Является ли? DLL, замаскированная под драйвер? Функция DLL, экспортированная как hooиспользование SetWindowHookExдля установки двух хуков, одного WH_KEYBOARD_LLи одного WH_GETMESSAGE... все остальное должно быть в значительной степени задокументировано в MSDN.
0xC0000022L 08
Не используйте это приложение в Windows 8 или 10.
Джефф Ланге,
1

В другом потоке упоминается глобальный перехват клавиатуры уровня NT:

Переназначить / переопределить горячую клавишу (Win + L) для блокировки окон

возможно, вы сможете получить дескриптор процесса, который вызывал перехватчик таким образом, который затем можно преобразовать в имя процесса

(отказ от ответственности: он был у меня в закладках, на самом деле не пробовал / не тестировал)

Марко ван де Вурт
источник
0

Я знаю, что вы можете перехватить поток сообщений в любом окне в вашем собственном процессе - то, что мы привыкли называть подклассом в VB6. (Хотя я не помню эту функцию, возможно, SetWindowLong?) Я не уверен, можно ли это сделать для окон вне вашего собственного процесса. Но ради этого поста предположим, что вы найдете способ сделать это. Затем вы можете просто перехватить сообщения для всех окон верхнего уровня, отслеживать сообщение WM_HOTKEY. Вы не сможете узнать все клавиши сразу, но по мере их нажатия можно легко определить, какое приложение их использует. Если вы сохраняете свои результаты на диске и перезагружаете их каждый раз при запуске приложения монитора, вы можете со временем повысить производительность своего приложения.

Сэм Топор
источник
-1

Это не совсем ответ на часть вопроса, касающуюся Windows API, но он отвечает на часть вопроса, касающуюся списка глобальных горячих клавиш и приложений, которые «владеют» ими.

В бесплатном обозревателе горячих клавиш на http://hkcmdr.anymania.com/ отображается список всех глобальных горячих клавиш и приложений, которым они принадлежат. Это просто помогло мне понять, почему перестала работать горячая клавиша для конкретного приложения и как это исправить (перенастроив зарегистрированную глобальную горячую клавишу в приложении, в котором она была зарегистрирована), в течение нескольких секунд.

Gerhard
источник
4
установил его, и он действовал как вирус, по крайней мере, программа с намерением торгового центра, как плохая шутка. Открытие всех видов окон, активация голосового наратора и т. Д.
Flion
3
@Flion: см. Обсуждение на superuser.com/a/191090/3617 . По сути, некоторые утилиты горячих клавиш работают, исследуя все возможные комбинации клавиш. В Windows 8+ (и в меньшей степени в Windows 7) этот метод проверки запускает каждую комбинацию клавиш, а не просто запрашивает ее.
Брайан
@Flion FWIW, успешно использовал на Win7. Но если я правильно понимаю Брайана, это может не работать и с более поздними версиями, и насколько хорошо это работает, также может зависеть от определенных пользовательских горячих клавиш.
Gerhard