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

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

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

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

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

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

Ответ 1

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

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

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

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

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

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 от Стив Тейшейра и Ксавье Пачеко. Мой пример выше - это версия, отвязанная оттуда, и этот сайт.

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

Ответ 2

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

Попробуйте:

  • Запустите инструмент (для меня он в C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\spyxx_amd64.exe)
  • В строке меню выберите Spy → Log messages... (или нажмите Ctrl + M)
  • Проверить все Windows в системе в дополнительном фрейме Windows
  • Перейдите на вкладку "Сообщения"
  • Нажмите кнопку "Очистить все"
  • Выберите WM_HOTKEY в списке или выберите "Клавиатура" в группах сообщений (если у вас все в порядке с большим потенциалом)
  • Нажмите кнопку OK
  • Нажмите горячую клавишу (Win + R, например)
  • Выберите строку WM_HOTKEY в окне "Сообщения" ( "Все Windows" ), щелкните правой кнопкой мыши и выберите "Свойства..." в контекстном меню
  • В диалоговом окне "Свойства сообщения" щелкните ссылку "Окно Handle" (это будет дескриптор окна, получившего сообщение).
  • Нажмите кнопку "Синхронизировать" в диалоговом окне "Свойства окна". Это покажет окно в главном окне дерева Spy ++.
  • В диалоговом окне "Свойства окна" выберите вкладку "Процесс"
  • Нажмите ссылку "Идентификатор процесса". Это покажет вам процесс (в моем случае Win + R: EXPLORER)

Ответ 3

После некоторых исследований выяснится, что вам нужно получить доступ к внутренней структуре, используемой 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.

Ответ 4

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

Изменить: Очевидно, я был неправ. MSDN содержит дополнительную информацию:

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

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

Ответ 6

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

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

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

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

Ответ 7

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

Ответ 8

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

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

Ответ 9

Я не был пользователем жесткого ядра в течение нескольких лет (я перешел на Mac). Но я клялся Process Explorer, чтобы узнать, какой процесс использует конкретный файл, который я пытался удалить. Может быть, это поможет выяснить, какой процесс использует горячую клавишу?