Dowiedz się, w jakim procesie zarejestrowano globalny skrót klawiszowy? (Windows API)

114

O ile mogłem się dowiedzieć, Windows nie oferuje funkcji API, aby powiedzieć, która aplikacja zarejestrowała globalny skrót klawiszowy (przez RegisterHotkey). Mogę tylko dowiedzieć się, że skrót klawiszowy jest zarejestrowany, jeśli RegisterHotkey zwróci false, ale nie mogę dowiedzieć się, kto jest „właścicielem” skrótu.

W przypadku braku bezpośredniego API, czy może istnieć okrężna droga? Windows utrzymuje uchwyt skojarzony z każdym zarejestrowanym klawiszem skrótu - to trochę denerwujące, że nie powinno być sposobu na uzyskanie tych informacji.

Przykład czegoś, co prawdopodobnie nie zadziała: wyślij (zasymuluj) zarejestrowany skrót, a następnie przechwyć wiadomość skrótu, którą system Windows wyśle ​​do procesu, który go zarejestrował. Po pierwsze, nie sądzę, aby przechwycenie wiadomości ujawniło docelową klamkę okna. Po drugie, nawet gdyby było to możliwe, byłoby to złe, ponieważ wysyłanie skrótów klawiszowych wywołałoby wszelkiego rodzaju potencjalnie niechciane działania różnych programów.

To nic krytycznego, ale widziałem częste prośby o taką funkcjonalność i sam padłem ofiarą aplikacji, które rejestrują skróty klawiszowe, nawet nie ujawniając ich w dowolnym miejscu interfejsu użytkownika lub dokumentów.

(Pracując w Delphi i nie więcej niż praktykant w WinAPI, prosimy o uprzejmość).

Marek Jedliński
źródło

Odpowiedzi:

20

Twoje pytanie wzbudziło moje zainteresowanie, więc trochę poszperałem i chociaż, niestety, nie mam dla ciebie właściwej odpowiedzi, pomyślałem, że podzielę się tym, co mam.

Znalazłem ten przykład tworzenia haka klawiatury (w Delphi) napisany w 1998 roku, ale można go kompilować w Delphi 2007 z kilkoma poprawkami.

To biblioteka DLL z wywołaniem SetWindowsHookEx które przechodzi przez funkcję zwrotną, która może następnie przechwytywać naciśnięcia klawiszy: w tym przypadku majstruje przy nich dla zabawy, zmieniając lewy kursor w prawo itd. Następnie prosta aplikacja wywołuje bibliotekę DLL i raportuje z powrotem jego wyniki oparte na zdarzeniu TTimer. Jeśli jesteś zainteresowany, mogę wysłać kod oparty na Delphi 2007.

Jest dobrze udokumentowany i skomentowany i potencjalnie możesz go użyć jako podstawy do ustalenia, dokąd zmierza naciśnięcie klawisza. Gdybyś mógł uzyskać uchwyt aplikacji, która wysłała naciśnięcia klawiszy, możesz to prześledzić w ten sposób. Dzięki temu uchwytowi będziesz w stanie łatwo uzyskać potrzebne informacje.

Inne aplikacje próbowały określić skróty klawiszowe, przechodząc przez swoje skróty, ponieważ mogą zawierać klawisz skrótu, który jest po prostu kolejnym określeniem skrótu klawiszowego. Jednak większość aplikacji nie ustawia tej właściwości, więc może nie zwracać dużo. Jeśli jesteś zainteresowany tą trasą, Delphi ma dostęp do IShellLinkinterfejsu COM, z którego możesz wczytać skrót i uzyskać jego skrót:

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;

Jeśli masz dostęp do Safari Books Online , w przewodniku Borland Delphi 6 Developer's Guide Steve Teixeira i Xavier Pacheco znajduje się dobra sekcja dotycząca pracy ze skrótami / linkami powłoki . Mój przykład powyżej jest zmasakrowaną wersją stamtąd i tej strony .

Mam nadzieję, że to pomoże!

Pauk
źródło
59

Jednym z możliwych sposobów jest użycie narzędzia Visual Studio Spy ++ .

