Découvrez quel processus a enregistré un raccourci clavier global? (API Windows)

114

Pour autant que j'ai pu le savoir, Windows n'offre pas de fonction API pour dire quelle application a enregistré un raccourci clavier global (via RegisterHotkey). Je ne peux découvrir qu'un raccourci clavier est enregistré si RegisterHotkey renvoie false, mais pas à qui "possède" le raccourci clavier.

En l'absence d'API directe, pourrait-il y avoir un moyen détourné? Windows maintient le handle associé à chaque touche de raccourci enregistrée - c'est un peu exaspérant qu'il ne devrait y avoir aucun moyen d'obtenir cette information.

Exemple de quelque chose qui ne fonctionnerait probablement pas: envoyer (simuler) un raccourci clavier enregistré, puis intercepter le message de raccourci clavier que Windows enverra au processus qui l'a enregistré. Premièrement, je ne pense pas qu'intercepter le message révélerait le handle de la fenêtre de destination. Deuxièmement, même si c'était possible, ce serait une mauvaise chose à faire, car l'envoi de raccourcis clavier déclencherait toutes sortes d'activités potentiellement indésirables de la part de divers programmes.

Ce n'est rien de critique, mais j'ai vu de fréquentes demandes pour de telles fonctionnalités et j'ai moi-même été victime d'applications qui enregistrent des raccourcis clavier sans même les divulguer nulle part dans l'interface utilisateur ou dans la documentation.

