web-dev-qa-db-fra.com

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

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

En l'absence d'une API directe, pourrait-il y avoir un détour? Windows conserve le handle associé à chaque raccourci clavier enregistré - c'est un peu exaspérant qu'il ne devrait y avoir aucun moyen d'accéder à ces informations.

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é. Tout d'abord, je ne pense pas que l'interception du message révélerait le descripteur 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 à partir de divers programmes.

Ce n'est rien de critique, mais j'ai vu de nombreuses demandes pour une telle fonctionnalité, et j'ai moi-même été victime d'applications qui enregistrent des raccourcis clavier sans même les divulguer n'importe où dans l'interface utilisateur ou dans les documents.

(Travailler à Delphi, et pas plus qu'un apprenti chez WinAPI, veuillez être gentil.)

101

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

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

C'est un DLL avec un appel à SetWindowsHookEx qui passe par une fonction de rappel, qui peut ensuite intercepter les frappes de touches: dans ce cas, c'est bricoler avec pour le plaisir, changer le curseur gauche à droite, etc. Une simple application appelle ensuite le DLL et rapporte ses résultats en fonction d'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 pouvez 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 pouvez le suivre de cette façon. Avec cette poignée, vous seriez en mesure d'obtenir facilement les informations dont vous avez besoin.

D'autres applications ont essayé de déterminer les raccourcis clavier en parcourant leurs raccourcis car ils peuvent contenir une touche de raccourci, qui est juste un autre terme pour un raccourci clavier. Cependant, la plupart des applications n'ont pas tendance à définir cette propriété, il est donc possible qu'elle ne renvoie pas grand-chose. Si vous êtes intéressé par cette route, Delphi a accès à IShellLink interface 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 y a 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 bouchée de là et ce site .

J'espère que cela pourra aider!

19
Pauk

Une façon 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 Spy -> Messages de journal ... (ou appuyez sur Ctrl + M)
  3. Cochez Toutes les fenêtres du système dans le cadre Fenêtres supplémentaires
  4. Basculez vers l'onglet Messages
  5. Cliquez sur le bouton Tout effacer
  6. Sélectionnez WM_HOTKEY dans la zone de liste, ou cochez Clavier dans Groupes de messages (si vous êtes d'accord avec plus de bruit potentiel)
  7. Cliquez sur le bouton [~ # ~] ok [~ # ~]
  8. Appuyez sur le raccourci clavier en question (Win + R, par exemple)
  9. Sélectionnez le WM_HOTKEY ligne dans la fenêtre Messages (toutes les fenêtres), faites un clic droit 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 Window Handle (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 Process ID. Cela vous montrera le processus (dans mon Win + R cas: Explorer)
33
user995048

Après quelques recherches, il semble que vous auriez besoin d'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'appel GetHotKey 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 avec 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 j'y lierais quand même pour garder les deux ensemble. Le fil a l'air très intrigant, mais je soupçonne que certains spéléos en plongée profonde devraient se produire pour comprendre cela sans accès aux internes MS.

9
John Weldon

Du haut de ma tête, vous pouvez essayer d'énumérer toutes les fenêtres avec EnumWindows, puis dans le rappel, envoyer 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 de raccourci WM_GETHOTKEY et WM_SETHOTKEY. Le message WM_HOTKEY est envoyé pour les touches de raccourci génériques tandis que les messages WM_SETHOTKEY et WM_GETHOTKEY se rapportent aux touches de raccourci d'activation de fenêtre.

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

3
David

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

2
Caveatrob

Un autre thread mentionne un crochet de clavier de niveau NT global:

Réattribuer/remplacer la touche de raccourci (Win + L) pour verrouiller les fenêtres

vous pouvez peut-être obtenir le descripteur du processus qui a appelé le crochet de cette façon, que vous pouvez ensuite résoudre en nom de processus

(Avertissement: je l'avais dans mes favoris, je n'ai pas vraiment essayé/testé)

1

Je sais que vous pouvez intercepter le flux de messages dans n'importe quelle fenêtre de votre propre processus - ce que nous appelions 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 comme elles étaient pressées, vous pouviez facilement déterminer quelle application les utilisait. Si vous persistez vos résultats sur le disque et que vous les rechargez à chaque exécution de votre application de surveillance, vous pouvez augmenter les performances de votre application au fil du temps.

0
Sam Axe