Spróbuj:

  1. Uruchom narzędzie (dla mnie jest na C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\spyxx_amd64.exe)
  2. Na pasku menu wybierz Szpieg -> Dziennik wiadomości ... (lub naciśnij Ctrl+ M)
  3. Zaznacz opcję Wszystkie okna w systemie w ramce Dodatkowe okna
  4. Przejdź do zakładki Wiadomości
  5. Kliknij Wyczyść wszystko przycisk
  6. Wybierz WM_HOTKEYw polu listy lub zaznacz klawiaturę w grupach wiadomości (jeśli nie przeszkadza Ci więcej potencjalnego hałasu)
  7. Kliknij przycisk OK
  8. Naciśnij odpowiedni klawisz skrótu ( na przykład Win+ R)
  9. Wybierz WM_HOTKEYlinię w oknie Wiadomości (wszystkie okna) , kliknij prawym przyciskiem myszy i wybierz Właściwości ... z menu kontekstowego
  10. W oknie dialogowym Właściwości wiadomości kliknij łącze Uchwyt okna (będzie to uchwyt okna, które otrzymało wiadomość)
  11. Kliknij przycisk Synchronizuj w oknie dialogowym Właściwości okna. Spowoduje to wyświetlenie okna w głównym widoku drzewa okna Spy ++.
  12. We właściwościach okna dialogowym okna wybierz opcję Proces kartę
  13. Kliknij łącze Identyfikator procesu . To pokaże ci proces (w moim Win+ Rprzypadku EXPLORER:)
user995048
źródło
6
Świetna odpowiedź! Tylko uwaga, że 64-bitowa wersja Spy ++ przechwytuje tylko wiadomości dla aplikacji 64-bitowych , więc jeśli nie widzisz WM_HOTKEYwiadomości w dzienniku wiadomości po naciśnięciu klawisza skrótu, może być konieczne uruchomienie 32-bitowej wersji Spy ++ .
David Ferenczy Rogožan
DZIĘKUJĘ BARDZO!!! W zasadzie zrezygnowałem z używania niektórych skrótów, dopóki tego nie znalazłem.
thewindev
3
W kroku 8 nie pyta o skrót, okno wiadomości pozostaje puste po naciśnięciu dowolnego klawisza skrótu. Używane wersje 32- i 64-bitowe ze strony github.com/westoncampbell/SpyPlusPlus (w systemie Windows 10).
CoolMind
9

Po pewnych badaniach wydaje się, że musisz uzyskać dostęp do wewnętrznej struktury używanej przez MS do przechowywania skrótów klawiszowych. ReactOS ma implementację czystego pomieszczenia, która implementuje GetHotKeywywołanie poprzez iterację wewnętrznej listy i wyodrębnienie skrótu klawiszowego, który dopasowuje parametry do wywołania.

W zależności od tego, jak blisko implementacji ReactOS jest implementacja MS, możesz być w stanie przeszukać pamięć, aby znaleźć strukturę, ale to nad moją głową ...

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

Przypuszczam, że ten wątek na temat sysinternals został zadany przez kogoś związanego z tym pytaniem, ale pomyślałem, że i tak połączę się z nim, aby utrzymać oba razem. Wątek wygląda bardzo intrygująco, ale podejrzewam, że musiało się zdarzyć jakieś głębokie nurkowanie, aby to rozgryźć bez dostępu do wewnętrznych elementów stwardnienia rozsianego.

John Weldon
źródło
3

Na wszelki wypadek możesz spróbować wyliczyć wszystkie okna za pomocą EnumWindows, a następnie w wywołaniu zwrotnym wysłać WM_GETHOTKEY do każdego okna.

Edycja: W tej chwili się myliłem. MSDN zawiera więcej informacji:

WM_HOTKEY nie jest powiązany z klawiszami skrótu WM_GETHOTKEY i WM_SETHOTKEY. Komunikat WM_HOTKEY jest wysyłany dla ogólnych skrótów klawiszowych, podczas gdy komunikaty WM_SETHOTKEY i WM_GETHOTKEY odnoszą się do skrótów klawiszowych aktywacji okna.

Uwaga: oto program, który rzekomo ma funkcjonalność, której szukasz. Możesz spróbować go zdekompilować.