(Travailler chez Delphi, et pas plus qu'un apprenti chez WinAPI, soyez gentil.)

Marek Jedliński
la source

Réponses:

20

Votre question a piqué mon intérêt, j'ai donc creusé un peu et bien que, malheureusement, je n'ai pas de réponse appropriée pour vous, j'ai pensé partager ce que j'avais.

J'ai trouvé cet exemple de création de hook clavier (en Delphi) écrit en 1998, mais il est compilable dans Delphi 2007 avec quelques ajustements.

C'est une DLL avec un appel SetWindowsHookExqui passe par une fonction de rappel, qui peut ensuite intercepter les touches: dans ce cas, elle les bricoler pour le plaisir, changer le curseur gauche vers la droite, etc. Une application simple appelle alors la DLL et fait un rapport. ses résultats basés sur un événement TTimer. Si vous êtes intéressé, je peux publier le code basé sur Delphi 2007.

Il est bien documenté et commenté et vous pourriez potentiellement l'utiliser comme base pour déterminer où va une pression sur une touche. Si vous pouviez obtenir le descripteur de l'application qui a envoyé les touches, vous pourriez le suivre de cette façon. Avec cette poignée, vous pourrez obtenir assez facilement les informations dont vous avez besoin.

D'autres applications ont essayé de déterminer les raccourcis clavier en passant par leurs raccourcis car ils peuvent contenir une touche de raccourci, qui n'est qu'un autre terme pour raccourci clavier. Cependant, la plupart des applications n'ont pas tendance à définir cette propriété, elle peut donc ne pas renvoyer beaucoup. Si vous êtes intéressé par cette route, Delphi a accès à l' IShellLinkinterface COM que vous pouvez utiliser pour charger un raccourci et obtenir son raccourci clavier:

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;

Si vous avez accès à Safari Books Online , il existe une bonne section sur l'utilisation des raccourcis / liens shell dans le Guide du développeur Borland Delphi 6 par Steve Teixeira et Xavier Pacheco. Mon exemple ci-dessus est une version boucherie à partir de là et de ce site .

J'espère que cela pourra aider!

Pauk
la source
59

Un moyen possible consiste à utiliser l' outil Visual Studio Spy ++ .

Essayez ceci:

  1. Exécutez l'outil (pour moi, c'est à C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\spyxx_amd64.exe)
  2. Dans la barre de menu, sélectionnez Espion -> Messages du journal ... (ou appuyez sur Ctrl+ M)
  3. Vérifier toutes les fenêtres du système dans le cadre Windows supplémentaire
  4. Passer à l' onglet Messages
  5. Cliquez sur le bouton Effacer tout
  6. Sélectionnez WM_HOTKEYdans la zone de liste ou cochez Clavier dans les groupes de messages (si vous êtes d'accord avec plus de bruit potentiel)
  7. Cliquez sur le bouton OK
  8. Appuyez sur la touche de raccourci en question ( Win+ R, par exemple)
  9. Sélectionnez la WM_HOTKEYligne dans la fenêtre Messages (Toutes les fenêtres) , cliquez avec le bouton droit de la souris et sélectionnez Propriétés ... dans le menu contextuel
  10. Dans la boîte de dialogue Propriétés du message , cliquez sur le lien Handle de fenêtre (ce sera le handle de la fenêtre qui a reçu le message)
  11. Cliquez sur le bouton Synchroniser dans la boîte de dialogue Propriétés de la fenêtre . Cela affichera la fenêtre dans l'arborescence principale de la fenêtre Spy ++.
  12. Dans la boîte de dialogue Propriétés de la fenêtre , sélectionnez l' onglet Processus
  13. Cliquez sur le lien ID de processus . Cela vous montrera le processus (Dans mon Win+ Rcas: EXPLORER)
user995048
la source
6
Très bonne réponse! Notez simplement que la version 64 bits de Spy ++ ne capture que les messages pour les applications 64 bits , donc si vous ne voyez pas le WM_HOTKEYmessage dans le journal des messages après avoir appuyé sur le raccourci clavier, vous devrez peut-être exécuter la version 32 bits de Spy ++ .
David Ferenczy Rogožan
MERCI BEAUCOUP!!! J'ai essentiellement renoncé à utiliser certains raccourcis jusqu'à ce que je trouve cela.
thewindev
3
À l'étape 8, il ne demande pas de raccourci clavier, la fenêtre des messages reste vide après avoir appuyé sur une touche de raccourci. Utilisé les versions 32 et 64 bits de github.com/westoncampbell/SpyPlusPlus (sous Windows 10).
CoolMind
9

Après quelques recherches, il semble que vous deviez accéder à la structure interne que MS utilise pour stocker les raccourcis clavier. ReactOS a une implémentation de salle blanche qui implémente l' GetHotKeyappel en itérant une liste interne et en extrayant le raccourci clavier qui correspond aux paramètres de l'appel.

Selon la proximité de l'implémentation de ReactOS par rapport à l'implémentation MS, vous pourrez peut-être fouiller dans la mémoire pour trouver la structure, mais c'est au-dessus de ma tête ...

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

Je suppose que ce fil sur sysinternals a été demandé par quelqu'un lié à cette question, mais j'ai pensé que je ferais un lien vers lui de toute façon pour garder les deux ensemble. Le fil semble très intrigant, mais je soupçonne qu'une spéléologie en profondeur devrait se produire pour comprendre cela sans accès aux internes MS.

John Weldon
la source
3

Du haut de ma tête, vous pouvez essayer d'énumérer toutes les fenêtres avec EnumWindows, puis dans le rappel, envoyez WM_GETHOTKEY à chaque fenêtre.

Edit: Apparemment, j'avais tort à ce sujet. MSDN a plus d'informations:

WM_HOTKEY n'est pas lié aux touches d'accès rapide WM_GETHOTKEY et WM_SETHOTKEY. Le message WM_HOTKEY est envoyé pour les raccourcis clavier génériques tandis que les messages WM_SETHOTKEY et WM_GETHOTKEY concernent les raccourcis clavier d'activation de fenêtre.

Remarque: Voici un programme censé avoir la fonctionnalité que vous recherchez. Vous pouvez essayer de le décompiler.

David
la source
3
Le lien vers le programme est maintenant complètement rompu. De quel programme s'agissait-il? Il y a tellement de fois que j'aimerais savoir quel programme a enregistré mes raccourcis clavier parce que soudainement ils ne fonctionnent plus ou font de nouvelles choses ennuyeuses.
James Oltmans
(Souvent, la Wayback Machine peut aider en cas de disparition de la page Web, mais ici, elle n'a également reflété qu'un 404 Not Found: http://web.archive.org/web/*/tds.diamondcs.com.au/dse/ detection / hotkeys.php )
Aaron Thoma
2

Cela semble vous en dire long: http://hkcmdr.anymania.com/help.html

Caveatrob
la source
1
Le fait-il? Une DLL déguisée en pilote? La fonction DLL exportée comme hooutilisations SetWindowHookExpour définir deux crochets, l' un WH_KEYBOARD_LLet l' autre WH_GETMESSAGE... le reste devrait être à peu près documenté sur MSDN.
0xC0000022L
N'utilisez pas cette application sur Windows 8 ou 10.
Jeff Lange
0

Je sais que vous pouvez intercepter le flux de messages dans n'importe quelle fenêtre de votre propre processus - ce que nous appelions autrefois le sous-classement dans VB6. (Bien que je ne me souvienne pas de la fonction, peut-être SetWindowLong?) Je ne sais pas si vous pouvez le faire pour des fenêtres en dehors de votre propre processus. Mais pour le bien de cet article, supposons que vous trouviez un moyen de le faire. Ensuite, vous pouvez simplement intercepter les messages pour toutes les fenêtres de niveau supérieur, surveiller le message WM_HOTKEY. Vous ne pourriez pas connaître toutes les touches dès le départ, mais en les pressant, vous pourriez facilement déterminer quelle application les utilisait. Si vous conserviez vos résultats sur le disque et les rechargiez chaque fois que votre application de surveillance était exécutée, vous pourriez augmenter les performances de votre application au fil du temps.

Sam Axe
la source
-1

Cela ne répond pas exactement à la partie de la question qui concerne l'API Windows, mais cela répond à la partie de la question qui concerne une liste de raccourcis clavier globaux et les applications qui les "possèdent".

L'explorateur de raccourcis clavier gratuit à l' adresse http://hkcmdr.anymania.com/ affiche une liste de tous les raccourcis clavier globaux et des applications qui les possèdent. Cela m'a simplement aidé à comprendre pourquoi une touche de raccourci spécifique à une application a cessé de fonctionner et comment la réparer (en reconfigurant la touche de raccourci globale enregistrée dans l'application qui l'avait enregistrée), en quelques secondes.

Gerhard
la source
4
installé et il a agi comme un virus au moins un programme avec l'intention de centre commercial, comme une mauvaise blague. Ouverture de toutes sortes de fenêtres, activation du narateur vocal, etc.
Flion
3
@Flion: Voir la discussion sur superuser.com/a/191090/3617 . Fondamentalement, certains utilitaires de touches de raccourci fonctionnent en sondant toutes les combinaisons de touches possibles. Sur Windows 8+ (et dans une moindre mesure sur Windows 7), cette technique de sondage déclenche chaque combinaison de touches, plutôt que de simplement l'interroger.
Brian
@Flion FWIW, je l'ai utilisé sur Win7 avec succès. Mais si je comprends bien Brian, cela peut ne pas fonctionner aussi bien avec les versions ultérieures, et son fonctionnement peut également dépendre des raccourcis clavier personnalisés qui sont définis.
Gerhard