Descubra qual processo registrou uma tecla de atalho global? (API do Windows)

114

Pelo que pude descobrir, o Windows não oferece uma função API para informar qual aplicativo registrou uma tecla de atalho global (via RegisterHotkey). Só posso descobrir que uma tecla de atalho é registrada se RegisterHotkey retornar falso, mas não quem "possui" a tecla de atalho.

Na ausência de uma API direta, poderia haver uma forma indireta? O Windows mantém o identificador associado a cada tecla de atalho registrada - é um pouco enlouquecedor que não haja nenhuma maneira de obter essas informações.

Exemplo de algo que provavelmente não funcionaria: enviar (simular) uma tecla de atalho registrada e, em seguida, interceptar a mensagem de tecla de atalho que o Windows enviará ao processo que a registrou. Primeiro, não acho que interceptar a mensagem revelaria o identificador da janela de destino. Em segundo lugar, mesmo se fosse possível, seria uma coisa ruim a se fazer, já que enviar teclas de atalho desencadearia todos os tipos de atividades potencialmente indesejadas de vários programas.

Não é nada crítico, mas tenho visto solicitações frequentes para tal funcionalidade e eu mesmo fui vítima de aplicativos que registram teclas de atalho sem nem mesmo divulgá-lo em qualquer lugar na interface do usuário ou documentos.

(Trabalhar no Delphi, e não mais do que um aprendiz no WinAPI, por favor, seja gentil.)

Marek Jedliński
fonte

Respostas:

20

Sua pergunta despertou meu interesse, então fiz algumas pesquisas e, embora, infelizmente, não tenha uma resposta adequada para você, pensei em compartilhar o que tenho.

Encontrei este exemplo de criação de gancho de teclado (em Delphi) escrito em 1998, mas é compilável em Delphi 2007 com alguns ajustes.

É uma DLL com uma chamada para SetWindowsHookExque passa por uma função de retorno de chamada, que pode então interceptar os pressionamentos de tecla: Neste caso, está mexendo nelas para se divertir, mudando o cursor da esquerda para a direita etc. Um aplicativo simples chama a DLL e informa de volta seus resultados com base em um evento TTimer. Se você estiver interessado, posso postar o código baseado em Delphi 2007.

Está bem documentado e comentado e você pode usá-lo como base para descobrir para onde um pressionamento de tecla está indo. Se você pudesse obter o identificador do aplicativo que enviou os pressionamentos de tecla, poderia rastreá-lo dessa forma. Com esse identificador, você poderá obter as informações de que precisa com bastante facilidade.

Outros aplicativos tentaram determinar as teclas de atalho percorrendo seus atalhos, pois podem conter uma tecla de atalho, que é apenas outro termo para tecla de atalho. No entanto, a maioria dos aplicativos não tende a definir essa propriedade, portanto, ela pode não retornar muito. Se você estiver interessado nessa rota, a Delphi tem acesso aIShellLink interface COM que você pode usar para carregar um atalho e obter sua tecla de atalho:

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;

Se você tiver acesso ao Safari Books Online , há uma boa seção sobre como trabalhar com atalhos / links de shell no Borland Delphi 6 Developer's Guide de Steve Teixeira e Xavier Pacheco. Meu exemplo acima é uma versão massacrada de lá e deste site .

Espero que ajude!

Pauk
fonte
59

Uma maneira possível é usar a ferramenta Visual Studio Spy ++ .

Experimente:

  1. Execute a ferramenta (para mim, está em C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\spyxx_amd64.exe )
  2. Na barra de menu, selecione Espião -> Registrar mensagens ... (ou pressione Ctrl+M )
  3. Verifique todas as janelas do sistema nas janelas adicionais quadro do
  4. Mudar para as mensagens guia
  5. Clique em Limpar tudo botão
  6. Selecione WM_HOTKEYna caixa de listagem ou marque Teclado em grupos de mensagens (se estiver OK com mais ruído potencial)
  7. Clique no botão OK
  8. Pressione a tecla de atalho em questão ( Win+ R, por exemplo)
  9. Selecione a WM_HOTKEYlinha na janela Mensagens (todas as janelas) , clique com o botão direito e selecione Propriedades ... no menu de contexto
  10. Na caixa de diálogo Propriedades da mensagem , clique no link Indicador da janela (este será o identificador da janela que recebeu a mensagem)
  11. Clique no botão Sincronizar nas Propriedades da janela caixa de diálogo janela. Isso mostrará a janela na visualização em árvore da janela principal do Spy ++.
  12. Na caixa de diálogo Propriedades da janela, selecione o processo guia
  13. Clique no link ID do processo . Isso mostrará o processo (no meu Win+ Rcaso EXPLORER:)
usuário995048
fonte
6
Ótima resposta! Apenas uma observação que a versão de 64 bits do Spy ++ captura apenas mensagens para aplicativos de 64 bits , portanto, se você não vir a WM_HOTKEYmensagem no registro de mensagens após pressionar a tecla de atalho, pode ser necessário executar a versão de 32 bits do Spy ++ .
David Ferenczy Rogožan
MUITO OBRIGADO!!! Eu basicamente desisti de usar alguns atalhos até que encontrei isso.
thewindev
3
No passo 8, ele não pede uma tecla de atalho, a janela de mensagens continua vazia após pressionar qualquer tecla de atalho. Usei versões de 32 e 64 bits de github.com/westoncampbell/SpyPlusPlus (no Windows 10).
CoolMind
9