David
źródło
3
Link do programu jest teraz całkowicie uszkodzony. Jaki to był program? Tak wiele razy chciałbym dowiedzieć się, który program zarejestrował moje skróty klawiszowe, ponieważ nagle przestają działać lub robią irytujące nowe rzeczy.
James Oltmans
(Często Wayback Machine może pomóc w przypadku zniknięcia strony internetowej, ale tutaj również odzwierciedlał tylko 404 Not Found: http://web.archive.org/web/*/tds.diamondcs.com.au/dse/ detection / hotkeys.php )
Aaron Thoma
2

To wydaje się dużo mówić: http://hkcmdr.anymania.com/help.html

Caveatrob
źródło
1
Czy to? Biblioteka DLL przebrana za sterownik? Funkcja DLL wyeksportowana jako hooużywana SetWindowHookExdo ustawienia dwóch punktów zaczepienia, jednego WH_KEYBOARD_LLi jednego WH_GETMESSAGE... reszta powinna być prawie udokumentowana w witrynie MSDN.
0xC0000022L
Nie używaj tej aplikacji w systemie Windows 8 lub 10.
Jeff Lange
1

Inny wątek wspomina o globalnym zaczepie klawiatury na poziomie NT:

Ponownie przypisz / zastąp klawisz skrótu (Win + L), aby zablokować okna

być może uda ci się uzyskać uchwyt procesu, który w ten sposób nazwał zaczep, który możesz następnie rozwiązać na nazwę procesu

(zastrzeżenie: miałem to w swoich zakładkach, tak naprawdę nie wypróbowałem / nie przetestowałem)

Marco van de Voort
źródło
0

Wiem, że możesz przechwycić strumień wiadomości w dowolnym oknie w ramach własnego procesu - to, co nazywaliśmy podklasą w VB6. (Chociaż nie pamiętam funkcji, być może SetWindowLong?) Nie jestem pewien, czy możesz to zrobić dla okien poza własnym procesem. Ale ze względu na ten post załóżmy, że znajdziesz sposób, aby to zrobić. Następnie możesz po prostu przechwycić wiadomości ze wszystkich okien najwyższego poziomu, monitorować pod kątem wiadomości WM_HOTKEY. Nie byłbyś w stanie poznać wszystkich klawiszy od razu, ale ponieważ zostały wciśnięte, możesz łatwo dowiedzieć się, która aplikacja ich używa. Jeśli wyniki były utrwalane na dysku i ładowano je ponownie za każdym razem, gdy uruchamiano aplikację monitorującą, można z czasem zwiększyć wydajność aplikacji.

Sam Ax
źródło
-1

Nie jest to dokładna odpowiedź na część pytania, która dotyczy interfejsu API systemu Windows, ale odpowiada na część pytania, która dotyczy listy globalnych skrótów klawiszowych i aplikacji, które je „posiadają”.

Darmowy Hotkey Explorer pod adresem http://hkcmdr.anymania.com/ wyświetla listę wszystkich globalnych skrótów klawiszowych i aplikacji, które są ich właścicielami. To właśnie pomogło mi zrozumieć, dlaczego klawisz skrótu specyficzny dla aplikacji przestał działać i jak to naprawić (poprzez rekonfigurację zarejestrowanego globalnego skrótu w aplikacji, w której został zarejestrowany) w ciągu kilku sekund.

Gerhard
źródło
4
zainstalował go i zachowywał się jak wirus, a przynajmniej program z zamiarem dla centrum handlowego, jak kiepski żart. Otwieranie wszelkiego rodzaju okien, włączanie narratora głosowego itp.
Flion
3
@Flion: Zobacz dyskusję na superuser.com/a/191090/3617 . Zasadniczo niektóre narzędzia skrótu działają, sprawdzając każdą możliwą kombinację klawiszy. W systemie Windows 8+ (i w mniejszym stopniu w systemie Windows 7) ta technika sondowania wyzwala każdą kombinację klawiszy, a nie tylko ją przesyła.
Brian
@Flion FWIW, z powodzeniem użyłem go na Win7. Ale jeśli dobrze zrozumiem Briana, może to nie działać tak dobrze w późniejszych wersjach, a to, jak dobrze działa, może również zależeć od niestandardowych skrótów klawiszowych, które są zdefiniowane.
Gerhard