Depois de alguma pesquisa, parece que você precisa obter acesso à estrutura interna que o MS usa para armazenar as teclas de atalho. O ReactOS tem uma implementação de sala limpa que implementa a GetHotKeychamada iterando uma lista interna e extraindo a tecla de atalho que corresponde aos parâmetros da chamada.

Dependendo da proximidade da implementação do ReactOS com a implementação do MS, você pode vasculhar a memória para encontrar a estrutura, mas isso está além da minha cabeça ...

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;
}

Presumo que este tópico sobre sysinternals foi perguntado por alguém relacionado a esta questão, mas pensei em criar um link para ele de qualquer maneira para manter os dois juntos. O tópico parece muito intrigante, mas eu suspeito que algum espelhamento profundo precisaria acontecer para descobrir isso sem acesso aos internos do MS.

John Weldon
fonte
3

No topo da minha cabeça, você pode tentar enumerar todas as janelas com EnumWindows e, no retorno de chamada, enviar WM_GETHOTKEY para cada janela.

Edit: Aparentemente, eu estava errado sobre isso. MSDN tem mais informações:

WM_HOTKEY não está relacionado às teclas de atalho WM_GETHOTKEY e WM_SETHOTKEY. A mensagem WM_HOTKEY é enviada para teclas de atalho genéricas enquanto as mensagens WM_SETHOTKEY e WM_GETHOTKEY estão relacionadas às teclas de atalho de ativação da janela.

Nota: Aqui é um programa que supostamente tem a funcionalidade que você está procurando. Você pode tentar descompilá-lo.

David
fonte
3
O link para o programa agora está completamente quebrado. Que programa foi esse? Muitas vezes eu gostaria de descobrir qual programa registrou minhas teclas de atalho porque de repente elas não funcionam mais ou fazem coisas novas irritantes.
James Oltmans
(Freqüentemente, a Wayback Machine pode ajudar no caso de desaparecimento da página da web, mas aqui também espelhou um erro 404 não encontrado: http://web.archive.org/web/*/tds.diamondcs.com.au/dse/ detecção / hotkeys.php )
Aaron Thoma
2

Isso parece dizer muito a você: http://hkcmdr.anymania.com/help.html

Caveatrob
fonte
1
É mesmo? Uma DLL disfarçada de driver? A função DLL exportada como hoousa SetWindowHookExpara definir dois ganchos, um WH_KEYBOARD_LLe um WH_GETMESSAGE... o resto deve ser praticamente documentado no MSDN.
0xC0000022L
Não use este aplicativo no Windows 8 ou 10.
Jeff Lange
1

Outro tópico menciona um gancho de teclado de nível NT global:

Reatribuir / substituir tecla de atalho (Win + L) para bloquear janelas

talvez você possa obter o controle do processo que chamou o gancho dessa maneira, que pode então resolver para o nome do processo

(isenção de responsabilidade: eu tinha em meus favoritos, realmente não tentei / testei)

Marco van de Voort
fonte
0

Eu sei que você pode interceptar o fluxo de mensagens em qualquer janela de seu próprio processo - o que costumávamos chamar de subclasse no VB6. (Embora eu não me lembre da função, talvez SetWindowLong?) Não tenho certeza se você pode fazer isso para janelas fora do seu próprio processo. Mas, por causa desta postagem, vamos supor que você encontre uma maneira de fazer isso. Em seguida, você pode simplesmente interceptar as mensagens para todas as janelas de nível superior e monitorar a mensagem WM_HOTKEY. Você não seria capaz de saber todas as teclas logo de cara, mas conforme elas eram pressionadas, você poderia facilmente descobrir qual aplicativo as estava usando. Se você persistir seus resultados no disco e recarregar cada vez que o aplicativo do monitor for executado, poderá aumentar o desempenho do aplicativo ao longo do tempo.

Sam Axe
fonte
-1

Isso não responde exatamente à parte da pergunta sobre a API do Windows, mas responde à parte da pergunta sobre uma lista de teclas de atalho globais e os aplicativos que as "possuem".

O Hotkey Explorer gratuito em http://hkcmdr.anymania.com/ mostra uma lista de todas as teclas de atalho globais e os aplicativos que as possuem. Isso apenas me ajudou a descobrir por que uma tecla de atalho específica do aplicativo parou de funcionar e como corrigi-lo (reconfigurando a tecla de atalho global registrada no aplicativo que a tinha registrado), em alguns segundos.

Gerhard
fonte
4
instalei-o e ele agiu como um vírus, pelo menos um programa com intenção de mall, como uma piada de mau gosto. Abrindo todos os tipos de janelas, ativando narator de voz, etc.
Flion
3
@Flion: Veja a discussão em superuser.com/a/191090/3617 . Basicamente, alguns utilitários de teclas de atalho funcionam investigando todas as combinações de teclas possíveis. No Windows 8+ (e em menor medida no Windows 7), essa técnica de sondagem aciona todas as combinações de teclas, em vez de apenas consultá-las.
Brian
@Flion FWIW, usei no Win7 com sucesso. Mas, se entendi Brian corretamente, isso pode não funcionar tão bem com versões posteriores, e o quão bem funciona também pode depender de teclas de atalho personalizadas que são definidas.
Gerhard